packages feed

haskoin-core 0.18.0 → 1.6.2

raw patch · 114 files changed

Files

CHANGELOG.md view
@@ -1,229 +1,816 @@ # 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.18.0+## [Unreleased]++## [1.6.2] - 2026-11-12+ ### Added++- `judgeInputs` says of each input of a transaction whether its+  signature verifies, is forged, or cannot be told without running a+  script (`InputVerdict`). `verifyStdTx` answers no both for a forged+  signature and for a script it does not read; to refuse a transaction+  before sending it, the two have to be told apart. "Forged" is said only+  of a coin locked to one key, spent with the items such a coin takes and+  a hash type of all.+- `verifyCashSchnorr` verifies a Schnorr signature of Bitcoin Cash, which+  is not the one of BIP 340 and has no function in libsecp256k1. It is+  held to the test vectors of Bitcoin Cash Node.+- `splitTokenPrefix` takes the token prefix off the script of an output of+  Bitcoin Cash, and `txSigHashCashToken` is the digest of a coin that holds+  tokens. `judgeInputs` uses both, and reads Schnorr signatures, so it+  verifies inputs on Bitcoin Cash that `verifyStdInput` does not. The+  tests hold it to two transactions signed by Bitcoin Cash Node 29.2.0,+  one of them spending tokens.+- `judgeInputs` verifies a multisig of Bitcoin Cash signed with Schnorr+  signatures, where a field of bits in place of the empty first item says+  which keys signed, as `OP_CHECKMULTISIG` of Bitcoin Cash Node reads it.+- `verifyStdInput`, `verifyStdTx` and `judgeInputs` verify the multisig+  of Taproot: a script path whose leaf is `multi_a` of the output+  descriptors, with `OP_CHECKSIGADD`.++### Changed++- `stack.yaml` builds against secp256k1-haskell 2.0.1. The README's Stack+  example names that version and the current haskoin-core release. The+  package and its bounds are unchanged.+- The README shows a badge with the status of CI on `master`.+- The Nix CI job deletes Stack's cached Setup programs before building. One+  restored from an older nixos-unstable cache was linked against Nix store+  paths missing on the runner, and the build failed with "posix_spawnp: does+  not exist".++## [1.6.1] - 2026-11-06++### Changed++- `encodeBase58`, `encodeBase58Check`, `cashAddrEncode`, `cash32encode` and `cash32encodeType`, and with them `addrToText` and the JSON form of addresses, are faster. Base58 divides its big integer by 58^10 instead of once per digit and builds its bytes directly instead of a `String`. CashAddr looks its characters up in a `ByteString` instead of with `(!!)` on a `String`, converts to five-bit groups without lists, and unrolls the generators of its checksum. A P2PKH address takes 1.1 µs instead of 5.6 µs in CashAddr and 4.3 µs instead of 5.9 µs in Base58. The output is the same: 120,061 encodings of random payloads are byte for byte the same before and after.++## [1.6.0] - 2026-11-06++### Added++- `Haskoin.Crypto.Keys.Slip39`: Shamir's secret-sharing for mnemonic codes, SLIP-0039, as a standalone module. `generateMnemonics` splits a master secret, encrypted with a passphrase, into share mnemonics under a two-level scheme of groups and members, and `combineMnemonics` rebuilds it from a threshold of them. `generateShares` and `generateMnemonics` draw their random bytes from the operating system, through `getEntropy` of the entropy package; `generateSharesFrom` takes the source of random bytes as an argument, and `generateSharesWith` the identifier and the random values themselves, so that a split can be reproduced. `encodeShare` and `decodeShare` read and write one share, with its checksum, padding and ranges checked as the specification requires; a share has at most 1023 words, the length the checksum covers, which is a value of 1270 bytes. `encryptMasterSecret` and `decryptMasterSecret` are the passphrase step, and both refuse a passphrase outside printable ASCII, as the specification requires; `decryptMasterSecretBytes` takes the passphrase as unchecked bytes, to recover shares of software that allowed other characters. `splitSecret`, `recoverSecret` and `interpolate` are the sharing over GF(256), and `rs1024Checksum` the checksum. No error text and no `Show` instance holds a word or a value of a share: an unknown word is named by its position. The 45 vectors of the reference implementation, a share set of Sparrow's library, and split cases with fixed random values made by an independent Python implementation of the specification, `test/Haskoin/Crypto/Keys/slip39_verify.py`, check it. The word list is `data/slip39_wordlist.txt`, the file of the specification, and its SHA-256 is checked.+- `tryDerivePath` and `tryDerivePubPath` in `Haskoin.Crypto.Keys.Extended` derive along a path and return an error value when the path cannot be followed. Use them for a path that comes from outside, such as the path of a PSBT record. `derivePath` and `derivePubPath` throw `DerivationException` in that case, as before.+- `maxRedeemScriptSize` (520) and `maxWitnessScriptSize` (10,000) in `Haskoin.Script.Standard` are the largest scripts that a pay-to-script-hash and a pay-to-witness-script-hash output can commit to and still be spent: `MAX_SCRIPT_ELEMENT_SIZE` and `MAX_SCRIPT_SIZE` of Bitcoin Core. `p2shScriptHash` and `p2wshScriptHash` give the hash of a script for those outputs, and fail above the limit.+- `p2shP2wpkhAddr` in `Haskoin.Address` is the P2SH-P2WPKH address of a public key hash.+- `SigHashFlag` has a `Bounded` instance.+- `decodeTx` in `Haskoin.Transaction.Common` reads a transaction from bytes that hold the transaction and nothing else, as `DecodeHexTx` requires in Bitcoin Core. `deserialize` reads a transaction from the front of a longer input, as a block needs it to, and does not notice bytes that follow. Use `decodeTx` for a transaction that arrives on its own.+- `dustThreshold` in `Haskoin.Transaction.Builder` gives the amount below which nodes do not relay an output of a kind, as `GetDustThreshold` computes it in Bitcoin Core at the default dust relay fee: 546 satoshi for pay-to-public-key-hash, 294 for pay-to-witness-public-key-hash, 330 for pay-to-witness-script-hash and Taproot. `chooseCoins` can return a change below it, and its documentation now says to leave such a change to the fee.+- `maxStandardTxWeight` (400,000) in `Haskoin.Transaction.Builder` is the weight above which nodes do not relay a transaction: `MAX_STANDARD_TX_WEIGHT` of Bitcoin Core, and the 100,000 bytes of `MAX_STANDARD_TX_SIZE` of Bitcoin Cash Node.+- `data/core_signed_txs.json` holds 97 transactions that Bitcoin Core 32.1 signed on regtest, one for each of 16 kinds of coin and each hash type. The tests verify them with `verifyStdTx`, so that the verifier is checked against signatures the library did not make.+- `arbitraryKeyPairC` generates a key pair in compressed form.+- `maxPayloadSize` in `Haskoin.Network.Message` gives the largest payload a message of a command may have on a network, and `getMessage` applies it. On Bitcoin Cash every message but `block`, `cmpctblock` and `blocktxn` is limited to 2 MiB, the new `maxProtocolMessageSize` of `Network`, as `CMessageHeader::IsOversized` does in Bitcoin Cash Node. The limit was 64 MB for every message, and one 33.5 MB `getblocktxn` took 19 seconds and 5.7 GiB to parse. A node should use `maxPayloadSize` on the message header before it buffers a payload.+- `getCompactBlock`, `getGetBlockTxn` and `getBlockTxn` in `Haskoin.Network.CompactBlock` read the compact block messages of a network. On Bitcoin a transaction position above 65535, the new `maxCompactBlockIndex` of `Network`, fails the parse, and so does a compact block of more than 65535 transactions, as in BIP 152 and Bitcoin Core. Bitcoin Cash keeps 32 bits, as Bitcoin Cash Node. `getMessage` uses them.+- `getNetworkTx` in `Haskoin.Network.Data` reads a transaction as a peer of the network sends it: without the SegWit format on a network that has no SegWit. `getMessage` uses it for `tx`, `block`, `blocktxn` and `cmpctblock`.+- `minimumChainWork` in `Network` is the `nMinimumChainWork` of Bitcoin Core 32.1 and of Bitcoin Cash Node. Once the best block has that work, `connectBlock` and `connectBlocks` refuse a new header whose chain has less, which bounds cheap forks from far below the tip. A node that is still syncing below that work is not protected by this.+- `getVarIntListMax`, and the limits `maxInvSize` (50000), `maxAddrSize` (1000), `maxHeadersSize` (2000), `maxLocatorSize` (101) and `maxFilterHashes` (2000) in `Haskoin.Network.Common`. The parsers of `inv`, `getdata`, `addr`, `addrv2`, `headers`, `getheaders`, `getblocks` and `cfheaders` fail above them, as soon as the count is read.+- `extractIndexedMatches` and `merkleBlockIndexedTxs` in `Haskoin.Block.Merkle` give the position of each matching transaction of a partial Merkle tree. `maxMerkleFlagBits` is the number of flag bits a valid proof can use.+- `leafHashBytes` in `Haskoin.Transaction.Taproot` is the tapleaf hash of a script given as bytes, for a script that is not a list of opcodes. `checkTapTree` tells whether every leaf of a script tree can be spent: it fails for a node deeper than 128, which no control block can prove, and for a leaf version that is odd or is `0x50`. Run it on a tree before you give out its address. `scriptPathScriptBytes` gives the script bytes of a script path spend as they are in the witness. `taprootSignature` builds a `TaprootSignature` and fails for a hash type that Taproot does not define.+- `data/taproot_mainnet_spends.json` holds 24 Taproot script path spends from Bitcoin mainnet blocks 840000 and 900000, with scripts of 34 to 635 bytes. The tests check that each one is read and that its script and control block commit to the output key of the coin it spends.+- `requirePreviousTx` on a PSBT `Signer`. With it, the signer refuses a SegWit version 0 coin that is known from its witness output only, since the amount of such a coin is a claim of whoever wrote the PSBT (CVE-2020-14199). `keySigner` and `xPrvSigner` set it.+- `inputCoins` and `psbtFee`: the coin of each PSBT input with whether it comes from the previous transaction, and the fee when every coin does.+- `psbtPartialTransaction`: the transaction of a PSBT that is still being signed.+- `data/psbt_complex_core32.json`: nine coins of every kind, signed by Bitcoin Core 32.1 without finalizing.++### Changed++- `unmarshal` and `unmarshalLazy` fail when bytes are left over after the value. They used to read a value from the start of the input and ignore the rest, so two different byte strings could read as the same public key, signature or extended key. Code that reads a value from the start of a longer stream must run `marshalGet` itself.+- The binary form of `XPubKey` holds a compressed public key in exactly 33 bytes, as BIP 32 defines it and as `xPubImport` already required. A key in the 65-byte form used to be read.+- `decodeStrictSig`, and with it the binary and JSON forms of `Sig`, apply the encoding rule of BIP 66 and nothing more. A signature with a high S value is now read. Low S is a relay rule of Bitcoin Core (`SCRIPT_VERIFY_LOW_S`), not a consensus rule, and blocks hold signatures with a high S. `isCanonicalHalfOrder` tests the relay rule. The form of a transaction signature, `TxSignature`, still applies it. The signers make low-S signatures, as before.+- `makeXPrvKey` throws `DerivationException` for a seed that is not 16 to 64 bytes long, the range that BIP 32 gives and that Bitcoin Core enforces. A seed of any length used to give a master key, the empty string included, and anyone can compute the keys of such a wallet. A wallet that was made from a seed of another length can still be opened without this function: its master key is `XPrvKey 0 "00000000" 0 chain key`, where `key` and `chain` are the two halves of `hmac512 "Bitcoin seed" seed`.+- `applyPath` returns `Left` for a path that leads below depth 255, which no key can have. It used to throw `DerivationException`, although its type promises an error value.+- The path constructors `:|` and `:/` throw `DerivationException` for an index of 2^31 or more. Such a path could be built before, but its text and binary forms did not read back to the same path, and deriving along it threw. The two constructors are now pattern synonyms. Code that builds and matches paths with them compiles as before.+- `fromMiniKey` requires a mini key to start with the letter `S` and to hold only characters of the Base58 alphabet, as the format defines. One arbitrary string of 22 or 30 characters in 256 used to be read as a private key.+- `getInteger` reads only the encoding that `putInteger` writes: a tag byte of 0 or 1, a sign byte of 0x01 or 0xff, and the shortest bytes that hold the number. Several other byte strings used to read as the same number. Data written by `putInteger` reads as before. The reader also takes close to linear time now. A number of 400 KB used to take 54 seconds.+- `getIntMap` refuses keys that are not in ascending order, which is how `putIntMap` writes them. A key given twice used to be read, and its second element silently replaced the first.+- `convertBits` returns `Nothing` for a width that is zero or negative, where it used to loop forever, and for a digit that does not fit the width it is converted from, whose high bits it used to drop.+- `bech32Encode` returns `Nothing` for a five-bit word above 31. It used to raise an array index error.+- The JSON parser of `Sig` no longer copies the text it refuses into its error message.+- `data/key_io_valid.json` and `data/key_io_invalid.json` are the files of Bitcoin Core 32.1, with its Bech32m and testnet4 vectors. The tests run every vector through `textToAddr` and `fromWif`, as `key_io_tests.cpp` does.+- `payToScriptAddress`, `payToWitnessScriptAddress` and `payToNestedScriptAddress` return `Either String Address`, and `toP2SH` and `toP2WSH` return `Either String ScriptOutput`. They fail for a redeem script above 520 bytes and for a witness script above 10,000 bytes. A pay-to-script-hash redeem script is a push of the spending input, and consensus limits a push to 520 bytes, so coins sent to the hash of a longer script can never be spent. `payToScriptAddress` used to give an address for a multisig of 16 to 20 compressed keys (547 to 684 bytes), or of 8 or more uncompressed keys, and Bitcoin Core rejects every spend of such a coin with "Push value size limit exceeded". Fifteen compressed keys fit, and twenty still fit a pay-to-witness-script-hash output. These functions also return the failure of `encodeOutput` for a multisig that no script can express, where `payToScriptAddress` used to raise an `error` call.+- `deriveMSAddr` and `derivePathMSAddr` return `Either String (Address, RedeemScript)`, and `deriveMSAddrs` and `derivePathMSAddrs` return `Either String` of their list. They fail for more than 15 keys, whose address would be unspendable as described above, and for a number of required signatures that is not between 1 and the number of keys, which used to raise an `error` call when the address was forced. Coins that were sent to an address these functions gave for 16 to 20 keys are lost: no spend of them is valid.+- `scriptToAddress`, `scriptToAddressBS` and `outputAddress` take the `Network` as their first argument and give an address only for an output kind that the network has. A witness program has no address on Bitcoin Cash, and a 256-bit script hash (`OP_HASH256 <32 bytes> OP_EQUAL`) has none on Bitcoin, where Bitcoin Core reports that script as non-standard. Without the network these functions returned an address that has no text form on the chain it came from, and the JSON encoder of `Address` throws for such an address. One such output is in Bitcoin block 211997 (transaction `a4bfa8ab6435ae5f25dae9d89e4eb67dfa94283ca751f393c1ddc5a837bbc31b`, output 0), so an indexer that encoded the addresses of outputs failed on that transaction. Every address these functions return now has a text form on the network.+- `decodeInput` only reads a pay-to-script-hash input whose redeem script is in the form `encodeOutput` writes. A multisig redeem script whose keys are pushed with `OP_PUSHDATA1` used to decode, and `inputAddress` then gave the hash of the re-encoded script, an address that never held the coin. Such an input now fails to decode, so `inputAddress` is always the hash of the redeem script bytes of the input, and encoding a decoded input never rewrites its redeem script. Spending such a script breaks the `MINIMALDATA` rule of the relay policy, so standard transactions are not affected.+- `decodeInput` reads a `NestedWitnessInput` only when the witness program is a direct push, as BIP 141 requires ("the scriptSig must be exactly a push of the BIP16 redeemScript"). A program pushed with `OP_PUSHDATA1` used to decode, although such an input is invalid by consensus.+- `inputAddress` gives no address for a redeem script above 520 bytes.+- `txSigHashForkId` writes the hash type as four bytes on a network without a fork id, as `SignatureHash` of Bitcoin Core does for a SegWit input. It used to keep the low byte only, while `txSigHash` wrote all four. Nothing changes for a hash type that fits in one byte, which is every hash type a signature can carry.+- `cashAddrDecode`, and with it `textToAddr` and the JSON parser of `Address` on Bitcoin Cash networks, refuse a string longer than the prefix of the network, the colon and 112 characters before any other work. No valid address is longer, since the largest hash of the format is 64 bytes. A string of 1 MB used to take 1.5 seconds and several hundred megabytes of memory, and now takes less than a millisecond.+- `cash32decode` and `cash32decodeType` refuse a prefix that is not made of letters only, as the decoder of Bitcoin Cash Node does. A digit or a second colon in the prefix used to be read.+- `cash32encode` returns `Maybe Cash32`. It fails for a prefix that is empty or not made of ASCII letters, and it writes an uppercase prefix in lowercase. It used to write strings that `cash32decode` refuses: with an empty prefix, a digit, a colon, or a prefix of another case than the data.+- The `Read` instance of `Address` goes through `witnessAddress` and `cashAddress` for the two generic kinds. The derived instance read a witness version above 16, a program of any length and a CashAddr type above 15, which no other function builds.+- `enumFrom`, `enumFromTo`, `enumFromThen`, `enumFromThenTo`, `succ` and `pred` of `SigHashFlag` walk the five flags in the order of their constructors. `[SIGHASH_ALL ..]` and `succ SIGHASH_SINGLE` used to raise "Not a valid sighash flag", because the values of the flags are not consecutive.+- The generators `arbitraryMulSigSHInput`, `arbitraryMulSigSHInputC`, `arbitraryMulSigSHInputFull` and `arbitraryMulSigSHInputFullC` draw a redeem script of at most 520 bytes. More than half of the inputs they drew could not exist. `arbitraryP2SHMSParam`, `arbitraryP2SHMSParamC`, `arbitraryP2SHMSOutput` and `arbitraryP2SHMSOutputC` are the new generators behind them. `arbitrarySHSigInput` and `arbitraryPartialTxs` use at most 7 keys for the same reason.+- `arbitraryScriptOutput` draws a 256-bit script hash output on networks that have CashAddr, and `arbitraryScriptInput` draws a nested witness input only on networks that have SegWit. `arbitraryBitcoinAddress` draws a Taproot address one time in six, through the new `arbitraryTaprootAddress`, and `arbitraryBitcoinCashAddress` a payable token-aware address one time in five, through the new `arbitraryTokenAddress`.+- `makeSignature`, and with it `signTx`, `signInput`, `signNestedWitnessTx` and `signPsbt`, refuse a hash type that no node accepts, with the reason: one that is not `SIGHASH_ALL`, `SIGHASH_NONE` or `SIGHASH_SINGLE` with or without `SIGHASH_ANYONECANPAY` (the test of `IsDefinedHashtypeSignature` in Bitcoin Core), the fork id flag on a network without a fork id, and a value that does not fit in the one byte a signature carries. On Bitcoin Cash this refuses `SIGHASH_UTXOS` (0x20), whose digest the library does not compute. The types 0x00, 0x04, 0x21, 0x41 and 0x80 used to be signed, and the result was a transaction that nodes do not relay. A type with upper bytes set, such as 0x101, gave a signature that does not verify. `signTx` still signs `SIGHASH_NONE` and `SIGHASH_SINGLE` when the `SigInput` asks for them: check the hash type of a `SigInput` that comes from someone else.+- `makeSignature` refuses a pay-to-script-hash coin without its redeem script, with a redeem script above 520 bytes, or with one that does not hash to the coin, and the same for a pay-to-witness-script-hash coin and its witness script, above 10,000 bytes. It used to sign whatever script it was given, and the output script itself when it was given none. Such a signature spends nothing.+- `makeSignature` refuses a SegWit coin, and `signTx` a Taproot coin, on a network without SegWit. `signTx bch` used to return a transaction with a witness, which no Bitcoin Cash node reads.+- `signTx`, `signNestedWitnessTx` and `sigKeys` refuse to sign a SegWit version 0 coin with an uncompressed key, whether the coin pays to the hash of the key or to a witness script that holds it. Nodes do not relay such a spend (`SCRIPT_VERIFY_WITNESS_PUBKEYTYPE` in Bitcoin Core), so the transaction they returned could not be broadcast. A block may still hold the spend. To make it on purpose, call `signInput` with the `PrivateKey` in its uncompressed form.+- `signInput` and `signNestedInput` refuse a key that is not a key of the coin: the key of a pay-to-public-key coin, the key whose hash the coin pays to, or a key of its multisig. They used to return an input that no node accepts.+- `signTaprootInput` refuses a secret key whose x-only public key is not the output key of the coin, and a spent output that is not a Taproot output. Passing the internal key in place of its tweak used to give a signature that does not verify.+- `verifyStdInput` and `verifyStdTx` require a multisig input to hold exactly as many signatures as its script requires, and no other item. `OP_CHECKMULTISIG` takes that many items as signatures, and the item below them must be empty (BIP 147), so no block can contain an input with an item after its signatures or with an empty item among them. They used to answer `True` for both, in bare, pay-to-script-hash and pay-to-witness-script-hash form. An empty item before the signatures is refused too: nodes do not relay it, and in a witness it is invalid.+- `verifyStdTx` refuses a transaction in which two inputs spend the same outpoint, as `CheckTransaction` does in Bitcoin Core (`bad-txns-inputs-duplicate`).+- `verifyStdInput` and `verifyStdTx` refuse a SegWit or Taproot input on a network without SegWit, and compare the input script of a nested SegWit input with the push of its program byte for byte.+- `verifyStdTx` looks up the coins once for the whole transaction. It used to walk the list of coins for every input, and to rebuild the list of spent outputs for every Taproot input, so its time grew with the cube of the number of Taproot inputs: 400 of them took 1.6 s and now take 0.14 s.+- `signTx` derives the public keys of its secret keys once for the whole transaction, not once per input. 400 Taproot inputs with 400 keys took 11.7 s and now take 0.2 s.+- `guessTxFee` returns `Either String Word64`. It fails when the fee does not fit in 64 bits, where it used to wrap around: 192 bytes at 2^58 satoshi per byte gave a fee of 0. It also fails for a kind of input or output that cannot exist, such as `InputMulSig 16 16`, whose redeem script would be 547 bytes, or a multisig of 3 of 2.+- `chooseCoins` and `chooseCoinsSink` add amounts as unbounded integers. A target near 2^64 used to wrap around with its fee, so that a coin of 1,000 satoshi "covered" a target of 2^64 - 101. They fail for a kind of input or output that cannot exist, and they stop with an error when the transaction would weigh more than `maxStandardTxWeight`. They keep the sums of the selection as coins are added: selecting 1,400 coins took 0.11 s and now takes under a millisecond.+- `greedyAddSink` adds amounts as unbounded integers as well. `chooseCoinsSink` no longer uses it.+- `mergeTxs` and `mergeTxInput` keep the witness of the transaction. They used to remove it, so a SegWit input that was signed before the multisig signers took their copies lost its signature. `mergeTxs` now answers "Transactions do not match" for copies whose witnesses differ, since it does not merge witnesses. `mergeTxInput` returns `Left` for an input index that one of the transactions does not have. It used to raise an error.+- `hasWitness` looks only at the stacks of the inputs. A witness item in a stack beyond the inputs used to turn the witness format on, which gave bytes with the marker and no witness item, a wrong weight and a wrong witness transaction id. Neither Bitcoin Core nor this library reads those bytes.+- The JSON form of `Tx` requires one witness stack per input, or none.+- `buildAddrTx` refuses an amount above the `maxSatoshi` of the network, as `createrawtransaction` refuses an amount out of range in Bitcoin Core.+- The generators of `Haskoin.Util.Arbitrary.Transaction` draw compressed keys for SegWit signing data, a pay-to-script-hash multisig `InputKind` of at most 15 keys, SegWit and Taproot signing data only on networks that have SegWit, and amounts of which a third are below 10,000 satoshi.+- `fromMnemonic` and `mnemonicToSeed` no longer put words of the sentence or checksum values into their error text. An unknown word is named by its position, as in `fromMnemonic: words 3 and 7 are not in the list`, and a failed checksum is `fromMnemonic: checksum failed`. A sentence typed in capitals used to be echoed whole, and the text of a `Left` ends up in logs.+- `fromMnemonic` and `mnemonicToSeed` do a bounded amount of work on text from an untrusted source. The sentence is split into words before anything is normalised, and a word longer than the eight letters of the longest list word is refused as it is. `mnemonicToSeed` refuses a passphrase whose NFKD form would hold more than 30 combining characters in a row, the limit of the Stream-Safe Text Format of Unicode (UAX #15), which no real text passes. 32 KB of combining characters used to take half a minute, and the time grew with the square of the length. Every sentence and passphrase that was accepted before, other than such a passphrase, gives the same seed. This adds a direct dependency on `unicode-data`, which `unicode-transforms` already used.+- The parser of `MerkleBlock` refuses more hashes than transactions and more flag bytes than the tree can use, before it reads them. A `merkleblock` message used to cost about 95 bytes of memory per flag byte, 1.5 GB for a 16 MB message. Proofs that verify are not affected.+- An empty witness item, and any empty byte string of the wire format, costs one list cell. A `tx` message with four million empty witness items kept 356 MB and now keeps 100 MB, as much as Bitcoin Core.+- `bloomRelevantUpdate` matches a transaction as BIP 37 and `IsRelevantAndUpdate` of Bitcoin Core do: by its hash, by the data of its output scripts, by the outpoints it spends and by the data of its input scripts. It only looked at outputs, so a transaction that spends a watched coin was not matched. Pay-to-pubkey and multisig outputs are told by the shape of the script, as `Solver` does, so a hybrid key or a key that is not on the curve counts. The `Ctx` argument is no longer used.+- The `version`, `timestamp` and `startHeight` fields of `Version` are `Int32`, `Int64` and `Int32`, signed as in Bitcoin Core. A peer that sent version ffffffff appeared to have version 4294967295.+- `validVersion` applies its three conditions independently, as `ContextualCheckBlockHeader` does. It accepted old block versions on networks whose activation heights are not in the order BIP 34, BIP 66, BIP 65, which are Bitcoin Cash regtest and testnet4.+- `chooseBest` keeps the first block when two have the same work, whatever their heights, as Bitcoin Core does. It preferred the higher one.+- `connectBlock` and `connectBlocks` return the stored node of a header that is already in the store, as `AcceptBlockHeader` does in Bitcoin Core. A known header at or below a checkpoint used to be refused as "Rewriting pre-checkpoint chain".+- The memory header store returns a node only for the hash that was asked for. It returned any node whose hash shared the first eight bytes.+- `calcHash` returns `Maybe Hash256` and no longer calls `error` outside the tree. `splitIn` with a length below one returns the list as one chunk and no longer loops. `Hashable BlockNode` hashes the header only, like `Eq`.+- An `addrv2` entry of the Tor v2 network is read whatever its length, since BIP 155 says to ignore such entries, and no longer fails the message. `fromNetworkAddressV2` gives `Nothing` for an IPv6 entry that holds an IPv4-mapped or an OnionCat address. `toNetworkAddressV2` gives a Tor v2 entry for an OnionCat address.+- `getMessage` checks the magic bytes before the command name.+- `bloomCreate` picks the number of hash functions with the integer division of Bitcoin Core.+- `Read MessageCommand` reads what `Show` writes for any bytes.+- `btcRegTest` has a target timespan of one day, as Bitcoin Core regtest, and `btcTest4` pins no BIP 34 block hash, as Bitcoin Core testnet4.+- The reader of `BlockNode` refuses a chain work that is negative or does not fit 256 bits.+- The generators `arbitraryMerkleBlock`, `arbitraryBlock`, `arbitraryBlockNode`, `arbitraryVarInt`, `arbitraryReject`, `arbitrarySendCompact`, `arbitraryCompactBlock` and `arbitraryBlockTxn` cover more cases, and `arbitraryNetworkTx`, `arbitraryNetworkGetBlockTxn` and `arbitraryPositionsUpTo` are new.+- A `TaprootSignature` only holds a hash type that `isTaprootSigHash` accepts. Its constructor is now a pattern that only matches. Build a value with `taprootSignature` or `decodeTaprootSig`; the fields `sig` and `hash` are read as before and cannot be updated. `encodeTaprootSig` used to cut any hash type to one byte, so `0x100` was written as a 65-byte signature ending in `00`, which BIP 341 forbids, and `0x101` as `SIGHASH_ALL`.+- `ScriptPathData` has a new last field, `opaqueScript`: the bytes of the script when they are not a list of opcodes, in which case `script` is empty. `viewTaprootWitness` now reads a script path spend whatever its script bytes are. It used to return `Nothing` for a Tapscript with an `OP_SUCCESSx` opcode followed by bytes that do not decode, and for a leaf of another version, although such spends can be valid. `verifyScriptPathData` takes the leaf hash on the script bytes of the witness, and is `False` for a proof of more than 128 hashes.+- `encodeTaprootWitness` returns `Either String WitnessStack`. It fails for an annex that is empty or does not start with `0x50`, for a proof of more than 128 hashes, and for a leaf version that is odd or is `0x50`. It used to write a witness that reads back as another spend, or that every node refuses.+- `taprootSigHash` fails for an annex that is empty or does not start with `0x50`. No node takes such an item for an annex, so no node computes that digest.+- `data/bip341.json` is the file of the BIPs repository, whose key is `fullySignedTx`.+- `Signer` has a fourth field, `requirePreviousTx`.+- `keySigner` and `xPrvSigner` refuse a SegWit version 0 coin without its previous transaction. Attach the previous transaction, or set `requirePreviousTx = False` to trust the PSBT writer.+- `psbtTransaction` refuses a PSBT with an input that is not final. Use `psbtPartialTransaction` for the old behaviour.+- `decodePsbt` refuses what Bitcoin Core refuses: a key type that is not a compact size in its shortest form, a public key that is not on the curve, a partial signature that is not in the encoding of BIP 66 with a defined sighash byte, a previous transaction that the input does not spend, and a script tree that is not whole.+- `signPsbt` and `finalizePsbt` leave alone an input whose redeem or witness script is not a standard template, instead of failing the whole PSBT.+- `signPsbt` signs only for keys that the signed script names. It adds no signature to a script-hash coin whose script is missing, and signs a Taproot leaf only when its control block commits it to the coin.+- `signPsbt` and `finalizePsbt` refuse an uncompressed key on a SegWit version 0 coin.+- `finalizePsbt` verifies only the signatures of keys the script names, against one digest per input.+- `finalizePsbt` refuses a signature that is not `SIGHASH_ALL` (`SIGHASH_DEFAULT` for Taproot) when the input names no sighash type, and leaves alone a legacy coin known from its witness output only.+- `addInput` and `addOutput` check the map they are given.+- A PSBT error names at most 40 bytes of a key.++### Fixed++- The documentation of the hardened markers, of the depth limit and of `decodeSecKey` no longer describes old behaviour of Bitcoin Core and of the secp256k1 binding. The documentation of `PrivateKey` and `XPrvKey` says that the `Show` instance of the `SecKey` inside them prints the secret.+- The documentation of `TxSignature`, `decodeTxSig` and `decodeInput` lists the rules they apply: strict DER, a low S and a defined hash type, the three signature rules of the relay policy of Bitcoin Core. A signature that only consensus accepts does not decode, so an input of an old block can fail to decode. The documentation also says that a Schnorr signature of Bitcoin Cash does not decode.+- The documentation of `decodeOutput` says that a multisig whose keys are not direct pushes, and a data carrier whose pushes are not the shortest, come back from `encodeOutput` as other bytes, and lists the scripts that the `Solver` of Bitcoin Core classifies and this function does not: a key in hybrid form or off the curve, and `OP_RETURN OP_RESERVED`.+- The documentation of `txSigHash` names `SerializeScriptCode` of Bitcoin Core for the removal of `OP_CODESEPARATOR`, and says that the pushes of the signature are not removed from the script, which `FindAndDelete` does in Bitcoin Core. The documentation of `txSigHashForkId` lists what is not supported on Bitcoin Cash: `SIGHASH_UTXOS`, coins that hold tokens, and Schnorr signatures.+- The documentation of `Address` says that a token-aware CashAddr and its plain form are different values that pay the same script, and that a value built with a constructor or read from the binary form is not checked against a network. The documentation of `textToAddr` says that white space around an address is refused and that some CashAddr kinds decode without being payable. The documentation of `addressToOutput` says that it does not know the network.+- The documentation of `maxMultisigKeys` says that 20 keys fit a bare and a pay-to-witness-script-hash multisig only.+- The documentation of `verifyStdTx` and `verifyStdInput` says what they check and what they do not: they are not a script interpreter, they never answer `True` for an input of the kinds they know that no block can contain, and they answer `False` for some inputs that a block may hold but nodes do not relay.+- The documentation of `SigInput` says that the value of a legacy coin is not committed to by its signature, so that a signer cannot check it without the previous transaction.+- The documentation of `guessTxSize` and `txVirtualSize` says that they do not count signature operations, which Bitcoin Core charges for in a transaction that is dense in them.+- The documentation of `InputKind` gives the ranges of a multisig kind, and that of `buildTx` and `buildAddrTx` says what they do not check.+- `leafHash`, and with it `mastCommitment`, `taprootOutputKey`, `taprootScriptOutput`, `getMerkleProofs` and `verifyScriptPathData`, write the script length of a Taproot leaf as a CompactSize, as BIP 341 defines the tapleaf hash and as `ComputeTapleafHash` does in Bitcoin Core. They wrote a base-128 varint, which is the same byte below 128 only. A leaf script of 128 bytes or more, such as a `multi_a` of four keys, therefore got a leaf hash, an output key and an address that no other software derives, and that leaf could not be spent. **The address of every script tree with such a leaf changes.** Trees whose leaf scripts are all shorter than 128 bytes, and outputs without a tree, are not affected. Coins at an address that version 1.5.1 or earlier made from such a tree can still move by the key path, or by the script path of a leaf shorter than 128 bytes. To rebuild the old output, put the old hash of each long leaf in the tree as a `MASTCommitment`. The old hash is the tagged hash `TapLeaf` of the leaf version, the script length as `Data.Bytes.VarInt.VarInt` of the bytes package, and the script bytes. `taprootOutputKey` and `taprootOutputSecKey` then give the old output key and the secret key of its key path, and `getMerkleProofs` gives the proofs of the short leaves. The long leaves of such an output can never be spent. The test "An output made by versions up to 1.5.1 with a leaf of 128 bytes or more" in `TaprootSpec.hs` has the code.+- `getMerkleProofs` lists the hashes of a proof from the leaf up to the root for a leaf at any depth, as BIP 341 and Bitcoin Core do. From depth 3 the order was wrong, and every node refused the control block built from it. It also hashes each subtree once instead of once per level.+- The JSON form of `XOnlyPubKey` reads the hex of 32 bytes only. It used to read the first 32 bytes of a longer value, so a 33-byte compressed key was taken for another key.+- `tapTreeLeaves` returns `Nothing` for a tree with a leaf deeper than 128, and no longer wraps the depth at 256.+- `xPrvSigner` no longer throws for a derivation path of more than 255 steps. The input stays unsigned.+- `signPsbt` signs a P2SH-P2WPKH coin by the hash of its key, without a derivation record.+- `signPsbt` and `finalizePsbt` use `SIGHASH_ALL` with the fork id flag on Bitcoin Cash when an input names no sighash type.+- `signPsbt` signs a Taproot key path when the record of the internal key lists a leaf.+- The PSBT scripts under `scripts/` run again, against Bitcoin Core 32.1.++## [1.5.1] - 2026-11-04++### Fixed++- `nextAsertWorkRequired` evaluates the aserti3-2d formula at the parent of the new block, with the parent's timestamp and height, as `GetNextASERTWorkRequired` does in Bitcoin Cash Node. It used the new block's own timestamp and height, so the bits it required were wrong for every block that did not come exactly ten minutes after its parent. The bits check that 1.5.0 added to `connectBlocks` turned this into a rejected block on Bitcoin Cash mainnet at height 975899.++## [1.5.0] - 2026-11-03++### Added++- Taproot signing and verification, BIP 341 and BIP 342: `taprootSigHash` computes the digest a Taproot signature signs, for the key path and for a leaf of a script path; `taprootOutputSecKey` tweaks a secret key as `taprootOutputKey` tweaks its public key; `signTx` signs a `SigInput` of a Taproot output by its key path, with a key whose x-only public key is the output key or whose tweak without a script tree is, as a BIP 86 wallet spends, and `signTaprootInput` signs one input; `verifyStdTx` verifies key path spends, and script path spends whose leaf is a single key check. `TaprootSignature`, `encodeTaprootSig` and `decodeTaprootSig` are a Schnorr signature with its hash type, `isTaprootSigHash` tells the hash types a Taproot signature may use, and `sigHashDefault` is the default one. The key path vectors of BIP 341 and the BIP 86 vectors check them.+- The Taproot fields of PSBTs, BIP 371, in `Haskoin.Transaction.Psbt`: `tapKeySig`, `tapScriptSigs`, `tapLeafScripts`, `inputTapDerivations`, `inputTapInternalKey`, `inputTapMerkleRoot`, `outputTapInternalKey`, `outputTapTree` and `outputTapDerivations`, with their inserts. They are checked against the spec when a PSBT is read. `signPsbt` signs a Taproot coin by its key path when the input has its internal key, and by each leaf script that is a single key check; `finalizePsbt` finalizes both. The test vectors of BIP 371 check them.+- `tapTreeLeaves` and `tapTreeFromLeaves` in `Haskoin.Transaction.Taproot` convert a script tree to and from its leaves with their depth, the form of `PSBT_OUT_TAP_TREE`. `encodeControlBlock` and `decodeControlBlock` build and read a control block, which must be 33 bytes plus 32 per node with at most `maxControlBlockNodes` (128) nodes. `leafHash`, `keyParity` and `tapscriptLeafVersion` are exported.+- `txWitnessHash` in `Haskoin.Transaction.Common` is the witness transaction id of BIP 141. `txWeight` and `txVirtualSize` give the weight and the virtual size of a transaction, and `hasWitness` tells whether it has a witness.+- `InputKind` and `OutputKind` in `Haskoin.Transaction.Builder` name the kinds of inputs and outputs: pay-to-public-key, pay-to-public-key-hash with a compressed or an uncompressed key, pay-to-script-hash multisig, the native and nested SegWit kinds, Taproot, pay-to-script-hash of a 256-bit hash, and any other output script by its size. `guessTxWeight`, `guessTxSize` and `guessTxFee` estimate the weight, the virtual size and the fee of a transaction of those kinds, with `inputWeight` and `outputSize` per kind. The guess is an upper bound: it counts 72 bytes per signature, the one-byte empty witness stack of a legacy input in a transaction with a witness, and two bytes for each count of a multisig script above 16.+- `Signer` in `Haskoin.Transaction.Psbt` is a record: `keyOfRecord` gives the key a derivation record names, as before, and `keyOfHash` the key of a public key hash, so that `signPsbt` signs the coins whose keys it holds when the input has no derivation record. `keySigner` provides both, `xPrvSigner` the first.+- Tests for every function of the `Haskoin.Transaction` and `Haskoin.Util` modules, with the key path vectors of BIP 341, the vectors of BIP 86 and BIP 371, and literal sizes, ids and fee estimates.+- `connectBlock` and `connectBlocks` now check that a header carries the difficulty bits the chain requires, as `ContextualCheckBlockHeader` does in Bitcoin Core. `validBlock` takes the required bits as a new argument. Without this check a chain of headers mined at the minimum difficulty was accepted.+- `asertAnchor` finds the anchor block of the aserti3-2d algorithm for the chain that ends at a given block. `mtp` is now the median time past of the block itself, as in Bitcoin Core, instead of its parent's.+- `getVarIntList` and `putVarIntList` in `Haskoin.Network.Common` read and write lists with their `VarInt` count. A count above `maxListSize` (32 MiB, the `MAX_SIZE` of Bitcoin Core) fails the parse instead of wrapping around and reading an empty list. Blocks, Merkle blocks, `getblocks`, `getheaders`, `headers`, `inv`, `getdata`, `notfound` and `addr` messages use them.+- `genesisTx` in `Network` holds the coinbase transaction of the genesis block. `Haskoin.Transaction.Genesis` exports `mainnetGenesisTx`, `testnet4GenesisTx` and `genesisCoinbase`.+- `skipHeight` and `medianTime` are exported from `Haskoin.Block.Headers`.+- Tests for every function of the `Haskoin.Block` modules, with vectors from Bitcoin Core and from mainnet block 100000.++- PSBT version 2 (BIP 370) in `Haskoin.Transaction.Psbt`: a PSBT of version 2 is read and written as its maps, with the unsigned transaction its fields describe and the lock time they determine. The roles take both versions, and version 2 has its constructor (`addInput`, `addOutput`), its updater (`setInputSequence`), the flags of `PSBT_GLOBAL_TX_MODIFIABLE`, which the signer and the combiner keep as the spec and Bitcoin Core do, and conversion between the versions (`withVersion`). The test vectors of BIP 370 and files of Bitcoin Core 32.1 check it.+- `addrToBase58`, `addrToBech32` and `addrToCash` encode an address in one chosen format. `addrToBase58` gives the legacy form of a Bitcoin Cash address.+- Token-aware CashAddr addresses (types 2 and 3) can be paid. `addressToOutput`, `addressHash160` and `addressHash256` treat them like their plain P2PKH, P2SH and P2SH32 counterparts.+- `decodeSecKey` in `Haskoin.Crypto.Keys.Common` builds a secret key from 32 bytes and refuses zero and values at or above the curve order. `fromWif`, `fromMiniKey`, `xPrvImport`, `makeXPrvKey` and the binary form of `PrivateKey` use it. The `secKey` of secp256k1-haskell 1.7.0 only checks the length, so such keys used to import and then crash `derivePubKey` and `signMsg` with an error call.+- Derivation paths accept `h` and `H` as hardened markers, so `m/84h/0h/0h` parses. Bitcoin Core and BIP 380 write `'` or `h`, and BIP 32 writes a subscript H.+- Tests for BIP32 test vectors 4 and 5, for a BIP39 passphrase with accented letters, and for every function of the `Haskoin.Crypto` modules.+- The P2P messages added since 2016: `addrv2` and `sendaddrv2` (BIP 155) with `AddrV2`, `NetworkAddressV2` and conversions from and to `addr` entries; `feefilter` (BIP 133); `sendcmpct`, `cmpctblock`, `getblocktxn` and `blocktxn` (BIP 152) in `Haskoin.Network.CompactBlock`; `getcfilters`, `cfilter`, `getcfheaders`, `cfheaders`, `getcfcheckpt` and `cfcheckpt` (BIP 157) in `Haskoin.Network.BlockFilter`; `wtxidrelay` (BIP 339) and `sendtxrcncl` (BIP 330). The inventory types `InvCompactBlock` and `InvWTx`, and the service bits `nodeCompactFilters`, `nodeNetworkLimited` and `nodeP2PV2`.+- `maxMessageSize` in `Network` bounds the payload `getMessage` reads: 4,000,000 bytes on Bitcoin, as in Bitcoin Core, and twice the block size on Bitcoin Cash.+- `scriptPushData` lists the data elements a script pushes, as `GetOp` does in Bitcoin Core.+- `getVarIntBytes` and `putVarIntBytes` read and write bytes with their count, under the same limit as lists.+- Tests with fixed wire bytes for every message type, the sample `version`, `verack` and `addr` messages of the protocol documentation, and every function of the `Haskoin.Network` modules.+- `pushOpData` gives the data that a push opcode puts on the stack, and `isMinimalPush` tells whether a push is in its shortest encoding, the rule of `CheckMinimalPush` in Bitcoin Core.+- `OP_CHECKSIGADD` is a pattern synonym for opcode 0xba, which `OP_CHECKDATASIG` also names, so a Tapscript can be written with the name of BIP 342.+- `NestedWitnessInput` is the input script of a P2SH-P2WPKH or P2SH-P2WSH output: a push of the witness output script. `decodeInput`, `encodeInput`, `inputAddress` and the signer use it, and `isNestedWitnessInput` tests for it. Such an input used to fail to decode.+- `maxMultisigKeys` is 20, the `MAX_PUBKEYS_PER_MULTISIG` of Bitcoin Core. `decodeOutput` reads a multisig output with 17 to 20 keys, whose counts are one-byte pushes, and `encodeOutput` writes one. Only 16 keys used to be supported.+- `decodePubKey` in `Haskoin.Crypto.Keys.Common` reads a public key from bytes that must hold the key and nothing else.+- Tests for every function of the `Haskoin.Script` modules, with literal vectors from BIP 67, BIP 143, BIP 173 and BIP 341.++### Changed++- `makeSignature`, and with it `signTx`, `signInput` and `signPsbt`, refuse to sign a legacy input with `SIGHASH_SINGLE` when the input has no output at its index. The digest of that case is the constant 1, which commits to nothing, so the signature would spend any other coin of the key wherever the same case comes up. `txSigHash` still computes that digest, as a node must to verify such signatures, and `verifyStdInput` still accepts them. A SegWit input is signed in that case, since the BIP 143 digest commits to the coin.+- `Signer` has a new field, `acceptedSigHashes`: the sighash types it signs with. `keySigner` and `xPrvSigner` set it to `defaultSigHashes`, which are `SIGHASH_DEFAULT` and `SIGHASH_ALL`. `signPsbt` refuses an input that asks for another type, as BIP 174 asks of a signer: the other types leave part of the transaction unsigned, and the PSBT comes from someone else. A PSBT asking for `SIGHASH_NONE` or `SIGHASH_SINGLE` used to be signed as asked. The fork id flag is not compared, since the network decides it.+- `PrivateKey` and `XPrvKey` show their secret as `<redacted>`, and no longer have a `Read` instance; nor does `XKey`. The derived instances printed the key, and for `XPrvKey` the chain code, so a value in a log or an error message gave the coins away. `toWif` and `xPrvExport` write a key out.+- `prvSubKey`, `pubSubKey`, `hardSubKey` and `cycleIndex` throw `DerivationException` for a child index with the hard bit set, like every other failure of a derivation. They used to call `error`.+- A transaction with no inputs whose next byte, the flags of BIP 144, is neither 0x00 nor 0x01 fails to parse with "Unknown transaction optional data", as in Bitcoin Core. Such bytes used to parse as a legacy transaction with no inputs that did not serialize back to the same bytes. A `Tx` string literal must hold the transaction and nothing else, as `DecodeHexTx` requires in Bitcoin Core.+- The documentation of `decodeBase58` and `decodeBase58Check` says that their cost grows with the square of the length and that callers must bound untrusted input. The decoders of the library bound it already.+- `taprootOutputKey` and `taprootScriptOutput` tweak the x-only internal key, as BIP 341 defines the output key. An internal key with an odd y coordinate used to be tweaked as a full point, which gave an output key and an address no other wallet derives from that key, and that a BIP 341 signer could not spend.+- `viewTaprootWitness` reads a witness in the order of BIP 341: the annex last, then the control block, then the script, then the stack. It used to read the control block and the script in the other order, and so decoded no real script path witness. `encodeTaprootWitness` writes the annex only when there is one; it used to append an empty item, which made the witness invalid. The control block of a `ScriptPathData` is a list of digests, and a control block of a wrong length is refused. `KeyPathSpend` carries the annex of a key path spend.+- `signPsbt` applies the checks of the BIP 174 signer. A coin known from `PSBT_IN_WITNESS_UTXO` alone is signed only with a witness signature, since the legacy digest does not commit to the amount; the previous transaction is used first, and a witness output that is not its output is refused. A sighash type no node accepts, or whose fork id flag does not fit the network, is refused instead of signed. A redeem script that does not hash to the coin, or a witness script that does not hash to the witness program, is refused. An input whose coin script is not standard is left alone instead of failing the PSBT.+- `finalizePsbt` takes the `Network` and verifies every signature it uses, against the digest of the coin and the sighash type of the input when it has one, as `PSBTInputSignedAndVerified` does in Bitcoin Core. A signature that does not verify, is not a signature, or uses another sighash type is refused. It used to finalize any bytes stored as a signature.+- `mergeTxs` leaves an input that no transaction signed as it is. It used to crash with `Prelude.head: empty list`.+- `chooseCoins` takes the kinds of the outputs and a fee rate per virtual byte, and estimates the fee from the `coinInputKind` of each coin, a new method of `Coin`. `chooseMSCoins`, `chooseMSCoinsSink`, `guessMSTxFee` and `guessMSSize` are gone, since the kind of input lives on the coin. The old estimate counted every input as pay-to-public-key-hash and charged bytes rather than virtual bytes, so a SegWit wallet overpaid fees by about 60 %.+- `hexToTxHash`, and the `Read`, string and JSON forms of `TxHash` and `OutPoint`, refuse hex that is not exactly 32 bytes. Longer hex used to be accepted with its leading bytes dropped.+- Transaction parsing bounds the counts of inputs, outputs, witness stacks and witness items by `maxListSize`, as `ReadCompactSize` does in Bitcoin Core; a count of 2^64 - 1 used to wrap around and read as zero. A transaction with the witness marker but no witness item is refused ("Superfluous witness record"), as Bitcoin Core refuses it. A transaction whose witness stacks are all empty is serialized without the marker, and `hasWitness` is false for it.+- `verifyStdTx` and `verifyStdInput` take the coins of every input, matched by outpoint, so that Taproot inputs can be verified. A witness on an input whose coin is not a witness program is refused, as `VerifyScript` refuses it in Bitcoin Core, and `verifyStdTx` refuses outputs that spend more than the coins hold.+- `nosigTxHash` removes the witness as well as the input scripts, so that it is the same before and after signing a SegWit input.+- `getBits` raises an error for input shorter than the bits asked, `getList` and `getLengthBytes` refuse a count that does not fit an `Int`, and `unmarshalLazy` returns `Either String a` instead of throwing.+- `setInputSequence` refuses to change the sequence of an input that is signed, since every signature commits to it.+- `WitnessProgram`, `WitnessProgramPKH`, `MAST`, `TaprootWitness` and `ScriptPathData` have `Show` and `Eq` instances.+- The package requires secp256k1-haskell 2.0.0, whose `normalizeSig` is total. `verifyHashSig` normalises the signature with it, and `isCanonicalHalfOrder` is its `isLowS`.+- `buildMerkleRoot` returns `Either String MerkleRoot`. It fails for an empty list, which used to crash, and for a mutated tree in which two sibling nodes are identical (CVE-2012-2459). `extractMatches` rejects such a tree too, as `CPartialMerkleTree::TraverseAndExtract` does in Bitcoin Core, and reports why a partial tree is malformed.+- `buildMerkleRoot`, `buildPartialMerkle` and `calcHash` build the tree in linear time instead of quadratic. `decodeMerkleFlags` is linear too; it took a minute for 100 KB of flags, which a peer could send in a `merkleblock` message.+- `genesisBlock` no longer takes a `Ctx` and returns the right coinbase for every network. Bitcoin testnet4 has its own genesis coinbase, so `genesisBlock btcTest4` used to return a block whose transactions did not match its header.+- The `merkleroot` field of the JSON form of `BlockHeader` is in display order, like `prevblock` and the `getblockheader` RPC call of Bitcoin Core. It was in internal byte order.+- `hexToBlockHash` and the JSON parser of `BlockHeader` reject a hash that is not exactly 32 bytes. They used to accept longer input and drop the leading bytes.+- `computeAsertBits` takes the proof-of-work limit of the network as its first argument instead of using the mainnet limit.+- `connectBlocks` adds each header to the store as it passes, so a batch with a bad header keeps the headers before it and makes the last of them the best block when it has the most work.+- `Ord BlockNode` compares the height and then the header, so that it agrees with `Eq`, which compares the header.+- `appendBlocks` dates every block one target spacing after its parent, so the chains it mines pass the median time past rule.+- `blockLocatorNodes` steps back like `LocatorEntries` in Bitcoin Core and lists the genesis block once.+- `encodeCompact` raises an error for a number wider than 255 bytes instead of wrapping the exponent.+- `traverseAndBuild` and `traverseAndExtract` are no longer exported.+- `getMessage` consumes exactly one message. It used to leave the payload in the stream and let the payload parser read past the declared length, so bytes outside the checksum could end up in a message. It now checks the magic before reading the payload, refuses a payload above `maxMessageSize`, and accepts a payload on message types that carry none, as Bitcoin Core does.+- `VarInt` refuses an encoding longer than the value needs, as `ReadCompactSize` does in Bitcoin Core. A transaction with such an encoding used to parse and re-serialize to different bytes. `VarString` refuses a count above `maxListSize`.+- `MessageCommand` refuses a name with a byte outside printable ASCII or with a byte other than NUL after it, as Bitcoin Core does, and serializing a name longer than twelve bytes is an error instead of a silent truncation.+- The `extra` field of `Reject` is a `Maybe Hash256`. It was a `ByteString` that only round-tripped when empty or 32 bytes long.+- `Version` refuses a user agent longer than 256 bytes, as Bitcoin Core does.+- `BloomFilter` refuses a filter above 36000 bytes or 50 hash functions when parsing, as Bitcoin Core refuses such a `filterload`, and reads its bytes in one go instead of one at a time. `FilterAdd` refuses an element above 520 bytes. `BloomFlags` reads only the two low bits of its byte, as Bitcoin Core does.+- `bloomRelevantUpdate` matches the data pushed by every output script, standard or not, and inserts outpoints according to the flags, as `IsRelevantAndUpdate` does in Bitcoin Core. It used to raise an error for any transaction with a non-standard output, and for a matching output that was not pay-to-pubkey or pay-to-multisig under `BloomUpdateP2PubKeyOnly`. It returns `Just` the filter whenever an output matches, unchanged under `BloomUpdateNone`.+- `bloomInsert` and `bloomContains` treat a filter with zero hash functions as matching everything, as Bitcoin Core does. `bloomInsert` used to loop over 2^32 hash functions and `bloomContains` used to answer `False`. `bloomCreate` gives zero hash functions for zero elements.+- `bip34Block` in `Network` is split into `bip34Height` and `bip34Hash`, the hash being optional. `btcRegTest` activates BIP34, BIP65 and BIP66 at height 1, as Bitcoin Core regtest does; it had the heights of Bitcoin Cash Node regtest. `bchRegTest` has the 32 MB block size of Bitcoin Cash Node.+- `arbitraryBloomFilter` generates a `BloomFilter` with random bits instead of a tuple.+- Replace the PSBT module with `Haskoin.Transaction.Psbt`, which keeps every field of a PSBT, known or not, writes maps with their keys in order as Bitcoin Core does, refuses what breaks BIP 174 (duplicate keys, keys out of shape, bytes after the last map, other versions), and reads and writes the global xpub and proprietary fields. It still creates, combines, signs, finalizes and extracts. The `Haskoin.Transaction.Partial` module and its types are gone.+- `pubKeyWitnessAddr` and `pubKeyCompatWitnessAddr` now return `Maybe Address` and refuse uncompressed keys. BIP 143 makes witness spends with uncompressed keys non-standard, so coins sent to such an address could not be spent through the normal network.+- The `WitnessAddress` and `CashAddress` constructors are now patterns that can only match. Build these addresses with `witnessAddress` and `cashAddress`, which validate the version and payload and return the dedicated constructor when one exists. Every address now has a single representation, so equal addresses compare equal.+- `outputAddress` and `scriptToAddress` no longer report the P2PKH address of the key for a pay-to-public-key output. Such coins cannot be spent with the P2PKH input script, so the address was misleading.+- `convertBits` in `Haskoin.Util.Helpers` returns `Maybe [Word]` and fails on padding bits that are not zero, or that are wide enough to hold a whole input digit, when padding is not allowed.+- JSON encoding of an address that has no text form on the given network now fails with an error instead of writing `null`.+- `mnemonicToSeed` NFKD-normalises the sentence and the passphrase, as BIP39 requires and as Trezor, Electrum, python-mnemonic and bitcoinjs do. A passphrase with accented letters used to give a seed that no other wallet derives, so the funds could not be restored elsewhere. `fromMnemonic` normalises the sentence before looking up its words. This adds a dependency on `unicode-transforms`.+- `toMnemonic` accepts 16 to 32 bytes of entropy and `fromMnemonic` accepts sentences of 12, 15, 18, 21 or 24 words, the sizes BIP39 allows. Entropy of 4 to 12 bytes and of 36 to 64 bytes, and sentences of 3 to 9 and 27 to 48 words, were accepted before, and no other wallet reads them. A word that is not in the list is reported with the `fromMnemonic:` prefix.+- `xPrvImport` and `xPubImport` require a payload of exactly 78 bytes, as Bitcoin Core does. Trailing bytes and an uncompressed public key used to be accepted. They also refuse a key at depth 0 whose parent fingerprint or index is not zero, as BIP32 test vector 5 requires.+- `fromWif` refuses strings longer than 52 characters, and `xPrvImport` and `xPubImport` strings longer than 112, before decoding them. No valid key is longer, and the time to decode Base58 grows with the square of the length.+- `prvSubKey`, `pubSubKey` and `hardSubKey` throw `DerivationException` when the parent is at depth 255. The depth of the child used to wrap around to 0.+- Parsing a derivation path reads each index as an unbounded integer and accepts decimal digits only. `m/4294967296` used to wrap around to `m/0`, and hex, parenthesised and signed numbers were accepted.+- The JSON form of `Sig` is decoded with `decodeStrictSig`, like the binary form, so a high-S signature is refused by both.+- The `Read` instances of `Hash160`, `Hash256` and `Hash512`, and `textToFingerprint`, refuse hex of the wrong length, as the string literal instances already did. A two-byte hash could be read and then crash `signHash`.+- `encodeOutput` returns `Either String Script`. It fails for a `PayMulSig` whose required count is not between 1 and its number of keys or that has more than 20 keys, and for a `PayWitness` whose version is 0 or above 16 or whose program is not 2 to 40 bytes long. Those values used to crash with an `error` call. `marshalPut` still fails with an `error` call for them, since it cannot report a failure.+- `txSigHash` and `txSigHashForkId` return `Either String Hash256`. They fail for an input index the transaction does not have. With `SIGHASH_ALL`, `txSigHash` used to return a digest over the transaction without any script code for such an index, and crash for the other hash types. `makeSignature` and `makeSigHash` return `Either` too.+- On a network with a fork id, `txSigHash` requires the `SIGHASH_FORKID` flag and `decodeTxSig` rejects a signature without it. `makeSignature` sets the flag, so `signTx bch` with a plain `sigHashAll` produces a signature the network accepts. It used to sign the legacy digest, which Bitcoin Cash has rejected since 2017.+- `isSigHashUnknown` is the test of `IsDefinedHashtypeSignature` in Bitcoin Core: after masking the `SIGHASH_ANYONECANPAY` and `SIGHASH_FORKID` bits, the hash type byte must be 1, 2 or 3. A byte such as 0x21 used to pass and be signed, although every node rejects it under `STRICTENC`.+- `opPushData` gives the minimal encoding of BIP 62: `OP_0` for the empty string, `OP_1` to `OP_16` for one byte from 1 to 16 and `OP_1NEGATE` for the byte 0x81. The empty push used to be an `OP_PUSHDATA` that serialized to the byte of `OP_0` but was not equal to it.+- `DataCarrier` holds the data of every push after `OP_RETURN`, as a list, since `TxoutType::NULL_DATA` of Bitcoin Core is `OP_RETURN` followed by any pushes. A bare `OP_RETURN`, several pushes and an empty push used to be "Non-standard output".+- `intToScriptOp` returns `Either String ScriptOp` instead of crashing outside 1 to 16. `scriptOpToInt` no longer serializes its argument, so it cannot crash on an `OP_PUSHDATA` whose payload does not fit its type.+- `sortMulSig` returns any output that is not a multisig unchanged instead of crashing.+- `decodeInput` reads the input of a pay-to-script-hash output as its redeem script requires. A multisig template with the redeem script and no signature yet, `OP_0 <redeem>`, is a `SpendMulSig []`, which `mergeTxs` used to reject. An input that does not fit its redeem script fails.+- The generators of `Haskoin.Util.Arbitrary.Script` produce pushes of every `PushDataType`, pushes that are not minimal, multisig outputs of up to 20 keys, data carriers with any number of pushes, nested witness inputs, and, on networks with a fork id, only hash types with the flag.++### Fixed++- A block whose timestamp equals the median time past of its parent is rejected, as in Bitcoin Core. It used to be accepted.+- `connectBlocks` keeps the block that was seen first as the best block when a competing block has the same work and height, as `connectBlock` and Bitcoin Core do. It used to switch the best block to every competitor.+- `calcNextWork` and `computeTarget` measure the time span of a retarget period as a signed number. A period whose last block is dated before its first block used to wrap around to four times the target timespan instead of being limited to a quarter.+- `headerWork` returns zero for bits that are negative, overflow or encode a zero target, like `GetBlockProof` in Bitcoin Core. It used to divide by zero for bits `0x03800001`.+- The block version is compared as a signed number, as in Bitcoin Core, so versions with the top bit set are rejected once BIP34 is active.+- The Bitcoin Cash difficulty algorithms follow Bitcoin Cash Node: the minimum-difficulty rule of test networks applies to the ASERT algorithm and no longer applies to the EDA and DAA algorithms on mainnet, the EDA returns the bits of the last block without minimum difficulty on test networks, `getSuitableBlock` picks the same block as Bitcoin Cash Node when timestamps are equal, `computeTarget` no longer overshoots by one, the ASERT anchor is the first block whose own median time past reaches the activation time, and regtest never retargets.+- A PSBT of another version is refused by its version. The unsigned transaction was looked for first, and a PSBT of version 2 holds none.+- `addressBytes` returned a length-prefixed value for a generic CashAddr instead of the raw hash.+- Base58, Bech32 and CashAddr decoders accepted non-ASCII look-alike characters. Base58 truncated code points to one byte and Bech32 used Unicode case folding. All three now accept ASCII only, as the specifications require.+- `cashAddrDecode` accepted mixed-case strings, which the CashAddr specification rejects.+- CashAddr decoding accepted non-canonical strings whose padding bits were not zero, or that carried an extra group of padding, so several strings could stand for the same address.+- `textToAddr` decoded Base58 input of any length, with a cost growing with the square of the length. Strings longer than any valid address are now rejected first.+- Binary deserialization of an address now validates the witness version, the CashAddr type and the payload length, and checks the length prefix before converting it to an `Int`. Previously an invalid value could be read and later crash `addressToScript`.+- The Bech32 and bit conversion accumulators are masked so that they cannot overflow on long inputs.+- `encodeOutput` had no case for `PayScript32Hash` and crashed on it, so a P2SH32 address could not be turned into a script.+- The P2WSH test generator produced P2WPKH addresses.+- `fromWif` threw an exception on a Base58Check string with an empty payload, such as `3QJmnh`, also through the JSON parser of `PrivateKey`. It returns `Nothing` now.+- The JSON parser of `PublicKey` accepted bytes after the key.+- `decodeOutput` matches the output templates on their exact bytes, as Bitcoin Core does. A script that wrote the hash of a P2PKH, P2SH, P2SH32 or witness output with `OP_PUSHDATA1` or a longer push used to decode as that output and give its address. By consensus such a script is not a P2SH or witness output at all, and anyone can spend it, so a wallet could credit a payment to its address that was not one.+- A public key pushed in a script or a witness must fill its push. A 65-byte item that starts with 0x02 or 0x03 used to parse as the 33-byte key it starts with, so `decodeOutput`, `decodeInput` and the witness decoders reported a key the script did not hold, and `verifyStdTx` accepted a P2PKH or P2WPKH spend whose key item carried 32 extra bytes, which no node accepts.+- `verifyStdInput` returns `False` for an input index the transaction does not have, and `parseExistingSigs` reads no witness signatures when the witness list is shorter than the input index. Both used to crash.++## [1.4.0] - 2026-10-31++### Added++- Bitcoin testnet4 network (`btctest4`) with the BIP94 block storm and time warp rules.++### Changed++- Update DNS seeds to match Bitcoin Core and Bitcoin Cash Node. Drop the Bitcoin ABC seeds, which now serve eCash.+- This package now requires base16 2.0 and secp256k1-haskell 0.7.++### Fixed++- Compute the difficulty retarget in `nextPowWorkRequired` and `nextEdaWorkRequired`, which never reached their retarget branches.+- Fix the Bitcoin Cash DAA activation height, which had a typo (404031 instead of 504032).+- Start the DAA one block later on Bitcoin Cash testnet and testnet4, to match Bitcoin Cash Node.+- Fix the Bitcoin Cash ASERT half-life, which must be two days.++## [1.3.0] - 2026-08-12++### Changed++- Make script and address parsing more strict.+- Use smart constructors for addresses to avoid inconsistent or incorrect represtentations.++### Fixed++- Support Bitcoin Cash 256-bit pay-to-script-hash scripts and addresses.+- Make addresses of incorrect length invalid.+- Avoid decoding scripts as standard when hash or public key length incorrect.+- Fix secp256 library function names.++## [1.2.2] - 2025-05-08++### Changed++- Standardize author name and correct README link.++## [1.2.1] - 2025-05-08++### Changed++- New home in github.com/jprupp rather than github.com/haskoin.++## [1.2.0] - 2023-07-23++### Changed++- Move from `cryptonite` to `crypton` for now.++## [1.1.0] - 2023-03-14++### Changed++- Update upstream dependencies & LTS Haskell.++## [1.0.4] - 2023-10-09++### Changed++- Export the BIP39 word list+- Add new BIP39 test vectors++## [1.0.3] - 2023-09-02++### Changed++- New dependency on data-default+- Renamed some arbitrary (test) functions for clarity+- Reworked the identity testing tool to include Marshal and MarshalJSON classes++## [1.0.2] - 2023-08-04++### Fixed++- Text files needed for testing now in tarball.++## [1.0.1] - 2023-08-03++### Changed++- Do not use alternative monad for Either String values.++## [1.0.0] - 2023-07-28++### Changed++- Shorten all field names allow duplicates, using DuplicateRecordFields extension.+- Use OverloadedRecordDot language extension and syntax.+- Use explicit context object from secp256k1-haskell library.+- Unify serialization into custom Marhsal and MarshalJSON classes.+- Use ormolu for formatting.+- Simplify module organisation.+- Strongly break backwards compatibility.++## [0.22.0] - 2023-06-28++### Changed++- Upgrade to lastest secp256k1 and base16 packages.++## [0.21.2] - 2022-04-13++### Changed++- Serialisation test now works for both strict and lazy bytestrings.++## [0.21.1] - 2021-12-13++### Changed++- Make Base58 faster.++## [0.21.0] - 2022-11-23++### Added++- BCH Testnet4 support.++### Changed++- Fix inconsistent serialization/deserialization issues.++### Fixed++- Makes `finalScriptWitness` field encoding conform to bitcoin core.+- Fixes bug in `finalizeTransaction`++### Added++- Signing support for PSBTs+- Helper function for merging PSBTs+- More PSBT tests+- Partial support for taproot++## [0.20.5] - 2021-09-13++### Added++- Support Bech32m address format for Taproot.++## [0.20.4] - 2021-06-08++### Fixed++- Add missing case for witness version.++## [0.20.3] - 2021-05-17++### Fixed++- Allow unknown inv types.++## [0.20.2] - 2021-05-17++### Fixed++- Allow unknown messages of zero length.++## [0.20.1] - 2021-05-14++### Fixed++- Correct case where binary search returned the wrong element.++## [0.20.0] - 2021-02-22++### Chaged++- Use bytes instead of binary or cereal.++## [0.19.0] - 2021-01-25++### Added++- Hashable instances for extended keys.++### Changed++- Mnemonic passphrases now `Text` instead of `ByteString`.++### Fixed++- Tests now pass for witness addresses.++## [0.18.0] - 2020-12-10++### Added+ - Support SegWit addresses with version other than 0. -## 0.17.6+## [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.@@ -239,6 +826,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`.@@ -249,6 +837,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
@@ -1,16 +1,842 @@ # Haskoin Core -Haskoin Core is a library of Bitcoin and Bitcoin Cash functions written in Haskell featuring:+[![CI](https://github.com/jprupp/haskoin-core/actions/workflows/ci.yml/badge.svg)](https://github.com/jprupp/haskoin-core/actions/workflows/ci.yml)+[![Hackage](https://img.shields.io/hackage/v/haskoin-core.svg)](https://hackage.haskell.org/package/haskoin-core)+[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jprupp/haskoin-core/blob/master/LICENSE) -- Hashing functions (SHA-256, RIPEMD-160)-- Base58 support-- CashAddr support-- Bech32 suport-- BIP32 extended key derivation and parsing (m/1'/2/3)-- BIP39 mnemonic keys-- ECDSA secp256k1 cryptographic primitives-- Script parsing-- Building and signing of standard transactions (regular, multisig, p2sh, segwit)-- Parsing and manipulation of all Bitcoin and Bitcoin Cash protocol messages-- Bloom filters and partial merkle trees (used in SPV wallets)-- Comprehensive test suite+Haskoin Core is a Bitcoin and Bitcoin Cash library for Haskell. It covers keys,+addresses, scripts, transactions, PSBTs, block headers and the peer-to-peer+message format.++It does no I/O of its own. It never opens a socket or writes a file. You can use+it in a wallet, a block explorer or a node, and bring your own networking and+storage.++- [API documentation](https://hackage.haskell.org/package/haskoin-core)+- [Changelog](https://github.com/jprupp/haskoin-core/blob/master/CHANGELOG.md)+- [Report a bug](https://github.com/jprupp/haskoin-core/issues)++## Contents++- [Features](#features)+- [Installation](#installation)+- [Quick start](#quick-start)+- [Concepts](#concepts)+- [Usage guide](#usage-guide)+- [Module overview](#module-overview)+- [Security notes](#security-notes)+- [Development](#development)+- [Related projects](#related-projects)+- [License](#license)++## Features++| Area         | What you get                                                                                                         |+| ------------ | -------------------------------------------------------------------------------------------------------------------- |+| Networks     | Bitcoin and Bitcoin Cash: mainnet, testnet3, testnet4 and regtest                                                    |+| Keys         | secp256k1 keys, WIF, BIP-32 extended keys and paths, BIP-39 mnemonics, SLIP-39 Shamir shares                         |+| Signatures   | Deterministic ECDSA (RFC 6979) and BIP-340 Schnorr                                                                   |+| Addresses    | Base58, Bech32, Bech32m and CashAddr, including token-aware CashAddr types                                           |+| Scripts      | Script parsing and serialization, standard input and output templates, BIP-67 sorted multisig                        |+| Transactions | Building, signing and verifying P2PK, P2PKH, P2SH, multisig, P2WPKH, P2WSH, nested SegWit and Taproot key path spends |+| Wallet tools | Coin selection, and weight, size and fee estimates                                                                   |+| PSBT         | Versions 0 and 2, with the Taproot fields (BIP-174, BIP-370, BIP-371)                                                |+| Taproot      | Script trees, output keys, witnesses, control blocks and signature hashes (BIP-341, BIP-342)                         |+| Blocks       | Header validation, proof of work, the difficulty rules of both chains, Merkle trees and an in-memory header chain    |+| Peer-to-peer | Message encoding, including `addrv2`, compact blocks and compact filter messages, and BIP-37 Bloom filters           |+| Testing      | QuickCheck generators for the library's types                                                                        |++### What is not included++- **Peer connections and chain sync.** Use+  [haskoin-node](https://github.com/jprupp/haskoin-node).+- **Address and transaction indexing.** Use+  [haskoin-store](https://github.com/jprupp/haskoin-store).+- **A script interpreter.** `verifyStdTx` recognises the standard templates. It+  is not a consensus check.+- **Signet, output descriptors, Miniscript and MuSig2.**+- **Signing with Schnorr signatures, `SIGHASH_UTXOS` and token outputs of+  Bitcoin Cash.** `judgeInputs` verifies a Schnorr signature, of one key+  or in a multisig, and the spend of a coin that holds tokens.+  Nothing here signs them, `verifyStdTx` does not read them, and an+  output with tokens has no address.+- **BIP-324 encrypted transport.**+- **BIP-39 word lists other than English.**++## Installation++### System library++Haskoin Core needs [libsecp256k1](https://github.com/bitcoin-core/secp256k1)+0.2.0 or later, and `pkg-config` to find it.++```sh+# Debian and Ubuntu+sudo apt-get install libsecp256k1-dev pkg-config++# macOS with Homebrew+brew install secp256k1 pkg-config++# Nix+nix-shell -p secp256k1 pkg-config+```++Check what you have with `pkg-config --modversion libsecp256k1`. Debian 11 and+other older systems ship a version below 0.2.0. On those, build the library from+source and enable its recovery module with `./configure --enable-module-recovery`.++### Compiler++The library is tested with GHC 9.10 and 9.12. It cannot build below GHC 9.2,+because it uses `OverloadedRecordDot`.++### With Cabal++Add the packages to your `.cabal` file:++```cabal+build-depends:+    base+  , bytes+  , haskoin-core+  , text+```++You need `bytes` to serialize values with `runPutS` and `serialize`.++Run `cabal update` first. Haskoin Core needs a recent secp256k1-haskell, and a+stale package index makes the build fail with a dependency error.++### With Stack++Haskoin Core is not in Stackage, so add it to `stack.yaml` as an extra+dependency. Use the [latest version from Hackage](https://hackage.haskell.org/package/haskoin-core).++```yaml+resolver: nightly-2026-10-28+extra-deps:+  - haskoin-core-1.6.2+  - base16-2.0+  - secp256k1-haskell-2.0.1+```++## Quick start++This program turns a mnemonic into the first receiving address of a BIP-84+wallet.++```haskell+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.Text (Text)+import Haskoin++-- This is the public BIP-39 test mnemonic. Never send coins to it.+mnemonic :: Text+mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"++main :: IO ()+main = withContext $ \ctx -> do+  seed <- either fail pure (mnemonicToSeed "" mnemonic)+  let master = makeXPrvKey seed+      -- Hardened steps need the private key.+      account = derivePath ctx ("m/84'/0'/0'" :: HardPath) master+      accountPub = deriveXPubKey ctx account+      -- The remaining steps only need the public key.+      receiving = derivePubPath ctx ("m/0" :: SoftPath) accountPub+      (address, _publicKey) = deriveWitnessAddr ctx receiving 0+  print (addrToText btc address)+  -- => Just "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"+```++The address is the one that [BIP-84](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki)+publishes for this mnemonic.++## Concepts++A few conventions apply across the whole library. Reading this section first+will save you time.++### The context++Most cryptographic functions take a `Ctx`, a secp256k1 context. Create one when+your program starts, with `withContext` or `createContext`. Then pass it around.+You can share it between threads.++### Networks++A `Network` is a record of the parameters of one chain. Functions that depend on+the chain take one as an argument. Functions that need both take the `Network`+first and the `Ctx` second.++| Value        | Chain                 | Name for `netByName` |+| ------------ | --------------------- | -------------------- |+| `btc`        | Bitcoin mainnet       | `btc`                |+| `btcTest`    | Bitcoin testnet3      | `btctest`            |+| `btcTest4`   | Bitcoin testnet4      | `btctest4`           |+| `btcRegTest` | Bitcoin regtest       | `btcreg`             |+| `bch`        | Bitcoin Cash mainnet  | `bch`                |+| `bchTest`    | Bitcoin Cash testnet3 | `bchtest`            |+| `bchTest4`   | Bitcoin Cash testnet4 | `bchtest4`           |+| `bchRegTest` | Bitcoin Cash regtest  | `bchreg`             |++### Record fields++The library is built with `NoFieldSelectors`, so record fields are not functions.+Turn on `OverloadedRecordDot` in your modules and read fields with a dot.++```haskell+print btc.defaultPort+-- => 8333+print (map (.value) btc.genesisTx.outputs)+-- => [5000000000]+```++Many records share field names. `Tx` and `Psbt` both have `inputs`, for example.+To update such a field, import the module qualified and name the field through+it, as the [PSBT example](#psbt) does.++### Errors++Functions that can fail return `Maybe` or `Either String`. The examples below+turn those into `IO` failures with `either fail pure`.++Hashes, paths and public keys have `IsString` instances, so you can write them as+literals. Those instances call `error` on bad input. For input you do not control,+use `hexToTxHash`, `hexToBlockHash`, `parsePath` and `textToAddr`.++### Amounts++Amounts are satoshi, as `Word64`.++### Serialization++Most types have `Serial` instances, from the+[bytes](https://hackage.haskell.org/package/bytes) package, and also `Binary` and+`Serialize` instances. Some types need a context or a network to encode, so they+have `Marshal` or `MarshalJSON` instances instead.++| Task                | Encode                     | Decode                       |+| ------------------- | -------------------------- | ---------------------------- |+| Binary              | `runPutS (serialize x)`    | `runGetS deserialize bytes`  |+| Binary with context | `marshal ctx x`            | `unmarshal ctx bytes`        |+| Hexadecimal         | `encodeHex bytes`          | `decodeHex text`             |+| JSON                | `toJSON x`                 | `parseJSON value`            |+| JSON with context   | `marshalJSON ctx x`        | `unmarshalJSON ctx json`     |++The first argument depends on the type. For example, it is a `Ctx` for+`PublicKey`, a `Network` for `XPrvKey` and a `(Network, Ctx)` pair for `XPubKey`.++`unmarshal` fails when bytes are left over after the value, and `decodeTx` does+the same for a transaction. `runGetS deserialize` reads a value from the start of+the bytes and ignores the rest.++### Hash byte order++`TxHash` and `BlockHash` show as hexadecimal in reversed byte order. This is the+form that Bitcoin Core and block explorers display, so you can paste those values+straight in. The serialized bytes keep the internal order.++## Usage guide++The examples below are fragments of `main` in this program. Paste one in place of+the comment to run it. If an example starts with `import` lines, add them to the+top of the file. A `-- =>` comment shows what the line above it prints.++```haskell+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (serialize)+import Haskoin++main :: IO ()+main = withContext $ \ctx -> do+  -- This is the public BIP-39 test mnemonic. Never send coins to it.+  seed <-+    either fail pure $+      mnemonicToSeed "" "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"+  let master = makeXPrvKey seed+  -- Paste an example here.+  pure ()+```++### Mnemonics++Generate entropy with a cryptographic source. This example uses the+[entropy](https://hackage.haskell.org/package/entropy) package.++```haskell+import qualified Data.ByteString as B+import System.Entropy (getEntropy)++-- 16 bytes of entropy give 12 words. 32 bytes give 24 words.+entropy <- getEntropy 16+words12 <- either fail pure (toMnemonic entropy)++-- The passphrase is optional. Use "" for none.+newSeed <- either fail pure (mnemonicToSeed "my passphrase" words12)+print (B.length newSeed)+-- => 64+```++`mnemonicToSeed` checks the words and the checksum. `fromMnemonic` gives the+entropy back.++### Keys and signatures++```haskell+-- A secret key is 32 bytes. decodeSecKey refuses zero and values at or above+-- the curve order.+bytes <- maybe (fail "bad hex") pure (decodeHex "0000000000000000000000000000000000000000000000000000000000000001")+secret <- maybe (fail "bad key") pure (decodeSecKey bytes)++-- True asks for the compressed form of the public key.+let private = wrapSecKey True secret+    public = derivePublicKey ctx private++print private+-- => PrivateKey {key = <redacted>, compress = True}+print (toWif btc private)+-- => "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"+print (encodeHex (marshal ctx public))+-- => "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"+print (addrToText btc (pubKeyAddr ctx public))+-- => Just "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"++-- Sign and verify a hash.+let digest = doubleSHA256 bytes+    signature = signHash ctx secret digest+print (verifyHashSig ctx digest signature public.point)+-- => True+```++`fromWif btc text` reads a WIF key back. `Haskoin` also re-exports the+[secp256k1-haskell](https://hackage.haskell.org/package/secp256k1-haskell) API,+which has the Schnorr functions such as `signSchnorr` and `verifySchnorr`.++### Extended keys++Paths have three types. A `HardPath` has only hardened steps. A `SoftPath` has+only normal steps, which a public key can derive. A `DerivPath` can mix both.+Hardened steps can be written `'`, `h` or `H`.++```haskell+let account = derivePath ctx ("m/84'/0'/0'" :: HardPath) master+    accountPub = deriveXPubKey ctx account++print (xPubExport btc ctx accountPub)+-- => "xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V"++-- The account's public key derives all its receiving addresses.+let receiving = derivePubPath ctx ("m/0" :: SoftPath) accountPub+    addresses = [address | (address, _, _) <- deriveWitnessAddrs ctx receiving 0]+print (map (addrToText btc) (take 2 addresses))+-- => [Just "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",Just "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"]+```++Pick the address function that matches the wallet standard:++| Standard | Address type        | Path          | One address               | Endless list               |+| -------- | ------------------- | ------------- | ------------------------- | -------------------------- |+| BIP-44   | P2PKH               | `m/44'/0'/0'` | `deriveAddr`              | `deriveAddrs`              |+| BIP-49   | P2WPKH nested in P2SH | `m/49'/0'/0'` | `deriveCompatWitnessAddr` | `deriveCompatWitnessAddrs` |+| BIP-84   | P2WPKH              | `m/84'/0'/0'` | `deriveWitnessAddr`       | `deriveWitnessAddrs`       |+| BIP-86   | Taproot             | `m/86'/0'/0'` | See [Taproot](#taproot)   |                            |++`xPrvExport` and `xPubExport` write the standard `xprv` and `xpub` forms.+`xPrvImport` and `xPubImport` read them back. The `ypub` and `zpub` prefixes are+not supported.++`parsePath` reads a path at run time. It returns a `ParsedPath`, which+`applyPath` applies to a private or a public key.++### Addresses++```haskell+let text = "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"++print (textToAddr btc text)+-- => Just (WitnessPubKeyAddress {hash160 = "c0cebcd6c3d3ca8c75dc5ec62ebe55330ef910e2"})++-- An address of another network is refused.+print (textToAddr bch text)+-- => Nothing++-- The output script an address pays to.+print (encodeHex <$> (addressToScriptBS ctx =<< textToAddr btc text))+-- => Just "0014c0cebcd6c3d3ca8c75dc5ec62ebe55330ef910e2"+```++`textToAddr` and `addrToText` handle every address format of a network.+`addrToBase58`, `addrToBech32` and `addrToCash` each write one chosen format.++### Scripts++`ScriptOutput` is a standard output script. Its constructors are `PayPK`,+`PayPKHash`, `PayMulSig`, `PayScriptHash`, `PayWitnessPKHash`,+`PayWitnessScriptHash`, `PayWitness`, `PayScript32Hash` and `DataCarrier`.++```haskell+let keyA = wrapPubKey True "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"+    keyB = wrapPubKey True "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f"+    -- BIP-67 sorts the keys, so that all signers build the same script.+    multisig = sortMulSig ctx (PayMulSig [keyA, keyB] 2)++-- The address can fail, because a P2SH redeem script holds at most 520 bytes.+print (addrToText btc <$> payToScriptAddress ctx multisig)+-- => Right (Just "39bgKC7RFbpoCRbtD5KEdkYKtNyhpsNa3Z")++-- OP_RETURN is 6a, then the push of 5 bytes, then "hello".+print (encodeHex (marshal ctx (DataCarrier ["hello"])))+-- => "6a0568656c6c6f"++-- Read the same bytes back.+script <- maybe (fail "bad hex") pure (decodeHex "6a0568656c6c6f")+print (unmarshal ctx script :: Either String ScriptOutput)+-- => Right (DataCarrier {pushes = ["hello"]})+```++Decoding a `ScriptOutput` fails for a script that matches no template. A `Script`+holds any script, as a list of `ScriptOp` values, so use that type for the others.+`encodeOutput` and `decodeOutput` convert between `ScriptOutput` and `Script`.++`payToWitnessScriptAddress` and `payToNestedScriptAddress` give the P2WSH and+P2SH-P2WSH addresses of a script.++The three functions return `Either`. A P2SH redeem script holds at most 520+bytes, which is a multisig of 15 compressed keys. Coins sent to a longer script+could never be spent, so `payToScriptAddress` refuses it. A P2WSH script holds up+to 10,000 bytes, so a multisig of 16 to 20 keys needs P2WSH.++### Building and signing a transaction++```haskell+-- The key at m/84'/0'/0'/0/0 and its address.+let key = derivePath ctx ("m/84'/0'/0'/0/0" :: DerivPath) master+    address = xPubWitnessAddr ctx (deriveXPubKey ctx key)++-- The coin to spend, and the transaction that paid it. Both are made up.+-- The coin holds 100,000 satoshi.+coinScript <- maybe (fail "no output script") pure (addressToOutput address)+let amount = 100000+    fundingInput = TxIn (OutPoint "1111111111111111111111111111111111111111111111111111111111111111" 0) "" 0xffffffff+    funding = Tx 2 [fundingInput] [TxOut amount (marshal ctx coinScript)] [] 0+    coin = OutPoint (txHash funding) 0++-- Pay 99,000 satoshi. The 1,000 satoshi left over are the fee.+unsigned <-+  either fail pure $+    buildAddrTx btc ctx [coin] [("bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", 99000)]++-- SigInput describes the coin. The last argument is the redeem script.+signed <-+  either fail pure $+    signTx btc ctx unsigned [SigInput coinScript amount coin sigHashAll Nothing] [key.key]++print (verifyStdTx btc ctx signed [(coinScript, amount, coin)])+-- => True+print (txVirtualSize signed)+-- => 110+print (txHash signed)+-- => "9a079e6b7939909415b7b027a19e093bc539dac5a2ed50df8beb0942ec821848"++-- This hexadecimal form is what you broadcast.+print (encodeHex (runPutS (serialize signed)))+-- => "01000000000101..."+```++`buildAddrTx` adds no change output. Whatever the outputs do not spend goes to the+miners as a fee, so list your change output along with the payments.++Fill in `SigInput` and pick the signing function by the type of the coin:++| Coin             | `script`               | `redeem`                 | Function              |+| ---------------- | ---------------------- | ------------------------ | --------------------- |+| P2PKH            | `PayPKHash`            | `Nothing`                | `signTx`              |+| P2SH multisig    | `PayScriptHash`        | `Just (PayMulSig ks m)`  | `signTx`              |+| P2WPKH           | `PayWitnessPKHash`     | `Nothing`                | `signTx`              |+| P2WSH            | `PayWitnessScriptHash` | `Just script`            | `signTx`              |+| P2SH-P2WPKH      | `PayWitnessPKHash`     | `Nothing`                | `signNestedWitnessTx` |+| P2SH-P2WSH       | `PayWitnessScriptHash` | `Just script`            | `signNestedWitnessTx` |+| Taproot key path | `PayWitness 1 key`     | `Nothing`                | `signTx`              |++For multisig, each signer can call `signTx` with their own keys. `mergeTxs`+combines the results for legacy inputs. For SegWit multisig, use a [PSBT](#psbt).++### Coin selection and fees++Make your coin type an instance of `Coin`. The library then picks coins and+estimates the fee from the kinds of the inputs and outputs. This example is a+complete program.++```haskell+{-# LANGUAGE OverloadedRecordDot #-}++module Main (main) where++import Data.Word (Word64)+import Haskoin++newtype Utxo = Utxo {amount :: Word64}++instance Coin Utxo where+  coinValue utxo = utxo.amount+  coinInputKind _ = InputWitnessPKHash++main :: IO ()+main = do+  let outputs = [OutputWitnessPKHash, OutputWitnessPKHash]+      coins = [Utxo 50000, Utxo 120000, Utxo 300000]+      target = 150000+      feeRate = 5++  -- The size is in virtual bytes, and the fee rate in satoshi per virtual byte.+  print (guessTxSize [InputWitnessPKHash] outputs)+  -- => 141+  print (guessTxFee feeRate [InputWitnessPKHash] outputs)+  -- => Right 705++  -- The result is the chosen coins and the change to send back to yourself.+  -- True keeps searching the rest of the list for a smaller solution.+  case chooseCoins target feeRate outputs True coins of+    Left err -> putStrLn err+    Right (chosen, change) -> print (map (.amount) chosen, change)+  -- => ([120000,50000],18955)+```++The change here is 170,000 - 150,000 - 1,045, where 1,045 is the fee for two+inputs and two outputs.++`chooseCoins` takes the coins in the order of the list, so sort them the way you+prefer first. The output list must include the change output.++The change can be smaller than the `dustThreshold` of its output kind, down to+one satoshi. Nodes do not relay such an output, so leave that change to the fee.+`chooseCoins` refuses a selection above `maxStandardTxWeight`, which nodes do not+relay either.++The estimates are upper bounds, because they count 72 bytes per signature. The+kinds include `InputPKHash`, `InputWitnessPKHash` and `InputTaproot`. See+`InputKind` and `OutputKind` in `Haskoin.Transaction.Builder` for all of them.++### PSBT++Use a PSBT when several parties or devices sign a transaction. This example+continues the one in [Building and signing](#building-and-signing-a-transaction).++```haskell+-- Continues the example above.+import qualified Haskoin.Transaction.Psbt as Psbt++-- Creator: start from the unsigned transaction.+let created = psbtOf unsigned++-- Updater: attach the transaction that paid each coin. The signer checks the+-- amount of the coin against it.+    updated = created {Psbt.inputs = map (insertNonWitnessUtxo funding) created.inputs}++-- This base64 text is what you pass to the signers.+let shared = encodePsbtBase64 updated++-- Signer: read the text, and sign every input the keys can sign.+received <- either fail pure (decodePsbtBase64 shared)+signedPsbt <- either fail pure (signPsbt btc ctx (keySigner ctx key.key) received)++-- Finalizer and extractor: check the signatures and build the transaction.+finalized <- either fail pure (finalizePsbt btc ctx signedPsbt)+extracted <- either fail pure (psbtTransaction finalized)++-- RFC 6979 signatures are deterministic, so this is the same transaction.+print (extracted == signed)+-- => True+```++Some other useful functions:++- `xPrvSigner` signs from an extended private key, following the derivation+  records of the inputs.+- `combinePsbt` merges the copies that different signers return.+- `insertWitnessUtxo` attaches a coin without the transaction that paid it. That+  is enough for a Taproot coin. For a SegWit version 0 coin, the amount is then a+  claim that the signature does not cover for the other inputs, and a signer can+  be led to burn coins as fee (CVE-2020-14199). `keySigner` and `xPrvSigner`+  therefore refuse such a coin. Set `requirePreviousTx = False` on the `Signer`+  to accept it.+- `psbtFee` and `inputCoins` give the fee and the coins as the signer checks+  them. Show those to the user, not amounts read from the fields yourself.+- `psbtTransaction` refuses a PSBT whose inputs are not all final.+  `psbtPartialTransaction` gives the transaction of a PSBT that is still being+  signed.+- `insertInputRedeemScript` and `insertInputWitnessScript` attach the scripts of+  P2SH and P2WSH coins.+- `withVersion Version2` converts a PSBT to version 2. `addInput` and `addOutput`+  then extend it.++### Taproot++```haskell+let key = derivePath ctx ("m/86'/0'/0'/0/0" :: DerivPath) master+    internalKey = (deriveXPubKey ctx key).key+    -- Nothing means that the output has no script tree.+    output = taprootScriptOutput ctx (TaprootOutput internalKey Nothing)++print (addrToText btc =<< outputAddress btc ctx output)+-- => Just "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"++-- Spend a made-up coin of 100,000 satoshi by its key path.+let coin = OutPoint "1111111111111111111111111111111111111111111111111111111111111111" 0+    unsigned = buildTx ctx [coin] [(output, 99000)]++signed <-+  either fail pure $+    signTx btc ctx unsigned [SigInput output 100000 coin sigHashDefault Nothing] [key.key]++print (verifyStdTx btc ctx signed [(output, 100000, coin)])+-- => True+```++The signature commits to every coin the transaction spends. `signTx` therefore+needs a `SigInput` for every input of a Taproot transaction.++To commit to scripts, pass a `MAST` in place of `Nothing`. `signTx` only signs key+path spends. `signPsbt` also signs a script path when the leaf is a single key+check, `<key> OP_CHECKSIG`. For other scripts, build the witness yourself with+`taprootSigHash` and `encodeTaprootWitness`.++`checkTapTree` tells whether every leaf of a tree can be spent. A leaf can be at+most 128 levels deep, and its version must be one that BIP 341 allows.++### SLIP-39 secret sharing++[SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) splits a+secret into mnemonic shares, and a threshold of shares rebuilds it. It is+available from version 1.6.0. The `Haskoin` module does not re-export it, so+import it yourself.++```haskell+import qualified Haskoin.Crypto.Keys.Slip39 as Slip39++-- One group of 3 members, of which 2 rebuild the secret.+let scheme = Slip39.Scheme 1 [(2, 3)]+    -- A secret has at least 16 bytes, and an even number of them.+    -- Use random bytes in practice.+    secret = "0123456789abcdef"++-- True makes the shares extendable. 1 sets the cost of the encryption.+groups <- either fail pure =<< Slip39.generateMnemonics scheme True 1 "passphrase" secret+let shares = concat groups++print (length shares)+-- => 3+print (Slip39.combineMnemonics "passphrase" (take 2 shares))+-- => Right "0123456789abcdef"+print (Slip39.combineMnemonics "passphrase" (take 1 shares))+-- => Left "Group 0 has 1 share, but its member threshold is 2"+```++A wrong passphrase is not an error. It returns a different secret, as the+specification requires. The passphrase must be printable ASCII, which the+specification requires too. A SLIP-39 share is not a BIP-39 mnemonic, and one+cannot be converted into the other.++`Scheme` can also describe two levels. For example, `Scheme 2 [(1, 1), (2, 3),+(3, 5)]` needs two of the three groups, each with its own member threshold.++### Bitcoin Cash++Pass `bch` wherever the examples above pass `btc`. A few things differ.++```haskell+-- The BIP-44 coin type of Bitcoin Cash is 145.+let key = derivePath ctx ("m/44'/145'/0'/0/0" :: DerivPath) master+    address = xPubAddr ctx (deriveXPubKey ctx key)++-- addrToText writes CashAddr on Bitcoin Cash.+print (addrToText bch address)+-- => Just "bitcoincash:qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6"+print (addrToBase58 bch address)+-- => Just "1mW6fDEMjKrDHvLvoEsaeLxSCzZBf3Bfg"++-- textToAddr reads both forms.+print ((== Just address) (textToAddr bch "1mW6fDEMjKrDHvLvoEsaeLxSCzZBf3Bfg"))+-- => True++-- SegWit addresses do not exist on Bitcoin Cash.+print (addrToText bch (xPubWitnessAddr ctx (deriveXPubKey ctx key)))+-- => Nothing+```++`signTx` sets the fork id flag of the signature hash when the network needs it.+Pass `sigHashAll` as usual.++### Blocks and headers++```haskell+print (headerHash btc.genesisHeader)+-- => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"+print (isValidPOW btc btc.genesisHeader)+-- => True++-- 3.125 BTC since the halving at this height.+print (computeSubsidy btc 840000)+-- => 312500000+```++`connectBlocks` validates headers and adds them to a chain. This covers proof of+work, difficulty, timestamps, versions and checkpoints. It runs in any monad with+a `BlockHeaders` instance. `StateT HeaderMemory` is the in-memory one. Write your+own instance to keep headers in a database.++Once the best block has the `minimumChainWork` of the network, a fork with less+work is refused. Below that work nothing bounds the side chains a peer can send,+so a node that is still syncing should not keep headers from a peer it does not+trust.++This example needs the [mtl](https://hackage.haskell.org/package/mtl) package.++```haskell+import Control.Monad.State.Strict (runStateT)++-- Mine three regtest headers on top of the genesis block.+let net = btcRegTest+    headers = appendBlocks net 7 net.genesisHeader 3+    now = net.genesisHeader.timestamp + 3600++(result, chain) <- runStateT (connectBlocks net now headers) (initialChain net)+nodes <- either fail pure result++-- The nodes come back with the highest one first.+print (map (.height) nodes)+-- => [3,2,1]+print chain.best.height+-- => 3+```++`buildMerkleRoot` and `buildPartialMerkle` compute Merkle trees.+`extractMatches` checks a partial tree from a `merkleblock` message against its+Merkle root. The header and the number of transactions are claims of the sender,+which the caller must check.++### Peer-to-peer messages++The library encodes and decodes messages. Moving the bytes is up to you.++```haskell+import Data.Bytes.Get (runGetS)++let bytes = runPutS (putMessage btc MVerAck)++print (encodeHex bytes)+-- => "f9beb4d976657261636b000000000000000000005df6e0e2"+print (runGetS (getMessage btc) bytes)+-- => Right MVerAck+```++`getMessage` checks the network magic and the checksum, and bounds the payload by+the `maxPayloadSize` of its command. Read the 24-byte `MessageHeader` first to+learn how many more bytes to take from the socket, and refuse a length above+`maxPayloadSize net command` before you read them.++`Message` has a constructor for each message type. `bloomCreate`, `bloomInsert`+and `bloomContains` build BIP-37 filters for `filterload`.++## Module overview++`import Haskoin` brings in everything below except the two modules marked as+separate. It exports many short names, so you may prefer to import only the+modules you need, or to import it qualified.++| Module                        | Contents                                                             |+| ----------------------------- | -------------------------------------------------------------------- |+| `Haskoin.Address`             | Address types and Base58, Bech32 and CashAddr encoding               |+| `Haskoin.Block`               | Block types, header chain, validation, difficulty and Merkle trees   |+| `Haskoin.Crypto`              | Hashes, keys, signatures and the `Crypto.Secp256k1` API              |+| `Haskoin.Crypto.Keys.Slip39`  | SLIP-39 secret sharing (separate import)                             |+| `Haskoin.Network`             | Network constants, P2P messages, Bloom filters                       |+| `Haskoin.Script`              | Scripts, standard templates and signature hashes                     |+| `Haskoin.Transaction`         | Transaction types, builder, signing, SegWit, Taproot and PSBT        |+| `Haskoin.Util`                | Hexadecimal helpers, `Marshal` and small utilities                   |+| `Haskoin.Util.Arbitrary`      | QuickCheck generators such as `arbitraryTx` (separate import)        |++## Security notes++Bugs in code that handles keys can lose money. Try your program on regtest or a+test network before you use it on mainnet.++- **Randomness.** Create keys and mnemonics with a cryptographic source, such as+  `getEntropy` from the entropy package. Never use `System.Random`.+- **Secrets in logs.** `PrivateKey` and `XPrvKey` show as `<redacted>` and have no+  `Read` instance. `toWif` and `xPrvExport` write a key out on purpose. A bare+  `SecKey`, such as the `key` field of a `PrivateKey`, still shows its bytes. The+  errors of `fromMnemonic` and `mnemonicToSeed` name the position of a bad word,+  never the word.+- **Secrets in memory.** Keys live in the GHC heap. The library cannot wipe them,+  and the garbage collector can copy them.+- **Review before you sign.** `signPsbt` checks that a PSBT is sound, but not that+  it pays who you expect. Show the outputs and `psbtFee` to the user first. By+  default the signer wants the previous transaction of every SegWit version 0+  coin, because an amount in a witness output alone is a claim.+- **Signature hash types.** By default `signPsbt` signs only `SIGHASH_ALL` and+  `SIGHASH_DEFAULT`, because the other types leave part of the transaction+  unsigned. To accept others, set `acceptedSigHashes` on the `Signer`.+- **Verification.** `verifyStdTx` checks the signatures of the standard templates+  by the rules that nodes relay by, and that outputs do not exceed inputs. It is+  not a script interpreter and does not apply every consensus rule.+- **Untrusted text.** The cost of `decodeBase58` and `decodeBase58Check` grows+  with the square of the input length. Limit the length of input you pass to them+  directly. The address and key decoders of the library already do.++## Development++```sh+git clone https://github.com/jprupp/haskoin-core.git+cd haskoin-core++stack build                                         # build the library+stack test                                          # run all the tests+stack test --test-arguments='--match "Slip39"'      # run the tests of one module+stack haddock --no-haddock-deps                     # build the API documentation+```++`cabal build` and `cabal test` work too. On NixOS, or with `stack --nix`, the+`nix` section of `stack.yaml` provides libsecp256k1.++`package.yaml` is the source of the package description. Change it rather than+`haskoin-core.cabal`, which [hpack](https://github.com/sol/hpack) generates. Stack+regenerates the file whenever it builds.++The tests are in `test/` and use [hspec](https://hspec.github.io/). The test+vectors from the BIPs, Bitcoin Core and other implementations are in `data/`.++### Contributing++Pull requests are welcome.++- Format the code with [Ormolu](https://github.com/tweag/ormolu). The script+  [scripts/format](https://github.com/jprupp/haskoin-core/blob/master/scripts/format)+  does this for all the sources.+- Add tests for the behaviour you change.+- Describe the change under `[Unreleased]` in `CHANGELOG.md`. The file follows+  [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and versions follow+  [Semantic Versioning](https://semver.org/).++## Related projects++- [haskoin-node](https://github.com/jprupp/haskoin-node) connects to peers and+  syncs headers and blocks.+- [haskoin-store](https://github.com/jprupp/haskoin-store) indexes the block chain+  and serves it over a REST API.+- [secp256k1-haskell](https://github.com/jprupp/secp256k1-haskell) is the binding+  to libsecp256k1 that this library uses.++## License++Haskoin Core is released under the+[MIT License](https://github.com/jprupp/haskoin-core/blob/master/LICENSE).
+ data/bip341.json view
@@ -0,0 +1,452 @@+{+    "version": 1,+    "scriptPubKey": [+        {+            "given": {+                "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",+                "scriptTree": null+            },+            "intermediary": {+                "merkleRoot": null,+                "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",+                "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343"+            },+            "expected": {+                "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",+                "bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5"+            }+        },+        {+            "given": {+                "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",+                "scriptTree": {+                    "id": 0,+                    "script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",+                    "leafVersion": 192+                }+            },+            "intermediary": {+                "leafHashes": [+                    "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21"+                ],+                "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",+                "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",+                "tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3"+            },+            "expected": {+                "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",+                "bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586",+                "scriptPathControlBlocks": [+                    "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",+                "scriptTree": {+                    "id": 0,+                    "script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac",+                    "leafVersion": 192+                }+            },+            "intermediary": {+                "leafHashes": [+                    "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b"+                ],+                "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",+                "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",+                "tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e"+            },+            "expected": {+                "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",+                "bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5",+                "scriptPathControlBlocks": [+                    "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac",+                        "leafVersion": 192+                    },+                    {+                        "id": 1,+                        "script": "06424950333431",+                        "leafVersion": 250+                    }+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7",+                    "f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a"+                ],+                "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",+                "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",+                "tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5"+            },+            "expected": {+                "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",+                "bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm",+                "scriptPathControlBlocks": [+                    "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a",+                    "faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac",+                        "leafVersion": 192+                    },+                    {+                        "id": 1,+                        "script": "07546170726f6f74",+                        "leafVersion": 192+                    }+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89",+                    "2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb"+                ],+                "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",+                "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",+                "tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220"+            },+            "expected": {+                "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",+                "bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq",+                "scriptPathControlBlocks": [+                    "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb",+                    "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac",+                        "leafVersion": 192+                    },+                    [+                        {+                            "id": 1,+                            "script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac",+                            "leafVersion": 192+                        },+                        {+                            "id": 2,+                            "script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac",+                            "leafVersion": 192+                        }+                    ]+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",+                    "ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c",+                    "9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6"+                ],+                "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",+                "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",+                "tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605"+            },+            "expected": {+                "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",+                "bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",+                "scriptPathControlBlocks": [+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553",+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac",+                        "leafVersion": 192+                    },+                    [+                        {+                            "id": 1,+                            "script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac",+                            "leafVersion": 192+                        },+                        {+                            "id": 2,+                            "script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac",+                            "leafVersion": 192+                        }+                    ]+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",+                    "737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711",+                    "d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7"+                ],+                "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",+                "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",+                "tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831"+            },+            "expected": {+                "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",+                "bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",+                "scriptPathControlBlocks": [+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91",+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"+                ]+            }+        }+    ],+    "keyPathSpending": [+        {+            "given": {+                "rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d",+                "utxosSpent": [+                    {+                        "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",+                        "amountSats": 420000000+                    },+                    {+                        "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",+                        "amountSats": 462000000+                    },+                    {+                        "scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac",+                        "amountSats": 294000000+                    },+                    {+                        "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",+                        "amountSats": 504000000+                    },+                    {+                        "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",+                        "amountSats": 630000000+                    },+                    {+                        "scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc",+                        "amountSats": 378000000+                    },+                    {+                        "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",+                        "amountSats": 672000000+                    },+                    {+                        "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",+                        "amountSats": 546000000+                    },+                    {+                        "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",+                        "amountSats": 588000000+                    }+                ]+            },+            "intermediary": {+                "hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6",+                "hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5",+                "hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f",+                "hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21",+                "hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e"+            },+            "inputSpending": [+                {+                    "given": {+                        "txinIndex": 0,+                        "internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa",+                        "merkleRoot": null,+                        "hashType": 3+                    },+                    "intermediary": {+                        "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",+                        "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",+                        "tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9",+                        "sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555"+                    },+                    "expected": {+                        "witness": [+                            "ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 1,+                        "internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f",+                        "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",+                        "hashType": 131+                    },+                    "intermediary": {+                        "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",+                        "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",+                        "tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080",+                        "sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d",+                        "precomputedUsed": [],+                        "sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d"+                    },+                    "expected": {+                        "witness": [+                            "052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 3,+                        "internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64",+                        "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",+                        "hashType": 1+                    },+                    "intermediary": {+                        "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",+                        "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",+                        "tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d",+                        "sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashOutputs",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669"+                    },+                    "expected": {+                        "witness": [+                            "ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 4,+                        "internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e",+                        "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",+                        "hashType": 0+                    },+                    "intermediary": {+                        "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",+                        "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",+                        "tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501",+                        "sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashOutputs",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef"+                    },+                    "expected": {+                        "witness": [+                            "b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 6,+                        "internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8",+                        "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",+                        "hashType": 2+                    },+                    "intermediary": {+                        "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",+                        "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",+                        "tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901",+                        "sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85"+                    },+                    "expected": {+                        "witness": [+                            "a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 7,+                        "internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103",+                        "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",+                        "hashType": 130+                    },+                    "intermediary": {+                        "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",+                        "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",+                        "tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b",+                        "sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff",+                        "precomputedUsed": [],+                        "sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10"+                    },+                    "expected": {+                        "witness": [+                            "ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 8,+                        "internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa",+                        "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",+                        "hashType": 129+                    },+                    "intermediary": {+                        "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",+                        "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",+                        "tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5",+                        "sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff",+                        "precomputedUsed": [+                            "hashOutputs"+                        ],+                        "sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2"+                    },+                    "expected": {+                        "witness": [+                            "bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981"+                        ]+                    }+                }+            ],+            "auxiliary": {+                "fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d"+            }+        }+    ]+}
+ data/bip39_vectors.json view
@@ -0,0 +1,1754 @@+{+    "english": [+        [+            "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"+        ]+    ],+    "chinese_simplified": [+        [+            "00000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 在",+            "7f7c7f91ef81f0fb6a3b95b346c50e6472c1d554f8ba90637bad8afce4a4de87c322c1acafa2f6f5e9a8f9b2d2c40e9d389efdc2adbe4445c21a0939fb39e91f",+            "xprv9s21ZrQH143K2LTAgxJMxVMKie6n9HQHMUohP6x2cx1TVBr6dxnL3mnSLRiXjiCM7g2ZF3BHzpdbFuhdeh7ZRrzv2EEjg5Tv7kgKZrqbVLc"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "枪 疫 霉 尝 俩 闹 饿 贤 枪 疫 霉 卿",+            "816a69d6866891b246b4d33f54d6d2be624470141754396205d039bdd8003949fec4340253dde4c8e11437a181ad992f56d5b976eb9fbe48f4c5e5fec60a27e1",+            "xprv9s21ZrQH143K2t2fMBqtVAVWU3JSpmEbbddwouLX8NoBbcTykD1Tm4s9api6K9zvoKSESUsA7aVxbZRunM5yrjZRNZnZckve98hxUorv2Uv"+        ],+        [+            "80808080808080808080808080808080",+            "壤 对 据 人 三 谈 我 表 壤 对 据 不",+            "07b6eada2601141ef9748bdf5af296a134f0f9215a946813b84338dcfba93c8247b0c3429a91e0a1b85a93bd9f1275a9524acecadc9b516c3cf4c8990f44052c",+            "xprv9s21ZrQH143K2cgeQUKgCSmaRVXFjEGThqrnNFmH71qG8z3bWqYcbX9zakkRxmDp583tqf3cQzmxtn4C2XqinMNb2HkhXBDYhekCB8AwZWV"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 逻",+            "08ac5d9bed9441013b32bc317aaddeb8310011f219b48239faa4adeeb8b79cb0a3e4d1cb460d2dd37888c0a19bef6edd90ced0fd613d48899eab9ee649d77fcd",+            "xprv9s21ZrQH143K3Zkkh1w8EbXYQWAS5ekbitA2WVrswJY9uEJzig2BtairT72n98ySwQUAhYBsLW9EBjJ1XUinrSb69Ty4mttMLnaUooJwsJ3"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 动",+            "b8fb8047e84951d846dbfbbce3edd0c9e316dc40f35b39f03a837db85f5587ac209088e883b5d924a0a43ad154a636fb65df28fdae821226f0f014a49e773356",+            "xprv9s21ZrQH143K36LufUjTLqXnTiY6ach28pUYMkJ63swx4FhjPkzqD9YRqDZ452whYcNzKpPC8yfBm1eomL2z3VLC4zcwU71oKvMQ5NnD4h1"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "枪 疫 霉 尝 俩 闹 饿 贤 枪 疫 霉 尝 俩 闹 饿 贤 枪 殿",+            "74187bbdce2dba25eed3b9aebdc65dcb7c61e74c58591451d47f9c7b7b17545a527880640bfb9cab36989eba1edddf57bfce7340697926de7f0b9ec1e0345c38",+            "xprv9s21ZrQH143K47jLpKzSgdpMLSKP2ZMLXHHUqYYGKbXNE4k3TA2czvJCx5JAJFNkWKZf2B1AbYoUBpc96YoiwM7yfxyr8gvfNNgL7sFNum6"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "壤 对 据 人 三 谈 我 表 壤 对 据 人 三 谈 我 表 壤 民",+            "e3629a601f4b87101c4bb36496e3dbd146063351f5e47c048211faddab78efdb91910f0eea5c8e53cfb851aa3e156b0bb5c501b83baaf5f5d4a1679a5bb7d885",+            "xprv9s21ZrQH143K4RfeWihCeh1FJL9SobvinRW4z76RL2X1TB6xreXgaMvJGggUgagkaNr7zX47YHEDYdzJmig8SG3Scuet8smspn7HicCtwHa"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 裕",+            "013c8d6868537176fac7bfa966e6219830008f03b650b0f18a12fd67d9ebf871c400c5f980aa073ddd1b23d60846e357aee193ce7644b574bf65e04cf913e39c",+            "xprv9s21ZrQH143K2YiskWzQq8kpFFCoFKKU4L8D6Y593dS2sExuVQ4GjnS57RhibwTWjnD7NTCE7ye4cQbCK6Bw674SHb3xWaQYH3NBLFCGYJb"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 性",+            "1981c3e3ddfd80f6e9ee1c5ef27ba2697df3d1468496f1d56ae3d8e0b3f0677bbbdfca954e48eb86fe6a36fc0f597bf18ea00248757a01e82182badff94abbbd",+            "xprv9s21ZrQH143K25ttGBGbx6h9VBpa9ELbpw35XQqDR8deXRyVP2AbtgJ79Nq2cW8KaDizbwuoHUYR1o4tLPhYvSCTNMft4ZEfiDztmjXKPCj"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "枪 疫 霉 尝 俩 闹 饿 贤 枪 疫 霉 尝 俩 闹 饿 贤 枪 疫 霉 尝 俩 闹 饿 搭",+            "b1eb831927f1c488e233725f9c409dd9bdb9342324393fa56d958e8842623d222510c322f5ba2899428ae08ece8bd87788748c67bdfa73588669ab816c5f3555",+            "xprv9s21ZrQH143K4VNNDqCgnETDkPiihzHpxC9wGE6TBGXaeEd9VkQRHQPotwheeaNFGHGKaPWv5zTqfknzgdWiKFC6DqaqBFKjNSmxas968Vz"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "壤 对 据 人 三 谈 我 表 壤 对 据 人 三 谈 我 表 壤 对 据 人 三 谈 我 五",+            "470e61f7e976fa18c7d559e842ba7f39849b2f72ef15428f4276c5160002f36416cd22c2a86bb686d69f6b91818538aa57ae1aab27b3181b92132c59be2b329b",+            "xprv9s21ZrQH143K3bUoGmLq8aXRKzvUhseqrXw1t7XYCirduP5XLJtVxCos3rYLDvW8V7pK3voZ4EWSdeXiKdbWNjxmiPRDfet23Av9VRaR3ej"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 佳",+            "8e6607a07fa664d6e4ead23fcc08caf72216d6f078c3b2e5be94e4b6e8d64c784d36bf9b70144fa05840e9a49899128111be5093a2b552b6ab76c0906e9b0e65",+            "xprv9s21ZrQH143K2ghKxX47TRr4GnQh3diJFN5rJfybjxuwr3xP6pafXrBhwXJsw4HwoiPZ1f6fFPR964eoXybV2su498Ant3kYuYKE3CszLsU"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "蒙 台 脱 纪 构 硫 浆 霉 感 仅 鱼 汤",+            "decd71d2824a1bbadf8c3942f43504a648a8db5f1cac0ae1d0f787728353002a12644b1a6b725147c91682e7f33aec13493b9a779a7dd8ee15a5d10ab21d49e5",+            "xprv9s21ZrQH143K44Xrktko35a7gPGVo91Va79LNer1MzVokcrYKFP6GMLAcuJP3fBSdbRuG2DauFC48H6LmyZLfkUyjpm1R2AxYVbnT2P5tur"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "父 泥 炼 胁 鞋 控 载 政 惨 逐 整 碗 环 惯 案 棒 订 移",+            "ff66373b70b72b34842f936bf3bb44d661fdafaee7740d574fed6aa2ef07783cb6111f2862cbd3fc5528e322dfe054557a74a568a1b46c020cb88938e2293ca0",+            "xprv9s21ZrQH143K3JLu5XeRDQA5RwHh6gUXaQfRf7ihtVguJvd6EFoAzsoyotvNTDZfC6cciies7fhcqaMRTEsZUSbRYqBdaviWRHatRMoHX6s"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "宁 照 违 材 交 养 违 野 悉 偷 梅 设 贵 帝 鲜 仰 圈 首 荷 钩 隙 抓 养 熟",+            "6ba622f907c61e29e44833b08441b7afa84889a48ca90ebf90f585e257662b2c1b0c35ad54088e745c73689921209fdd4b5b8ace5d850e366d7c2042a076e660",+            "xprv9s21ZrQH143K3dE2RQqFbapci6WBj47vZCkLVf4r14QsRZ6Ny2ck6s8tzZQzQaM55Tt4d2tRS9AuFrEJ4yBXsmP8yKeVGXT8E97iRVkDzCj"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "伐 旱 泡 口 线 揭 县 杨 断 芳 额 件",+            "7346996be5f2b02c67ec465c677197375b589b6e8871c842505b139c2d47feca75a2a941623d6486aff6b21c95193a8177960d123cf610f03f3224a9fa7d0eed",+            "xprv9s21ZrQH143K3A2p7cttKM5L39rutYgY4jqZ16z7hpAEdyiT8fr5eKdaHGLMM2ZmsUFNvGcSfyGMp1sz9nVyg3ZXop1hesUbgAvVBzx4rix"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "福 惜 怀 叔 筋 酵 货 科 牙 冒 辈 罩 悬 耕 浇 呵 连 级",+            "09098e00fcc1bfa7d5b9f0c12dfe1993bbd5a0915200a53fb40b2d6d487b969a18463565c1e035569796a7d8b99f82a4c4b17002b0c582037da95bacfeb422b3",+            "xprv9s21ZrQH143K4LdjFgCrosa76XBrNQ4imGB7XNc5MhfkASrmAHVNWYLNGY6kNj8foXBLS9aUD8RDubyj3NKWarmXxRQ28AeWVPbLD3a1FEq"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "仪 未 九 茶 队 梯 妇 孤 托 病 泉 贺 产 绘 吹 测 局 碳 征 墨 晶 帮 息 延",+            "6d55f2dd8d42f1cc5e0b4ef6e8a95200580ff4e29d2a3dfa7f9ddb1af0aa2e93780d84d952d39776a379ddc017847ea01aa01b85dc208e7f69891d5b7cbf2eb0",+            "xprv9s21ZrQH143K2tkCCjLXj2L9Ds9FttWwH6Eqt3uvihwUvpSpTm3RmRtSkapxASEUW5HXE6Qx2H1viBxXbLZVKxyfQ7fyFn6NcDspgJfdPaT"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "济 扶 块 言 穗 定 万 绘 姻 逃 颗 焰",+            "bf8dcc2fb4dc8fd2311943b527864feabfebd5fffb6641555519da3606265e895bab5aa1647f6e5afb0cb6ea4d0b27e8a9f2f49251b68ad6bf898937581351cb",+            "xprv9s21ZrQH143K2i53x2geCJi6A2QJpaHpxeGK8oeMxYs1tUrUwU3ddu9jHVucF3ePQ1koQ1TVFHnfP21ScWzp8xKnMinNUSW5itm7PicXWYW"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "虑 铺 目 祸 英 钩 尤 添 醇 嘛 触 独 起 赋 连 剪 邦 中",+            "07e1a2dc2eea79bb12be53d6fb662edf87796cad60e8d10a655ba39a95c5a68eb21f865a1b2f37d780286adbbddeccba3f7844c8a2b1a82029e6a855c713aecf",+            "xprv9s21ZrQH143K4V2oh58ZSb1CAbYkwB6ThJmTpRC51uKha9hy51R6RVzzbcEog3n6h4u7FtotS9arBvLofX7A3Apvcaedmeg1jkt7Vkt9TtA"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "而 怕 夏 客 盖 古 松 面 解 谓 鲜 唯 障 烯 共 吴 永 丁 赤 副 醒 分 猛 埔",+            "0402ae511062cfacbd5e33637a95e57e2e14fde0c5dd471fe66fc1154b6373802aa8641a78b91658052bff0a5c5bd075f01fc74b0d73e95a890430ff6f0e728e",+            "xprv9s21ZrQH143K4LSoFcKBWtmUHjzuH56srzrkpPfGx3i36UXpVUEtGvCuZ3egRTyDkaqRN5Ec1HXvXGSGTXLyAKzCRnTspi1D9NdmQmWqM5x"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "昏 途 所 够 请 乃 风 一 雕 缺 垫 阀",+            "aa7e38f64810007db63e31c479b9848cd5ffda839546749669bf53476dd036a33fd77d0a13d4418fb536ea78b028fc19533db4bc9e0e12a14a9432cb9fd112a2",+            "xprv9s21ZrQH143K31afj91bWiGw2aC2xHJwrWsMs9MuvEWZETkpWr15ZNF6LFjkHh53iD2bwJYEawvCCvDRaqDsr37fhapgGDkA7UATtwBsZu3"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "瓶 顾 床 圈 倡 励 炭 柄 且 招 价 紧 折 将 乎 硬 且 空",+            "2a6181cf2b069ba30a87228d54770ed5abf61e8151abdb0b27646a87a6100d4b7b496c3ca26f027d0b06724c6c5a469f43a7f1ffb7782e5afb01d143ca65973d",+            "xprv9s21ZrQH143K3ji6aQ9QQi4WkFhAqnrzRANJkYkRWkMqbr51mAV6JYUt85oR8Jt3D7DreQdsb9292ips6VKwpitAhLR7rfLdhYz7zgYei2F"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "柄 需 固 姆 色 斥 霍 握 宾 琴 况 团 抵 经 摸 郭 沙 鸣 拖 妙 阳 辈 掉 迁",+            "4dccb0a3578716975b840c51e279c2af728567ff42e98dd09b9e61742b41d9f30d411a501172cce9b7d5706a480dd4d4e7fb26021a36a74381156b09d251d65a",+            "xprv9s21ZrQH143K2hLNNA7KnimwonwFXCiGVM3K29DgZiTcVUJ8t8jkc2mXUzFZmoFgXWh9UhJFbyKM44Qm2KeGsrEevajZZfKyoLyFmyoDpUx"+        ]+    ],+    "chinese_traditional": [+        [+            "00000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 在",+            "7f7c7f91ef81f0fb6a3b95b346c50e6472c1d554f8ba90637bad8afce4a4de87c322c1acafa2f6f5e9a8f9b2d2c40e9d389efdc2adbe4445c21a0939fb39e91f",+            "xprv9s21ZrQH143K2LTAgxJMxVMKie6n9HQHMUohP6x2cx1TVBr6dxnL3mnSLRiXjiCM7g2ZF3BHzpdbFuhdeh7ZRrzv2EEjg5Tv7kgKZrqbVLc"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "槍 疫 黴 嘗 倆 鬧 餓 賢 槍 疫 黴 卿",+            "f38af46f6bc3222b0f5aa14dd5b8b506e51131510f2450ec9fb52c28617cfa59d436055fe542e25dfa01415639d2171e41796f169f8bbc18516941dfdee8fb72",+            "xprv9s21ZrQH143K2HYJ8dR81cQGust8Gm4MeyfC6off5BvCfffAxE33WhiYxB4aLV6meXP6QmoKZkLX8UJgrZPcA4A2EKU4iaPWenb6Wg9kxzd"+        ],+        [+            "80808080808080808080808080808080",+            "壤 對 據 人 三 談 我 表 壤 對 據 不",+            "33f373da1a6b4300dad5cc70d2329ed614512e3c8a423673c294110521326ca66753b9663bdd7c844f17d81609a410a61809dd5113823009f729e2f2f940cab9",+            "xprv9s21ZrQH143K4SV7MzxYhSQtxP5gWGCD75QiUsp7z1s7Kuc8b4V4F6wRqKvhdczy3qi2uNeN9Vw4PnKYJtoaFdHFX4qbxweuRDmQqgMjKRJ"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 邏",+            "cfd5f4fa6f2a422811951739b1dad9f5291f9cbc977a14ae9dd35dc8ab17aeec9ee6f1455b20f881838f4f945850765dd002a9abcdbe7be002ffcdaf6f63fdaa",+            "xprv9s21ZrQH143K2BptXPaTm7CCWCs4v3tfG5jGAa9zLqSTKNL1ah8veMWc53hio5grKVriWhKjNKbCA2w6svkL4NC6pkiRwVkbwazVXtjdgEv"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 動",+            "717f4f70c7550da57e42c6b49ac47b5bad3249605ed2f869900596c2de7653a8528380e5c31709ed9c2d19b868bc530158712e97276886b4863d036177bcab33",+            "xprv9s21ZrQH143K39TvGFp5nfiw3zXib4v4waTbxFYoDNoD4pp4DMBneg9MgSqfK4xL7hK1YjDEa5BWMXKVgTxMiNXNSdT3Wv59pD4PB3LDDKP"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "槍 疫 黴 嘗 倆 鬧 餓 賢 槍 疫 黴 嘗 倆 鬧 餓 賢 槍 殿",+            "2b219a8be0a8e27a6b50d0a74eb42175bd23e22cf4081518c9a74cbfe2cbace46f0adad8d390f8a2ac30feb26226db14fbc545d18ba0e56a853cbf103c92539e",+            "xprv9s21ZrQH143K2wC9BUJ9F5CsmV7a6PJQkxc9TT8gUrkoTXurPztKKosq5REGzdEzEuKn221vm2A5KnjrDBC1KcLo4VeYGSkkwxXWYrqSrXW"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "壤 對 據 人 三 談 我 表 壤 對 據 人 三 談 我 表 壤 民",+            "d29225f73231521784d98820ebf0ae4d827c5a9e0c0f8845fd63866cdc70b3a40a2281f3f6c6181c5a53e440528dbf83947a4b2056749cb9cc9c83dcd5c91b0f",+            "xprv9s21ZrQH143K2MQFKxX1ReLYY2rNunsfLpU8F5WRVWeLfauPhD5huvXEzxoPvmLhD3QtSj1Z5jnM51q9NrRjYBaHG5XJzfVsjXWYQop9pXz"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 裕",+            "013c8d6868537176fac7bfa966e6219830008f03b650b0f18a12fd67d9ebf871c400c5f980aa073ddd1b23d60846e357aee193ce7644b574bf65e04cf913e39c",+            "xprv9s21ZrQH143K2YiskWzQq8kpFFCoFKKU4L8D6Y593dS2sExuVQ4GjnS57RhibwTWjnD7NTCE7ye4cQbCK6Bw674SHb3xWaQYH3NBLFCGYJb"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 的 性",+            "1981c3e3ddfd80f6e9ee1c5ef27ba2697df3d1468496f1d56ae3d8e0b3f0677bbbdfca954e48eb86fe6a36fc0f597bf18ea00248757a01e82182badff94abbbd",+            "xprv9s21ZrQH143K25ttGBGbx6h9VBpa9ELbpw35XQqDR8deXRyVP2AbtgJ79Nq2cW8KaDizbwuoHUYR1o4tLPhYvSCTNMft4ZEfiDztmjXKPCj"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "槍 疫 黴 嘗 倆 鬧 餓 賢 槍 疫 黴 嘗 倆 鬧 餓 賢 槍 疫 黴 嘗 倆 鬧 餓 搭",+            "fd50ad67903b2046356e67e55d67309b6f0ccd7c23bfefd049a5b8a40d56c507d73a5517e2d2785f024a7794854594aaad845dd0fbd0432c25a96f2a7181a2cc",+            "xprv9s21ZrQH143K4TP9sQD1LnxuSy6WUe1hF7JMPo4qN6TMX5udfEcJh9x4PqbetYoC9c1hpx7RxP6VcgzdPxPCJ91De4R1TgGNVC9AFhxMwkX"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "壤 對 據 人 三 談 我 表 壤 對 據 人 三 談 我 表 壤 對 據 人 三 談 我 五",+            "d029fc9737b801cb4f9aadf5feed02a117b76ead7058e055cc39cb44864023eb492e6a15c68569d6a03a5b11bf15a456c64e1781a553589b47ab569801239a00",+            "xprv9s21ZrQH143K4bYXrWpQYgiSHDM1iVkNVnyqbpjyZksLS2fmxKCbwQjz3sBFTD1aFY4xWrYdHTyeFYjnYWfKGLc5WCkpokdAZyP9XEGJCsa"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 歇 佳",+            "8e6607a07fa664d6e4ead23fcc08caf72216d6f078c3b2e5be94e4b6e8d64c784d36bf9b70144fa05840e9a49899128111be5093a2b552b6ab76c0906e9b0e65",+            "xprv9s21ZrQH143K2ghKxX47TRr4GnQh3diJFN5rJfybjxuwr3xP6pafXrBhwXJsw4HwoiPZ1f6fFPR964eoXybV2su498Ant3kYuYKE3CszLsU"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "蒙 台 脫 紀 構 硫 漿 黴 感 僅 魚 湯",+            "27ca577f0318b6c6067acce7aefacd12bc9fbbc8e365fdc16bfc0ffd76379b0768dc56877f19eee4c1222dfb5a94a5516c5707e6a6ad070af9a0fe7f7799ac5e",+            "xprv9s21ZrQH143K3FtWQPZHP7Gpf5qgbvXqPNo5iCSfkGhWnATeqM5FuQ3YTx4sSciJx1MjVnbM3XQ16N83x5gNwcsVG7PTf1cRDhvyGZ45EY5"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "父 泥 煉 脅 鞋 控 載 政 慘 逐 整 碗 環 慣 案 棒 訂 移",+            "fcac6cdda6c67e46ea46e66d00df3cfb1e437aa05f1b280f5427c0ce521a94b5a01ab016d235b7944f36d76ba0a297968ae0d882fde95c96cae34e35f2433c82",+            "xprv9s21ZrQH143K4YANCnajhJxfsFsDEZGitTmP934osTHvUmTEtfSaYk8rmvj914uGUYaJH5ALjDgVyNYW5gRGPdQBFaqoSbGkHXhfdVkNVY2"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "寧 照 違 材 交 養 違 野 悉 偷 梅 設 貴 帝 鮮 仰 圈 首 荷 鉤 隙 抓 養 熟",+            "969aaf00b9af97a1c3fd0b7b35480aebf51577658067df966caaf5cace472d2ecdaa2978470be83463262340527c0564d8c57f86764d48e9bebd1ce594955a6e",+            "xprv9s21ZrQH143K3aULbWnFhpGS2h19R5JdizbLLLQTDsGmKP5A1sqQop1Ff8Q7NEetRHRNFR8AAsPm1kr5hGU5VLYKURZeiVe9k5hqVmhZ5zF"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "伐 旱 泡 口 線 揭 縣 楊 斷 芳 額 件",+            "09c172005e7dd81fcd55b87d13f114207ce7726376ea74a1b9085a799b2afbd5ac5526059e722987a65f858e5301edd5f4c91deaf9d7b4f9bcc38919e5ec3725",+            "xprv9s21ZrQH143K2sMUzpWRrPMF1Xx29Yos3Kah6H4E9YaJqDm2yEMXypoKdX1ugkn1Vx3k9Pr2LsKNQnHqoDFP5Jepm2PCkUS1GrQZps1wxkD"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "福 惜 懷 叔 筋 酵 貨 科 牙 冒 輩 罩 懸 耕 澆 呵 連 級",+            "3e09d89450ae45cc1a07ab308649f291ad5c1452da509d7269daef52ddd04db8bbb6bcb8a71322c4d25ed4686d910e84156fccfbac2838ba482bdd1e4b2ea693",+            "xprv9s21ZrQH143K4bLxh3ir6ziqU7t6URBK7uA8Md4tzAd3TAuBNyZREqEoZBevwu8Uw6tfMujFRWrZvCy47nbnKgDhsFaC2hvFJwRdkhnoM1v"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "儀 未 九 茶 隊 梯 婦 孤 托 病 泉 賀 產 繪 吹 測 局 碳 徵 墨 晶 幫 息 延",+            "d687bb89cb435fe1de166e953b41500f3717a497ca35c78322f66cd63e675fe0c8aba92463544631cdd6a985db03bdfcbfd839002ec609879e8768a3ffdb5fea",+            "xprv9s21ZrQH143K2LB4bPuHTErkGLt8DV5BbRsS82ySFPdQr57fzBY5D7VadhJHruxFzVcYdEbDqK8QWPSqj6LCiyQgFFurfnWWm1v6N2nGovN"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "濟 扶 塊 言 穗 定 萬 繪 姻 逃 顆 焰",+            "806655cee21d12c952d6a11c12e742809c4452b6e07458c6ddc2cc2a8920e308476f3c6ba7fbbdab3de3a7bcecd4de5dd82dee7a217d0cd071eaa2313ca390da",+            "xprv9s21ZrQH143K3Kcz7sz3UyDYJhSjUYujBdxFwDHCbqmKWqN3hSf1pjnqoxuAKsQXbnvrxzRBLqPN9BKxHUiYgn1DjhwaStTfZQTCsaTEWxZ"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "慮 鋪 目 禍 英 鉤 尤 添 醇 嘛 觸 獨 起 賦 連 剪 邦 中",+            "b609a4e17fa8c3c0b4da704b1699631f0d85f5b7bcc7d1488270551670b5393a0dfcb4d8eba9860c2c211324bbf3b587763ad1ac6a9e61a4e2e015bb6cc6a58a",+            "xprv9s21ZrQH143K2Gmewuzc56mbdKTqPezqWqk7ih9AdnLc9A2865TYtywU9sZ547mKYFKnbVatAuUUGtqgAz1ENkv9FU85jARHkCmtAGLmExn"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "而 怕 夏 客 蓋 古 松 面 解 謂 鮮 唯 障 烯 共 吳 永 丁 赤 副 醒 分 猛 埔",+            "8ce6b92bf95337a49bfd3d80774c9a73d05046eb2cb41789092a3bfbe7005ca668c427a42f1a93982d9076511330817b6d0bd49ba4f5a39e5756472b162f7ba0",+            "xprv9s21ZrQH143K41g7SsRpGA4g25xDiqjkhq8DNbJD7jgSksvt9kakaTzukN4SfVrUM2A9yGVPggP9eFAmuriRSpPLahm4ngaxvNHyQp82cee"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "昏 途 所 夠 請 乃 風 一 雕 缺 墊 閥",+            "e62457aa7f30c24fa46b90aeba2cbb9e77c28fcafa0c10dab01f5323eb1cef22f23c0e52cb5dffa2b2911a29992213c2cb20564af268eed03ea11292fff1a737",+            "xprv9s21ZrQH143K3JPEGRnwbdy5xdTvVdXEDGJX8gc385oC3H4veWiCVDrdeeFPWrU7Pz3PNMHcZrbutLY1UGs5dE83U925xKXLUrXXpz2ihKw"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "瓶 顧 床 圈 倡 勵 炭 柄 且 招 價 緊 折 將 乎 硬 且 空",+            "6b5591c758a069d3425bf93399398e8ef3e1c32c27f46e0a5284976dcacf25895f5d7747b84f38596247557debd133576932d394ad24c7a00aa24555fa668c5b",+            "xprv9s21ZrQH143K2hh1em7CqtG7uL1bTNG5hfP35X6Y8uwnTDiS9Cm87WNcZvmFHLf3JwoP2W82VzaRejpqr7oPnFLguhVSr5pWnG9YSV4SdeH"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "柄 需 固 姆 色 斥 霍 握 賓 琴 況 團 抵 經 摸 郭 沙 鳴 拖 妙 陽 輩 掉 遷",+            "17ec1a79121f3541e2d78ece35c8cfe7f5763b39d93fa90492c4beca26ee69d3aa7f4b1e6a2ac5e8225e08dded19357ee44b852dca425792842ec8eae09ae43f",+            "xprv9s21ZrQH143K4RoVseL4UENdN7Ag1WmcK7Q6Pk329krQW4RifHJ5sNizkG1PiyRXAouyL7KDFJSQAD1VarGTPftD1yZZAni3QczW8V5gNVG"+        ]+    ],+    "czech": [+        [+            "00000000000000000000000000000000",+            "abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace agrese",+            "872501bed75c98fbf943a67907bf394995f337e9adfa23687282d1135c262421715a0bcccfe2d3f5f8b72c8e2fa12a7a7267f8047b744557f4a9d49d11ccc75f",+            "xprv9s21ZrQH143K3rnjkVvSaFkwgg1J2tnUAeqv8SCEWTWdLVZiJsjM6Z5ieeUnrR1Ws6sDb8Guqp43CXVRmPooUs2cjwefSXh3EyXx2vmzZ96"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "obrazec znak uznat zubovina zeman skupina zrcadlo vzchopit obrazec znak uznat zubr",+            "68e1bd31ed5f20c9ab108c03b524e85209b0b27af80cb5d48fa71d03dbb528b73c2349bb8576f9b68825272984061594f520e54605a4898ba61c433d06bf5de7",+            "xprv9s21ZrQH143K4JXKqKovjKKSb7zgQcgbuKNNjaWyYBjjdny1SyXiPbjzSNUR6uLmNmPJ1NcWzeHDLqmopeTTbZ3DbPobsm4pbDrNrqhEDbK"+        ],+        [+            "80808080808080808080808080808080",+            "obvinit bageta doma amputace bidlo jedle arogance butik obvinit bageta doma akce",+            "067089f8edbbb8bc8ab6d0f3e29f250d136955745797a20b63fd4372627c51c4576ebd5fb6c6d4825d21f448cc24b342ce3b0117fedf41369cb5a6be77494aa7",+            "xprv9s21ZrQH143K4YKjBNHoBtUM2qhAvjVvXsBZJGAhogbqhBbgTvjRjPSgC5ShPUeGRCRTdRMYLZ7wWxRWwcvgQm9xX7b5udk9p5yfiekNMQK"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zticha",+            "04d0a733d43c640a4492b670a9549c60a358a681891cc2337a01a3c8288cd2941b7e057dbcf2dffd1e614cf5fcc9d38d9228fbd3ea5ceb508b8aacac5f35ccd9",+            "xprv9s21ZrQH143K3pcHonm1bGBQvbLf6Ty3c7HKCyzFr4C4DnvLPaT1JUUJ4MxkWyNhPpCjVsLMJEndqUocNxhqZ3UfFwxAsn7zajkE7n3yXYq"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace balonek",+            "b5eb0b74cb5f2c616e7136182597ab61dd94594d22f15ce6c94e04eb7336a56d3e445ec1279c1f04b861de5f7c6b2fc95227db53be4996de3ba87d6d76b09098",+            "xprv9s21ZrQH143K2Edeaippdsom8CKbdVxcorYqTexQoxhdci7pVuv6K1yFYnGunPP3d6oKyE3ax354UgfNUu6Kes1hvjvJcoyHfyjxwbjhFqM"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "obrazec znak uznat zubovina zeman skupina zrcadlo vzchopit obrazec znak uznat zubovina zeman skupina zrcadlo vzchopit obrazec zmije",+            "93ff13dee31715a6568609df3f7ea295d58728a65611ea03620d2105a0efbbaa39d8b6541b3b5a57a25dbdfd5006f0c58779a7ed196e25a1a97d1442e3f080fa",+            "xprv9s21ZrQH143K3SW9C3X4jc47HaXuDHjRvpCDxoo7PgTpMG5EZoFkDCkCzhs2yMvaUQidKKXBDChe2ns524iFEbrJFqNCPFvR1bjyRmq5rxj"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "obvinit bageta doma amputace bidlo jedle arogance butik obvinit bageta doma amputace bidlo jedle arogance butik obvinit bezinka",+            "1843be39a115dad287e10d256d2e9bb81244cefda2b7ead8a762f53033512abc7b6db26e2ebe8053fb82e313c24bcf62ae84ba4aa2900ca0fcdcb1affc38887a",+            "xprv9s21ZrQH143K3uYc2wzd6UfPLXqfQKsYJVyWL2HeCZ1YKhqd3jt5y4w9bfLTf4bAqvD3NbJ8ifa6vbTvHL2t87wsgi22RXd8c8Moknbzdcc"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zlehka",+            "43b7d9b1b25d046f8a89fb57ba10bed11b5273574bb820eb01cc0733a421f1c98ceb2db42d299e7e96aa2c58435e916821bc9d505525b3b5448ecd4c97babe0b",+            "xprv9s21ZrQH143K3LM3GwVcGcoMnK9UmQKxTaq1GiX1HsG5u76FmfUi5cTcH97WSXxVNLgURJCgL8RT1qDNsiQ9Sw1DDVAhiu4h4xMz6mtFDzL"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace abdikace branka",+            "dd2a9f662649585707dadc6e8b2df2c0e0e2691d53bacea2212aff4063ab4fdc79b703a7ce6744da31cd2ee12e56b9ee0f430a238b892fa660ed0ce879f2c472",+            "xprv9s21ZrQH143K3RHkXA6CEXUxT6nXGMM1zKuxgGQBD6k9gErraXywHoiqx4syweAftp5SCU7XSoFz5T3RbPs3HNZtZFa5JcR9ZD5TUmV15tB"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "obrazec znak uznat zubovina zeman skupina zrcadlo vzchopit obrazec znak uznat zubovina zeman skupina zrcadlo vzchopit obrazec znak uznat zubovina zeman skupina zrcadlo veskrze",+            "f4e4e2d8817cbb3925d6a0e8a2a466dbe1353a5885ec203030722607b8b5f229c71066c18681fda4291d0e323e4f6ba099b5b7efff442adfa14124fd07147fa8",+            "xprv9s21ZrQH143K4bgmXSbkNXn4ntJoQWUwRscM7yiZjzHbBFc5nMmgMeVcVJS6ZqXcWaTp6Phy8PAji336TDSNmiSWK7cmuzCSxkobg11RTN2"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "obvinit bageta doma amputace bidlo jedle arogance butik obvinit bageta doma amputace bidlo jedle arogance butik obvinit bageta doma amputace bidlo jedle arogance cihla",+            "bb8b82baced0db7764e102d1d1f68035269e84ec6c1ed0e09b2a31094330967aff9e1a490a407fef736fb8719c60bfb8cb0be9b27fce97c3b619409c195e2f1d",+            "xprv9s21ZrQH143K2aufrtmAWe6Ppto9BA4jSHu3XmKbMemyWEJ37Zfwxie44JJi8pXDxfjkSv8LxdKKs9rmV1R9SxCfAtcttf6g2JK3mRwPyzM"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zvyk zavolat",+            "3991e1ccc78af78d58cae577b786f7c950e1f23311d0c5f6d51b884d6142a6b4fc91a227c895313bf3d35731682678653101f51546717d60438d54dead32f834",+            "xprv9s21ZrQH143K26BAmMqZUKy3rpA3bYvGiip1tjQ2gnLwKrm9zucVU6n5PSJ9FLWeW1JjQJyZJ52QAV3HtevGNQ3BnTh91mAWgaacd6UenQ7"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "pokoj jogurt malovat kroupa holub malvice rachot uznat hnout kasa karamel potupa",+            "f3922b8086d559436ba2d04bc2aae4174e6504d7d4d451f7282d0b41a1b8cc958b45a896985e0b9316ad09c62f7d62dac85bc3d3e2e2423bcad3336412fd33f8",+            "xprv9s21ZrQH143K3gt3UuAosyvKpAmRpGfWuM19LVhrpWshTnv8o9Z2SPx7PxfgPQSZtm7U34cWo2NUrUtWCtXLhKDFZxg9pjM9KRde9dLwsZC"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "masakr odtok pijavice tajga upravit krmelec krvinka buditel zavinit lakomec flirt traktor kresba plevel kapitola tlupa paseka ladnost",+            "8c7c2b7767caa63139099cc5faf955cc582abc43494fc0f94b1f490ddfb7c221df55b663711755602c1354862c1da7a1241d17888f1be6f4ba7f58634758ea0b",+            "xprv9s21ZrQH143K4TFHpb3sbWvTgFjmH2b2r6TAWi1L9UxyL2hBXQeDwVgQmR7CG3agahDGRPWJZDWZjcAYwEGjRzycaRzTmbmxjaJxujKScF1"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "migrace iluze pukavec kaktus drogerie hrobka pukavec onehdy suchar veterina rorejs cukr mihule koza makovice uvozovka oklika inzerce odhadce zprudka spousta namluvit hrobka obsluha",+            "4b315b6c57139dfd19187b6029ad8b2fc6165dd97a43e59c4606a11deb192b25df5ad4e8fc3b4c2da9b9e80eae48946d769bc7bc95786f93b934bbc842cb8002",+            "xprv9s21ZrQH143K2ro2PraMEHNWQX4w969ZUPVBZFY8pViiRNPhsRjFbj2exS12cg2YCa6kkYUCSJBhuHCicMAGqj1ZtSvKYXLjaErhCBve7XT"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "sledovat ticho potkan dotaz carevna pahorek ihned nikterak hematom pokrok neochota cvik",+            "ce64d48ded9f32127bc7ef66c829e32b576927ffa1f323f0020f58c3256fdeb5ee2ec7bc257bb492e4fa1ae1e7f41b8affd9f68a2143e2d54e443e54d866e6e0",+            "xprv9s21ZrQH143K24d38NZ3eGjiZ5cuqmVJjsC2piMq6ZPSsEfgPf6F9XZJvfsExo6M2P5NCgdaUYgeLchyBw8sAWkH8joq4yMeNF1i12zXWWN"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "most uvolnit novota usmrtit terapie tehdy litovat filozof radon svrab surovina zdivo stanice pejsek ukrojit vymizet helma decibel",+            "8efd8d7625a41d02e53b8f363678acb389136ee9b19512381417e7f3295cc5bea28a7feeadf29ed8c2bd617e67feee3c736bed06f29ed3538777d58187458955",+            "xprv9s21ZrQH143K3Vd6GfvEYmC1ut3Z9uKS5weiaKT77zoz4uc4MaJMnQWsXYFLK2wp3DiARFbUyEv8yLqAirLmWMDmsXFhPioprwBiqQbTPb7"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "poloha koruna dobytek makak domluvit svatba paluba utahovat orlice jakost sypat podvod barva technika pastelka kurt ikona obvod mokro recept napnout kabel lord nasadit",+            "e69afd8c8b83713ce327570ca2dd9d588dd1f266fde95d7848059fdcec016e8f9f587a8fcbdbd061ecd9a5e1e90f51f9453af914df7d8e9b5758f91a1963a413",+            "xprv9s21ZrQH143K2zXve6cknQXePKGSMLedfyGDpgQZ5oNB7YKgyJqjXoZeQ4qj1vd2HZpnF77g1nqihMeAi7wnYd3c3hrgczNAadCKdetb2tf"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "drak uniforma kuna kapka tmel bedna evoluce technika vypustit popadat rychlost vodstvo",+            "cb409aa4d44356187fc1f0777afc3f0057bad31090e589e1a8a13911a0604e9ebe976bbbd1633b3320049e58ae4939591150f1b84fc552975d4c7b5774efd20f",+            "xprv9s21ZrQH143K3S5abSGNCkH4QWovuVGtqjrufboCgkqDvPGUtnUgPFNqXwVyWMD45MmwRFDYUe7Ny89sqvCqxPMWtq2CDa14t1CYxLjefgT"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "ochladit silnice exkurze zrnitost jiskra zprudka pstruh tlukot vytasit valoun najisto kralovat bobek uklidnit helma ubytovna pysk andulka",+            "a295037a0335fc58e639e1eb4ad8c6679386cc0b6696c6c0947b9d4420083740c02a3c7e429c698b3650c5d370b87427ff094e17a18778d917cf5fa274c89b23",+            "xprv9s21ZrQH143K3se5sFVgT6vXcaaKoaHse8ATQJAgDHVBfdwAT2BDgm5YoEPJKR7NN7o5ukvzfKvjZ16NsLG6qngxkRfQRKuSsqyFmcyjpf9"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "bavlna mozaika ofsajd kukla obliba kormidlo monarcha batoh chmura mdloba makovice obilnice popel pohnutka duchovno panika neuron okupant rukavice kouzlo stehno badatel sklenice rozchod",+            "a3314b5d32a47a746f1605029ef41e446e589ec3879b8509a93779bdb5df018c102dc93d3925b1bc04badc7b7e78ed3c79f05485a289d3a8f4731282f4b65f2e",+            "xprv9s21ZrQH143K2NiewxC1w9akTDfjsfRQ7yyv42HTukKMZ2qqx9zRrq4YYoHUnEeSR9kVa8hY8vHzCYhPxzK8VF4vKJhQNAjGpn94j9r7nEi"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "zajet nutrie beton kobyla kriket sranda elektron abeceda uboze lump vzorek potvora",+            "a5fe5fd9fc7a02f2753029978da50e8c1af2a773977ecfda7147c184374376fc1780d4c2516d23eb1559e9cf46cc6f60d30418ba05fc789295adc483f26641ea",+            "xprv9s21ZrQH143K3Pg1Wcq1fLNYrSSbfioYAiYMqkY4bEN9JrUFnJNLsK2Z7b7stHtJae53w7GD7rrDjJ56CCMNYJzwpdC54pSFkCGV3VYwGbY"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "slezina navzdory odjinud oklika ucho ropucha rohovka zavalit graf panenka invalida katedra odebrat deska metoda ohryzek graf flotila",+            "73716db54d10471971852df2bda795452cfe9b38aaef918da7a2216a3b4249d76b4627b603a03537e5dc2152e54c709acba85d361f4978733af4b7366b13a61c",+            "xprv9s21ZrQH143K2YLLxKi6GZjifUEoqww1zAAUd26JY5T54Z6HgEip3hkFKUfAAeVqEmZrdoHoahhm71MvBwkMTZALYrJtZfe4eLqMyZE1op1"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "zavalit genetika kapusta tvrdost dopad ujmout zdobit mistr splav ptactvo fosfor hoch pocit beztak slon poplach mazivo tancovat pravda uvalit konkurs surovina nazvat vypadat",+            "dba03d22ab6f6963abff6d9f9433a6aa733490dab58b39e3eef635c9f4fd6ad8242c3eecc4db0f26a447af06a089d935b3225e36615a07babdf2177ea3fd1670",+            "xprv9s21ZrQH143K29UvdRPWan6npJHgHs7bmiduFR6qWzf5z7gFuqqcR7XoHpSBamvgTn1Qzy3JqMSuyxJEnFN5s2s9mbMna7WAiKheLFAA4x4"+        ]+    ],+    "french": [+        [+            "00000000000000000000000000000000",+            "abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abeille",+            "3bf3366c40256d7e2fca716fddf8673425c7c7e444af290ee1edf1bbf095e6e78a7190253f3e46f1e2069345d4b05ac17b242faa225c0a3e4d268976744e0698",+            "xprv9s21ZrQH143K4ZsEXSdGmcpsqn4YxjPgHqa4DFvMRmD4oTkiFuYia3srKewVsU75LN4jP6PeXPYFWYpgP8B74tQjwG1GoQ9T8eJxGDjzibF"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "implorer visage sonnette voyage véloce pourpre volaille tribunal implorer visage sonnette voyelle",+            "ab9180b7dfdde74e5cf8781e5692e2c0b55afa8bc1987fa8e14e3fb83c88b195c53e9f939f8febc33d2958f5fcd8add57843cb318d8886130ef9c9879c826357",+            "xprv9s21ZrQH143K3MCzhsD85FFZ2d8vDN2QdRTN63gbU3qHjZxn7utU1kwYGR5bxQkssS1Tji4Tuw8vpTzDWogXxhbWYDKuZveeRBe5wiNsuFJ"+        ],+        [+            "80808080808080808080808080808080",+            "indexer acompte bolide abrasif agréable dédale abusif appuyer indexer acompte bolide abolir",+            "0c1ece83a464688d74744723d609e30e191d05ab8c082cf34bb2405bc4363dbcf6a9f83707b577d230728b3943920f876ec844e86dd0d117152c23802d25be3f",+            "xprv9s21ZrQH143K36u4b8J8pxUwCCnciUPZF3JjZsq5mPCDEpNWA1j4DnYDJ53NV7b6cmNTM6Je6DG11hJw8Fq1HXKEasrpxDC8WqaEsD5BPGq"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie voter",+            "7d2f168ce71ba3e40e74baf47a072a94e49973c0dbdb33a62b3a285ab167c704a85d6ce0d15cc6a4dd3bf1311334ee0d290ae7d20115863d5f5633b8dfacf2d4",+            "xprv9s21ZrQH143K2AsqW9AAdu5C4zUaV45MgyzBQAYbEKtKusR83UzLwCZdqDwqJ59ebNryoNuVA5pEiY1eBYqr64UGkwZGezwaceCqxPWia7M"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser adéquat",+            "93d81d146eccb7c624cc25daa4cd52736d64bdc0fe020940157e73c108a87ee34d94d7e9554e02ea0f9a7ea5574426220bae7c4959c197a6c9e2318cb252683c",+            "xprv9s21ZrQH143K2iCVwUFixFL9fh5f6XrcBfaLgRzozbBgzj3dTo7fBTn4EsQ8ERgq9hTPxFf2hgE57vJDfEGKwsegQFoo2mEM6HEgFavJkQU"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "implorer visage sonnette voyage véloce pourpre volaille tribunal implorer visage sonnette voyage véloce pourpre volaille tribunal implorer vinaigre",+            "dcf42783150cdb92672c9ea7d13f145401661f10b89bfb012a803ca7713e97181ee28ac327a982060a7f8aaa6e8c649ca2c5b83c24458393fe41739ced31d987",+            "xprv9s21ZrQH143K2Lq1fbvL5nedyALbSBmmm4EbcVduaPFArtunM6azzNy96pcKbLvVBQcFFDa3Agp54eMhCBH5Ehtbxitv525n2SrhYvALPiD"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "indexer acompte bolide abrasif agréable dédale abusif appuyer indexer acompte bolide abrasif agréable dédale abusif appuyer indexer agencer",+            "b039606212ccadb0d05c7a0c08605c5137028d0253d26b9ad6ee113f9595700d9834b2eec8b224975a6d9585d7ad39e962036edcf07d5b125b0fc225d519982f",+            "xprv9s21ZrQH143K3m5hJrQxzUdU4bDDvLSisrf1Cv5HQNCEzbUokwaVa8X1huTY2JaTPJug14EbopY3gtdfWjmVoo4CHLzuwjf5Jhdjgaqsh9n"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie viande",+            "e12d20a535ef5e9e2f87e05b5261bdb51451e052fe484feb87543f5cb7a8822c4aa0152492be1259fba00a28c1e95518a90f0645bdd0eb822516d37ac881f7e0",+            "xprv9s21ZrQH143K2woSZYBmSoRYygWNGUUrkLiy2i4FmReSqyc568kb1siagkBpHFj6MzLbfuh8TaWKDckfnfphrYyKTAG2rk5y4sGv4fjn5gD"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser anaphore",+            "0f3eec3279b55f3cacdbf1aef705a086078d7eb8048e402202572e7038e9487e39104b4794e88a42192af030a176b034fa36ca6641fb8128fd23c30806b96c23",+            "xprv9s21ZrQH143K4UeephsshHgatV7uj2D2DotKpGrWx6ShwxPdRxXtyQWxnadSvjT8Df7UDxmRuiFJa92evePgqwgJSG5pY8VPJ8hNiyGHr1C"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "implorer visage sonnette voyage véloce pourpre volaille tribunal implorer visage sonnette voyage véloce pourpre volaille tribunal implorer visage sonnette voyage véloce pourpre volaille studieux",+            "8f12b35fe92a7586dfbdab9721a91300d0dbe3185d0943021667e62fd5a643e0cf2443e544738c5234009aa50faac0dbb123ac847c31dc25d875c56fe39c6186",+            "xprv9s21ZrQH143K43qySSryByKtU1Df5foSz6kDEHUsR524HZJzxnKrrU3wTdeSvHVvpitC8nAb2kdeVWGREu22KaiaMF3YjEZQdb7htpY6CXP"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "indexer acompte bolide abrasif agréable dédale abusif appuyer indexer acompte bolide abrasif agréable dédale abusif appuyer indexer acompte bolide abrasif agréable dédale abusif axiome",+            "53ab1d10dc8de3a80171b5f00495a3b49e2c5afd486f8111b1afd0ad24f43eb0aab4acab1d4c51126beea32405947924c237157b29dca69fcf64eb635708895f",+            "xprv9s21ZrQH143K31JiBbFJEViuVwAuWSD5X8PKzS4d2PoSCxHpBXpicCEMKndL8TURUZ2zeoA4vDGkECeJS9wMhrRcmW6ATmPmmwC3ziuBRdP"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie zoologie valable",+            "b5e96f552ba44ec827c1bc5ef362e8cea68dd6f36f2c8640aeb171cf9b66198fbdf155fdbcf7dc505431068f972a92442f33cda0065afc1e9a7f5f7097ea6c6a",+            "xprv9s21ZrQH143K2umcmDhRrUZ8wsZ7ACj6rvFCEuDK1coHWpo1RwYPvy3dpWmvPjstSMm9fm3igm9gjsAesjqAU6Tnejizy919FSmyofhRTyS"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "monument dépenser féroce entasser comédie ferveur optique sonnette codifier discuter dioxyde nerveux",+            "d322acd69a849cce8719674eeb7cd76520de01ea35210012a44a5dcc19faf285202c3fb3c749a46d338ad54ddd398029ee308ee352a89f65180dbd3ff750dd50",+            "xprv9s21ZrQH143K4RswQfcyMEjx6Kc5tEBR43MNp7XJH5mWushqBryZTc97VNPNVfRop62sGwu7Tg3vsd559P1G8h6M2N9j1Zj1vVn1919uvmb"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "fiasco ivoire mardi révulsif signal enlever envahir anormal vaisseau essayer céleste sagesse engager mener différer ruisseau lutter esprit",+            "3c0c90b30e1a8bd7aafda95f92fb09bae64988e2431d6c3896c8502f76203652f0db1d4640417d8d3f00ea4de59f1719513f1c01145eb8ee4b0fd73d4c4f706a",+            "xprv9s21ZrQH143K3eSdUPkhpFZamFuu35iaZqRwFWijYkE54fvUKoSNBHkXetNx56yvcJSFoeHT9y4AFuWTcFFzezPuk6D6LqnNscjFnvdGYDt"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "flatteur cultiver oisillon destrier brusque crainte oisillon labourer remède substrat parfumer banquier flèche enclave fémur sombre jongler damier insigne voguer rasage gomme crainte incendie",+            "7363c9fd3127cb683ad39697f3a7282a06f1fd1ab1ceae8e2e0d7ab2766f3b8fb29162bf46e0d6a4917a0085b763f7f6f36adfdde742b6aa4ff1973149b5d239",+            "xprv9s21ZrQH143K3YEnEhMfv1Mwoc91DekvEqXxL85fFtRKtfFk3Jsqu72bUd35jHsTA7aBShvTmhhBrHhqGZ1uVg7RTwzTpnarDUoGU8ZXQ9D"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "prélude routine négation brasier arlequin logique cuivre hiberner cirque moqueur halte barque",+            "c46b545d5e7398d0b5344ecbcc20769fb0fbf674848eef1591725a1113f5bed0edf6d78925798cf87994157f43bd9d0eb5e6f3de7959e2e88f6a586e7499b79a",+            "xprv9s21ZrQH143K4U7WubDzyDun3RrpuVsFECM9u9tNxjhJhZFCJL5DJvGhKkUAbmmhqpwUV7UvgPPG2QFoDq7jYY6Neb6MnQmQYTo3RF57ZXS"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "froid soluble horde sinistre rouge rocheux exiler causer orbite résineux renfort vaste récolter maison serrure tonique cirer bélier",+            "93856e02d3ab2e6738958350f2a96a18183c0c02aa7cf50e4e6877b1d4f9eb4be1806b034e4a4a271390b7b6ba6b4209f5e293840e93a41a2ecb16ad47936c03",+            "xprv9s21ZrQH143K2wjyyXXC3Cra6k8WJ3CPEWc1EgwEyUNjLAxGVoegrni9eMik71k4L6wWxLtqcwgD9TzKLLiaSddwLFp4F6hJ4LuDBWqVJcq"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "mouche embryon bison femme bondir renvoi louer social largeur déborder rétablir miracle adresse rivière machine époque culminer indice frégate ouvrage gourmand déposer exulter grappin",+            "08cd47b905df56e3bfbca6d1ddb7ee7ae75d45f6e5928d337bacf34754d392c7225c611136148e130dc516cdc7ade8e8a95ba62ccfdac01a107875ce3e2cefd2",+            "xprv9s21ZrQH143K3BFxuNuyFSUgQQ3f4tv6HLktHoVxvMfBKVBow3i9GqeTF9oBbG87rZBX79QTCQqgPbn3ywWc8G4ZiUdhdY3xDAciXLtE9aJ"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "brochure sextuple épisode digérer ruser affecter cantine rivière torse muscle permuter talisman",+            "8e4635efc7352a6fa18723aff498fa297c1ed1997c0f3e77a11e65155b25934cf90e74ac66d207175507887068a5c85d24b825d06a31ce75651fc9893e509869",+            "xprv9s21ZrQH143K2CpRxCahnStujg79zUscDimXKHemoY4A1Kep85cPai2e8wJ4Y5R4uhrG76merXXjSuukqjVrzJMBPsGC8apQqHymDsBPf9e"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "informer poivre capable volcan dénicher voguer offenser ruiner tragique sortir glace enduire allouer serein cirer semaine opportun abriter",+            "ed4ed89acf10eb53fd67c9f81f4e8cbe39dafe42c27e942e67559a825c6083d3373a3e98215c37318f0f28c13546895e76a080521222f6d70a9528a582dcdcef",+            "xprv9s21ZrQH143K3N7dvQS9PoT9BemrTHWfp4qzUPT7hbGdZ5uMn6RgMwKerbmrrAqjrMGUskpGJaaorTXnbgtidgEbboJVjH18qDMkLFPf36V"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "adverbe fuite jaune épaule imbiber éluder frémir adulte attentif filou fémur idylle muséum mobile bureau loyal hélium jugement péplum encadrer rédiger acier posséder pavillon",+            "81ecca7ce712963df79d6611d2510e9fa31d307557a5eeea9513a9a940c2531472fec2c6988b70f649b8a3416f8f90f5c9c8f0ac4897f4a5a1304c651226f330",+            "xprv9s21ZrQH143K2ZnKtKjoyhEcsuEzLEzq5oC8sGegz59C3vf4T8VjvRN2UQQsHw2ndFj1UMNo1uuVtxKzjmNLvVAd2SURTgkVXKykFc1S8jG"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "ultrason hublot agacer éclore englober ravin caféine abandon séduire farfelu tropical nettoyer",+            "2efa119637c044ba28eb610178d7de49dabed93fc16f5af675aa661b731567ed3ad7aeb36a04adfbfb694bbd065f6f840ab80369ec3c253ca122deb208ef9f7d",+            "xprv9s21ZrQH143K4NWbtgfxvJK1Us782vFXDdb2bTK839KWEUZFnQTXXt85hAHXML9xkKyuiaiKgD1n6ytUZdgT8hirdn4SQ4DR4T4MPiNQz3K"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "prétexte grogner instinct jongler sembler paresse papier vaillant chenille louve cynique dissiper inoculer besogne flairer jeunesse chenille cellule",+            "887a87c38340befd47d650b73849907b5892a0db26e17ab55601e4e789ae1d0dd4bc3e7fcae0fae25c3e0d3315456fe8a5d84944d2b799cb63fb9544fbd0e568",+            "xprv9s21ZrQH143K4G36f6wnN8JWRYHdbvANd8fF6c1pjb6T11LKxw3Xch5iacc9vyGQDcf51GrF7kknKumq5csscAByQiC1gaY7vFygMghGtJ4"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "vaillant chance dimanche sécable bonus séparer vecteur forcer raideur officier censurer cohésion meuble agiter prison mutation filière rincer novice solitude élargir renfort gronder tornade",+            "e59bf24814adb55cfc2399e03d94e81df4a906ca5e75f36f2e297623ffc418b8202e9b1444e0e97234e2d55e194d45f89491dc9533a1c799fbb86c5838cc3454",+            "xprv9s21ZrQH143K2gBerhfhuxyfCEXGiLW3sxY1scEJUtvroAtxPqqatzqTzDmkgxDsL7C7MMbBk1ZKBXHpYiJziBM2VFbTbA3G1qjDkTt8PU5"+        ]+    ],+    "italian": [+        [+            "00000000000000000000000000000000",+            "abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abete",+            "d2ae4bbd4efc4aba345b66dc2bfa4ea280d85810945ba4e100707694d5731c5a42ac0d0308ba9ad176966879328f1aa014fbcbeb46d671d9475c38254bf1eeb7",+            "xprv9s21ZrQH143K3ZxfinfrsmnuKNwdvRtypJ1TEs8JuE6MEmAMDwsSZApCyBFopme4iR7RnRt9XKFprfLKs9vooFuFK6h2a2hzHuXTmE9md1a"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "mimosa vita sussurro zinco vero saltare zattera ulisse mimosa vita sussurro zircone",+            "f8c609647319a50116e9b7d1a0ec5535c6d08d6c958911fd2c8b2dfd55a61e63e9c6c60c22b5c3aec725acb41980e63cb3ed75fb80648092dee1bbbeab476a6d",+            "xprv9s21ZrQH143K3yxi91AAWZvT8797G7kL34h3453QNFSiQybwss1gmx2zYCmbg4tiZdYSEsd7arPWYd5MQh28VBvcEqvXSBRu1zYajyJnzFD"+        ],+        [+            "80808080808080808080808080808080",+            "misurare afoso bravura accadere alogeno dottore acrilico arazzo misurare afoso bravura abisso",+            "4025269bc4f7550bbc3c61592944946b0d4ac855a5e4582bf86069cc0c9429455cc40d84ba215ed1cec28e27ffc88460c38b9c4e8c486ae878d7c85e95b222bf",+            "xprv9s21ZrQH143K4Hh5BqryXtMu7QLbJC7yDh9kscJ4h3PuxA382w6YyjMMkiFVyfmdYFwfP8sVWR1eLygHmczccbzH7pTGXbeqAy54fNVA13M"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zerbino",+            "24182cf43f956410b5def9df90e3db0d6f3199c2ebd26e7ddef888ee3bece9101d132e449bb9e1c23dd9ccc6131d2f649c021ee591e88cef8d17cb434ef69efb",+            "xprv9s21ZrQH143K2hiPzq8SzzER9TQDFYpnYbfg1hE8wUYwcb7JNRyM4aDB14WfoUghRrBKFGffUx5YsTZvPbdCbuVMJG7egUZVsRTJCviSror"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco agitare",+            "2161a4b869f98778b6321714e2502adb11ea120c12163b46fa34e36442ad1981b911a2f9ec82b497e7cd206fa7af2f21a94bb6e4a90159965854784e1558658b",+            "xprv9s21ZrQH143K48ZSAvHY5BjAyPXxL3pREwQPZ9DxPagzRuK8f5TcKnDr3z2MSjB58uC871CjjjsNTaUa9BxEzEwraQutT42co4mfFGusE2B"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "mimosa vita sussurro zinco vero saltare zattera ulisse mimosa vita sussurro zinco vero saltare zattera ulisse mimosa virulento",+            "d9a6205a985fde8c2337f6cc6acf77a93d6ec7dc792551c01400f5d9aaa86aa943416c99fe60be141ca27ab333d9f96648b40b266d6b2d6a6e5b07c8939568be",+            "xprv9s21ZrQH143K2wPR9TuAQcWLNSM8X9oRjUCC3GN6aGevV1zfm8cwi2JtrzS21GzAaqd2MJDszoka7xiduQrDc666Wk27qYhUTagEfr4E218"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "misurare afoso bravura accadere alogeno dottore acrilico arazzo misurare afoso bravura accadere alogeno dottore acrilico arazzo misurare allievo",+            "cfb1f800cd5a0f7a8cffb12231fc61739f5f87c963ead5e205dd48221c3417eb1173d3209d9a8ffc4f00ab291bc22c1480b4a0a4fdeef9a1f3916d0ccbed5591",+            "xprv9s21ZrQH143K2EQGrM2arQhKSEhd4DwraP86UrUKsVcLvM2NujY9rPJK78rGUCD4Z3AG8tz8brxcSaDQYDw18jZz8FuT1H6ZdwFyUQVERus"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa vile",+            "05a43b9c258f6e83f4073fe4a66d6309e94610fe12dd5d598f4725e4e85ff1fde5ff5b1e61b40e09a481a98953f9dc818342172a460e5e6d17d9ab14874447e2",+            "xprv9s21ZrQH143K39Y9dY1q4sf3Bv7dkFfDasdSbWUKNx7GCsMp1JTo8KFDRq5TNkGr1wQYm3QZaALpFhcHWhPV67oVZQFMRFu51yVxeL9KDMF"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco angelo",+            "84055239f41c182bbfe6ede6db2e8bc4a97cf86746643b7ea6910c71d67bb2a678a97ecd378cfbf59e30db720b1cfde0faaee73afd3c5deef2188e307d04442c",+            "xprv9s21ZrQH143K2b5TRb8ReAEzasjVej3ttSzyy5YRu91SdQKP4XUZtgeUipuJx6YoArxkiRSBU5eP2wu6dmgLhgBQJ8Bx5UXmwFudc423DdN"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "mimosa vita sussurro zinco vero saltare zattera ulisse mimosa vita sussurro zinco vero saltare zattera ulisse mimosa vita sussurro zinco vero saltare zattera tarpare",+            "f0e226efcd929216020a9e8f879f06b146d28fecd2856bd401a62ecc0ece8bc6ea717e3f9df523a6a00bd4ca8965e0498d63e779e3156dbf174ebac74ad7be31",+            "xprv9s21ZrQH143K2dys2Z5k2tZoxJuBENLaf3cTEpb7JUeXDp9i63vbPMqVNqHqx5bGJ5tdNug3JFWJCC4amzYLoFRi3YS55MiHnnUN6w1V57h"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "misurare afoso bravura accadere alogeno dottore acrilico arazzo misurare afoso bravura accadere alogeno dottore acrilico arazzo misurare afoso bravura accadere alogeno dottore acrilico baco",+            "ef549c1e44a7b183031b41f9f692795406de605e43ecc628911a38d7c92f392660c48313a08cf1a055a420d4a8c6b12bef7ff354c903303bc3a5dc12948ff5be",+            "xprv9s21ZrQH143K2mWU418GviWbPrYTveuukEHsYJRn8aUxV5PrE4XacaDX74amiRkH3m3Qmnz7YPRRXkF33z5472Trb39RksWRmcJitkT5uJx"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa zuppa vedetta",+            "5089f33aee7852d86a01e8afbfdc8a0ad5af51538e62e3f007d098fa4fc9817ddc990fa87b7235273798e2df52228b62738df923bc2d711fed9cc0558b3ebfec",+            "xprv9s21ZrQH143K2VqjZNjnyCMUrTw6XhLt2BsaPVpHD8MKpKq5mq9S5SLyMSv58y7nCofNGZSgQQpky6aujCURjVuAM9jCcN6xjzbeRQYYACi"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "pesista educare imballo formica curvo imbevuto raddoppio sussurro croce eppure epilogo poligono",+            "4ffd8b7879c0c6d7eee14682a26465d6429b8b921d6ea3299fb8a448d84d19b47ead5b23fd14449539cbd358abd19a23560dbd8c4bf6c153d98ea0fce7f474de",+            "xprv9s21ZrQH143K2bsiFmYu4jzf5fjcHrrEarDgY3NYhmUEU5bxz3eS3NwS924WYnioF6rf6Fij6XQdmCNjcCJwqdFJ1AKKLMxa4obLnbdFgA6"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "immolato mummia oviparo sigla stirpe fonetico fosso appetito vasca galoppo cigno solubile foderato pargolo enduro sociale ormeggio galateo",+            "188305ae9b45e400f6a3ad839061265f36e6050118283b85a3ea842aae1cca29c808978b3b0e297dbd794b74916fc43da57172e90c9fdab930638863c3472522",+            "xprv9s21ZrQH143K4RNJpMYxxAuHtA77jzNEAjh3MznXe6FejAGGUPHTDUFL3z6LB1FioxHnMirHNcHyV4QVsXs8rZACW8YSJcNo3Wqjg7dNoUC"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "infatti dire pudica elica camola deposito pudica nobile servire taverna restauro baritono inflitto flacone ilare suonare nastrare dito montato vulcano scrutinio lisca deposito mirtillo",+            "093ef04fe24f1c45148f3d4d9a54fb033638011507418cd7cbd91a8fa12157e1cbd9d095b2a660db26e8d674cbf6033a384954fdeadcd7c20cbbd3da46d90f1a",+            "xprv9s21ZrQH143K2ZBg22DZJyspAmZcVMa1efjpsz89Hmcsu7dPZ39sH5RCCNr2kBRNFQUUmcwsNUTyZ6UGk7zjZKgCdXrmxg8gJ7FBCEeE5Au"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "sarto smottato podismo burlone aria omissione dipolo marmo coricato peso malto basso",+            "d9e2a2e18ca8173859b0030186941149f630483cc9fcf3b189e5752d4f8b7dce2b285008f52ff1301dd2e2a673a4c76f8ffec9f8617fd577173b90c6af95631f",+            "xprv9s21ZrQH143K2FR5gEkuFMhmYGVyUKiSrZspfn52HonhhbvwWrn5fL7aST1yxtAfd8cQSyWVGH9TamV4SPvyKRZbSV2U7ZTurX1Zoe86ngE"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "italia sultano meccanico strappo smeraldo sipario gommone chimera raffica sforzato sfamato vendemmia segnalato oscurare staffa trio cordata benda",+            "c40130a2db00d82c2dfb127c768724c522cbf7f47b464061198c65e9bf4e3879262dd112cb7a526bf4450785e9f7f7e7511f05985d9104d9e75e1baf038c91e6",+            "xprv9s21ZrQH143K3QTUdUYC375Cvtyea83NqatsgA5z3BMp95Fbd7QfQRvzqgjuCMU9zhSgiVtAYuDoR1GK9gQRP8juxnFR2Hd4v3mNgfMySGq"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "piacere feudo bisonte ignorato brevetto sfida onorevole stufo nulla docente sfuso perbene albo sinusoide orologio fulmine diradare mitezza iride rata londra egoismo gravoso luce",+            "e384b6486328949618978c6d2607df3e7a9db9acc94ab24183aa4e7c1af0107ecbcee2dcead27d7f20acaa427d3d6eeac620ff24ae4ac2ba3b6ef01585418f25",+            "xprv9s21ZrQH143K32TYshJvJnEHVnwukKyvaQauzBPx42JYyGT1FfyUvjT5iEfYuaETkYiAUJE434hs5Qkh7FRSqK7kwy5GwjsfS4S5txFKwbL"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "calmo statuto fucsia energia sodale aliante cedibile sinusoide trovare pila rinnovo tiro",+            "5d5faba1d0db08a9f0cdb602e571a9b73565707429d2482e4fcde5a9bac1728b053c65853199fbdba73716bcb8da0616820fc817a309c99607dc56dddb34c344",+            "xprv9s21ZrQH143K2h5d8kmFvaQT4xHzXrAVwmwAMyF1p7jBg2QoE8RGGFWe1NyqcNPhy18CJ6UKcvHTXkijihhLE18yrpec1DxggywYDVWGiUo"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "modulo rubizzo cefalo zavorra economia vulcano prudente soccorso tuta svedese limitare fluente amico srotolato cordata sportivo querela accusato",+            "7f7bd54b8bf5c99a949d3ddc1d4baeec78e503f14ddd20500e307be89e940e5ead97530c014c33053a9b0c942094ea1bad649b2d23d6288dea8fcfe2e3a83c52",+            "xprv9s21ZrQH143K4AwqRHE1Swki4WW3ok7NF5cCs57wYAEhcaDhw9oVh1xwSbCeofP9GMZkTmZo6kSjj5Xh36tuuPebZhDBQRcgRnzLaSgrpL7"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "alcolico lacrima muto frigo michele fessura irrigato alce ateismo incendio ilare metallo pilifero pergamena canotto opposto manovra nemmeno rimorchio fisico selettivo aforisma sabotato riciclato",+            "197457046ab546a171b247c54bb8392aa2ee2d40f07831019776745f17aee46fe9f1611f86f9d7f0cbcacc03ce696082fc13529ba0cab0d57f76934383be0f3c",+            "xprv9s21ZrQH143K416VC54NFDGXsQeXLSAf2JaUD8jQR4SDqAFq2S3BdcSRW66avfcoRK5FsPPCuBKaShrskgoGchxdAeBzVMPFHrJkCwAWgiE"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "utopia melodia allegro evoluto folata scuola carisma abbaglio spillato guanto unificato pollice",+            "53c4c5de8a16381908e397fcb8ce5dcd8c90911d9b538afe83862468816889768d94d040bd249f4eb25d915b05b31addfa0b06d89fe15f521fbf3c8545bbb434",+            "xprv9s21ZrQH143K3u8cCFUJiaxUCKdGZ9Z2u76hUbKNMoCy51gq982aahsMbN5ArmhR1VHFJSMKLsFLkTsSX2gwjrEfj31QsDqCh3nE6FmcV4e"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "satira lusinga mordere nastrare sposo responso replica varcato colza opinione distanza erario monetario bici india narice colza cilindro",+            "5c8c80b1e440dad220a295b282fad7e8a44bfee5210d853fd52d26e8a006787ac7bf4b0a4f81d029e2ae9cdf71814f193bbb23e4b3e149d2f99b03e2417b39a0",+            "xprv9s21ZrQH143K2gMABxA98Be2P18eaj1zxU5qV6sCiMT42cuvLQ3dM2VBnEAvpFwWEDtDcbSkteNAF8nUuCyBETqpAeKmqV6niTPFEECGcLx"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "varcato codice enzima spessore brillante squillo vento insieme scoprire prugna circa cruciale peccato allusivo savio pilota inarcare simulato precluso sugo fegato sfamato lusso trono",+            "e89b83bd1a5fa859922e0045acc84cd04edeb4bf6b5352d197fbed50af0938b17bca7ab9beb8c882d0e0a67597d9e14e88c10e63b824e9206d2848fbb8a55b64",+            "xprv9s21ZrQH143K42yTQvEavGhekzrzpxHY1nspnh5qcgThCuNVAyJPWRMeznMV9FnChg5f7k51DBMawNZKWTFCLy2az1CnyGgCJ2Jk5PGb6Qv"+        ]+    ],+    "japanese": [+        [+            "00000000000000000000000000000000",+            "あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら",+            "5a6c23b5abdd5c3e1f7d77ad25ecd715647bdafb44dab324c730a76a45d7421daccee1a4ff0739715a2c56a8a9f1e527a5e3496224d91293bfcd9b5393bfff83",+            "xprv9s21ZrQH143K2TDo8AAss7eUkUqLFzBnypFpqjQUMVUrSMvrrgLiRxQPrYnhfoS9NPp3rex725rcuN8pkDL6pwqWfdPtiqa9ib1B37vZwfy"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかめ",+            "9d269b22155b3c915b09abfefd4e1104573c528f6977cde89c6a68152c3c714dc6c7e0e62f221c322f3f76e4d0bcca66c06e3d2f6a8d70d612c87dd6dee63976",+            "xprv9s21ZrQH143K3kavBMu7K49k18vjQHhNL1ciMgn7S9kDMKdyK1vEpF46UWyoXCvdBLEp8U2bhissPkC6iwXjMgRXyQ6SHbyYYGcnFqNXTW1"+        ],+        [+            "80808080808080808080808080808080",+            "そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あかちゃん",+            "17914bd3fe4b9e1224c968ec6b967fc6144a5795adbb2636a17f77da9b6b118200ad788672fd06096ca62683940523f5178f6ce3845c967cbd4ad2b3643cc660",+            "xprv9s21ZrQH143K2NAPUK7UVbLB4Dd7Hvb7fqysvFyKES5iujX4BfrwUmy1wvWJb3kBc1Zs2jxTTBxBPHuHziB1ZWWUxELrn8g8VLrbjaWDmRe"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "われる われる われる われる われる われる われる われる われる われる われる ろんぶん",+            "4bd21b75de4f262b0771a97d6fc877ee19329236ced6e974c4c81a094a5f896758033f7eae270216d727539eee3bc9ba5cad21132a1c6e41a50820e0ac928e83",+            "xprv9s21ZrQH143K43f5tXeZRJ2RMiS6nLhxcqvopg9mc84xTiX1UjLjVQDBHrwmypY45SPscFqT7zJet5b3riay95fJMM4dfZXtoBTcKKkKEao"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あらいぐま",+            "a59401a14bb821cce86ec32add8f273a3e07e9c8b1ed430d5d1a06dbf3c083ff2ffb4bb26a384b8faecb58f6cb4c07cfbf2c91108385f6773f2fefd1581926b5",+            "xprv9s21ZrQH143K3VMwP9nGq47t86uaVENmykbCRuUDKFDSfgDFjJUFuF88JfPPMzwgwFPoaJaAD3YeqrCQBFY5S2jaCXXuc5Vg7u9jb6iB6XF"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れいぎ",+            "809861f80877e3adc842b0204e401d5aeac1d16d24072f387107f9cf95b639d0a76141ab25d3dc90752472787307a7d8b1a534bea237c2bb348faac973e17488",+            "xprv9s21ZrQH143K29NaQCx2DxPkszvvbYj5FmZ7RMMAoCE932mUkfzaAYFf18JiaFC4bJujA7XAsm7TFddxdkXfn6U34te59Bp27CWp71mw55c"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら いきなり",+            "01187da93480d0369fff3fc5331284ad6a60cd3ce1f60dbec60899191afa2a2b807cd030038a93ddaf14d4f75d6de4a0e049ee58c92197eb9ca995770b558486",+            "xprv9s21ZrQH143K312KY5MRB74dWa9BDHG7rV4oK7VAfA1eVKPDddPEUTXRZ5ShDzVQJp7d8q8xQLPYVcHYVuxb7sUo6EQpJUytZRHxyytSYbV"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる りんご",+            "a1385ef66f20a905bbfc70f8be6ecfec341ff76d208e89e1a400ccea34313c99e93f4fba9c6f0729397b9002972af93179dc9dd8af7704fa3d28e656248274dc",+            "xprv9s21ZrQH143K2LmZMWVM3JxKTHZEDZg1ZEUZH6hx9yJhBWRSgGGYD8TAaPa6MYto1t1bBXgPYFMLx1Gidw8fJADdFNzvqrAcXiadUPfTTVh"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん いってい",+            "c91afc204a8b098524c5e2134bf4955b9a9ddd5d4bb78c2184bb4378a306e851b60f3e4032fc910ecb48acfb9e441dd3ceaaab9e14700b11396b94e27e8ac2da",+            "xprv9s21ZrQH143K2WD9BSegbAkGzg4XbqeY7gzCYGWaWfmRifMJJtmDo1pjXmyuEwPnKcLwTZ1uqosvTRBAcPdUUvdHxY6rKj6R28vWVWGLKuu"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん まんきつ",+            "79aff5bc7868b9054f6c35bb3fa286c72a6931d5999c6c45a029ad31da550b71c8db72e594875e1d61788371b31a03b70fe1d9484840d403e56a1a2783bf9d7e",+            "xprv9s21ZrQH143K3ZPQiQ24sxzu5PsqazSLn1W48saAFWiDhugDPp7dKB3v5JLfMqzTbqjdwE8P2UxFSKwnFc5CpgMaH2dSmoWDRuaAbrZbJF2"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる うめる",+            "0f46c02350b3f1227c3566dea2ff0f2caf716495a95725b320a31a3058d5d62596fdb816be75909d2c5f7094beb171dc504ea8ea60f5e2e40bd8aa0d9339aab0",+            "xprv9s21ZrQH143K2TSJ2oumYNzqQGKmvehh1NKNzAjpu6Ue5yPvtzFvX8aCvBk2eTg8TJfwjiGLfA2KCiajZ1VXBvtQXqk7Wryxgps4BYyNmZt"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる らいう",+            "a0705c2feebefb61509dcc49c57586c35379c1981c688fc1d452da44443d9a651a374f1ad2ee3d7847b50655cf9241d7e607be436c0df7c8bac42f2a82985a79",+            "xprv9s21ZrQH143K2k4V9TkTiFB2LviDq1oSHbRha8AmnPvBtRRVAnf9WJERojPPdki6sbiuNbxv91VhhdreJnaZh29Ay892Mj6KB2aZnysqfvR"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "ておくれ げざん しねま こりる きぼう しねん ななおし ほんやく きない けむり けまり てんない",+            "b80f83f27ec3a6cbe804be0661e9bcc30583484dbbd37f689d4952bdf4ad29d9b9f5774fc4c87b733169416418b81f272a3eab37feb22f5c8f6deea6bb08f8c1",+            "xprv9s21ZrQH143K3VLugmPmzLDwfFeo8cgoZ2ajCQwWhGKfXut8C1XhdSNuAnXjz5W2bbbNPmr87yMHAaPMaUhE6MNnfGc21WRc5Lj94Tbbnnh"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "しはつ たいちょう ちめいど ひりつ ほくろ こやく こんかい いひん よろしい さくら がはく ふっかつ こまる つごう けぬき ふすま ちから さくし",+            "926ce4647a8f91552ae00efa8880ed7e43b6f8e9cf51c38851b0e242569ea96d77a19c777d28dc33d8912c3e3bc6c59f7a82b6daa25add2c39a492fdebae79da",+            "xprv9s21ZrQH143K3eVmFcDa4FcZCGeNryYBv99mdgidpSeFBt6ppQQjE6MXegs3xmT4arCDXvtg6dGpr7vFY8A9bzzeJ7E9jyzWLPDSrixwqub"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "しやくしょ くちこみ どんぶり けつじょ おとしもの くうぐん どんぶり たずさわる ひたむき みうち にほん うわさ しゃけん このよ じどう ほめる たいよう くふう そんちょう ろくが はんこ せあぶら くうぐん そっこう",+            "94308a93dc1bc12f8e917b2445581240d83cd82ad3c52f9ba4125aa6ce5490a3624fb3dfd7e22923ef7ff3b778157e8bec76392b122bf465fcc56ab4f5a73401",+            "xprv9s21ZrQH143K3ResRww4txupLREzrNjYYi6X5FKpKvFUoCwhJS1HtSn21gDkXcBYxQBd9RngafHfWqktMmLgPrVJTisEQbRn4strGawHGpQ"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "はいち ふかい てんすう おさない いろえんぴつ だんち くださる せんちょう きさらぎ てきとう せもたれ うんどう",+            "5fae92448afda85f10a50236144cee3068ce21e20a34447a9a4b6e9d5b000a4347a151d7024c2068c4d3c29c46a6f541a94b98624cc25b2c8ff42cafe6a3087b",+            "xprv9s21ZrQH143K3PD5fc3CCepnLDsenZAqyKpcbquY3BR9sfosi7rbjXMo5nL9qUb91FwYUFdN9wJt9v95YoKJJFm9V7a2Xb1sZgstQkZ54cL"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "すいえい ほとんど せんやく ほしい ふうふ ひんそう ざんしょ がちょう なにわ ひはん ひつじゅひん られつ はんぼうき ちそう ほいく めだつ きさま えがお",+            "a49ff09e55b62b8fd3d4b88b4fae2c9062e18c06105c796505fde3cc7655cfb9c922c02817d18dd40832ecd19d80a71fd62d915c34cd8d95fc55591d12b6f677",+            "xprv9s21ZrQH143K4AKun3sek391JX1qnWpxriP3eyJviCUq6ouiH3RDzJ1f7Gm6c7dgAkxn18EcaZ6CBNvVkmLxVvB1fdBdLiGeHXf69j4eCR1"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "てそう こつこつ えんちょう じてん おおや ぴっちり だんねつ ほそく たなばた くらべる ひまん ていき あんい ひんしゅ ちきん ざいげん くたびれる そなえる しんか にいがた せきむ けしょう しあさって せたい",+            "55d101db3cb8872853a3e84ec97fdeac63fdab33d92def4dc4694beff0f504da29f953bb463d9cbaf0c4d442672d40c5a58d6aed35d5fdbb2768dcc482b59bc0",+            "xprv9s21ZrQH143K2dmFtHdM2tu7mDdKwsbeU7ekYU2dsFDBt7dNbXcsLTnN2MNi3hQssGdcPgEfiXNJcgnoo76t1nAHaoyVBXZ1z6hP8L2UGTs"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "おたく ほうりつ さいかい げねつ ふせい いいだす かいてん ひんしゅ もえる てのひら ねいき むいか",+            "1b0fca340ccf977258987a793b3e61b0e20291f8c27645e74621e87c01d0e881cd601bbb0ed98388d22dea341498ed74aea32975a56bd3bdc6027196b7a21640",+            "xprv9s21ZrQH143K3Z6vsGR17YLtiGF2gTkCm9Z7XnJm1ffrLQ9ah7AVye6NyACmsWLZBoREcdCRS7tq4Hrx1NDWqHj3MctxWixcH7XBuigtJri"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "そむく のぞく かいふく ろてん げきやく ろくが ともだち ふじみ やおや まかせる すらすら こぼれる いぜん へんたい きさま へきが なたでここ あさひ",+            "d7919c8a5458fa4ff55431021938b15bf39b7542df902f371625d9f064a37b1d06a34cba920a9c8b8d6bf34514f542f5ee91b9eab81a063f9acfad932b43c62b",+            "xprv9s21ZrQH143K462YnNY39Q7Pq8ZXzCXs9k9GexnGbyjVvu3QhrNLCkPxNUtaKZBzqffnmiuU4ZiA5UQKtwVqr1WmXyM3QjVaVg8Cc93xMDQ"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "あんぜん すうじつ たいふう こんぽん そこそこ こたつ しんせいじ あんこ うしなう しまる じどう そうり てはい ていし おめでとう たんまつ せんげん たおる ぬめり このまま ひいき あまい のらねこ にんそう",+            "1003a30a516cfa1c30de2a53fe6c5936dcb8ae893f944f459ea4e1f2202716320350dc2ee5d92289dae3c5b1771fec863fbbc40146fed04d0855c6af70b0c7aa",+            "xprv9s21ZrQH143K24jAnV3a4K7xAoD486pMXSvAj41fkB61DopHqcyznUX1zmkFgEGBpNkXi6dckNcpcwXB65i71eBwP25t24QZHVRGJYURZ2Y"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "ようきゅう そあく いきおい こうつう こもじ はんだん おんしゃ あいさつ へいたく しすう ゆうびんきょく てんぷら",+            "17593d396c66d776bc15c06e5348bdfa38927daf92402c335041dc500d7c8ea9eccf4f4d1c187785b8c9128d06bd7048a1706006fab82abece74185448caa811",+            "xprv9s21ZrQH143K3P24yRmaXsA2pntRyM375Kb6cF4P73bMQNKyXUPPR86X5o83UmTi4iyQWaCL48EoJUVqC7KxsPXbrAC4nKxjMh7FZJcucRE"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "はえる せっさたくま そんみん たいよう へこむ になう にっさん よゆう きあつ だんぼう くねくね けらい そんけい えほうまき しゃうん たいむ きあつ かぶか",+            "2567b4dc469b5d1d7c4aece40e642dea3d5cebd80a577b5a4d72fc2b60da6ca657d3a01270c47530ac71f812e648bac01aaadc62c444749fddec8982430fce7c",+            "xprv9s21ZrQH143K2UfZfzss5CkDCtkRJvLN7DmM4sWjUaYMF4rfeWDERACvg7jGSSkGeFLrcHdpXUFty7wJ5yzyzcbRDqv7b9H6usZ16PnKy2c"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "よゆう かんけい けぶかい へいこう おかず べんごし りえき じゆう はんい ともる かほご きぬごし つみき いきる はかる てふだ しほう ひろう とくてん ほったん こさめ ひつじゅひん せつぞく めんどう",+            "909c8c992019adde332a11f0ebd1b0c0fbc9dd96e4d3d30ca4ecb0d06f743841cd25380f87b3a538f46dfa3fb3a5ab330487f99d128b1c6bcdbe476d3bbe2af2",+            "xprv9s21ZrQH143K3a5iyuaeKiPGQbhQLUaBhzfd7inUA5ndrmcYEc7zZzTLGM37Du2M11nXChrzXyZ8ZYtH2dG1CkWE39R749XhcKUcVs9avTR"+        ]+    ],+    "korean": [+        [+            "00000000000000000000000000000000",+            "가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가능",+            "a253d07f616223e337b6fa257632a2cc37e1ba36ff0bc7cf5a943366fa1b9ef02d6aa0333da51c17902951634b8aa81b6692a194b07f4f8c542335d73c96aad3",+            "xprv9s21ZrQH143K2EgqD4YCzE4rJ8rojXAMzqftKGS7694ApSkSoLgheNMawZYv2sArwHhLq4AaJfV6WWtGLjFWdFBT47wsjnq9v6iMXojWZ1V"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "실장 활동 큰절 흔적 형제 제대로 훈련 한글 실장 활동 큰절 흔히",+            "e6995bf885f5c64932ca28bbb00bc100a6b89cb6edc987bb05f05f99ae7caf78329029c189834c1cca938000bcf08423da011558a60cf3d90c9035eaaf241b9e",+            "xprv9s21ZrQH143K3CgBqqMtQxnP4VvSCjaTs2GfqHadwo4ddmKkvzasAHRwAsSDqjvJQu8jmAdEgBrLkVYhHdyZKWkZbyfuwPQG84PrWjEuCLQ"+        ],+        [+            "80808080808080808080808080808080",+            "실현 감소 기법 가상 걱정 무슨 가족 공간 실현 감소 기법 가득",+            "1bb52039a6cc288cf806740836002abce493724edac3d3b9458e3581427df76414b422171ef115d823a01c6b39fa68bd0fed20bf5e64dec008fcb22e4b7f26bb",+            "xprv9s21ZrQH143K3v6uaDUdZX7Cr8uC3hCE6gJ1MiGsnmRojRtUN6Y8nBiWVfMUc1skLwuUb3NshkJSdCuFew5G2y6YTDCRsSWNxYsJgZCpRxL"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 흑백",+            "b6eb986d6aaf7d0cd0eae2a667ff8bde68c8780fb5a728cf500e29119ce99c9b079a4217836879c1e73b8a85422a85b564d819699a4310a1d007b5be24c24b6d",+            "xprv9s21ZrQH143K2P5vpGbzebpVw9pZzmHHyuoTFDAFCR5UTVwQPdkuKPJB5fP4a6YgAT6seAuv9uGsMcnoyVFkVxoKZxQgB1SXjy89nHvvSHx"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 강도",+            "f40a8db48df9a7fdd73a7b3ceb45f668e4eff098f275a0a5cd739d31572c90aa92bc08b9043d0adf059a945e47e2fdbc26c89dcc15b3893a2a705e4539523ae3",+            "xprv9s21ZrQH143K4FQh3Y65u7oFeQhimhg8fjRV12vp7kYxB1EnjiBVFY1B7AdjQgLcfzK8qLjaZKFq4gfPC5pQtFtDnHi6Y988Skm2oRr758w"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "실장 활동 큰절 흔적 형제 제대로 훈련 한글 실장 활동 큰절 흔적 형제 제대로 훈련 한글 실장 환갑",+            "3162bc17e0f2f01ee571022444d2c5fbddf6a68dedfe734c319fb574592e9c0328f6526116b3b0b025b23391781d0bef8f43bc8ddc2b054b9f52e1fd6a88e3d2",+            "xprv9s21ZrQH143K2cE8CZ9tqfnmvNZcEJpwnyRRcsqDPokupVqx1hecEjQF2zrM6amGPiDiEKGZdNWrps6DdEcP83UABBjV7yJSkAFUWcP1FDF"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "실현 감소 기법 가상 걱정 무슨 가족 공간 실현 감소 기법 가상 걱정 무슨 가족 공간 실현 거액",+            "9fa92e4524e0f7412935b2deea23593c0955f9679d3285e3b955f5cdd2a659ee005ee99bd385f63d82cbdb54a3849229fc9a700e198b65a1452b511884b543eb",+            "xprv9s21ZrQH143K2XL77upQ6FjJr8a9oDGHakG1HQozxUzHJ1aVbv6JLPuX2nXdbopUnxBoKgVBHiZphPg4Krqm1xxUm6YPcZg9NeAiqT1MAcK"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 화살",+            "2543a88c8a31570dc9ee868a7b153f7f2e42700778bae7a3aba7017357e708b5cea97e0d9753c9226abc90b83c76ae369d74515ac64102c51a5fd0f809cf8b92",+            "xprv9s21ZrQH143K3AK2RE2fUZyLssNUUJPyAYjXGDGE7Ysq8hTx7MG43NG7KDALxx6TaVjXCmxFwB2wZnNtSBM2asDYC4YcDMEc9sbCyPPYjdo"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 가격 계단",+            "edb71011bc0c227103ba8a769cc36ba609e5407a771727fc0c8cba1b5a44d21ab9163d9deaa37427ccc579864e21f08d0fdd3a53a6be258d3c73b898a01ce2b2",+            "xprv9s21ZrQH143K2vZcddzTch42FzN5kcvsdtyMNR6QfEgZy1JEor4wzwFKj4JGq8kJEyC4zMQHhN7Q9mhRZJdYceTALfwxmpUAD6AtSzU3n7L"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "실장 활동 큰절 흔적 형제 제대로 훈련 한글 실장 활동 큰절 흔적 형제 제대로 훈련 한글 실장 활동 큰절 흔적 형제 제대로 훈련 통로",+            "dbd640cc9d3e99939bb0fc4473738571e314c29468f01fa85f57e296cf6e8e269d6e32434e46aaa63384930cae83728623195a932a48ccb71a9ea247720d9371",+            "xprv9s21ZrQH143K38C9hPGqNHuxsvKmNVwY2gRiRjjbzuBNme4PgfyLzkhYEt4fGm73xs6c9QDh2GxxkntMiFyeDsMDPkLDa9TkubnWZFCsJP9"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "실현 감소 기법 가상 걱정 무슨 가족 공간 실현 감소 기법 가상 걱정 무슨 가족 공간 실현 감소 기법 가상 걱정 무슨 가족 구속",+            "9a0ec04a48287ae628d61428f921de5f40fc1035f21883798e05c36f9705b2525a00ebd6bb89fcae9b8af8e9861d0083de331199d6b85b24cff598609a49b305",+            "xprv9s21ZrQH143K3PRHhckbBdnb1HMFJWXd2oPh4sXWWP8iMrH5fkxKnDucKqdWFv895Z7BRWKmbHj5gRvB6QddHeMUrk7wzSRzf7ZqkkVCipS"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 힘껏 허용",+            "340bd57209e54e8bde6ca750147933f7e44995047da87b61f64f70f26f289a377e25a65f5efb11f9e651917ec9866d54846516ae0fba956f5f536422bb47d91c",+            "xprv9s21ZrQH143K4Cr2Z2NiQPe5Xz6W92ZCE6SpW5NVmfvjtJgJUAG6WwkQ4iZ1AyfbUDb3yoiQsdoGHSSXNEMtAX3xPdtD38n5JeD8Lx7XNR9"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "원고 물질 생일 부산 마요네즈 생활 일찍 큰절 동화책 반성 반드시 의식",+            "8d148c7f8ed529d7a88fe2bc8bff574b56406f9928ab5426df793f4d3a5121c7c6974c856ad20f66ecf04fbecd3bc025912b3e41d500f1e5be896505e01d08d6",+            "xprv9s21ZrQH143K3t9XU9hQ1sEMc3LTT3MZ4Ar51RchXDvo5sjxDF7bFNaLfz7DttnvUqKAThXzv99QKccqpLQB6DdgR3NJV58AixcCdbw6ZTu"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "서비스 알코올 오로지 착각 카운터 부근 부정 고양이 허락 비디오 단맛 체온 본인 완전 바람 철학 영하 비닐",+            "3b67b06a2386240f75abe8f7905fd0fdb4cc2baa88c090eb9bca3cf144e6e33bbf3dd9085addfa52cd0ff9f2f9cd63ca69e7e77ce903ace942ec7f5b451148a2",+            "xprv9s21ZrQH143K315xG5w3u3mrnhSMVsNVdKmA9VZVXeV3fAcLX94k4N9oDEgSAxsMi3RhACEKFfgWkFex6T1uHSjSeHPxnnVF7muEAVNycgE"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "설렁탕 모범 일곱 민간 낙엽 매스컴 일곱 언어 지진 통장 잠시 국제 설문 복도 생방송 큰딸 약수 목소리 아직 횡단보도 중독 수필 매스컴 실컷",+            "06b321dd10cd2d0dec17212163c5d31f5ebda67027c0159380348d31ec5c5e7914ec75a44d4e225bbe5ce3db967e2f1ae2c9d463a638951b3e16d75ecb92cb17",+            "xprv9s21ZrQH143K4TY4YjtQLxBPerqUshALn696njp6gNVkBTYWWZ5jrQPZRuqxiMoJ6RjQpVgVZFqKBRsWnGcci6UNgPdHbRBuVn93aKdCD1n"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "제주도 처음 의견 김밥 공부 연출 모델 시장 대합실 원래 시금치 군인",+            "5e68ec0b343b62e221ede6dd5d6f33dcf8b5b4f4925ce6a30f49b17182ed0a40f7c7f3248463843f1999dd671a2e9c2abf4e5443a4e88f2bbf10b79524cab827",+            "xprv9s21ZrQH143K4B4gzTqVD1szP9igPch2u3xqQZ2gBsYe4u9kunYh5smSB3Md8EXktwjWafLwMasGuLPiSGXPCwNTgdcEPBsgtq6MiGMscje"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "소음 큰길 식당 커튼 책임 창구 사흘 다양성 일행 질병 진급 혈액 증가 예산 치약 하룻밤 대한민국 그날",+            "c8a07b4a163c3cf4ef400a96bdb7edc012dacb957326de185e66f7804e912c02329ab07520ef05dba38b2b3f6ded8a8691e1b17a38658aaddaed7ca95ff1588b",+            "xprv9s21ZrQH143K2h4eVKk7zbdHmjz5U5poyBbddPTS38xb7rjA9NMdUjYC3Dy2X7top1kVw51cFuJ2UHmFU3WGAzb5Hz4imvuz3ZKWy2R7gAP"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "월급 보너스 금고 생물 기분 진리 열매 콘서트 에어컨 몸무게 집중 우체국 강제 창고 영혼 분필 모든 심리 소나기 자랑 순서 미디어 삼십 술집",+            "683d1f6324fa54a4c4efa9b0573fae573ebc1c8b373890eb9b1e6f760f586126af2a3a39e0494f653ce6dbb954353023c304dd42d80aa939eb5a31acaaa3a60b",+            "xprv9s21ZrQH143K48a2XvjEnkvM3ry54SZdbERB51GvtK6S5hbSwV5SbBkqa2KNW7b3gwY7hC2JMgiNMD45aMhABCDvXUoZTvrq6typLaUAz6k"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "나들이 침대 분야 바이러스 첫날 개선 논문 창고 하필 윗사람 저고리 팩스",+            "f767f63c4febb5c832890f6129d0c3721555de40c28ac11093d23447f507b98f134cfef190cf0f12f1e41278fae5334f460c24c69cadc9aacc5d98efb3903f06",+            "xprv9s21ZrQH143K3K9kkX6aq8Tmqp5RjoDwbrEbqStVfNmSWBofcYA3wYA2MQsDsTZjkfkeu7gz9yJB4foKXE1mveXDf48vdA7L5mPZfC8aYdW"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "씨름 정도 놀이 훨씬 물결 횡단보도 인천 철저히 학비 킬로 수염 본격적 결심 취업 대한민국 출연 일정 가운데",+            "739584c55ab1c8053a44ca3fb50237e066590c92043cf3f45748768df65778bb79175d511543d96112f0a0e7960df081f74e6e477b953a1681cb5331de8abc3e",+            "xprv9s21ZrQH143K3XQJtWHu2YWz62N1SFBLpbweVNpCUoeFEWdukPL2rXvQ7HoDBwPWCnBc5otLQnHomemkmZ19m49YjQTqZY6uvjbDQaWpS5B"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "개구리 속담 액수 북한 실내 병아리 소망 같이 관찰 선물 생방송 신용 유난히 운반 남대문 열차 시설 양주 재판 보편적 증세 감기 정오 장미",+            "068f3943d3b3ba61b74e7900d936fcf4d73fc74852bc011e7405213edebed9f1d6b9a25db10c3ad5552b779225321a36304c757d0479e8b591655d0188961120",+            "xprv9s21ZrQH143K4ameDhGSneS8UaxoYBfQ547c6eDEBvDJJ2cqzFHCCfqcoybLTVreVRjQo6SUDKzoHeNtR9bCQY2qF3iWCJTheeDYTUSGdTs"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "해결 식초 거실 백성 볼펜 중세 냄새 가끔 출근 상인 한번 의심",+            "5f7125457857a8870d1ace1eb0f87479385d08ab8827998f57cb0cab5289d31a360310cdffaf4e8d1202a13fd8bba2ed9bc240a59b6d486d418647c55c7bca44",+            "xprv9s21ZrQH143K2fhpmAA6AkCVJWY7je61aLnS882ga5H3cMuoSp4G4SCzg47MRUjVFQYVytFdSu3M1hYyhW3xkuGqxnUXx1K6jFTg4C5gmst"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "제한 스위치 아프리카 약수 출입 잠수함 잔디 향상 당장 열정 목록 반장 아시아 그토록 선풍기 약간 당장 단순",+            "8c6f94c633c8752381e7bb207083025d7cef6c448695393fc21553e1ac269991a3ace1a2562a6129bdc34494c7a6c01d19f600da9af985eb001d71d2fb9e1480",+            "xprv9s21ZrQH143K2b5dBGtut1we5Wz9S2L2XAXfj6xftmT2omNHimaPwbTWszDvwSLfpjpQCjXKfqW827u8a4ADhCQEnTiiFj4dtbc2DZJRucn"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "향상 담배 박수 추측 기술 충분히 협력 성적 줄무늬 인체 단위 딸아이 왼손 거짓 조깅 유명 석사 참석 이야기 크림 변동 진급 스케이트 하지만",+            "0ecef71bd6f0948d9186c2786086a00f7140a00d37c836d01567077aac0dbc69f62189c02a9138dcc79a74dbb676b74aad4959fdbbf1d06a7798385f8eec97b0",+            "xprv9s21ZrQH143K3zjSpKnVTgqBrJfMK5iZXLrGb5Z4zcWKo9URusFq4n7YjnLR3RCqD8Mytzk4qjHnk52PfdQUHCCJUnKnZxqhMXGGVVe1NXZ"+        ]+    ],+    "portuguese": [+        [+            "00000000000000000000000000000000",+            "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abater",+            "ab9742b024a1e8bd241b76f8b3a157e9d442da60277bc8f36b8b23afe163de79414fb49fd1a8dd26f4ea7f0dc965c760b3b80727557bdca61e1f0b0f069952f2",+            "xprv9s21ZrQH143K4ayH97er9xirrbGL9hEywmMfh1LdQjwtrsnrvENN7c2yKs82HypXo4GAMt4wpnna9doa1FDFYXQTSFReWDY4XAf4imN5m6s"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xingar",+            "298d1614ff06ae803709f5be5331135cb74e6cc77fa09e07a3e887c2e370401f9a73a409dadf58b5a5197b27ffb3fa5dd528aad9a1a8750d7669ce950ee60c2c",+            "xprv9s21ZrQH143K3H4uYufypQuRqf28rLoZpLTwjpeCiPBpcZtJpupYd2oS39oGnrHd7fPAgU5d8bfTpYd97MShp4f9fBGj5K1F9NqgZFpmbVj"+        ],+        [+            "80808080808080808080808080808080",+            "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abduzir",+            "800fd4e7691fbc3ceed246c211a38949c3607fe269a35829e40ca9d3e26515a4ebd64d8bfe9b66b49543fe9dab78bde7cb7102968ce669f55293bcc02e26ba0e",+            "xprv9s21ZrQH143K2qEuVaHnXDY1HbcQ9nMyNoi5tgGyFxfYXVxHipQ9hcmS2QitJxsNFatCZk4EYoyyNdWamAjJiMLhuimpjiWHPvJCw28EMSr"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido xeque",+            "7fb404372815ea28ef97a64249acd71a293ea0437b3dac8f7e193a10f3584e2055753cc8d6f025229f65e61318fc4e10d4017bd3cc3496f535eca3247d26acd6",+            "xprv9s21ZrQH143K3tfxkHq74PSrq4HqTjs5sHHxBpjoP26GnJ5Q8w7tEn5kmPESuqN5TQb9Lu4LysqMAcXHWNsr3DKSnvuu3vx6sK7V6Hhv2iD"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate acumular",+            "81c66b6789e8b91c169335be4436fd9736ca9c06425acd09b0525e1d6836383130f7f7d31378aaef8b7109503972f40d42f6c6b9f99765827bea762515d3404d",+            "xprv9s21ZrQH143K4LsdmZ3NinpZHGbQ6n7aQr5uV8Rv7Fq8yNPdAzsyrezon8NZ3ZCzode5jS2PXzN1C68amwDmp2bgAEF4jDG7NUbdRYCjR8r"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador viga",+            "17040704dd985478b7d0666c7078201e3cd7d1fd1aca0d7d47c98a91ec7845500c611d987339a1d4c12bc506feb7c486eef0aa8ce679b1d184db5ca40fe8ef67",+            "xprv9s21ZrQH143K3XAGBJxF36qFzxkFaSEsNjqKvCtT3s7WfzgMBRcv8G9K2CRGXXhLYPWKSipBrd9dyLSRuTzQWbAJkhghki7Nn13QfHqCSBq"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador afastar",+            "0f637bf3a487c26fb73f6a464f62ef1f6ca73a6ae083e220374c82881bf4ed2dafd874956ce368c4441e6269759c5864197e87421fbcdb7f6d63df17b4f7df81",+            "xprv9s21ZrQH143K25VJsZdwtDCmzZ5C2LkJyNDKrHiFfHHBEmtvn77g7qrBRGHTum98TqU7sWCGFZ8vjtcPVAfgGcV51vi2BrPR66KC7wNxb3z"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido viaduto",+            "042e40dacea1df76335445c37171e5c0fde334236abe1b5d69378548875d157968dfff5889641f16690dca9baa4d9e5fbf56e3aaf0765144ba96b819f37fd0ae",+            "xprv9s21ZrQH143K3xAXH3i9VReyK3HSyzSmN11DXikYFTA3senE3iWDCdYTFXL9JkBM7CeCS31NBBHNu2tH5T1iDWCBEu1TeWLfELH4CaGAiRR"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate alinhar",+            "8fe67c9f53a30f75513830e18f6bd0950354297a4977393fae3577363393e679cc13452bcfc9460b28a913ab8de9efc55f5901d1ba77e5eec791afd967768607",+            "xprv9s21ZrQH143K3UVmtSkznKJCHUGjkjoHkUd6dxdfo91K8sdpEVmzjyRMB3Sf2WC7WKzmZLw2cfJ43LAEENq2v1gASK4TmMUTkbWVoJaZLge"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso sucata",+            "feac9ad4e1a4a4399a7d57fe47bf64b404a7588eca1025abfa299365f7a75639317e2c89a94812db33405aa0213846bfd6d53dfd02743e2cf3b6984eb9fcf19f",+            "xprv9s21ZrQH143K2Syz6ZuTcKmPtPee4Xn5HMEnuZcNZ1s7hJdgjRLFJ8f4d8DnoBBTUchCBjBnjubsz3wum7zbhdp5h9hm4g6DVDo2LaHGJ2a"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre asilado",+            "32c8feae6a0bee33166468a770cb28459727e10f4f5ffef64977d5ef52a68ec51d832751a10c025058612ab256052cdfa9d8c5c87560de0453efe5a7d4597771",+            "xprv9s21ZrQH143K4XMzpdgrkDGxnU31ezynvtw3iBEfRB8sriHpEwyu2Gb65ao9YLcTTWTyxNh2g2AdtYmZ4wpTaAxLpJBzWY7jAvG1HyotoEt"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido validade",+            "739a6edd208d09e28fa97f8e8709aaf185e173125b1b427c04d1539173c88b78e81610a759e14f97a2038dcdc2c466a072788e3d7c88cc9bf36b96cb29510e77",+            "xprv9s21ZrQH143K45qUnDL5tCHKGpht6amXS4d278iTXWRJXthNTufwnbnvr6rU8kd4Nb5s9aBWdYFtfcdGkg4GdL91jRhGRtP8mZbjtTvpGsy"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "mexicano crosta farpa empolgar chatice fartura olaria sogro centeio defesa dedal multar",+            "1f0397e6d2aaf8d6867d648e9bc27b12a4ee1b61a47fb63c6676c153c472d708f02344ac56fd1a8e135e18cce4eef711e7e88529bd6c54b90715e9b3d9fb8467",+            "xprv9s21ZrQH143K2ynvNymKuhxopGpKrkH5skB2FqBYXP3N4goobBbXfYS8Vqhr6QAZQqxxr5caCjvSC3P3itpqqbntU9jTmmEdoM1E8Z5f1GU"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "fazenda invasor magreza repolho separado emenda enchente amassar valente enxerto cachorro roteiro eliminar marinho deboche rodovia locutor envolver",+            "1335d5a679638f63a494f1b71f6c6c448ccb0384167f3ef3ad07456f0b70f13b8f6097c1315186543a09325ca3581f329cdb66674d0f97c4474950e3aef3b9e9",+            "xprv9s21ZrQH143K4MHxCTSXTFPDppSnKcW22kjYEg7NMCcHEobe5G337nzsfnuud8asiYwnyMQMG9WkAwPW78E1otspYE4T5t2AU2cArUhhQGg"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "filho comentar obrigado cunhado biologia clone obrigado julgador rebolar sufixo pacote atingir filtrar edital fardado siri janta conhecer inibido vogal quiosque genoma clone impor",+            "ca0462775b11a28cb36c497a36da8e46c1ab618facf7640b42fd4974085e1d682a6ca660560a3b72dc602ff56e4027b53a8ffb691a96e4c827ef4e6d665565bf",+            "xprv9s21ZrQH143K4B1eibfqLNQbrVycyNgNCgQvDFtBLDYbEPtVXRBn44GPmw8fasfeCrV5tc8BFhzSnbg3pHdafof5rrXHm4XUK6EBrpoZni5"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "pote ringue muda benzer andaime levitar comando guloso carreira micro grilo atracar",+            "556b77fc49cd57f7c9c92fafedac1c8341598666721b874db50da261c7cded491c22ecd3235e508822507212698f645bb198f0bb1aecd50d22339e77619a765e",+            "xprv9s21ZrQH143K2NhKv3Pg9xyGsPMDwukoomCFE74Fk4w4K6pe8hetKjNrSWEmzPSr482zkiZ73aE5wTYY8jvLN4Wruv83p6mmxnxkF7qkUBM"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "frieza sirene hibernar setembro rigidez retalho exagero cabana oliveira refugiar recrutar vazar ramal lousa segmento tocha carpete autoria",+            "3059ded790eac1a5bb614c2a88989a4b5d7d4db20acc95f2a9eee1a5889012fa2b7ad2c0b0c5b81abe94d3e4c6c58265058fb257bb051552fe5f82fc45389132",+            "xprv9s21ZrQH143K2EN6vqxgJ2XMXNZx6GWcdcFXajpxqDfWvsrYFETHVWUEZr34sP49GinmReyfUgpakD6xT99vcV7yiAvPnf8BbEJ2PsWf8EH"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "mimado drible baioneta fantoche bastante redonda ligeiro simpatia lamber copeiro reitor mensagem adeus resumir lombo enraizar combinar inapto fosco orfanato gincana cubano exibir global",+            "f26d817eb6474c6ac3358737afd31a4b4be74ba268cb7dd0bab44f959c9b4be5ef7d65245bd0da8f21694bf380e3e4a9fe7f82777b4d59a8c738fc460b0c3210",+            "xprv9s21ZrQH143K4QArHuoZ7djwjgsdKRekuqKqFEBZbYpAQJU2a1mDMm4z8iG28oBs2kAEaGQDPjLu3kiNz3BV5Lio26Zt1z5KzEBxtSJGtCR"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "bifocal semanal enlatar debulhar roedor adquirir branco resumir tora modular patrono tangente",+            "fccd6ccf9cad7af20eaaba21e9f4fbb149907c41b5e000ce398e8956c15a5436e04091402d4851c4cc8cccf9e8a90dc8219f3104698d1022064538c6303dcdef",+            "xprv9s21ZrQH143K25Rf7o8uUBtDjSZkJ4S2Cspm3JN9USxBAXmmUYvw9n7W3qsxbkkptFhejjufMUtKx5QhCMG8Z6RjT58F9kVrRGKMQhatmtb"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "indeciso pires braveza vontade criada vogal nutrir rodeio toxina soletrar gelo elaborar ajudar sediado carpete sarjeta oficina abreviar",+            "c22c6e716250a3b98b0f342f77d4f1fe9a4eab81304fbcc9eb9f9852db769b4164daf2aecf72e6d9879713768628780d0b9900ece13c75aa1f433d48ea5c9839",+            "xprv9s21ZrQH143K4UsyxJK2nxQFmMtrqR5QUuHaK1KiZkp1LZKMqPuUCCe3xqVWDycrKjDSctxm2sPQ72MBM3yZx6tX7Jvemccncy4nw3HEjFb"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "adjetivo fugir irritado engenho iluminar dourado fralda aditivo aprovar ferrugem fardado ignorado moeda mesada bisneto linda guache jejum pastel ecologia rapel acionado pneu palmada",+            "6fc110cbcad37d6a43aadf52643ca9172f51c6fac45bf0a17e60e0fc24b5d1cffc73dd0606c427ef789f9e96c1d05af88a806005a21a4f2f93a5e41ab19fd7d6",+            "xprv9s21ZrQH143K2VBbZdEtqReQrWkiSUvySRGbPbyP9sm8vHAWDwXc1GwsBHc8LQU82gydYVQ2YCDGZKKzKPX8SaWCf2jrQRC7hVAgb2GYZuD"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "turbo hoje aeronave diagrama embargo rachar bochecha abaixo sanidade extinto tridente mundial",+            "d666e6f21c3f8934aa38e45db96ee64eb490156655c2be5d4da4359fc9b11cf9ffda5802ef0eedcc154fb790c41f50ec0cb40b4236972538d8a6e27e54115706",+            "xprv9s21ZrQH143K4CtpNWJhUqGJ7sMBw5MfYzKSNUmFABS3NwoqGtdAXUMyci5F11H9Vbx3kbiEqjDPMvTj84nMVZM9PF7u8ouNEruefbPFc9G"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "povoar gorjeta injetar janta saturar paciente ovelha vaidoso cancelar limpador confuso degelo inflamar avisar ficheiro italiano cancelar cacique",+            "cb72a39ca9d39c4283398a9995754bdff1785ccc0d96f842d748f4fe904f9dc9a963b7e89ca8070154edbc7d1efc90e1554ac4dc34646c38d6950ffaabeee350",+            "xprv9s21ZrQH143K2GgN5EwWK5f6MXbTAvTvN4uiu5pZSgb4MxBAy8VTabQfb7Dc3kp1VmEuKTWJbUtM2Hu8KpFUKWPm48ubQ5JHUHg5PeLwz2m"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "vaidoso calota decote sambar batida seda vazio flora queda nuvem cadeado certeiro matinal afetivo praxe moinho feno resgatar nervoso sintonia dobrador recrutar gorro tonel",+            "8c6d156ba11fbc606a92071e7230fda2446333510ef5f9bed4712b2d737ab43d2e06c4fb3929dfd072ccc8b9003c6bfa62d5b8fcf04396508c54215357f6f8cf",+            "xprv9s21ZrQH143K3fnoXd9pUHy7b6hH79o9bMW45Zc9486jaN1KKJVZrm1XosHw3sduFw58ADzqLZR1TvaB7enqXZoB8hyptgcijczutBxqKNB"+        ]+    ],+    "spanish": [+        [+            "00000000000000000000000000000000",+            "ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco abierto",+            "29a2ee16de47d07025de37e7d9c596869439f9bcd26a702d2bae64db2bf0f68383841c5444b5b3bd39dd720d2ebe59969e110e5955c8e6d32c6c3294fd87439b",+            "xprv9s21ZrQH143K49iYfUTNyLe6mVRHvYSg58nfiLkcSREsu5QefrsvQ9KWsMtX7SXsXwvs6J1esWdna2weySpUFZNN6qiXuHcobEMWGLfyaHG"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "ligero vista talar yogur venta queso yacer trozo ligero vista talar zafiro",+            "1580aa5d5d67057b3a0a12253c283b93921851555529d0bbe9634349d641029216f791ddce3527819d44d833a0df3500b15fd8ba4cae7ca24e1464b9167de633",+            "xprv9s21ZrQH143K27g7EMkgY2F1fuyqSEKq6n1iJCHuiUX5F3oGESmJSS6DcKW5JZ6qWWJ7x8wS1FCrd1NhRS4xCWDn9Bb1HzBuNpitD7FeYGv"+        ],+        [+            "80808080808080808080808080808080",+            "lino admitir bolero abrir álbum dejar acelga aprender lino admitir bolero abogado",+            "a89366f7f9c4bd98afca8edf1242507506562b8eb8a3a60468cafcb6f3037aba1e4d9a7497f6d49fa94aca87c95703873741441a719325af371f8eda9b59dc83",+            "xprv9s21ZrQH143K2dG5ptughpsWSbdXdRVg6ZZtF8RehVMaDNM2TJ6NZAtorTDm2EVpt5nwpsgtjgYW3GD6oP8Nk3ZhibRQFRJ4f5JoDrunY1m"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo yodo",+            "a9d1f751178872cc53fc5433e9b2a97526448adc4b824cedeadd8a127c2416481345dfbef2bfc78275f3498e40b4e8e2e00560100e543aba3f324e752f032bc9",+            "xprv9s21ZrQH143K4TU3oETVCyPLTqmC8C7zqqSR7L8JpMiR68YNhyvfEmXpRh6pP8gPghpFbvNSQCQppPDf55iNnZhT9iza6HRpTvKeLSDNFCg"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco afición",+            "6c9f21d46c56f723cd734e308f10ebf44b5b92a2e0d80fd66a2952b8d37af5219e0b93c59e1d8e63b47ac657ec2c524e5fb951d87cac824f84a3ac6264b7aaac",+            "xprv9s21ZrQH143K3AjqVTqs8gbpkEoSkCVEM6dAGvRm2TJgxTLih4TuPPSp6gheAkXpPRhG63Fb9Cr5MEto2j6hGJ4ZVPa3C1GHXUyPPwMRXCV"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer trozo ligero violín",+            "f73b28d7e180e0a92c57276a29489c10a992c8a465ab61be0ade4708543436a682b2a3c22de57c48736ae6f29bebf3e506779c74bc1a835ad6b9f4e174126ca8",+            "xprv9s21ZrQH143K4PEMCi1dMq3ZwveC5um6cXR3tp4Z6LUGLhz4pmkaDU44UoSTiMQHv5icYPjH5EooZNorbDB7fLMDa531HHrKKnEEqCT5Tfs"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "lino admitir bolero abrir álbum dejar acelga aprender lino admitir bolero abrir álbum dejar acelga aprender lino alacrán",+            "f799e5c2782b50d0eb1d25b5f94984c5b4037ade236c6aa3b48b3df01b703d8ede5f94555f4e78f87a642a9676ba052865418c469c5739b3e93acc528fad30b7",+            "xprv9s21ZrQH143K2UeG5FGYFmkW7oTy35ZFgQ7qdR5tzBdKxGLCEfVJPPA98e2wUpfD3Eg6GS4833dFVVKafB3RNGMSZtxoWc56Yxo7PaBgYds"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo viejo",+            "2fd3964ac77c52232dc0eb2ab237fea2de9b7509005214101ecbbaeb40f34bce7735e848fca6339f76f289904c6db959fa573fc0aa607d969ac256693b4fb7af",+            "xprv9s21ZrQH143K3zwjASrAazc9EGeoVcQXA3unTmgxG9ZS7nc75inZw19oktj1y3n2Y7yetBatSN3v2UpS9ms3PvmrgQEMC4jox4ZV1ZrW6Qz"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ancla",+            "f600536eca941ed937318828e9ebab24b3b571558250e7a8342fc3cf16c458b2d7b36c36155a86cc308f7bef6d87b05d5dbe347f1a83c3dfbabd89e9c45b7883",+            "xprv9s21ZrQH143K2J842VWEPWH3ssnSvZYhRCzPoEcifAN4UgETeYkTdvnMUgnuftLyeGttvdvec8F1YJCmQz5mS956jsb2m8yDXZtnxRgiYgX"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer teatro",+            "3d2a3aec779195f2628e800879d600cfaf2d7fcfa998657068db53906a00608fcc94fc78ceab8c97d6191389c4e468815ea0d11ffa4280c34c3cf17721a27c73",+            "xprv9s21ZrQH143K2WBRPum95TFxfz8niK5sbiDpQjyr915SjEJc99BrYoRhPuYvfzFhPwqUNAFtEdw4khqQMK4ge8EnTZZASbv7oy8t6SMzVSM"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "lino admitir bolero abrir álbum dejar acelga aprender lino admitir bolero abrir álbum dejar acelga aprender lino admitir bolero abrir álbum dejar acelga aumento",+            "dd095dddb50de059f5cb6932d529ad37dd32d40f72da3d0c7671ffc6bd967b4392fe233e5e9a4d9e5e60413160ae215e34375db85e95ccbab4fd4712f32216ab",+            "xprv9s21ZrQH143K3SQcqhHXxwZ793d4RXPpUQJQm8Bpf799AZPzicBxB87hu4Sm2DvWBqFvmxEjN3bY6EVzUxhJbHKdtV2k6u4c3ZNRFGhZDma"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo varón",+            "deea21c6902df5ef4a8efab8e14de53004c68817ea3de421cdd184f4159a6e9947376ed794c3ce67534f37f80b46674e85335555b5c53f44fdfef27991fedc0e",+            "xprv9s21ZrQH143K4TyBobPSoDLEze5gKjiTZXzaJaND1QHqmrnx6kULMJhGvQkraSHgUsjmisepryPQqTfWyM3ETLjusTsW35KumQ3w3RyusSM"+        ],+        [+            "9e885d952ad362caeb4efe34a8e91bd2",+            "obra diadema gorila farmacia colgar gorra pausa talar cocina duda dragón optar",+            "fcf6ebfc7d9eebab56ca868cbd2d5d05a6f2142ba903c52855dad4ab8c0c2cf6b4e047a2dd97cf382ae717dc18d155a45fc798e6f0a0b89971a4224e2a285701",+            "xprv9s21ZrQH143K3pxsjjkbjzEu1f9qaeGb9wTLZ39rUF4CoP125zApXELXDspSNV1Cn8vjvXKNQymm8iwSRpJr9AZyKvHTVVbg1Tq6kY48iTV"+        ],+        [+            "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+            "gráfico madera muro rutina suelo falso favor añadir variar firma casco semana fácil neón don sección morder fingir",+            "5b48222ce814960e3b2f507ba58e96b4fa655f76060943b47c7a1396d431c570849e6f1595add9474934a72110bd3da06824428650be819f8d093e0023fccee6",+            "xprv9s21ZrQH143K3JpnkDoEPYdCo6QEBW4eUfKe1CCweKfHF5DkonVNPQRkDcYEva6pBBfGLeRt56UB6oSnhemWzbSBXCpdefk3hBDWqUAPb2E"+        ],+        [+            "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+            "guion culebra parcela diluir buitre crecer parcela marzo roce tejado picar azafrán guitarra exilio goloso tabla mando curar loco voto reparto insecto crecer lince",+            "5dd9ecc2a8f504413ee001e4f27f25ad14533a35b3986b4ad505a9774740d0c0a6bbac6953a1ee47104357f4a5bc4acbc0f71813f9532fa667f3d3b6f2d6dd6d",+            "xprv9s21ZrQH143K4XB8BVsQ9TsrC12DWTkizmcY3X6kVqycT1pAVLhx3EHfWow3H27QV4YH8kMLum7nzjSexrAxh8tKZzHRYPPexmAaaM1KEYQ"+        ],+        [+            "c0ba5a8e914111210f2bd131f3d5e08d",+            "ración sapo opción brinco árbol mismo cueva lamer cigarro obrero júpiter azufre",+            "49b0de91db6c84527afe1bccb2525b93dbdae0306bd3ea8a1f629ea1704195d450a0a3211894c417f586fde217f024b4159a4f6ac7f5d18bb8b7bbf72c4f4d20",+            "xprv9s21ZrQH143K3W9fk6GQj1oNT2gYszJhNBhZjPmkTsR2jmoE89oRHHknYt8WLWREtopcnPs1ZWoJjV3A5QxrW5crBA5s6bWgp2DCfLUDA5Y"+        ],+        [+            "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+            "honor tabique lata sultán sanidad salón gafas carga payaso rostro rojizo vena retrato móvil soplar trabajo cifra balde",+            "484af722d01c9cdc9ac50f3fdfeec010c7f713fb90dbfe84dae21d8215b683e660ddeec44d685faf3e653f396ef8ce0d341097c50bffcf67ea094ebb44294df4",+            "xprv9s21ZrQH143K2ouVUYDQXJpeurwoTTmfvUThuo1vRhuSXrVrKgsxAtaet1U9M5WZDpqaiLAPz17LLSuHmQG4eMMAy48Zt4AMzUwgNnEXY1Q"+        ],+        [+            "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+            "océano eterno bestia golfo bomba ron moda sur médula danza rueda núcleo agrio salmón morir ficha cuidar linterna higiene pensar iris diario ganso jamón",+            "8cc9507c9ccafaf341a243e5b82c348e374b24c8c594131add8684cfc1e61ab51e5476a4006d4d780bd2b82e9d9581ae1af67c8845e40246d5b1110814a88088",+            "xprv9s21ZrQH143K3rpBRE8Wy6o6fZ6P6SX9ZrPCATgfgMHBHK4y9vbkj3VW19wRGR6tAeT7td2iB5bvwRXqXZ2FkBCmeCsJD4Uxf363BEuBwtx"+        ],+        [+            "23db8160a31d3e0dca3688ed941adbf3",+            "bucle sótano fibra donar seco aire campo salmón trato odio poco tierra",+            "55b603a9cd15a9769e21fd22a384d12de9afe0b9c0af0f07aee688cddd792b2863064767a6df9e8aebb4bf10d4482de07ffe6d7f7440df73f04fc544236fee06",+            "xprv9s21ZrQH143K31JznDuVnKRYfF538PE1GSe5W75rLGwpSuDFWaQrW48bSuzvkD7cPWqLHcjxi1AjQYFjzCEVkwpU8sq4XScgZigY4TQrSEk"+        ],+        [+            "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+            "llaga pudor candil yate detalle voto papá saxofón tribu talla infiel exponer altivo sonoro cifra solapa pata abuso",+            "b63a7651d8655add895fd8a45f0fd4c0c71bd8863a8e0fd72782b2f36d43ef2fa8830ab46647afc8c437e701aed41b0bc6b2df9f11887c44457aefe2c11d413d",+            "xprv9s21ZrQH143K3CSbDbXN4ckMcyDHyLwkrQqvXL4xBRATernArEtGvZG8eFhxLbbRBZqaBnRc3ZyrN349dRxUeAozveLohZ5AWRKsDuk6XT1"+        ],+        [+            "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+            "águila hoyo maldad fértil libertad estilo historia agudo asilo grosor goloso leopardo odisea nueve butaca molde lacio mañana plomo exento rey adicto puño piña",+            "e4df51858246fe7a1f5b7e0045704ba76ff9d2b099707ea1d8b731dc3216c3de4edc63bad0911179d818b20e2c2a4e8da9e62dac242f6369221802e25abd0ceb",+            "xprv9s21ZrQH143K381uk3B2rYMvPGDjn8oqzxK2tGqoNmB9eN2puxmZtB3BximsnfUd6u3eKP58aVTrUmKW4xA4ckETJwd6YPVa7G8nLkdBknC"+        ],+        [+            "f30f8c1da665478f49b001d94c5fc452",+            "urbe lección ajuste enero faena reptil caimán abdomen sobre genio túnel óptica",+            "f5e417f1f68c479cd3058e836ce47aaa52629ac4cb93e99e8025ab38e76a6fab56f6b5a6c1f20637bf29e108f41bca76a1a061d8f8ea40f7c0e5a15552c23ae2",+            "xprv9s21ZrQH143K3sNRU3fx6SQaMRXAAJPHtmBFpXF5MhZ6AEURc2zE2gmVexqAA6EFyTBUBfxv982cwPLR2fM81SivaJXcN2FdbmgzrFbEdqs"+        ],+        [+            "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+            "rama jeringa logro mando soldado pezuña pésimo vampiro cerrar mojar cupón dueño llover barro guerra mambo cerrar casero",+            "0ae0e69a6ab7c290e1319018a36a7481b6969f73745db1fe56ed4b928b17458bd86e580b6925ec6b64558e4a1431b4761d0928928b689c37efad8122edd7762c",+            "xprv9s21ZrQH143K2VwsYAPzaKfUBfB8WtQXQux4e5R2cnLhipQ7zQfwWqg2Hozhrh44r1ZtMDzxVDA2PyUqEBtu9o2BidJjQ2psh8QspU8siVK"+        ],+        [+            "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+            "vampiro célula dos simio bono sondeo vencer haz remar papel castor codo nivel alarma rapaz ofensa gripe sagaz otro tabaco esfuerzo rojizo jinete traje",+            "c87970357a0faf4ebf604d9c486726e1af8d2874d40f3ba30e5774d615c6eb7ecc6cc04d85d6be4e3e36cf4771f8e15350152351f918bf4a555a33d57f90d61c",+            "xprv9s21ZrQH143K4UfkWbEcDPVrme1ea7d9BcQR6XtJ1VJucg1haNWCKCxqkXBshF3QkTSq3HHX2V3qiZmob9bJTnnj6ny5SbvwMp26v6HwxTd"+        ]+    ],+    "russian": [+        [+            "00000000000000000000000000000000",+            "абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац авангард",+            "b4992d0492476a6f8ddef9704b1a34718b7be7df7c6977c456cdc36c1e999d9f2e61837e74ef499b828bfbce54af229f60cb15630c331df7b0e4bcb637f1718a",+            "xprv9s21ZrQH143K2giFszsMo3JcaECAq2BVNdfYzBScYQrgC5oQaEaTbJfbmBhvckkBUftJeWV7Z5FrKBYnU6NLxdQMkVJwHXaQEokWM34EqNx"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "обед эпизод уважение якорь шланг скот юстиция хутор обед эпизод уважение январь",+            "b918bd21a3d4bc9ad48fbf554c1b2170899bdfd6ec116b537d341c1225a9b2acf2f9672363be0041d73d7c648252b42032275fe8f6e185776fc71f87abdd48ae",+            "xprv9s21ZrQH143K4BUbYRa7N1sVZmEGzZF45eQr8aptdLiPLfZhz5vDSDy5B2fCfiR68g6gNRZ6PpaLxRr5AbjgbUbvbeyYMH3Aa6s1VzedHqz"+        ],+        [+            "80808080808080808080808080808080",+            "обложка антенна время агент бандит заново академия бунт обложка антенна время авария",+            "d0c56ec1f3c77a8258eff821c03751bf33c7e5a79e6077eb266d5189c637f70c44767406e0cf45f155e1776d54f8d38d44713e79b8be7962beb987be49656922",+            "xprv9s21ZrQH143K4RsygSRYGV6FTZGUF7762ZihgxxLwJYuj1ZEbk2co5LJ32ZaGZxauCmLhjYGvwpQj4TXiZXBZnsgmZB42QjdDj5YXKsCbLm"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик яйцо",+            "57ca8a8f55680bffef79708cbd65140021b7e221b2846befb056d90883cc3e9c074b841289742e521a85354c18ae7cb7315b1aa7bde6dd9d1ec5b584e60d9d43",+            "xprv9s21ZrQH143K2HeiRMaLyPjxTxXhUU1hTeCBdfm9TGvct4gqtupxDQ3LsMUDm4uwS61UxqkU73WBB6nmbkNzKWDQpfjXE7SaCGdtcatLBJK"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац аргумент",+            "5c4f8e125883d9f11d9b6f8bb9ab55ded2a5282df1c8bc4daa46bd7a6649bfecf2b6e9560a336db719534d0c57609baca1d49bc522280d2044791736bfb1a171",+            "xprv9s21ZrQH143K2YdWwtmHMfsmGuPrmbRcXWeEj8yM5A6vi8x5nkCcEh7sHQn2PK6FvGq8sjii9WUhvMcPPPEvygriynjHVD8Y6AEdqChjX5f"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "обед эпизод уважение якорь шланг скот юстиция хутор обед эпизод уважение якорь шланг скот юстиция хутор обед экран",+            "5d2f8d26d13deaf9b18dcdd757c02d18aa180e8ab5c02a1b977a3ac57996f59aa7338d44ffb740ace2122750f99c1da2a236c60a113882e93d99175a36521bbd",+            "xprv9s21ZrQH143K3DAEtUMX7ZRKfT2mGkE231EFFYrtnsRBLgqqKy8DXWtU7Q4vnvCgU1EWc3AQDkz27fpW2MDrzGmkuVNTtD9eHFEDmBSu34Y"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "обложка антенна время агент бандит заново академия бунт обложка антенна время агент бандит заново академия бунт обложка бактерия",+            "46031966bcb1de4496e85fb3cabbf538e0319b4621498e9dd49e77d271e5dd9dba8ada21a20a1ca2037225efa3bb69e256fe59f48186acaa8dc70730bbd401db",+            "xprv9s21ZrQH143K4TAkYZVDxfRTcK1ev8turDKM86xykq7qgeAt77MHkqA7Gsf671T1mJnu4Vq3iuDa2tW6infg3cBz3G9WbHA92PjdGGT3CLa"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик шутить",+            "ed0f6a5dd6a5585a5a76fb2dcdff3075beb0f65ac9b91012f331b17e1efc9d4d8221f1a9f99c7662c07e742ccc9d77ed24c5d1c131bdad99dc1e9c67a02cd9f1",+            "xprv9s21ZrQH143K3XeENAs3xCce1rx93vXYrMms6mCig5j8YFcXxPPoFHGevpcRaMs6sdCFS6QBHSKD6FKZMsXeusjTCCD7FFGbDEd47pjmNyq"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац абзац большой",+            "320ad7214b5174a3b49bf7525a5c4bb6de96ff2f10b73c6f30130ba66fb4bdad9676ae540a56ffd08ea405d13a3a8e7cc26285da3d8d6bfff36fe202fbe7575d",+            "xprv9s21ZrQH143K48cthZX5fnGtX8agLdraF3zKwcfo56TTHNg34r33U9VTaPZ9MHGB5y5uQRbxBkELM2BgndH9VQwXC9St7kLUmKTSHq5MUu7"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "обед эпизод уважение якорь шланг скот юстиция хутор обед эпизод уважение якорь шланг скот юстиция хутор обед эпизод уважение якорь шланг скот юстиция улыбка",+            "a14eaf06487df7ebc49779f1393dda273fce4be5799e8b9ac8550687e4111c6825fd49c920a42c0947f80c6e3a3ed2ef7e75af7de43591d01641606b4d80691d",+            "xprv9s21ZrQH143K4VvixRuJc3AgrdUNB5kG91kzA8UdjknRSKTS7JNr9w14NAWuQZcQGjmThGi8MiKNAe1bXGiE4TLuxiT2HKXhGZ7XbW5vwcz"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "обложка антенна время агент бандит заново академия бунт обложка антенна время агент бандит заново академия бунт обложка антенна время агент бандит заново академия вздох",+            "4d8ff971ca6f1f5294dd3153de7176c5634f1c70f8445072c79f8c5e6ba36a9729ac2831b53a5babe23f0e1dd0180437bdc559e6c44d338162be0930074c5ed3",+            "xprv9s21ZrQH143K2h71y4su2ffL4NQR8ZdsZ21WPKHdsH9YjTgBMAWaqQxcR4WXUBt5NSRqfqbn7N6UP44cUct6FPhAkCb8dwnUJHcyKFDCUjW"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик ящик шанс",+            "421c7e6f3365b41ed491814e2c4724f4ee09761857e4f7e5a03f67b6e29a32e4af774b3ef8bbbf3bb192b2906ad8319f88645031180956f38c9c295e6d8940d3",+            "xprv9s21ZrQH143K4HrLURDc1BhtYRSDs3cQDRo9yv4sMPHurQLcXo6JQ9A4hXmZbZ7Jz7XMkrAq4AN7iTyeifMGsevRKiFurNWc7NGTNGjTrbV"+        ],+        [+            "bb54aac4b89dc868ba37d9cc21b2cece",+            "сесть пугать роспись монета трогать директор физика экран степень бояться рюкзак порыв",+            "4156db1656dfece6891335740f11dd29d5b9edcb896e2728db9ea8c58ba13e3400d3559a9e5dc031b0c40acec2f1a8d98c3cc0280d6b6782e0b5fcace23b9298",+            "xprv9s21ZrQH143K31Y2PwmSdNGeyfKT3iGy9dbQ7xdNoqcEJykKCGZw2zQFNugPaqqpbL4G8QSAPajyYGn2KPLReU7F6LZrbABLG8pww1CVSYC"+        ],+        [+            "9f09b43ceb7e57a0ea8766221624d01b086464359164e7a0",+            "поцелуй калитка будка тесный условие супруг ракета химия вывод роспись динамика должен оптимизм дефект терзать грудь факел атомный",+            "f467318ffc53cf0b7d81fb6ee12a120f50e58568215a395691912c3be11eac41864a9d101bd2ea61759732ae0ab99059271e9a4484fbafe9429297dacd9d9b40",+            "xprv9s21ZrQH143K3Y4mrcPNQ6umuqdkXr1hZLbAyti18xGkyuSUd3xqP2t5PdEVrAuobwiuPfGF7dqzQxfq5gjAmiAMvuASg2ohUQV9RmavcV7"+        ],+        [+            "06aadd75179d6d5c5e19fde90cf9b4838622421e57a12862e1811b4cdab215dc",+            "аукцион комиссия круг дача терять репортаж натура залп философ лягушка мечтать бабочка легенда вспышка негодяй чистый глупый вечно куртка досада стоянка корабль компания палуба",+            "bd4593253ad99e8aaffd90d517c33b75b0aede40006f4f60f1cf302aeba27e5aedcf50d856f369c59e64dd696ae1127748b29067f9f46dc23528dae65fb22e48",+            "xprv9s21ZrQH143K2Q7td5VztniaTJ1CH13BFYZRSEJFatqUo3bzmEYG1Ls8YNY3k45Z2o5144jtxP9zgpKf88vCpbaemKQ7FUUnNpr36LjhKqA"+        ],+        [+            "934f1cecb1c2236ab4866d6245f7c8db",+            "пешком небо жареный лестница выгнать сарай сыграть стоянка легкий двигать чашка свой",+            "24a97ab44257cfa0ba99a0417e755d5470d97f376795e4393dad4298febea8b3e44f15ed8c4e1526bd2b520cf77116d29bf52479bd15ab8995849c3821b085c4",+            "xprv9s21ZrQH143K4c7VXaAyRDpTKoxkNGaPzmaYd6CYcXvptVYieNuSkdv3WLvkkki5Uy8TsL31oSkcc5ZU9kFZqAuYhvb6G7iqnVCnrwh68iR"+        ],+        [+            "815171aac963d551f0a4140f62df9da1ceb7739ce1c2c249",+            "оборона оттого медаль петух забрать развитие смелый объект будущее вечер фабрика звонить фуражка семья мужество дружба слюна интуиция",+            "15620497c365d1411d731172309a186e9ac3e585a57c5e3d0d6238139d22c52c6a3e83e3014ed7b60b3e1773920ae55a5238b10bf83aa6adcd6b9fa6fb85316b",+            "xprv9s21ZrQH143K2J7CmPPcF7JqzphYz5XBVVnbZ53cwkkfQPQCFotjgPosemTxgyF3rCDVzYYNVKLYxqRp4Ftz6Jp8bN2moAeTVUzeUSNrs7w"+        ],+        [+            "8d79c52beb01af2b8cfbc74713c164e33a7c9a1afb6cb6a88d344e16fe97084c",+            "падать струя иногда тепло внешний плоский дилер небо избить после корень соленый пятно кавалер менять миграция сверху злой кавалер помидор могучий интуиция встреча почва",+            "e57e5f3e9e5d8048817c57ab4893379834e6387fe3f8e546d8ee50b3c3184bbfbbd60255e3fb0fc19465f6f11bfca5db58174bed2002ae5be0fe9c4ad30db848",+            "xprv9s21ZrQH143K2jsuXtR8fxubVRgj7oebLhyTi7hHXAmLHfqKSJ8ebm93G2WhFp7ctDi7yyEXGrv5o6cC11MDZ128jbsBVbu59T8YwBSVhTd"+        ],+        [+            "4bee29faea0a596253797215e1eccf9d",+            "ирония монитор нуль тарелка пузырь роспись камин давно вершина будущее дилер жадный",+            "659aa5e40fcc2d2bda5feb4df9b4b237c8fcb0daaba65ee58dfc4776bd71907816d3ed2811eab37952a9c677b5ea603125d29d8e1797efe1ce39665ce379bf47",+            "xprv9s21ZrQH143K2we3aRVJzfB8Hp58inWapdTbW44A4jCy4kvT2b1t4FfHFkAKo5zNHozyvPxwgmPD6Whc3pXmdzxXKsS7GK9zs4z2S9jYrrK"+        ],+        [+            "a00cdab43456290799635064362851cf7d1edc74d4a8ecf2",+            "практика лучший резной мало лежать одежда ловить матч лидер рост вексель похожий съезд миля такси пласт хирург паук",+            "23affb7f9a5f64ab2b5fbba57df4d280b1f0222a8cb6dbcd9ea240ea37bd088d956ff3e712bf6ebb328185e29259e399076da5667852c84e5f9f5183de92a157",+            "xprv9s21ZrQH143K4a31ef7tRvwRY6f8zttbGLfnjTzJRssfEvjFjxDA7kMEk9R8zQYDyYU2LNo3fcyT2DmKAhufNi1i5g44cTgKUM6Qfu3SVHF"+        ],+        [+            "6325252245b88bc10e007d1e3acb8e924f03d499f8be13a5061c2d1bbfdfd7cb",+            "лента гнилой изредка оттенок отвлечь угроза дрожать букет волчий терзать унижать вялый цветок форма магазин взнос ведро глоток орган котел сигнал хутор культура наглый",+            "baf6c06e3c917c623ef26b7750ec63d85017d60e6e4d8c7e49ec53dcf6a4d6bf606f2fa55e3dcd860b6adf58033d21fc58b093a6dcf24e7604dcfcf0844329b1",+            "xprv9s21ZrQH143K32a1ZKWu8NpmX55JAejQmV5tUgDDL52dkfZzTNXYF2qyKfUkZSjrLJicaiLMTUvjxpvUU55HjwUQFuf1gB2LSJHMcjhovXC"+        ],+        [+            "f25806fe5e6125d9602d50d57129725f",+            "человек слабый скосить симфония ванная хвост обмен ракета темнота отец кричать скорый",+            "f38baf015700f685d1c1f26f9ab52db8700b27ee640f1ba2bde81149dace30415d4411795323eb61cd30d07d149dc0ee3ea33d99e4f52ee0235489d52b9f04d8",+            "xprv9s21ZrQH143K2JUoSW56YcVVgrPj3FP31H8jCX6aSimFw8xmgWFG8dtcKf5AVsxtRMibDde1q6CNCzjMSvetMGp6ywHSYW4QKJKuJJM3r9n"+        ],+        [+            "df0ad99c35a4901afbbba60cb8a96e340d1fcdcb13cc0014",+            "тысяча комедия льгота мелкий изящный ботинок хребет мыться болтать совесть кредит магия съезд учесть грош неделя абзац профиль",+            "36f72aef7f6af7f2a687fba3a5e785ab171f6e08f7b7f077e24c4b8b49ac6e026bec1402031a128d11d02a9681711ca2b8ef09d34ae770a1577335d1b59a3b67",+            "xprv9s21ZrQH143K3647uZ91pci7dmAb9tbDYmNmntYV51kDRd5Qg4odoJd4jYh5qAWGAZXvkjaJGGuYawysxeUeYy35QfUrDLD1WemPa5d9uza"+        ],+        [+            "dee92aa4e56d2debb9be104a4a3774d153456787c5f7ad23bf6e53f1892b8ff7",+            "туча икра раздел ссора сюрприз шагать учесть случай инвалид кепка трубка приют директор рядом восемь скелет рейс вокзал трепет пятый видимо плоский замок хаос",+            "79f63bcebeb97e95586fb6814b8be4781df00139917075fdf22018f61f547e1b66add4b63264326ec2f46542bcf748104c92b4f9f73705fe8769c0b3897eeb88",+            "xprv9s21ZrQH143K3qEhb6NQP35AFEguHn4iAVEJnfAVgd8pmRQVZnATZwnNACGmeg73XFU6szbt1LkrNgyfLkgJGNtdXLYE4aNfgzpvFssQcAj"+        ]+    ],+    "turkish": [+        [+            "00000000000000000000000000000000",+            "abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abdal",+            "4c14199a1b8df442bb96586e3c3d43305884a05e643fd89ba9d0a18775d80d4a91be22719f0e6ad42a7ad293872f6af429377e4b35863435b4647c617ab4ab4c",+            "xprv9s21ZrQH143K2ugGhLVYL5dCJkVLLSqfQqgYH6WNRVxqurmbLMR16tC7FY6HSH3fir8R7yx5LsAyKtbdqSrXj23ZZicU75g389hf4QJrvUY"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt vokal meziyet yüzyıl tosbağa zirzop",+            "823d592ed10291fc25837c58bc122b2b37c0d20ea67530349270bcdb92e4892e13a7443eca530e9544694c8fb91d2fb7e8c29caeb598f627f90858e9fda408a0",+            "xprv9s21ZrQH143K3yg9nmyHAJvPDjBzGrWW2r8hqsGGTRrhVGEqvj4dWzwDfX9r9Pj8QWrCmnkdSgtAHJzxfxwRy9GnZvhfsHq2fGGwpDRLd3o"+        ],+        [+            "80808080808080808080808080808080",+            "milat aidat cazibe absorbe alperen fıldır adezyon astsubay milat aidat cazibe abdest",+            "8529df63b2fb01db07df4f519d1e0c9066326ee1b7d699499af8a980de371c06e54a33d8788f19811c1b25c326cef643ba4bdd23c790e259a6e109b01e440e0d",+            "xprv9s21ZrQH143K3BV2UQMgVeVKsHWDof76rfWSWtDMvYy6HcCkTzVujez6vEL9o2zCXPAy52nQqU8uyRxmZu21AbRrqyqJe2S7N4aLXmSrTPc"+        ],+        [+            "ffffffffffffffffffffffffffffffff",+            "zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zihniyet",+            "f154669700ee53ef119f7bcbbe62b2bec8296109a4359228e9d6200bf39b33eff7ed443ed92ee28af3504a31d92d12a595eda36f6dd2ab34bb2916ed5a9beaa7",+            "xprv9s21ZrQH143K4Z5Li8i6VfgWhfh5Yf5myQgwD2XQn21pMzn3LdrAQHEXe1WUrpnsfWr661XzBefNRanUfQ1uLgC9GTGiDZzkX1HU2b2Eu3M"+        ],+        [+            "000000000000000000000000000000000000000000000000",+            "abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur akdeniz",+            "c11c692fc712c924732c0ea93d1eb38de8e53c591cc1117fc3f957046b9518c7e8eeb0bd156b1c23031557b712f9b6ba4f59b831ab2f35a35e06bca5d1584631",+            "xprv9s21ZrQH143K4U9eG6arRqDzwPxtvPY97cKPPtvsBuYXg3bQFS8sKvxf6z3PLRjER5d11XJaH9J4f9aBcP32QeNsEg2MucvdHQAdf1r6zCW"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt vokal meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt vokal meziyet yürek",+            "7d070c17f6421b76ea23f91cda3373f6b87f137643d8117007ca9046c523f61a0f5b824fae61d76ba34505d3a06532c41279957a695b535651702df7688bb765",+            "xprv9s21ZrQH143K3ESh22bdZvgwnjv1rfUBhhqW7MmFe3gCvgssaQMEWZmw3wJasgMbfyjtZ9PtbM4c18Tdw9EFmuVHaWRdE9zmKP1tBnxQZ6G"+        ],+        [+            "808080808080808080808080808080808080808080808080",+            "milat aidat cazibe absorbe alperen fıldır adezyon astsubay milat aidat cazibe absorbe alperen fıldır adezyon astsubay milat alıngan",+            "67eda05205e79bb0aea25640c85dec7a6e090df121113cfb8528df87288144a777a12365fc5ea26d78dc4f063894fe1f5692c1cd20b6528cb2dd1fff4aed8c74",+            "xprv9s21ZrQH143K4UxdxPUHqDx1TbUa5ymqrGT4LpxPTokXfjvWg1LwJ6kPrFjtnWQ6AJ4LhMgGJvjpfr93GgkCdsY4bCFveCAqjoG7aL1PMkB"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffff",+            "zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre yukarı",+            "527598540e10421e68a749ef0f2c6f8c874aff96d5aa885703bdd324dc691bd5edc5aa055adf5582017e32315e8c7d2a059c77928e15acce814765295fe442ad",+            "xprv9s21ZrQH143K3oTbjcF7aPkGaGbu1XWBbJmpFWRe6u4m871jzcuxe9UJwuTfPPDqy8sahH6pX3choSBAtJsKYKw9fQAQy6gykuBgGDXH4Af"+        ],+        [+            "0000000000000000000000000000000000000000000000000000000000000000",+            "abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur abajur araba",+            "28c310afd7368bcd8c0b0b85f2ea8fdaabde393b603f0ef4c457f6b14d8ec2a2b9520dbaf775686d75e786ffd016a6373c956e3a96d726c6f1a5e604c14b8941",+            "xprv9s21ZrQH143K2AWncG5V4X2SYmfNGNTJadWjzCgiwhfS9QVvAu8RHAvtMnQX7KGFvdZwuPMSjuwycA51FDezqbvEDVuAQtwewnzSbYvZvkC"+        ],+        [+            "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+            "meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt vokal meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt vokal meziyet yüzyıl tosbağa zindan yoğurt silecek zılgıt tuzlu",+            "b37520e13cbd00a4f3f1d9bed02d6dd01668f51204d2cddbf0fb28815567882d79daf30b3971c55ac96ea4b35569f05f9bb4d827de366b4304bdb2c23d595be0",+            "xprv9s21ZrQH143K38tD64zHfNMTLQyszcLiZfsEqx3PKotFEusJJpAzV1LNGrDkKQ2kZyGUcNYkpwJET6xRihooefwYnU8sDc1AVEB8CAmYbSS"+        ],+        [+            "8080808080808080808080808080808080808080808080808080808080808080",+            "milat aidat cazibe absorbe alperen fıldır adezyon astsubay milat aidat cazibe absorbe alperen fıldır adezyon astsubay milat aidat cazibe absorbe alperen fıldır adezyon benzer",+            "5d92ee9efcd9cc3f19b8644dc72741caebf4c15d77aec6ab066d5ac3544b942616b567bbdc5eb25b20072d7082cdd7692c369d7575db53fbcdb4be2a8ae0b097",+            "xprv9s21ZrQH143K4TFEM9Dy2iDjmdDijpn3K1HRV5WMfpoaS3Hyt8GNoC3RRDb4MZTKfcckmmazJ5YLtYuxXCM3TZZvrg3uG5ptxrabr6cbbT3"+        ],+        [+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+            "zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre zümre yeniçeri",+            "2822919551b434114e51a2c00824cdc2dbaf1b567bb71d56c3e388ffedccd5a4054d7f71fc4149ed73f376fc9fad629bc40d2af89f4d61b6292153b73ee5a54d",+            "xprv9s21ZrQH143K2YKCt9cmi7pXRoXpX2AjXR5ydny2pDSbVueGZdwSNN7B1HnTQRf5RtZMtnFghHgH9PRCVAeZUHA4o8wb3anoyAjvtURAB4h"+        ],+        [+            "bb54aac4b89dc868ba37d9cc21b2cece",+            "sentez pirinç rota lamba tıknaz edat üçbudak yürek şelale arena saçma parsel",+            "15a94bd53a53df03882bfe746ec24f5349dd8d7c3de8884b032b6cac37cb9771526e2b656e94137b190399e70e6b7e7d08219aa1ba965e58e1c139533362b2a6",+            "xprv9s21ZrQH143K2qNy54d9ktxsVXsrtgmW7nePreUeHV7nTvc2hm5Pyu16xzWrAUE3XAZjnD6rtLALGJRrqWgknVdGbL6TQ72fArknZe9xxCn"+        ],+        [+            "9f09b43ceb7e57a0ea8766221624d01b086464359164e7a0",+            "patlıcan havuç asfalt tekdüze ulema taktik pusula varyemez cilt rota ecnebi ekose müstesna duvak teftiş derman uyuşma akustik",+            "f04f5a4067b689cee057321f6bf400a25d96f4be59559d09446c1669595d6b856f79495609d2a1f933ab5f128f3d84cbbe8a471b613fd1e6354383c57ae0cd74",+            "xprv9s21ZrQH143K2M1c94zNSatufZPKm7r9ZvbQBb5XsTv32QbNanqRhY1FcPGXpvFtKu12gzFxaicf8i4x9aRVymXtfBMYWm4SBC7kqx4c2gg"+        ],+        [+            "06aadd75179d6d5c5e19fde90cf9b4838622421e57a12862e1811b4cdab215dc",+            "albüm iletişim kahkaha divriği tehlike rehin masmavi fesih üçkağıt kolye kupkuru aleni kenar cepken mavra yatak çuval belge karmaşa elastik şirin irfan ilkokul odun",+            "29cbaa235e2205e406ebe495999f850b309ef442d2a091a2b750d5431bf5c6d308e1a4852172c9558430049b6036ccfed3789da2367e70efe1a8bd701208435c",+            "xprv9s21ZrQH143K3uxUFEhXpHvTZhgDnmDs18GkJkswLP27qQZTDUVF8PiNieqwRR5uCsX2y7ZkMKgi1THaHgeR7q4VcMxHcNRvGeavrrzpzJ2"+        ],+        [+            "934f1cecb1c2236ab4866d6245f7c8db",+            "otopark materyal eski kılavuz cisim sakız tapan şirin kerkenez doğalgaz yanıt sarkık",+            "325bf129f10c4b4fc66663958fb09bae1df60a8dd37d219cfbf8c776891889d0e918b1dbe66bf78191687694897bc94a2b2c1b84ab1865eef60a2c03438afa29",+            "xprv9s21ZrQH143K35KwmVQGSrd7X58mMCqKqpSLRSMYnTWzdYTDgPKJnHz6A7PXBvCLJj4vXVfmSNKwraCYm6Cwqd2uGk7J8TLDchDUQMPUjG3"+        ],+        [+            "815171aac963d551f0a4140f62df9da1ceb7739ce1c2c249",+            "minyon nilüfer köprücük otoban ezber prim somut morötesi asil bekle uykusuz gariban üzüm sefer lütfen empati solfej hafız",+            "42839d70ad42ad51716b6c30ad29cb7a256fc6e9c6865470e9b302a3bec99be6754714d647870ee02cc75b6e2b46f11c4d4a63009b1ae44aba3f4c1015b846e9",+            "xprv9s21ZrQH143K3tRHyXhqsWfrZ7JT11sMZyYiwkC5Eo4WvRPXLCEHRYP6BcisHLWKNdwQywHet8Fpbzz4t44Db5TcTEYZrpUKZY4iTpLsbrx"+        ],+        [+            "8d79c52beb01af2b8cfbc74713c164e33a7c9a1afb6cb6a88d344e16fe97084c",+            "nüfus şöhret güzel tedarik boğaz ölümsüz ecel materyal göçebe pasaport irmik suhulet portakal hapşırık kudurma kurdele sanatçı gemi hapşırık pansuman külçe hafız cerrah patolog",+            "05d2850b6d5b32ca9f9d8345a40362d6f3be8fcc65dc77542e8ca50efaf940ee11cf91594c64c7b511def83fcd5ef3eb4b8c8d6ae3cb10b946f1aa2c0a1f12dd",+            "xprv9s21ZrQH143K4GVKptAzKSVqEsJiG4NpdudPYfqNxBMRhdt4Bf7xXoCeRorRc5pD9VvdgbMNQZHJrDPPCiZSuQUQ2WpufLN2JD1Z5pD9JNf"+        ],+        [+            "4bee29faea0a596253797215e1eccf9d",+            "hakan lansman metre taşınmaz pist rota haysiyet dinozor başlık asil ecel ertesi",+            "332a44ae607176fbf85941af7bcd9685b84c2a3d200058e82e099e7b20b799c2619bc85a6c4115be91ed3796546b9a9a1364f862ae156d0f923b96f21d37fcff",+            "xprv9s21ZrQH143K2d1M8F9uPvNEd5QfuvGPCJE8XBh6wf96Arz6CEzs1Hag7WFaUedyzvMXT7HQEN67V2k5RYEo2tc7hVqPpTxaEYDNLzHhPPy"+        ],+        [+            "a00cdab43456290799635064362851cf7d1edc74d4a8ecf2",+            "peçete koçbaşı rasyonel konak kesirli musluk kızak koşul kırsal rozet bakteri patika tansiyon kurultay tarumar öğrenci vasıta olay",+            "feece1c9aba98740471e6025b53f6e071815f2302b5d9cdb1796f791cb27dc5d0149c9dd6f7cc1178bfb471fe622eed7f67b85519a609cf336bc986e18c8cbad",+            "xprv9s21ZrQH143K4WQDYJVHrxMNZPzGACWvtgSEN82CCAtE91pJwMrGCnHbqRAb1YUKk4AsZMmy1vPZbDEAV7v81LZMKp75LyAgVbwE8YhuaWF"+        ],+        [+            "6325252245b88bc10e007d1e3acb8e924f03d499f8be13a5061c2d1bbfdfd7cb",+            "keyfiyet dalga gövde nicel nasıl transfer emisyon askı burun teftiş tünel çayhane vurma üslup kombine berjer bağlantı çubuk müzik işlem seremoni vokal kanun mahrum",+            "6e51f41ff64b1cf5b464e1ee4d65346652fbc455db859faec1adffe64f1c3e0153bf223a514591a31f7a35f2c1edf8112d01b488c347771cb689791b0c5fca47",+            "xprv9s21ZrQH143K36WkoNrtpfR2W31M9zfoS9b6m8TiVArdY59tzh6RraoaRj6Nid9kcPG1eZWKvjAosoKHziqBUtpkNR4X73JwoXjcK9mJGQc"+        ],+        [+            "f25806fe5e6125d9602d50d57129725f",+            "yankı simit silah serüven avukat varsayım milli pusula tazminat nazik kadın sihirbaz",+            "6ec0dea6dae649330517c17ba129179d1f5ec5ae7ed0d4a6f05caa8aff580c88404bbfd55ccd1f4e7408bc75fa3490aaf4523cd853679cfb82eb2797c7302be5",+            "xprv9s21ZrQH143K26t84vvKN1EqXrpVzKCJmv2CkZQVKBXGVdLoCW1bdFKeVTbTxbhMHRfv7S5qc9Z8cLURAW5HtVa2WvLFNLH3M6tHRr8EpfE"+        ],+        [+            "df0ad99c35a4901afbbba60cb8a96e340d1fcdcb13cc0014",+            "toplum ileri koğuş köşegen gözleme arefe vestiyer mağdur apse sprey jakoben komedyen tansiyon uyarı dere mayhoş abajur pırasa",+            "e584247a087153923e5b00bf14d179714f151d1064e74dc6dd19905fc37d7bb6fa2fd7757d6d3ca157e01fe517671693ab514eed7d449d160cbbbd0d90ac3468",+            "xprv9s21ZrQH143K2vRvkt63VCpuem6vXLt1pqtVBm7YSuxZbm4G2LihmCfAcbKXJc9E5LnEKngDHqo7oF2YDB17EBFQrYFgxJaqmcyTJbREMct"+        ],+        [+            "dee92aa4e56d2debb9be104a4a3774d153456787c5f7ad23bf6e53f1892b8ff7",+            "topaç grup profil şaşırma tarım yengeç uyarı sohbet gündüz hudut tırmanış pervane edat sade bütçe sırtüstü razı burçak teşrif posa beyin ölümsüz festival vakıf",+            "bbdb82c9d5e188f792a6ffa189b3f0fda5aa289f918adf34817918891510e312d351f945e74da91855d1432369344a3094c523d129bad0520951aab542e07b75",+            "xprv9s21ZrQH143K3opfxeLxFyAKr1ufr9qK8crqeht2XaHqsnZ6FbtoovHsocVDjfWXA9PS44CS4NkZ76CKCQhhw489dx9RAN9JXQbj3Ta5UpT"+        ]+    ]+}
+ data/bip39_wordlist.txt view
@@ -0,0 +1,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
+ data/block_100000.hex view
@@ -0,0 +1,1 @@+0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b57100401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac000000000100000001032e38e9c0a84c6046d687d10556dcacc41d275ec55fc00779ac88fdf357a187000000008c493046022100c352d3dd993a981beba4a63ad15c209275ca9470abfcd57da93b58e4eb5dce82022100840792bc1f456062819f15d33ee7055cf7b5ee1af1ebcc6028d9cdb1c3af7748014104f46db5e9d61a9dc27b8d64ad23e7383a4e6ca164593c2527c038c0857eb67ee8e825dca65046b82c9331586c82e0fd1f633f25f87c161bc6f8a630121df2b3d3ffffffff0200e32321000000001976a914c398efa9c392ba6013c5e04ee729755ef7f58b3288ac000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac000000000100000001c33ebff2a709f13d9f9a7569ab16a32786af7d7e2de09265e41c61d078294ecf010000008a4730440220032d30df5ee6f57fa46cddb5eb8d0d9fe8de6b342d27942ae90a3231e0ba333e02203deee8060fdc70230a7f5b4ad7d7bc3e628cbe219a886b84269eaeb81e26b4fe014104ae31c31bf91278d99b8377a35bbce5b27d9fff15456839e919453fc7b3f721f0ba403ff96c9deeb680e5fd341c0fc3a7b90da4631ee39560639db462e9cb850fffffffff0240420f00000000001976a914b0dcbf97eabf4404e31d952477ce822dadbe7e1088acc060d211000000001976a9146b1281eec25ab4e1e0793ff4e08ab1abb3409cd988ac0000000001000000010b6072b386d4a773235237f64c1126ac3b240c84b917a3909ba1c43ded5f51f4000000008c493046022100bb1ad26df930a51cce110cf44f7a48c3c561fd977500b1ae5d6b6fd13d0b3f4a022100c5b42951acedff14abba2736fd574bdb465f3e6f8da12e2c5303954aca7f78f3014104a7135bfe824c97ecc01ec7d7e336185c81e2aa2c41ab175407c09484ce9694b44953fcb751206564a9c24dd094d42fdbfdd5aad3e063ce6af4cfaaea4ea14fbbffffffff0140420f00000000001976a91439aa3d569e06a1d7926dc4be1193c99bf2eb9ee088ac00000000
+ data/complex_psbt.json view
@@ -0,0 +1,18 @@+{+  "initial_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAAAAAAAAAAAAAA=",+  "miner_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH85eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7EBCGsCRzBEAiBAf8l/MAdx8EmbfrjyzdhwXOemFfyJOh8sJekKMi/4QgIgH3xp2zAzrr9/67CAnfNqdQ0GXbVrn7Dx5EklAO58H0QBIQKOyYCO13OUw/9ulMHA+D+guq7JFe/yBWbH6LCXWPCL6wABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH4Dw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppkBCGsCRzBEAiB0ZFxXOnPW6ezqx/K2Gw1BHfQfZpHvYS9fLSUgm2zEwQIgEU+rCj39WKfnvtWqgohE6M/VIUULJCgzDNJQcieyK5EBIQNpMZK4G5y+/dBTIetMRgJkoQgdZDlqdjMvPbOxIb3UMwABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAQdqRzBEAiB4Lzld0e52+DOVJz6yFL2p7iEATna+iJUbhjmCKHyFDgIgIAQ0lX9y4LriyhClDIz5f/J51R4TiAuCDY6SPNmSlq4BIQNQEPK9oqWZaq9u2sbNiemHcw+K0bdiDqwppyeV1ROmIwAiAgOpgiiaiULEEOdaD/0O9VFT7Kj/sawKXm/e/aKbEi8x6BBnnHl0AAAAgAAAAIAAAACAAA==",+  "p2pkh_psbt": "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",+  "p2sh_pk_psbt": "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",+  "p2sh_ms_1_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH85eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7EAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAABAR+A8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAIgIDiZk4Rf6NS9KEJCxttsdjcLCR688XBDP3ME9uwcypSAxHMEQCIHNtMH2gxzGIqzEuVdEsMCbggEpeKhfdmaPYajq8pzdeAiBJm4a7JOGfEG74g+Wj44suYtx1VNoqRHIEzPODvtS9ZwEBBGlSIQMVRqGbYX3GqDgQ2OB/M0zjUbQYYT6vJjWqQnsVJqXH+CEDOVaBbrUNQEs9XzJ5kVoXogcopGNj3wWn8p1G1VQGJlAhA4mZOEX+jUvShCQsbbbHY3CwkevPFwQz9zBPbsHMqUgMU64iBgMVRqGbYX3GqDgQ2OB/M0zjUbQYYT6vJjWqQnsVJqXH+AQkPXW6IgYDOVaBbrUNQEs9XzJ5kVoXogcopGNj3wWn8p1G1VQGJlAEReMpVCIGA4mZOEX+jUvShCQsbbbHY3CwkevPFwQz9zBPbsHMqUgMBGrkS7MAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAAA",+  "p2sh_ms_2_psbt": "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",+  "p2sh_wsh_pk_psbt": "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",+  "p2sh_wsh_ms_1_psbt": "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",+  "p2sh_wsh_ms_2_psbt": "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",+  "p2wpkh_psbt": "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",+  "p2wsh_pk_psbt": "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",+  "p2wsh_ms_1_psbt": "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",+  "p2wsh_ms_2_psbt": "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",+  "combined_psbt": "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",+  "complete_psbt": "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",+  "final_tx": "02000000000109d308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500000000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa485001000000232200208637ab26e75a3150d2aafa9d5d4fd9ba4db83f952ae98c7301f3a25752f479e5ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500200000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500300000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500400000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500500000023220020b9d25e328ff72a0302abdd98e790099ed3f326775a1a9c7b31458b9f2d955f63ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa485006000000fc0047304402207b0a8e5b2163005382621e4810d59e0e5c729b574a7abe4f2dec0670be202177022068203b4a9ffe67ac05bca14569887050d6a3727e0bbc254a972b7a1c2f3fa039014730440220736d307da0c73188ab312e55d12c3026e0804a5e2a17dd99a3d86a3abca7375e0220499b86bb24e19f106ef883e5a3e38b2e62dc7554da2a447204ccf383bed4bd67014c695221031546a19b617dc6a83810d8e07f334ce351b418613eaf2635aa427b1526a5c7f821033956816eb50d404b3d5f3279915a17a20728a46363df05a7f29d46d554062650210389993845fe8d4bd284242c6db6c76370b091ebcf170433f7304f6ec1cca9480c53aeffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa4850070000006c47304402205ba47300dd590f445cb56443ce6c13f0bcd07129de44764a26b83705f66262a602205bba7cd49fe34aa1c06e83ead381d64382dcb0109adcd9157250656d781929ff01232102628c8b13d6ad3290b100d40ae1cdecae515efc3f1abd5ef854c0755cb187d1f6acffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa4850080000006a4730440220782f395dd1ee76f83395273eb214bda9ee21004e76be88951b863982287c850e0220200434957f72e0bae2ca10a50c8cf97ff279d51e13880b820d8e923cd99296ae0121035010f2bda2a5996aaf6edac6cd89e987730f8ad1b7620eac29a72795d513a623ffffffff01c041c81700000000160014f3a3640c6de0a3a5c14b72164e98f96ca79d65fa024730440220407fc97f300771f0499b7eb8f2cdd8705ce7a615fc893a1f2c25e90a322ff84202201f7c69db3033aebf7febb0809df36a750d065db56b9fb0f1e4492500ee7c1f440121028ec9808ed77394c3ff6e94c1c0f83fa0baaec915eff20566c7e8b09758f08beb0247304402207434a180083d825e5f034279832541fa3a140894a7950119d04ee4b64310c1e302206218a7134dc2e19d88b1fb931cd1577d988f1aa004690c17193a63bcd4a84dd4012321034f208b00923a0c6c5a91d735206b0a63e746d63a8b232b6aa9bce9832f3f4da5ac02473044022020a694a50573f2261baf3593afcc10ac9e6e74fb4b36a8db8273ed976195c0a302205e3c5c64ed8ab19aa1a1934195c89f4424b34d99f9fa62c781654b55a7c58427012321020ae496cfca86e51c88c45438b70697ade7969429d79ceefde7a824fc585d0f7fac02473044022074645c573a73d6e9eceac7f2b61b0d411df41f6691ef612f5f2d25209b6cc4c10220114fab0a3dfd58a7e7bed5aa828844e8cfd521450b2428330cd2507227b22b91012103693192b81b9cbefdd05321eb4c460264a1081d64396a76332f3db3b121bdd433040047304402203c168f021ee2bc3aeea53b2b289b2add195162fa090d3859e1f4ada6551dbc5302204badf65a27f22c22023eb0bf46e39f190fd3c794c98881ee1f9d44e5a1a5ca140147304402200c112ae3a3758c252af58c48b3ff756b93e43ac3e7a388651c7c1f8054209d7c022063e679bdef2b69b14c570fd6315096e372ea888b4149682cdbb37a8d12ada90f0169522102f8310173f04a117fa3c2ff96593908d1ec72c3e9da848099fd91daf118c48af3210346c02205f439c57cdf441a8cb55f4c339521c8d037ad804a15ed68e3391eb87b21034cf24693086f890b5d20b0c3f3152839ae00461ad1b29771e459a1676c47d57253ae040047304402201fc9a9292a2a92db4e99081fca2bf4c7062715c48b05c487b844a49e677e14b102207153528156c535e1500e1980593ba37ac649f8b2c1f8693a0a47beaf2dd4c67a01473044022026b040772ec6a96f2e29f457c89e9520c22f47e368945e0ad5f1af0435ea8079022028b4d895852c3efb4ad7b76133ccdee0f425431781e8b2f098014ade4c7369fc01695221021cf20f5e699d4b29397b2485f322f21aeb93264ab8532bc61127978418d49ed12102c06f94c7526991f26901f227e3ab7b398749bc0c307b0a4891ddc25ac88af51c210378fc18eb885a72bc6c3e3ebec5eade1bce0ef68a4863b2b7ceba2d96e511972f53ae00000000000000"+}
+ data/core_signed_txs.json view
@@ -0,0 +1,685 @@+{+ "description": "Transactions signed by Bitcoin Core 32.1 with signrawtransactionwithkey on regtest, one for each kind of coin and hash type. Each spends two coins of the same output script and amount. Core's testmempoolaccept allowed every one of them.",+ "source": "Made for the security fixes of November 2026 with a private regtest node. The keys are the 32-byte secrets 0x11.., 0x22.. and 0x33.., known to everyone.",+ "vectors": [+  {+   "kind": "p2pk-compressed",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07300000000484730440220643d26f9a430fd0a9bc7c9bbae25c48442e54fbf25abdf7d8631a799e27db3c6022029bf0cd7a9d90468e545748396bc16224d9a67912a50ef1e62c26a01ac0bbdff01ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073010000004847304402204786ca72263b747fd1531026ee967923aa7a6931c12e32732405b46ca7150bef02202af3d40735df4980756e062ec713aa8381896bca4d41c4f65987f5fd1e94ed6201ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-compressed",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730200000048473044022073b8d78fad1eebfca7b5490d01a5f4cea387ac451e3dd1e5e918f6220faf20380220015bd0ba9d766c7dc5eb65648bc573602e083e8f0b24751bf49082a07073493302ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073030000004847304402200e300e56ef26ea4275ef6bc32cfa45b75ec64dec993e3e6f8040a9f17751faa30220638d665c29a1da36282160e9cc8882cdd5cc44edba2b87e3afe1f006ad1c29b502ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-compressed",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073040000004847304402202952761335904340762c47804e07514789e9eea91faa92faf689b3d90c5470f9022076178226eea8e927b3e2f173453b84849acc5ea2b9fcda471e6065822c30e0a203ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730500000048473044022055c26c7f912637a8c0cc2ccab9d3f65cf5f4b44b4052cf7bbd2be4d3ed79323e022077de50ae02778c36e6ce3ff5f30db5dcc135fa5554541e714e6f4c3774c81ae203ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-compressed",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730600000048473044022038561f7dd14d726d0ed5f0acf6c04aa2151f373bb7f45c62fd11dc10f57c50a5022045ffaf6e101aa07beab4e144aadb28e6ac6196e7c7453356078f8aba43ec383a81ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073070000004847304402201813c2bbfa383aba74cc69c9d000dc18691bbe5b58c4b4a82a37946b59e1435702204f24d61c548bde3e53e347e82055ddd7508f610dadee16b830f69e107671d2ec81ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-compressed",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073080000004847304402204c49f01694b6c6fea876ba6115611a5846849297908272cf916aff72194f208e02203a185a172a7971d75eb4546df6ecd422f2faafb2812db9218d81efc480244a9382ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073090000004847304402203a5426014db1be5bd73472d389f4bf8caee79204cb69f112a4011a7c5974886402200aa90afd76185553b6ee96883976aefe05ac579d089a99b5691dd88ae202730a82ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-compressed",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730a000000484730440220754b0548462928ec2bae61626573898604be001b5877a37cccbdc5de04cba5b2022007b01993c9d6dc2362f7cb253d26d4374cece627295baa0f76935f0e4356837c83ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730b0000004847304402202d8df54d5a95714a62b646a43f8df261bce9f5712a26a621f9ac019f637225e502200d4aa3ab38c081d2bcbcbc2180859c6bb50f8afe51ffd5909dc845e6a1347b4d83ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730c00000048473044022068fef9ce783d54b669410ca99002714d2d95b55e9bfaaed19578b8156403564f022007b1f929406d7338e4bb15c0d4ed3f2bd286613b387825f73b6aa556ca0b25f901ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730d00000048473044022062cec0f5a0e9d5133e80632c4693ad15876f487d13fad823125d736a8aae573702200a9560155686835a1fbc829667a9b8307b91ae439edd74712eca1f64037bb0ea01ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730e0000004847304402202bee0fb674bd8d709db322507489d6377fb3a17f22f49f220c3e5c8d47c672bc02205abd6f213b3ecbbe9344df0d6c42afca43dc70c84d393afbf19f86047db9e43a02ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0730f0000004847304402200a14096d9b9088be20905475009843ec22e5f52424416196b7367a1c0221af110220134c932d912b4afdb7d13e4c23240d53e4cb3fa674ab9b882d3fc436ba746a6a02ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073100000004847304402206fe56b1fba2358416d9faa474a2844ad5453ca2874d538b5b96c6525a0ee499f022067d18f1282abd511f12818b7d65c10ccbfdfab547e39c35556dbcd6f702cde3203ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731100000048473044022020c6274a5547e0b1ea5caba8cab40bbc91875d597e34e42b2ef858bc77be422d022025353e6ae87206968ff5e166ad2c70f468986a97da076ca98d6815f4eebda5be03ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073120000004847304402207d321a4df8d061bdfcb9fbd4fae8adfe78a93e490ea6eede0555d7b5e2f6be4c022021a9ea0baa9f872a51683b0998958a28da3b9545fa9960098ed6b162d1f411d181ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731300000048473044022033aabd672b4612e57f5ddcbd9c2fb2a9120a40b4f299d1264438f7ce893aa1fd02202a123a03ce84edd57ad1a5b91faa08f7327cb82dc55101cec6e71b5f3b9b982b81ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073140000004847304402201f568b867689accb45893d505ec820d0b49eeeb087e9cbc1c020174f18f46507022046771ccda5fb1369ede99b22a2afe4e0c3831b01210faaa3c381cea77d04c18882ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073150000004847304402203be325305d690ec647f16d2d9ea4bedcdf676bf27bf03cb7e7c54e7ffe365bae02203acec8ef0260abfbeb7bf4d428ee2105c437e4785c06127ba3a59b72076dd19582ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pk-uncompressed",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731600000048473044022057bb1b6dd542818a217568c41ee256e27220ceab71f33596b347e414a7a6399802204823491cb16977216af42fd07fef24f7f16db05f585fa4dfb4512ee3499f4cdc83ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073170000004847304402204c7a648da539efe4db2974365930130fd9c1821b2e207630891f37736d95ced202205318a908848fe67912ab422d97a7bbdf19ca6456684ec0c8438983151d1baa7a83ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "4104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278aac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073180000006a47304402207ed9289733d485fa73afd0c45bc56070f3e83e91cf4fd8c56a407dde94b436ad02201ad10498f9d272af45c07420c4d1264d6a25cc3e5bab7586a42e9791f9cd7eeb0121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073190000006a47304402202e30a90562e01630c9cb57249cfdd0aaa7a09dc0fc45328d2a0b00eda7ecf5d9022024745a9b0d01c8012d7312d05e4f5b52ef9371ac320e021e686a1e15f77512260121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731a0000006a47304402203eddbcbbfd639eb49b9d2cc5be52666ed39fc912128b5344064abe35f49c3ed70220524bde923666e4320f7c35bddf60431fd752804158a2b061fd908b3cfd0b544d0221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731b0000006a4730440220076678b4d37b29d777c417c1386a10abad484d3596e47e199e59d62ad9e3098d02201d766370fb41f9d65bb965645d78fa787c9893742927b5029ee28843acf5f8e80221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731c0000006a47304402203cc02ab2709050a6d3c0d3d9e9e3a983bfba8fa1150107642186c26a15c32c7d02201b48a13fda28b76dabacf958f89e9391ef94b43b9acb8b12a48a926b81f17df50321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731d0000006a47304402205bba2d93d2ea11aa506531e259094456ea6f6e3c9e7e108173ba43b704bcb7bf0220117df00ae3927534f4297737b167610b5697bace27450b626a09bccbe475ee940321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731e0000006a47304402200ee0168d9f1c3ed6ea29ba724fbc44bb39f762fb68b68724dd796cb31bb2a1c002200f03f0fdcdacec498568539cb6c9b6ba74bcb2da211afd313b3ee20f8f05dfae8121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0731f0000006a473044022016e8e767ba5873d4b034221445d534ae296691a3e4300090e1016c4593b55cea022076bc7eecfbd841862ce26f844c3d55201f237769c31394a98c4eac1fb58a07568121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073200000006a47304402202be93681f318cd7361def5b35bf57e25bec974bd267d93d04eb98ea28cd0e369022028483f27d337d0a8ab383eb7df342cfe7ad2b41ad47bcf977997995a7910830f8221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073210000006a473044022008091895c85876698b8aee121ee2fac2eb6bef609d76dac62181430ef8cbefdf022039f6cc40719029a56132760ac4229cc0831f136188c43722ca65c635631cd7c88221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-compressed",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073220000006a47304402205e809145de419097b07a3d8387533dd2bcbdad123732be7602e0070007da74bf02201dad97619593c2c464784b87e3439c22f58de27d89e69a2c6320def8abd903968321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073230000006a473044022026ca60c8c08adae24cea586f9bc960db2f51f42cfd767aef687e135cd88a9afc022042171b6d4334680d4200739648cc907a465dd39bd75ad9a8e3ca4866c94b1cdf8321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914fc7250a211deddc70ee5a2738de5f07817351cef88ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073240000008a47304402203aeff029012ee314590c90a22cd490dd1e626530f403155e66182d4bf60055bc022015e5ba5c025a4cb78bb2921c80d937f4e4d9c756d3fbcf092eb1c5a5f55f0749014104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073250000008a4730440220178b67fede61e0573edae1de64a71faa610cba5412a7e0f9043a4794d50bf3c902201b4cd0cee70ecf32df682717840f26122f3859ead69f689b6dd198a7f04fe46b014104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073260000008a4730440220380bbe32cb389a2dc8412ebe1189ea03f5273b6aac0fa682cfcdee433e9bccff02204154a88b46ac26795e22e3db73e0c0d25ec4d8643ea8ae137a49481bea591453024104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073270000008a47304402201d0f8988ea1f7a6b5f155041de7f548b3dd55a46ac406de9dc9727e87b7a7db902205ac5e54883d8af5f7042c60f35047b96506fe52636dafa973df52804dcf64852024104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073280000008a4730440220607104ca1dcf1bea360556a9684d2f131c4d70591b3fcb03ed70943244375a2302204b92bea0aab2639c19630b030c0d36f9084b706617a4dca0788071b46fab9c9b034104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073290000008a473044022054460ef9eb3afda0319abfd184ad86cc1fa70505f8dd583eb673e7b5474f68cb0220150933a10acdfad72c2cda1916de955b8e2140fee6b1910c48546cbaadc9b031034104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732a0000008a4730440220216e20e43c80fe509c8cd3311645e641035f29f15e4f68b2b31b77ceb029e6be0220703c774a30fb20857cc2bd4d7bea104eb9d4cced1cbd64c9755e307af7a4bd27814104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732b0000008a473044022030c0faf9c0eddebe5749863ecdca9ba6d6177f6aeac0498e3f51c39369b40d3d02204e07c5fd160d43309af90d278322e05aca85418988756c1df01080bd6631fcac814104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732c0000008a4730440220082e908bca23a29f694611a761d41588274d87b97a9ff1178a99943c8d91605c02207b4bf2727cbb4dfc696023ccd81e203506f28014e5686e3f657f9e475825497a824104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732d0000008a473044022024265d6a827d56b823062119bac5f8deaa6804a490e74f43915381ecdde4760502205aa092365b6fae7b4cb643f153262e5e0e8eb59d8befe1f3a4254cfa9ad1c5cc824104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "p2pkh-uncompressed",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732e0000008a47304402206bf3b70948eb92ab7e6347d058e53b0f494d303a46f27a36c4209e1c0efb472f02200f8ff0d48a995113c2b5094192ba1f97ba16373fd281a83e4deb20e560942ea7834104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0732f0000008a4730440220206c11b2bd7b19113ebef49d97ccb6652799b8708d0aa15c969c0b95aa10a4c70220182c4d96f66de7e81d424a07b777c23802d4b1637b51586663f6086d6c16557c834104466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276728176c3c6431f8eeda4538dc37c865e2784f3a9e77d044f33e407797e1278affffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "76a914b256082b934fe782adbacaafeadfca64c52a538488ac",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07330000000490047304402202c03c419ab80b472dac7817f22498256de37b3d54c5286995210a747cba2957a02204f069714e803a5511dfc975034ad4127b52a2b7ea24d88821152fa5460b3101a01ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07331000000490047304402205d843bcd16348ea42a35c631665a68f367133fd82a0256e19b919d2bfd52bc8f02207b0c0cc3372978c9a21c4476a3a6f3f4d3e7168ec986fb0e0564c769ec1a493e01ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07332000000490047304402204e74b34374248afd0b38838339fbc6be924cb11da065d5b125375c83c53a1d300220689f3fb8df7c3f2f88d9ee08491abbc9bc1598adbba7af8dc041244d331d487d02ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073330000004900473044022076eb582bdfdf42c8439fe39577cd9fd57582a28ee8ed7d9d531fef326726992902203cc5636d11cee6c33c031ff14be197cac63bd4ffe75bc088e1b301aba4e34d1902ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07334000000490047304402205926fa699ddf107361418aa6707823838b480014647042e021f26cf9366f442c02207069d2146f6a31fb0c0850033438e268f86ea6c8531e9a488467c2b614a949b703ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073350000004900473044022029794c5f68e8674448070e881dbc1c90d7c15c33a3eb4eb5bf7639ee683539f102205d9472d79c594916580168b521b72b09105c2fada21b7dc96ac11f3aeebd896603ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073360000004900473044022054c7baf3be180a52f98571fea2ddaff4cecbcc666b68460dcd033cbc0bf7e29b0220550b1d25924af027e6e6fcbc2141540e09f3c8ff9658698a923443242d95824881ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07337000000490047304402203bc685898f87e96d5b733c39b031bb75c132074893d49b4a2ee9d208ebf7baf70220624902c2d5104e2e0a16218bd65b8af155fe0c178cf69c5ac2b02e4841bf3f6781ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07338000000490047304402202ed05eac09b108f1a2f5877fc79057f7a26347f35842b4871135b407f3b76a0f022045a1c8d55ba76c8baad2da9ebaac23b133724afc432dd687387573997f0530ac82ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073390000004900473044022023b3be7011f5012c9606e0dce1f86bb940868c46fe6ba1a1b2cfe7a5540c61eb0220165b020367610d2c6eb20554ef74eb60147d94b0504acc405af669c04f28511982ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-1of2",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733a00000049004730440220048a95cd96f6fe9180788436db71579ae02ea5eb48976aa4f96f1704508c56ae0220701d7d83b7e19317c52572c40344491ca29ab9d5e6d741631e1967c78d77656483ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733b0000004900473044022071e9cfcf60a2ffaf0cf7c509b236d0458e58fc5c7632b166711a737f3fc89c7402203e1b88c8d2b69387c7323644a7fc9a39561406e8f20a4e3cfd158e479a67fbc683ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2752ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733c000000910047304402202e44989158bf40737e8929fb597586962b3d131305aa3824651a0fae6aa21296022053f5dcd96a7be9c101dd9e92faf768099320e496b2cca5490df9b53f0bb6b3a70147304402203d58721f75ff60640c8d3aa2b9199a0ed067e3c8926f902252dd7295571fcd50022025059705c3d8d40d90fe5991d42e24219c4d5ffce2e4a567e0827acb585f9b1201ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733d000000910047304402201f041853983609d71325362323a12281a3ab7b55fead7eb6c54486c6504c1f0802201487c6c582e84b464d36d57046e8e625951d8ac15e4847324fdcdab10ff05a8201473044022006686ceadbd510737a1537342f23b7f90e1e0841aa039abd58c1fa68410b581a02204966c04bd209d8090535d42ba2774682aedcb94c49f65bf13e7cc61f49ac46c201ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733e00000091004730440220487e0c67fa3e49f816e16415dd31e1daba6e31ec7f53a6decafa841458ebba2202207069996ccc311e6479c6ba8954232f5a17899dd0b0640e7987f82129b09ab1320247304402203716e7d9ad20bf00cf59d652edcd22af51de4d4e39fdae4ebdb338ef065d64f802201a7fbeed44c97f4ecfc9c9888e0e6ce0a10094131b5b142d13ee7a7606dfe0a502ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0733f0000009100473044022063e3d08e81c2afbd15e25941f9eb731e3a87cf233385ec02f110dee023e3a4c202206293d17bec5a6de369168f5f2ca33ed79b93780183546648cc3f2d4739e05ae3024730440220109817eacbfe568d637a43da56979b19115bfb759f51938e15d7349414ece58902201c4b91bca9fe64d94da00ee6369055d3420e5aa0fd221a882f6be7d0d32a445802ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07340000000910047304402203495851910655386c1f62b49f1a84a62c06acb1af2560356007e0d9aadea9436022017db189c86a3f68a54c09f6c87f043b9f82d8b5a8d50eadabfc513f1ebc0d0ff0347304402201aa7df802ffdc7ac4ec5abda3b9d52a49f07d0ec3db7040b1e032586623c747b0220041c2a08f4d263cd60ad7e089c6eff00ae28d115c388cb240e91cba3b7282dea03ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07341000000910047304402206bd3bc23bd22e867c4245e4b75a80761804da9659913c241f5b808cb322840d902206f4d7f7bcf6be669821b501d1c3e1819b7dd19eaf41a0d861cdcd19107429860034730440220462e8c58594f968e124dae5c03414a0809af47a694c0d2205eea27361cb99d7a02200467ab0c04f92e8286becaf92471213819f75eea01b57de33778e345f87357cb03ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07342000000910047304402206920fbd11a1de530043faef0266e514dfac3d96c55a470760449387680fc230c02204f09ac93a9f8497cfe6bd36b358ee3908ec957e7a0e1b071b6870ad70e3c89a081473044022072fbfc9889122a22ce3479a63d99098771bcfa8f4d3cec301ba693ded4546f2c022048335517664d963d911ae89aea4244cf551793d1600e9822ac0d0adc874a319a81ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734300000091004730440220634d225af4abc019e4a908da7d15de44947d2217059eace8ec2faae01ad1473202202604a9d396231e4b0a60dae246c95684965b5da36850a59f0c644f874e4e3cf78147304402202bd3b6b4b2a37f2c5adc169ad31f864004cc1c6accd2474ba970c07bd6588d950220362422af26df79ae82a7ff95e66162254d2d4d49f18831197ed3220f26abab8281ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07344000000910047304402204e26091a3c7e5d05ca7ff2b1e285892d6896421a336fac3b86e9c4846ece8d0c02206832f2957b476b76c7d80ce468edf1873d5be4ca0c9421ad2e9ab91f8eced02882473044022002feecb839910d8c059c38228780fc663d3020d0da1daf7b46f5a30304e7627102202b340248c32a730391265141b768b11fecdddac594f4f0388621c5f8b31c71cf82ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073450000009100473044022012dd3cbb57e1728c2d9b75c71e19f9d198700613cc5b6d4aa5cf53b1f88c380c02206f275c7abc5249c81e738d9091e56f4029a7cea8ca8bec9c55c10429eb31544e824730440220760de12199f3079ac7db1fdbe763e433eac071cd3ce6a3efcdf4d5f412e15c750220686508470ce1f0b21fba1e12d9581dba2905721cb838cd14f16a2e1af9864fbc82ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "bare-multisig-2of3",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07346000000910047304402200b079fafeed8010ce7b7b6e2a165494c115e20d4a0111d0be13bef0a7f32e88702202d8db2f78d8da0cb6665b2df829a408cb69f0c0e12895c6288cae394ce498e65834730440220408ca70e59e30fabb80ff390f1640014dbfebd69a9275fcf402439db91587aae022066718bfc71d18837f8b0e5bca43a770249713f2752c81b94b88f78102a4fcedf83ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734700000091004730440220540d7c60e75fbf4f7c9dbb31864cd63ed07cb2f62188f14e19eda793c6d7c7d90220102f9d53f682264f57a2454b848c48b56e76be5ccc3d631cbb187ef6a9500fb08347304402203f0f2ff7f992307e6f1b80b0f3f9bb4ca6d37a42f92cca7498d466591a107e9c022039b17ee8cff16363cb2a7a0020f91dbdf188952e33b3f4594162d16153c4db8583ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "5221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07348000000fc00473044022066b38151271450639be56f7911fa98a03b3d3e2716834743f017a37026645c7b0220238a801c5c961d685a6b0a466fd2194307351a0589a3fea138d976aa75c20b1001473044022056c5ec6fc10fd807659708fc7b8acab1024ab2ee595643480c5d1de4506820a5022053cac3596a7dd49d0244a3417fb9f8137d10bbf2e08a424ee60b78faf33f5feb014c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07349000000fc004730440220123415033d329b2dd5399fbef5a39b58ba575d2689f29440a96026009a94ec4c022044ddd5956121609dbe8e6697f1bee37406744c593c098fc666ae20f47e82883b01473044022014d5ca593b59c5aec786923027a1b4ce04b267d9a30e39de96b6de25091049d002204c50e60e3161c39600b3a9848697577dc6dd26723017339b123fece3f1590dd3014c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734a000000fc00473044022057c38a21d84469f14420753f018015a16acc247321cfca4b1bfe4750d831196202204ee098d0c82c521774fe22cbbabae4ef869bb4e8ac38f155aef837a0fb21989102473044022024749c4783f0b6572191156ef571235825b185e75b27fe83e804777172bab38702207b9513f48c08a3982bb7b3e30fdbdffb0de2717ccd64941b9f6f9229dd225a12024c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734b000000fc0047304402200f9bc9efc504b2657b30885f1d758fad09dd456f5a67ff02e8d009352f9ffae50220161a840b06e054ec9e082f95fe85c16696225e7d9064e5fcfc2ae8b287dec9d602473044022036960252f49f2bf54c1ed5382f31c6a7d3d2e1b6865269a3093d38024e66543002201cdd820f05e9cdad17c5b7b3e63bf4b1882e7dfa316ce00ad6c777552c090d14024c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734c000000fc0047304402202c498064e28bd15bd626052a17eae7eb7e637983779387e79520ea5bbbaa3f110220170f223fecbd6ed6d0e8de89a58123d3ae9edc1ef4d933c48c7e13d38e017d7c0347304402206c23f6b2b89ade45fd082f198ae3f90cf97c1e8cedf745e6316ea86a29d0ce60022014a758a843370f8064231c4adbeddd6d7713930337ecd1ff7c9d5f8d33dc8c9e034c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734d000000fc00473044022031edd50f5f2972d0511e46c88f881a4d2b9f61f04a7852680d1df900cdd7cd660220741d0a06f3aa88b5aeae160a48eb0f6b4f5f42a4eeec32fcecf9835528ffd61703473044022036a5c43221847e875e512323480bc0d52135931f4fc1efcb873f4c2c1b4ed14b02205fac1b81a0e0e65b00abd113d47b30958487600b83beeb20ea0202233259a655034c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734e000000fc0047304402201a9332aad5208bdddef4ffa9ee677f16f0c38bb2e3955db8537cab2b1bd95086022021be00e529d71398a1a8f5b81197caeece65125235cbf82380a8e9f4d7af61e08147304402202dac43b7b6130bbf493acc042467e5fed458e075f6d3f768884d63d53988cf680220395a3861225676e8867806ed248778f5c99ed7015228194ecb50e30f17239768814c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0734f000000fc004730440220304e026286ee83ff5a83f82a30160284dcb2b344dd65f4854daf387fe7fa9c9102202bb6a45d3939210c87ebf9b5cd7acfcf4524ebaba12868ba3607461a7ba9d7a78147304402200234abf2404455a5277e9d9872910ba7eb293b7537c6a8c6c6cfa3628c9a944802204396a7465e4ebaf1bb62b2549f92df094e820712f385c1870263edb9c6b7b289814c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07350000000fc0047304402207263189197bbc39e148890d430d595e101bf6fecdba3a9dbfd602b9acaf19680022055504831084922518c16321abe39bd4f908a6d9d8cf6b207b7cc4c77a207c7f78247304402207e0befaf61ad598f2b9bb1a1abdd8dec9ce848a73a91c0fa72213cc1f20809dd02200bc50c2662a45cc1153fc4d740235c75858d87d5f8f150b4339f37250d90ba63824c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07351000000fc00473044022020a84829703c92c43ba42c3f6037cc8ae78c40bd526c02e7b6d53f91a2e88f7a02203e9d6232dbc32f0ef448b25212e323115f7ca5628b145cd77fcb15967bc9214e8247304402207596176fc1b6a598cecb9133825301ea5228f4569469cb7a93260d18c1c3481a02201ec7a15c0cf4051767df6c7f28eb846d77f0942d904699b2f44c4a55a262746d824c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-multisig-2of3",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07352000000fc004730440220352cfd50305cfa395f55342413fffb5f44d6e9e5f1b329cf0cffd7de2e1d94eb022009e869b89d465ec1dc799c4252c70e1d58d1f3e9426e34290229c594cff7e9db8347304402204991f4bf9e2180fd934187c659de08fd29b76c8f4a126c3465196f6e37f48677022074e4733c0aa72cba43b133e9eb77fec20350c1fd383c5fa81c18c3faca863159834c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07353000000fc00473044022044e5e95a29fa23f0aff6fc61e47adb166805c7c4c3fcbba141e05c5c2dce4cee022033c0feda159b2c4505b0e46afc3dbc6cd9c1eabb68da744ac121a591aff08ece83473044022067bbc723b74392dbeb0df9ba76e4cb66d5c0286710056c62797198a65d8f47f602207e73cd116f635a457316da996a94057db9a873025bbf67ccd4219b7f4fdd8281834c695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153aeffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a91419130817a355e1a4df9cb1e25052d39374b83be887",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073540000006c47304402207b85f40a276a53a15235d8ee42b5fbe9ae1d5d97db2cb682d452e87c4556c99202200113505da7676365016eccc641b63b81b80f899db6c9cfe322bef2c753a54d31012321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073550000006c47304402200d9947789b70402445d357eab92f919dd420b093cee90d92ac371b9fa30ffd7502200d920497483ab4d1cdf20a58079e6f9803aa2537532b6793709edb03e0609ea1012321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073560000006c473044022042b741924ccaa76144797bfa16bc5b3ed3926ceee7109da78e4d5108efeffced02200a7a1bcdb06965058371446cd794d84dbc0c6203e992ac3d7ffa876687bd577f022321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073570000006c47304402205b52dcbce3a4c7e02c30c3e0e2bc6659a6750ec996777fac1555fe22c15b36450220330035a9f25aa159b4655f552ffa16eb9ac4bb9f6efb27e369ee1f6838e1c34e022321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073580000006c473044022075c2331485c58d4b266cc894fcbbabc8ed50fc59ba84a4d394c0ca73c4f2d3b602206a5ecdd34f7d60c3e9e4af1a1e68198160e2d27705e84ed7762565b5f7f31251032321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073590000006c47304402203435202621803d2faf7d24327a4e84824a836b85f4cc63d87b43bb7a029b081e0220410552e32c6cfeb9cca15b3653c158d427f4334bc2f8c3ef73bf247757031ad4032321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735a0000006c47304402207b402d445c2f78c4db31cd62ea4bae5be84d976ac4e34dbc2b313b74879e9dcd022041129bce2f81c6aad30d21e8bf3b245703446da4a8a20617198ca30f7fff2eb2812321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735b0000006c47304402205361ba5f55c70435cb40b4916afafc8925e4c6220c4e32350304be779f46049f022069032295a34e2feb0228f30048b89c774cacbdd6bfce334c0170e1841c7cc3cd812321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735c0000006c4730440220658c5bd18f3eb5f44ce86654b759b9d8581abba4ce5711bfce1e1f9f65222921022075a72776a47326b737187205140d687e240692e4c79835939aee103d6ec6b6f9822321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735d0000006c47304402201cbdb986f1d3e3467e1d6133c4664d9073048ecbd502ca87fcd2487228ebc18b02200cd71120944fe2866d56ba3d2372c63781baa613f7042138d171df642606ab1d822321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pk",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735e0000006c47304402200d0e37eaaf781f240c779e45e3e853132816d28a1aaeccb31a65549a5f95f50002207a0323290ae02f099c667020cbdd842e2bb8130643e71fc189c4d966949418b1832321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0735f0000006c4730440220494bb27590faf5f4317e6423bbf8f2c6037869afacb438144652f908ca7a3001022012db90fa5c51ee8b10621951224f1cd1e35a8894eceb414f360031b26b7a1783832321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaacffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a914defdc651f607ebd7fc7ae1aacde8e632f40fd64f87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 1,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07360000000844730440220622cc22edcf795580025b8a89b649855a9f07e38bd204b8f107de3c935afdd5402207c4754046994e66457bacd21c28c9d1102190a87457627d754c75c9ab1dfaba50121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07361000000844730440220257d299050856d1f4e110ecfab996556080c668d38e3dc227ee90cea635ac3bc022041d83298e5c9b996b35751eb861df6020b64d141ea5875ad610a603f47e055120121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 2,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073620000008447304402202c643ebe5ac49d1f733781fc0c49fb30bc7d47ee599e9c48fd82890d6369e5ca02203c1c9b6d066b86f0b42ad192e61881f14082af8bdd443852e554bba572ea47950221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073630000008447304402207565d64e8a58a7c6db2b66c3d410d6cf510dd6ffe2e0686b54c666a4cc56b90d02200c3097fb4289c5acc191bb6f79fdd9b98c1d9dce3df7d1e4bf29a868fa431db60221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 3,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073640000008447304402201cdccca5b1547f09ba819191fecaa54acbd32c3a013319a4219b7ad93c38488d02201897af812b8127b70a19a7c9b9cf68df5a83cf6a36af025d6cafd624a10d63040321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073650000008447304402201b76a1442f2db1ad539463232b2242c4312f1a34cca41b174eda65e19a093af40220486bb89dd25c9d50dcd1063e5d21db568f47313803a438c94524542dbf3693570321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 129,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f07366000000844730440220204dc46de3b37d0cf96692347ca6fbcf64fb1949f95b6d9ffa1200d3ab5bf70a02205f9ea1246332b109154f5a6becc962fb839e8c7bf060f3500c21f2e5de50c4878121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736700000084473044022023e708fd5c26c4e6bb89e4498b2a5faf0e6bddf434724320529c8667a2468a12022012e67ad65a70b323c70b2a0e9a089168b13a3710e965d4513aa07415749670228121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 130,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736800000084473044022009b38210cce493113440bae4375659a0c99457ade8899cb75ed65d8aebcda52e02202e9ab36ebd5a44cb11d710a87993bded95944f73c4d31e4af3b3194160b557818221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073690000008447304402205163e057c885de79237dabcaa3811275cb11a5a9f789f42878048f1e19f371eb02202a2e61540c197388f42145a85421441e1f3156270c1afbc1fcbb0c22bf1c2bd68221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2pkh",+   "sighash": 131,+   "tx": "01000000024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736a000000844730440220501b1c4604bbd12267c45eb631dba9dcea1a010381e7558b2982fc0a8ad1bf81022007a18547ea2ed05b995affac9d3cab1e9d93ec3ba70a0bcec37cd472f20f49498321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736b000000844730440220141f6068e4173e592af3801855caf30e9488b877a71fba727cee425fa1a9103c02203558a22251a349292c51ad3f8aa34099b3340587c1639c332b8054aeb4b59c6a8321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88acffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef00000000",+   "script": "a9143161c7bac54b3849b493ddd011b8549345df8baa87",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736c00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736d00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0247304402205c54ea213b56a4707b9a2225c2ddaa3448c1a0fc030eabde4c38ca2b4e3c59f3022004ac7d674841b773ce95b1ee030b8ad9518f8b16508d105aadaedf7ea010c6a70121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402203d5f106ead625a15fb9af2a434bd11a9629edb0349dc5ed90136deedd008e73202207a7320e73d2b37419b6bd6fdffc16fb175de4891b2e4d0eef4bf0751df5583660121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736e00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0736f00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef024730440220683fc430f76da0cee2dcb284dd2c384ad7fdd29cdcbc801d791b005bee29d2e50220684b48114274d3a1b4398724c8b9a97b8aa8ecb0d1952fa9bc884c9393fcc2d40221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa024730440220782c8f2afcef060c14f93d1bc4dd80aededa8c2afcbdac1ca9d014031e17aa84022051eb2b739625a3971fe80cde963d269eed6f20d14651ca0acee715fee827bea40221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737000000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737100000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022020b4c6b63d30e5b6c2ddd9d290ecfccd424d5191626f9c5d5c282f99a21f74930220387c662e5a80fb96ad05d376dd0e392c861e69dc407b7784872c77380a11818d0321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402203a6429ac08009781aae8f693f8564c01d036bbcf23c0a8e3225c43b911c2d1e0022036bea8fb248772f20d11e4f58334d7b1f9b18582cade6272ac294c2ea3e61dd70321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737200000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737300000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022036f82d724148b2d11feddd3f785cbe49b00771d05a246703d9b753046735405202203bd41261516d8aca66ba4a77c3bcfe09b4eae2cd3a7a0d8f86b7339e2cb83b818121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa02473044022039c2e3747ae371a08fbdb4a8c559892d5be197b7dc47e0950f03cceba5c5422502200bee786403414e091a6a3563f879dc0b6a72308b7ac3ec266c0372d8fee9f8f18121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737400000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737500000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0247304402207c16b45bd31c671da524e4e7a3fec84b179edf5c80bb9ab823ae30667873de3a02207ff9cc488e8b3df8ea82d8ff6162cc97244f795bbb31c7cc2ee93ccc24f59bae8221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa024730440220100b75d07f013b7d903b664951e6b258a0756dbbaf210ae1924b68411ea94bfd02201acfbd70b3b662954d83eb13459070406843924f04351ef8289a589698b74eb98221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wpkh-compressed",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737600000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0737700000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0247304402204e6a6add91afa327c35b44d8818823754e5cb9cb4df8e15370fa8b75d8fc04ec02202515ef99eec0ebbd9a80ab0adf17ba2bee80d541827eff6b94d42aa2625fdf778321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402203d2ec1aa45c903c2731c08055ee6a91e4bbbe258dacf82969d61e640f5ddbcb702203db8893e74e56c7f4e83fcb7d09e2d71903c5818026a1812a94762ca062ee78c8321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "0014fc7250a211deddc70ee5a2738de5f07817351cef",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738400000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738500000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0400473044022051892dc6dc5e482369252b5cd13e243ad9ff82e97ed75898fe5d797d8624fadd022014d018f56c34cc56532de9b162f911a6d22c5d0adcbc68fa48cff86fc9e5bc7b0147304402204bbe2594ddd7423d68b5090eef60e5a77b8aa1d1351424907f14e84f290b462a02207b625b8a0332433734ee367c907c36e37e9e16d14081baf1dd51946bcb347ea101695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402203f44d79e67897444f697975cf1974100dac6648c333bc986109c242a3fb9091d02207fd7fefe4cef5daf3307180eadb5d4500af95d2247525f1609f8601b63add35e01473044022028e7a4c85ff1ad6838df65d897ca1d8a2c2146c00bbc929988fb085a3f04b93102204535ada01b7bb9c3da14f3d17a820a65d0d4a1ae8a4a742bc937bc334d78c0c701695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738600000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738700000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402202d6c547aa1a6fcb7b5a7ba0faa16af8e3468e3eeff0ddae2b924c6a951e9c0bc02205acb1e256a0f5f418c2d33373c8b7ace9286f4faca9aa2a13df6436c2f89056802473044022038c4f8a581e751b2016cbb029c54d58983346c339b8d0fb63288b31b2e77a47302206e4121c528145b0fde57cfe9304c7b74e1e758dcdc2da40f4de728f04aa771bf02695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae0400473044022037d7c66fbeb2d31c224293d335f2cb8d6f74077934575bb95f41f97706d8f25f0220070a29e5f4658b199efc685aafd14ee7d958983bebe67ce25d3d777e1d22de4d024730440220416137b02f447d5507d1aa76da0003f31e290f363a8220636e997aded18e1e290220424267f6fb1ada3f5f6531f61d0f2d4e38fa7e5e16901239017d1dfb778d3a3102695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738800000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738900000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0400473044022012a2b4d0ebec4e2bf79d44a82a401dccaf0e03c1fd916d698ed731b74e284edb022073aa793c677b63bf6fa0f0870955f4c824fd2088c65b5584def77f5447988938034730440220170aab2379b64a15b538be44586525914b6859eb25e8ac7399fde6f1754787d40220207a948593b8d65a2560c9de509e08e21edd1abda17b8aa5eee756f2f3dad56603695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae0400473044022041ab42c24b38689bc2e706e67ac5c97f2463f2266a7c663459e92b202280d86402202a5aa808e019aae087b89e42d293b13096065f0d2a51ce92c0208ad6c5ce4ccc0347304402204b11beecab5b3901290cc73086ec086b4239ae306c96e2482251237d90efc791022023ca949d2a5eef358227e3895c8875844f3b155a74da52d0bcdd6f4f34114d7a03695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738a00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738b00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef04004730440220359ae160f02b582c1245899988073e0cf46000175c0a06ef14e303a2713a60c2022065c4d0281ce60179962b5b10399777de9f474681171af575231fd06b4ee67f0a8147304402202a323a606a8b159e5615c259f8fa61ed291f3ff4b9736536a574fb8687fd0ff80220599e9849302d94312378afa00ab06b0acf39876daaad05d806edac3f016b56f681695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae0400473044022005fcb340fa8d34e15df8bc30e564ebfa086fe8844073a66ab72fb1bfcee06871022055c8c35b724295814a5fa336f85e1d626d49719deee755da566c5535554becd18147304402200f3e3c739f3c82e78af8b0a4239e03da76d71290cd802f3cd3fe67f7b0b5c08f022051d3e1332b6ed54493ef6e596e5b0c1acff4adb45d3ec52eef42b29d13b70ea681695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738c00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738d00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402201388c1d6b9cd6a6f8ad90fc419bf1d19e29edbf3bf35232563caf80595a1725d022064d6727f74c3e0aa7acda74d13067a90ffcaafde587563202059db0792852ebe82473044022039c44ea437a895d6db25a877b6594e8f33e561c6540f37962440474a377d6a7d02201cf6af2401730fe0cdc1a0695107b587806a8a9ab88f011861080b65e300a81e82695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402200dcd03fe97e6cbbcc557d453a7e168218877913de98fbd4dd651f080981d9d5d022009870ef68e07cd30604a413c8e26649a0d81b9fd5592a7fdefde03f1cdcc149182473044022021678836dcf9d4a9424b24eb8dd509d91a9f9e014f3ae05d089aa3603c83b35f0220270082343946f98ed89996456fcdcc8e521a87bc5bc22eeb266c2e55b0c3149582695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-multisig-2of3",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738e00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0738f00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402205da11d6458e2eb985c218fc4f84af900fc64761542fd0f5a67284273dbd7c86702201c94eae730e6520ef13d53ea88d6e0757cbd2d8693ac092ac84841e9033276a7834730440220536645be24ed53a2e09d26758f0142acd42cb8ec319a67269250e8bbf9aa3d4f02203c4c5e12d0e5b84d542a2cc7f5ec352f121d8100e1d801879c9e6cea18a8866a83695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402207f0af0a36fb4576ec42f5a2fb195aaafddfbdc50239d8b3a93ab9a12a260340b02203eca3fd9ec0adaedf87d296db70326302ecc432297f5e2a4baf398a39fcf10b78347304402206f7ab7cf009f0c4547eafff8fe0d23d2bc0916dcfa1764d525609f9710e4f28702204b974d7d2700783e1780bd12a2bf8f9dded370bb89fef6a8545ead1e758cb14883695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "0020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739000000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739100000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022047e219693de01d455e78aea8bc257dbd42e16a0de381dc647975a2b696381ba0022007ef511dce6748e20df3c52b8a5847e733647bb8675705937db04f91e5259972012321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac0247304402204f571135da0d235a12ba6ddfad49c03aea3f7e06f310ba3ba6c41c59bfd3ca3d022041a6f9eea5f5ea4d1c3429439d34f31a7248989ed6278ef9d7945532e3f04004012321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739200000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739300000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022022d11e579c730324434dc8c38ee7bfa488a7c7ebdccfb7a91e050ac746d948110220598c34a2c684ef1c620059d9c1ec9bb89ddde1847a9c384e1f9c52001fabf3e6022321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac024730440220344e7439cf177e8c775bfd99f79eb2b8125b50f3e306b1d2fec7c8557138193a02207e05a604b1a71959f2f445f6ca094a4b36cadb21f4f641eb76d4f5d57802c432022321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739400000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739500000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022017c752818f24634a46ea847b145314b7f0dda1a63f41cb8e54965f670669461c0220116382753c585d1b33b6c541a4186fcd221b7a72ae59e76874df2ab82c61d8aa032321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac0247304402201844fa020fc5de01e3d091ac438fb726830986137598981780e277467889f3530220102643659e131edced6a4225d493d5386704ebb705d9c52e7778f0c8e052e945032321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739600000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739700000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022057ba3f26e7e1cdda5abe1361ba423beb74c4e6d93f5a85027f852f83384b0faf022023bad2b046d9d5e104e37a73614fbf61588e70eb7fe5d53521f48de13781fdb7812321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac0247304402206c6ec58ec34ff37abfa50d245726677551cfdd2d1d4bbd479bca07b2f9de6e1802206e65f433349f6db634507fd8ce3ba3ef128694656941613ddcc0a0661b5c9bc6812321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739800000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739900000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022039d87d464d4c306b049024d8c3eaeee27982180fc3c9f17e23d453cda4d45e2f022023637bcabb7059c5b9f3ce78c7b4eec6bc5eaf06d112b0e845f727482556e9e7822321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac024730440220345e2d317aff8f81813b0c87642133640ec3621f844bdf572a4a0eb6fb737efc0220417c2dc97ca3a92e6ffdec0d143233a73e59ff41a5c49f0a4cb72c461b395a43822321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pk",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739a00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739b00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022071963909861d4876084f8433097a7d7c92e3f363ecba827b4630a5566d2f29f602201cc21d1ad4dd9ef46f36b9c7bde567f2901374ff6dbfdbe7f017e718077d88ba832321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac02473044022025439dc7a4cd403e8a10df406c22ee657997b48a728c62621707db8819c9a45f02206968133d20a2fb04846bd540db82d36bc04cb1aeaf88ef2b9a19b2b74460b15b832321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac00000000",+   "script": "00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739c00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739d00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0347304402205255daaaf1a414eef471de9ec8c139dfd030ec96548571529139cb5eac1c1c0702200080125fd7ffdee360cbf7b7d639c6622ad1f4400ec6ba01b52c3bcc534aadbc0121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac0347304402201d062523a6ec81aa2d49e0fd2fe36e8da08852bab64df3ea9c6f39203e9e0f3702200c72137acc29be5661ee269e15ebbb44845a16b44cb9806322dd939e226ad69c0121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739e00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f0739f00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0347304402201eac57f1b6b2f9cdcf94de615eb43c17ebc3e92d1d7f5c5838286cdeaccf29e00220550035e81dc57a3fa672ab0d085f4d670577c59305a11d66f17375fc1640586b0221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac03473044022026ea930230c1332e7e066cc97e52967b512c4585e7c69757c536c421157e839c02202ebd9a00a54776a66aa39d048a6403ae6d2fa9ed0f89bf28dc9efc73301395230221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a000000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a100000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0347304402202f5bd145a41cd69621a6764eb36778b2be8f011a9122e400156c7f99cd6cdfe402200d51df2c553851afab9311edc9fc1a43ee9879ef755b06b6afdecc855fa9eca60321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac0346304302200af5f498237bad8524676397a253f26d1ae6170a4ce4bc329418b110837d5a36021f60b151427fa2ccebeb8f776a7f8f23a560e1210d0f45fba4270005da8edd840321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a200000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a300000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0347304402207dd2470f0413747914615d6e0a2cd3fc99480710ed2080070a52f177f17cc89f02201136dcd8fa8769d37d03a15a5830e85a8e7713658ba40402b6e7cc9038954cbd8121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac03473044022041fd5d478ecc912193455546d101ac3b9be79d4987b00a30c6564d8df9eaf4a4022001fbc3f6057f9c5c2a7183acb807702c0561b926421429d803062febda9f6ef78121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a400000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a500000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef03473044022019801bf5dc7be46be0370c5c4399ca60cf2941e44d31b8c035d60e021973b329022026b5445cad1a43af7465d841d4d2e8d22e5ca2e88c1e11202c6ba508ab9b99e88221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac03473044022074150dcaf5cc5482fb299d60cf109ab162134991677b2c07c18c568ab6c1445b02207cbe5335508d10dbd3ae549ba09ecc5d9af0db282d5e994574d64b3b4de445e68221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2wsh-p2pkh",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a600000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073a700000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef034730440220093ddf93976553b78c7be6fea44f3da8d8193f2518eb74d759801b006c4061b40220111e13e7719727b02ed839bfddc0a6a8e5c769593c622abba86dee363ec2206c8321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac0347304402201b2f68d12b2ae873925264b3a0d360435db505415a58c948f9283ae15891550602207b66a98459625953cc09f0fb2a81ff1c99e8c77ceb17b1300b326a466008565b8321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa1976a914fc7250a211deddc70ee5a2738de5f07817351cef88ac00000000",+   "script": "00200085f44548c4e5276a92d8321cb79cbf7fca6333b4d343cd25c853fd855b7c4c",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b400000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b500000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0247304402206afbbae9dbaf7e82230798b9426b025803dc5433d44f67e0cc977ab0d222a6f2022009fe2032d2adf3296e0635c4d0adf33ca8cf65523c600ad71b19686d7cae26070121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402202ec4974bd7c61fe917ab52a5c6b8cb0ff4be07d616c2f48b2ebcda84070cd8cc02205792f343f45598321711a5f344233d4db383bb916cc7b72bb8d8657ea5ff27400121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b600000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b700000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef024730440220472431020987c2748c2c8c68dfcd634fdd2c9df7dcf187da9df01dd5e5bc2ae5022028226f5073e85fa838c71d6a9fca6ebb34ff0bc375ab976eb5d7696f25242a910221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402207cde8bc59f125bd1a8fec7d291ae88e07d38ab698c98d1571cbf334860cd2d12022075d66e59b5a77f9f3087d94af1f9538202947d4f698cca242dabf3b34da427d00221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b800000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073b900000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022058cfbb968f33e4c74cee22175a1d5d84d0e18a44fb22f663cc99b7c07be275a602205f4366274756917a6422b9681d9798c9a12600a02013b238086fe94c19cada9b0321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402200338d28bfd3cbe9730969e7ed449023ffba55edf47eeee935197350b1fc75dce02200b12e93a061fb0f45d776f5edf9d77fbc32d757a9aa36e3c4f562f9cd28af12a0321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073ba00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073bb00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0247304402202fcd2011ec7e30f6e3da9b4581548436f673172e6233c9d1e2cf88a755e56e08022008b9065898fcbfa10c9a28467a82be1b6166ab0060f73b23e7c78687271629e88121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa024730440220751d4cef6c53395ee09be9c0185db31ede3996ba44dcd512287b1c6fc4df0cc402206d0a111c74cc017df4b77d9be3bc928cef834044330337446ec09ee53550b4688121034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073bc00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073bd00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef02473044022051a98a1a3f22f608fe446524184f1da3f297ff374fec9244c76df0b86182982602203f23d47addefc23613a0b0fd0b5cc3e18adf17a82f5b8914785d8b96d039a4d38221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0247304402202c65ec18248728c62e15fbffd78d3a5f07fbd17bb8dbda819f88076914995f3c022045bcabf20c081c986863d875ac32e4f94a628da50a06a440f40de14126575db08221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wpkh",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073be00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073bf00000017160014fc7250a211deddc70ee5a2738de5f07817351cefffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef024730440220152e00c6ae2015f0ef445105b9cbf742ffa286b3112687714215f9190de3238d022023ff5e4f1759ee32f722e0cc71d5d586b760b1233c937f08b5cfcf4609f0a7938321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa024730440220724d1a180ff37826ac634bb1cc6aaaadb5fed9c3aae64723ee64edfd24949e5402202a8668b79e6a79b172bff8bc0c9ed3e7f0a27eb10eca217f91e6a5808b9bd4e18321034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00000000",+   "script": "a914ec8f3d9c2763a0997a465b968d99db47e82e69d287",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c000000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c100000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0400473044022032ff45b15adc9576f39a78e8cfb61fee598d14b065eb87b3a48fef5b1e06d85c02207acd8dad5b524f7c25de8396fa3c8b7bf6181a015f98d3c39d8570fa65e510d7014730440220777b1f7ae1ebbe01da66537b77ba188a2643ba0de31970d40546ba59907e31ce02207bdd1338e952a0410996f440d997562ce9fb1e4ba2b72939cbeaa6690e088d6201695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402207c50ae837e9afd62ff75a862bdd005b1cd98d98fd7e2e5eec3d8c0da1dadd25b022044fbb0f19a600849b31081f016fb876e4d12268cedbcb897458485a687b2de71014730440220625353a48038147083722deb9130a762b18ec27df5b2a5b98affe96ea7031096022011de99eeeac9ead2a25001ddba8590b928908f6041ec8979cba516edf28ca7b101695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c200000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c300000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402204009c630080bea5fc1b06d1a37037fe889f84619de9f1e2b3b9a8f81c1ca7f0402203314224cb3961fd4d49359a11e92d9b25d8ac3b5ce6942e459bbb45edd5758b8024730440220194a88d833cafbf0eee4407cba0c7254c694adc36f3d51df5444712e02363d780220008972a2381f486f6ecd125547552253f54b60becd034e0a0b8d0ac302462be402695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402202954057d893fefba65b32406f69a523600e41f5275f1856be8332b4ee3a71f9802201af7ca064834fe33f051418318cb7388681ef48002b235dc0274c3829bd63b800247304402205a53760c6d67d0e471cfd2672119f413bda7cdd0a329e14e90d2a68f5054c02e022019a4e5df356514f429a7132339c3772bc5413b6a860c13d37e923a3f802d84e302695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c400000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c500000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402203bcd9ae7d2b0bd4a4becc165aee9f3c24232dc2167109fe7116b84ea2a1932a10220627482c372eb0745ced44c3b5f63acc119eda9d92be5a7db0db29e5f225a61e90347304402203eb4f8aa5ed3bed6f64d727db7bc47c04fb25b595171e548173a44992db93d3602203fb86d93e958bc4ce8cf4d3cea9e81213d5ef15c4b593cb4f3bd3440cef5676d03695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402204a149e7690a237be9339af08f6e0db58dd1028f4b485875f68e4822adeee4c6b02202cf6746459ffed866cd81231abee47cff049e5ee4b59e8ce67f86523583802cc0347304402201a3fd47cf5a86c5292af4750299db6c44f55252970b4afde78fa02c3b5419c9502200b4bd526f18a03f5a79c11880b77dde959ac5bec02fc74688ba51132322d350303695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c600000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c700000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0400463043021f49670f225b8bb7e023cbcda912ff20266eedf270aecd91269f3ea1d6c638ba022043130654c2eaf3a940b858d99a2e57f2df59df5c8f7e2d470f548e75209683dd8147304402207bffd35a522306906adb2dea0e04a79e392428dda9792f31270dc5049c9f7566022032cab77ce9701ce77e2b903790450572aa2101ad6b4df49ab3ed596560656ff781695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402200e97a174abdfa0ac529848d3e9a9ed6b5abd66f446dd05c487dccbe5e2ac9813022014e18955eeea59e6e28ace238de77bbe4508fbb851647ddb7c93ffa091a7b64f81473044022031f38ae252d6406cd58129b195aa4ca48e4030f049401df7ec4b185b12b672f402206a691e594bb57a09a14c25466e38fe8e675fb748de424a59118d965f9f4b64ab81695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c800000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073c900000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402200149f907269cff5737653e98dfe17e79351bb9ce9481aa09788f83dcfcc0cd4f0220264dec8321376216726318a2451138fae7a4ab7b0fb25032ad3313393e89ae3b8247304402202da268625e80ee074db140073ddf535f89fdf8903ade16280fe0429befeb1f8b0220183ea0f784aae05531f57254daf817e81077b713d05f7246a6797abd6eea7b1582695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae040047304402206134409a26f51ef97bc6f9eb750407cbdb39e337e1d6d9c9695a9ddcbdb2c9c902202ab14fcb42f6a42461461c79711168cee927602193194bfb2ff92c3434b872d982463043021f69864b3a75642da059c979ebb2fbc71fe6dce30f563feadf09137f5f04a08b022024f75bab325b99cb58619289cea462f2a68be03b972f8d391625b0572de461d282695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2sh-p2wsh-multisig-2of3",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073ca00000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073cb00000023220020090e494a0afd279cb8de52b66664aed6fde1021cf0fede59088653fbb87dfe46ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef040047304402206ab27c2770c07d05ae94972c628f8f3d4fcfda219a96fddfb86a55a01c8138d0022010e794e724beff43a277dd28b7cc92ce3f28a3317055d27ccdaa1179fdb96fc68347304402205401c024e5e3965b81a4aa09e38acef6c57db4998f65d70a32ec904febd231c2022011998ff4ef4e974d17e2c6e88d209f6f768b5ea9d6e608507dc2433208eb8b5d83695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae0400473044022042b31a943ce8b5116c7a424af264c19b48f3f3591cc215f6667ec4d1a7f6890f0220196479633d6f68b9b96c9d6a7375f4d4efe5424777791b8f82acbe6c4063e83a8347304402207910b001e0b547988cd64f1e3abfb057fbc0107e5f7183b7a724b430e5283fcf02206178bba6c47f6782ee8ec22be1f91ed26632f236b2ade5129bbbfcd3aa944faf83695221034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa2102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2721023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b153ae00000000",+   "script": "a9149874cbf3ba7eec9fd18eb1e9c4c9eded95c791c787",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 0,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073cc00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073cd00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0140a7525b59673ba07220287e317537bcb64610bd2b3719923bfb7211455c13282c4004dbb0048f847ce1dec6ff62ba92923e74bbc7e799ac6806a54454087492c40140ca61980350029e3a61db8d3ffca07ae007e42964d4b53f2b113106456eb311bd1597e1dc412a726c84cbc2b10b55e242f74f6a8c2ddf5dcee646b832c614b36000000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 1,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073ce00000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073cf00000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef014181d98d0bc23dbf0b0a7ebd10af0f77d290d68fa35d2b3848b76ad1dde4f88d0f083b3054f5d3468709f42d4fa0937bdbb5807a329c39a1704c2d76fc3d8705f8010141420e7eab87d312d23b79eeffef42680418e6266fabfd9fd3157be010b9670e94ea2859c0e9760a419b3b6f3773d54bc673259324c6bd9817a31273ffd98682d20100000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 2,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d000000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d100000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0141de96009ab188e2692a986d635b5de18b7f5f79623c0c666793f8850e081d40c84fe68ac1c95a375c2ce69c09b32dd2970fa5bebff6c04322a82f765db88f5d3c020141a7098a9b0dd0b6861d007cbfe59e2b9210eb3602a087d95d64ef460d0a273db4a213ca46ee6a39a277b1928dd2922f46cdf4d9e925222b52e0dfb5ad88a4bf740200000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 3,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d200000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d300000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0141fd0309c4b193aca92b6d1dae141a3829f52b680b8cec0b9d0a047def3239124beeff37dfb5325213188833b881c54e4d75695a4a6913c9eea720affb24b4882d030141d3c3f3ff95a127b64de9fa60c450c3ab259fa30cd9dab9b65c71c5840113966e817b1b7733c9691e4b92bbcec80210516637064aa57e38b00f6436c2e41a02710300000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 129,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d400000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d500000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef01410860e17bb9c33f2ed6ecdccee6d3b2b30422cda7032f73c5f5a4dd238b2fadaaeae9fe675127bedfe78ed711443cafdc874d0dc736a7e9f7d9ffd331e229e69f810141988ed3a0fbfa22d3156c9b4a16e2e6aecc73cbf581b4924b9543f8e5bd4aea054b127a784a13faf4a82126f3684daadb35c76f542de1dc443eb9d74a0846062d8100000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 130,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d600000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d700000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0141e39f7e087ba1d9d35900bb7706838ed9822b01471686a619d1ad7402a51b86d8337a745aa8b7f81d69888f3ac7e0ade6251f72ae70c9a2739459b8efcce301648201417128998c6a11aaeaba28b88b31604321bcf0f547349af9040530cd744b07c6b1b19c2080fd9f21ae99aa64d54c77dcdaf46a99685ac56491695a0c985686e9328200000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  },+  {+   "kind": "p2tr-keypath",+   "sighash": 131,+   "tx": "010000000001024d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d800000000ffffffff4d6de58f388b8ffa90cb8e97d7a62fd52d26b677b35ff13218e9fa0d06f0f073d900000000ffffffff02705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef705d1e0000000000160014fc7250a211deddc70ee5a2738de5f07817351cef0141940324907a0bae87ffcdd31f23c94a9bb0639e0694e33ebb42574057c4b35812604a96db93f9f83f79198464a142ef34cadf350cb32ba8f9544987df40ddc0e18301411a5ae3e6f80bc11c76aaf6a88f5313a8445b43824761a5655f45a5e49f24f54f9843464b72eb18051d0906411e0de5ad468a0a0fee468db91af4837698a127af8300000000",+   "script": "51202a64b1ee3375f3bb4b367b8cb8384a47f73cf231717f827c6c6fbbf5aecf0c36",+   "amount": 2000000+  }+ ]+}
data/key_io_invalid.json view
@@ -6,177 +6,207 @@         "x"     ],     [-        "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"+        "1GAdfviErV2Ew95FPtZyikz2qGP3gyCB6Hyu94sedAkPpA523m3fQwps9YKUZkKgQckGPKhRsFR"     ],     [-        "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"+        "37G2kMDLpmWVhimxRdzwNfE8JFvWXnJYnVcXeeGrek2qumdJuK7XArcVVpRtLLjRra3t64BEPF2"     ],     [-        "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"+        "giymtio7u7oqWtmC9YnvAEKkLF3JQpAdkEFkVJKYrVDfaLbhaDpX1ihfF2vZmya1i61fwLPC3YQ"     ],     [-        "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"+        "8iVk9nLM3nYwRuwypjy9NK5rsuZH7BbrQRZ1pgcQmvMnjAgRXD"     ],     [-        "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"+        "cPTVQ1hbo4qdoysf6Jx5GthqucNmdfqt6J2pZRFeXv8Ep7Kmjqud"     ],     [-        "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"+        "cQbR2Ny85XFBzUMx3Ed6HsTLw2pVruSgPvt5AofnBUnhiv86gYeW"     ],     [-        "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"+        "2UB3iG3VJbX2TRrMwm6ssWskgvU9VjFBYSqCzwqkrihCwo7mg4mtS4WuGZgxTKuxf5A3EcotYEymz"     ],     [-        "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"+        "cQe12pqwPR6ExtZKfrKf1q4b3CTh1Qi7MwuvMvzs79nWXDvESfBJ"     ],     [-        "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"+        "tc1qeul5g2xfkvdkrhcfmdursv73ad64jnkjl9c40f"     ],     [-        "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"+        "bt1pq65rzej5glw3ra79gav6fqnx4haa0z257qr3mc8cggkefahmgvyseufhc0"     ],     [-        "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"+        "tb13hty4qmumlwpp6chxjvcyzza4duqgtmxw3xhm3u9ahj4nyhtwz8eq7ynrj4"     ],     [-        "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"+        "bcrt1r2qxpwuge"     ],     [-        "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"+        "bc10uexgzna2dpfk0vjt35srz6a27ps6m0l89jweznt83n2sqn2fx4hvn9ym5af8wut34sfrqhk3"     ],     [-        "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"+        "tb1qum6uh0pt4q253qaf520929737v63w5gf"     ],     [-        "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"+        "bcrt1q888ryfgxpvl0k7vum8zpyar2u2sexvdhkf38ue37yknmqq0ycrwpl3w48y"     ],     [-        "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"+        "bc1qdsuzmn04k2z8vryw8l4dj8m5ygqgnne5n"     ],     [-        "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"+        "tb1qlj8es50nc8j8r8xshrjgzmw5azx89efghmw8ju6zcqla0g6xcnrstsjz7k"     ],     [-        "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"+        "bcrt1qzwmyj0z924g7fzs5yvnrkc43y76RVyr2lh5t4r"     ],     [-        "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"+        "bc1qpu6d26mrulzetu4jqhd7rsunv9aqru26f5c4j8"     ],     [-        "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"+        "tb1qun6d26ufh77ghny6u5u8cwz9da7qwc6k4wkuceae9tth06eqlw0syupl4w"     ],     [-        "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"+        "bcrt1qj7g2jps453kj9htk9cxyyc2nxe69x4kzzmth7v"     ],     [-        "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"+        "bc1p702xksx4z3uqf0u2phllxkfe5cgu0adxptqs0uelx0tqt8e885sqryes2l"     ],     [-        "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"+        "tb1z7gmh0v6pc30z4xum76lmw8w86yswrlmw"     ],     [-        "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"+        "bcrt1sjsrw6nun4h502cr97xmnyyuhkr22q0s6efrgtu"     ],     [-        "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"+        "2UVPFpGYnLHJezFzjUo42our6PMEoozzRdM"     ],     [-        "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"+        "2MygHQjE1U33q3LSC53p69YqFjP8PihumJAF"     ],     [-        "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"+        "KzNbAQ4mexfAxa6RKBzHQqfoTycaeWpv2p"     ],     [-        "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"+        "2jDPrDfAKihCGPbPD9ztY8TswAia4V8Bc6vx"     ],     [-        "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"+        "4VQUNG1hG64QFtaNyQZQWDdwpxB275Pwb3tvyPt2HDxB8Mi2MgH8Tz3AC83YYiz9LydsLNXEZJLHY"     ],     [-        "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"+        "39TKsUQ5QpEL1wowc6GMUqak94ijirPuP69ooV3xsFmiKQX2dau"     ],     [-        "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"+        "2UEJjT3dSdwc8dAo7oedPzznXceXCEsBbDfAvSymqpqDrkZMv7JBEUpLyhkghioYAWC9W4sKysry"     ],     [-        "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"+        "7VmMEkphxCFSV1y659Th4dkk6x6bJS5eQvbt8rzUYKQyd6ACgwQ4vXHtXKFUwP2kW3XULipnHJdZ7"     ],     [-        "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"+        "tc1qdlapns4zkn03juf2k9xwwpct209suj6mgcd9gh"     ],     [-        "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"+        "bt1psa5eptk29c4jc9yumeseat3a0l5e2fpmw635za2p4gpwdnthueysxga9je"     ],     [-        "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"+        "tb13w8c43lykfj3lvm9sgp6dsnfjla3d57cm83seykunf0ltxjc9lt2q4efm4d"     ],     [-        "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"+        "bcrt1rjqr2tdkm"     ],     [-        "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"+        "bc10lyxwnxa70l270e6fcmxr4x7dtgu2yvy7gzkurwxy4zhdvgaqrrn6pfg2flyhqzy5t5se8yu3"     ],     [-        "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"+        "TB1QFDFM763VXVSUNZHQLPWC0Q8FG5LJX6ZN"     ],     [-        "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"+        "bcrt1q60chha7wfwlau4kdr4mlvyeyc8mnnh9dhxk05e0hmrxcuhghefj36uwyha"     ],     [-        "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"+        "bc1gmk9yu"     ],     [-        "EsYbG4tWWWY45G31nox838qNdzksbPySWc"+        "tb1ly0q7p"     ],     [-        "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"+        "bcrt1qdwttaw38uf42wxw40kwk3u8nguyTQH3hx6jmqp"     ],     [-        "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"+        "bc1qtsvlht6730n04f2mpaj5vv8hrledn5n5ug8c79"     ],     [-        "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"+        "tb1dclvmr"     ],     [-        "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"+        "bcrt1q3fqvctqu48wsvggrt09vj0yk2gzzcscdp4h98u"     ],     [-        "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"+        "bc1prklpq7tjcawg89cmwwqr3u5apwav36xa4zz56ady7crsllm6mpnqts7p86"     ],     [-        "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"+        "tb1zkm58zyhxz3ffkfgsyprflg543slsl4c4"     ],     [-        "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"+        "bcrt1snzr5kaypnfhpnjanrhd20fhqcjxm3hfh7dw9fu"     ],     [-        "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"+        "2GgnYKqBGuA2Mm5GnrPsMTZR81xPhNtgMYoFUZngZGiobhCuUpCaTriUHRcgFreEekNdPAR17q8d"     ],     [-        "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"+        "AZEah8d1EK362okRBS66e8SvdtYkrE8tsX"     ],     [-        "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"+        "gep8xr77FyPW6zYP15RiV9W8nL6w2HyHB16cUDakfyDceMA6ZzUdhJjk2LPuLYHnLkBqkRTTi6z"     ],     [-        "bc1rw5uspcuh"+        "2NDNP7GY59tTJPZTpbkprhM9SR99Nn5rUs7"     ],     [-        "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"+        "2Csgzy2T287YAjeU5tFtt1nPshBZAUFQi4WtgaWyZGKSBNnKXHy2Tmxo8QK4Mfdds977ShcDWC5o"     ],     [-        "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"+        "Kwjk3Vy6sdXMQDGWJzaWmqFxUNtWZCX1q4F4Kpt8jNNUoWJUUaTY"     ],     [-        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"+        "Svj8kk98bAS9V4L2crmxakbhmnPm3cJ1tJ4Je4yVzDreU8eSTFURS1SPYv5oWEQD8Q9VBDvx5uF"     ],     [-        "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"+        "KNYsv6v9GtkGeD4WdQnBEJCrPKQm91PTxAbCfXr66LEd4JDmhPWC"     ],     [-        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"+        "2UJ2H2xvAeXmFKfQwMyDoSdQTTPFMNCT3SsoUafBWKzoGP3NsUK1buEgQZG38viyD53jgMdpqfT7"     ],     [-        "bc1gmk9yu"+        "6aLMfayKF4TW4ecn5SEc8FExpyJA2peKxYRGZhes6tQ4NTTzuGy"+    ],+    [+        "7VP4FmcebU2thJns9MnXde7LWfuqR5vMizrAuUoq2GcJjzTyA4RHFcPVdZL8PLg1SbpSFdJrvLXoY5"+    ],+    [+        "tc1q5qdvt99uc92jyz663dtdpfpv6nr67ahmgwcpq2"+    ],+    [+        "bt1peu3ppd7x796sjjenp09r8cs22rhylqm9lhggk72qp8q22vzft0wq2a0x6j"+    ],+    [+        "tb1323z3lnz7dl3kd0nsuh6xy4he9almzl67anxgg3xdzkaxc9rwntlqdhdzd7"+    ],+    [+        "bcrt1r2gc42sky"+    ],+    [+        "bc10fd889x4hd54tqu2ewg9t4hhft2wl7m6x50av4uswzw46xe6as0xmltfg7vrjfkvm459vld7w"+    ],+    [+        "TB1QZY7V0F2AT3308YGGNGN66ULJTCN3RY6F"+    ],+    [+        "bcrt1qjg3cwht92znyw0l4r5rtctmls337nrc7g0ry9drjxmlecjd3atl3fake7c"+    ],+    [+        "bc1qmgf8xt8xkecl79k04mma3lz34gqep7hg4"+    ],+    [+        "TB1Q3F9WGNXE9ZMTTMDN5VKVKHYZ8Y0LCV72YV7V5LSXTJXEYHNHEHASLYL0TZ"     ] ]
data/key_io_valid.json view
@@ -1,536 +1,610 @@ [     [-        "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",-        "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",+        "1FsSia9rv4NeEwvJ2GvXrX7LyxYspbN2mo",+        "76a914a31c06bd463e3923bc1aadbde48b16976c08071788ac",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "main",+            "isPrivkey": false         }     ],     [-        "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",-        "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",+        "36j4NfKv6Akva9amjWrLG6MuSQym1GuEmm",+        "a914373b819a068f32b7a6b38b6b38729647cfde01c287",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "main",+            "isPrivkey": false         }     ],     [-        "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",-        "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+        "mzK2FFDEhxqHcmrJw1ysqFkVyhUULo45hZ",+        "76a914ce28b26c57472737f5c3561a1761185bd8589a4388ac",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false         }     ],     [-        "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",-        "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+        "2NC2hEhe28ULKAJkW5MjZ3jtTMJdvXmByvK",+        "a914ce0bba75891ff9ec60148d4bd4a09ee2dc5c933187",         {-            "isPrivkey": false,-            "chain": "regtest"+            "chain": "testnet4",+            "isPrivkey": false         }     ],     [-        "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",-        "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",+        "mww4LvqtTMKvmeQvizPz2EQv26xTneWrbg",+        "76a914b4110ba93ac54afc14da3bdd19614774a2d55d2988ac",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "signet",+            "isPrivkey": false         }     ],     [-        "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",-        "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",+        "2N1r7aC69VHeE7yQJPDLi9T1PYq4wnwvjuT",+        "a9145e5a35ab44b3efaea5129ba22b88ba3e2976614587",         {+            "chain": "signet",+            "isPrivkey": false+        }+    ],+    [+        "n4fajahJrAuKbN7uNsKjLjQkz9Qn5ewJXQ",+        "76a914fdeca3b08e38af53d7c4c60e3ad208ce5066441088ac",+        {+            "chain": "regtest",+            "isPrivkey": false+        }+    ],+    [+        "2MxFajLApXpYk4VodBSZSt7rw8y4ryABkfA",+        "a91436e9f191e0b75036a77f65e2eaa4752443233fbe87",+        {+            "chain": "regtest",+            "isPrivkey": false+        }+    ],+    [+        "5JuW2AMDYu4xVwRG9DZW18VbzQrGcd5RCgb99sS6ehJsNQXu5b9",+        "8f8943bf956de595665c38ffff23827e17c10cdc1c27a028caae6c9810626198",+        {+            "chain": "main",             "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "isPrivkey": true         }     ],     [-        "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",-        "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",+        "L5nJeqKmpHp4P7F8ZYyjwc5a7P4d8EabuGAzfGJk7yC1BJyzNaEd",+        "ff778740f88ddcf102aeb81daee289c044c4a4571c4b6f287400f4b8e0b843f8",         {+            "chain": "main",             "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "isPrivkey": true         }     ],     [-        "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",-        "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+        "92ZdE5HoLafywnTBbzPxbvRmp75pSfzvdU3XaZGh1cToipgdHVh",+        "80c32d81e91bdea04cd7a3819b32275fc3298af4c7ec87eb0099527d041ced5c",         {+            "chain": "testnet4",             "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "isPrivkey": true         }     ],     [-        "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",-        "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+        "cV83kKisF3RQSvXbUCm9ox3kaz5JjEUBWcx8tNydfGJcyeUxuH47",+        "e0fcd4ce4e3d0e3de091f21415bb7cd011fac288c42020a879f28c2a4387df9b",         {+            "chain": "testnet4",+            "isCompressed": true,+            "isPrivkey": true+        }+    ],+    [+        "92QuSnywrhsV7WPZChTgSQA23uSmj9MCEEno1eRBDG9sg8M29cX",+        "6cf636ed8ac1bab033b64f66feaba65f70e684731e3f39105605968d3a963801",+        {+            "chain": "signet",             "isCompressed": false,-            "isPrivkey": true,-            "chain": "regtest"+            "isPrivkey": true         }     ],     [-        "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",-        "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+        "cND53Dhp8eCZqG2ghe8YhSCGesXZ8fE5PGD1khrqNvEi4RBoXhEK",+        "12b5a10f3a11e708dc5412833c47ab7c368a21b9efe19293793ec879ce683018",         {+            "chain": "signet",             "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "isPrivkey": true         }     ],     [-        "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",-        "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+        "91mn1wYKEB1zyof1VFm8tMtocZx1oBrKKRCu9GCpgZvPmBLEJjp",+        "18a86e5a6c6977ddba0daca7fba5190f67ba56ccdc1b3f31308972236c2e4776",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "regtest"+            "chain": "regtest",+            "isCompressed": false,+            "isPrivkey": true         }     ],     [-        "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",-        "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",+        "cPisAUdLvqqAr6MYtXnrWvgvyUAwuNyuTvZkDGw6miPhZdaiSDNH",+        "3fdfec1371cedcdb8c190ca6ff8ad603f817edc0d93c2a687c7b36dd66e70f2a",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "regtest",+            "isCompressed": true,+            "isPrivkey": true         }     ],     [-        "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",-        "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",+        "bc1qvyq0cc6rahyvsazfdje0twl7ez82ndmuac2lhv",+        "00146100fc6343edc8c874496cb2f5bbfec88ea9b77c",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",-        "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",+        "bc1qyucykdlhp62tezs0hagqury402qwhk589q80tqs5myh3rxq34nwqhkdhv7",+        "002027304b37f70e94bc8a0fbf500e0c957a80ebda87280ef58214d92f119811acdc",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",-        "a914c579342c2c4c9220205e2cdc285617040c924a0a87",+        "bc1p83n3au0rjylefxq2nc2xh2y4jzz4pm6zxj4mw5pagdjjr2a9f36s6jjnnu",+        "51203c671ef1e3913f94980a9e146ba895908550ef4234abb7503d436521aba54c75",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",-        "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",+        "bc1z2rksukkjr8",+        "520250ed",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "chain": "main",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",-        "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",+        "tb1qcrh3yqn4nlleplcez2yndq2ry8h9ncg3qh7n54",+        "0014c0ef1202759fff90ff19128936814321ee59e111",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",-        "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",+        "tb1quyl9ujpgwr2chdzdnnalen48sup245vdfnh2jxhsuq3yx80rrwlq5hqfe4",+        "0020e13e5e482870d58bb44d9cfbfccea78702aad18d4ceea91af0e022431de31bbe",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",-        "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",+        "tb1p35n52jy6xkm4wd905tdy8qtagrn73kqdz73xe4zxpvq9t3fp50aqk3s6gz",+        "51208d2745489a35b75734afa2da43817d40e7e8d80d17a26cd4460b0055c521a3fa",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",-        "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",+        "tb1rgv5m6uvdk3kc7qsuz0c79v88ycr5w4wa",+        "53104329bd718db46d8f021c13f1e2b0e726",         {+            "chain": "testnet4",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",-        "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",+        "tb1q3vya2h5435jkugq2few7dmktlrwq4ejmfaw7kr",+        "00148b09d55e958d256e200a4e5de6eecbf8dc0ae65b",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",-        "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",+        "tb1qxkhrl2s6ttrclckldruea0e8anhrehffl8xv7t0pdyrzm08v2hyqy408nf",+        "002035ae3faa1a5ac78fe2df68f99ebf27ecee3cdd29f9cccf2de169062dbcec55c8",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",-        "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",+        "tb1pae5um27ahn8n73pgexe3kcwlp8dhswpn684h2k2w6t9a7w3eq65qephd5y",+        "5120ee69cdabddbccf3f4428c9b31b61df09db783833d1eb75594ed2cbdf3a3906a8",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",-        "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",+        "tb1rx9n9g37az8mu236e5jpxdt0m67y4fuq8rhs0ss3djnm0kscfrwvq0ntlyg",+        "532031665447dd11f7c54759a48266adfbd78954f0071de0f8422d94f6fb43091b98",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "chain": "signet",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",-        "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",+        "bcrt1qdavt4j2sd7dlhqsavtnfxvzppw6k7qy97tmnu9",+        "00146f58bac9506f9bfb821d62e69330410bb56f0085",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "chain": "regtest",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",-        "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",+        "bcrt1qan8gntac7z7me2ejt4hpru42ad2f759fmy0m3ejvs98656znv7eqga4uhv",+        "0020ecce89afb8f0bdbcab325d6e11f2aaeb549f50a9d91fb8e64c814faa685367b2",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "chain": "regtest",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",-        "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",+        "bcrt1pfwxjqvtt4tcxrtdluukfmy2dv7xd2qzdfy6kajv5nwn4yam3wxkq3553uh",+        "51204b8d20316baaf061adbfe72c9d914d678cd5004d49356ec9949ba752777171ac",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "chain": "regtest",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",-        "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",+        "bcrt1sx6p8njlx7h9mc2agz4yg82dzne23050ncq72cneeecez2pst8mahn8xecsf8g6hzx94420",+        "6028368279cbe6f5cbbc2ba8154883a9a29e5517d1f3c03cac4f39ce3225060b3efb799cd9c412746ae2",         {+            "chain": "regtest",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",-        "a914188f91a931947eddd7432d6e614387e32b24470987",+        "1FjL87pn8ky6Vbavd1ZHeChRXtoxwRGCRd",+        "76a914a19331b7b2627e663e25a7b001e4c0dcc5e21bc788ac",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "main",+            "isPrivkey": false         }     ],     [-        "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",-        "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",+        "3BZECeAH8gSKkjrTx8PwMrNQBLG18yHpvf",+        "a9146c382dcdf5b284760c8e3fead91f7422cd76aa8787",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "main",+            "isPrivkey": false         }     ],     [-        "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",-        "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",+        "n4YNbYuFdPwFrxSP8sjHFbAhUbLMUiY9jE",+        "76a914fc8f9851f3c1e4719cd0b8e4816dd4e88c72e52888ac",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false         }     ],     [-        "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",-        "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",+        "2NAeQVZayzVFAtgeC3iYJsjpjWDmsDph71A",+        "a914bedc797342c03fd7a346c4c7857ca03d467013b687",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "chain": "testnet4",+            "isPrivkey": false         }     ],     [-        "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",-        "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",+        "mnCBpkNMJEJLehgdEkzSo2eioniyJMxLpZ",+        "76a914493c455551e48a1423263b62b127b436106a685488ac",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "chain": "signet",+            "isPrivkey": false         }     ],     [-        "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",-        "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+        "2N5sNHomeNJDZv67AcFx9ES7FBZY4jx9KDA",+        "a9148a776a0f34d56b63e7c595f2b205dbe1c393617a87",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "chain": "signet",+            "isPrivkey": false         }     ],     [-        "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",-        "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+        "mfhE6jAUwjUDNZhaX1PAsDTKfneQF2Nshc",+        "76a91401f15a4cc063dae4f4d56b89bfbc8bcc9ae5387c88ac",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "regtest"+            "chain": "regtest",+            "isPrivkey": false         }     ],     [-        "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",-        "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",+        "2MxNm1VHyVU4RuP3u1c1v5aQLk2dQjwy1Qk",+        "a91438456f7c076356abadcc67b92ad777eb20fb9f8887",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "chain": "regtest",+            "isPrivkey": false         }     ],     [-        "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",-        "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",+        "5HsL2nZuEebU5nM3RxNVQD9GcAnvNMahqQskf4fkqHe54zwd14e",+        "06e8649790a90615a46d22dd762e0c42615336745356c2e16147c0f3d46b40d5",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "main",+            "isCompressed": false,+            "isPrivkey": true         }     ],     [-        "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",-        "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",+        "KwuVvu6hsuEMHrfFWJQV64tRrWX3QzqHH18JuAHYqYV6dqBvNKxd",+        "147804bf8a0dfff35939a611c7f5a60ac107f33f33d6059f273d2079ab1d90f2",         {-            "isPrivkey": false,-            "chain": "main"+            "chain": "main",+            "isCompressed": true,+            "isPrivkey": true         }     ],     [-        "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",-        "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",+        "921M1RNxghFcsVGqAJksQVbSgx36Yz4u6vebfz1wDujNvgNt93B",+        "3777b341c45e2a9b9bf6bfb71dc7d129f64f1b9406ed4f93ade8f56065f1b732",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "testnet4",+            "isCompressed": false,+            "isPrivkey": true         }     ],     [-        "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",-        "a914e930e1834a4d234702773951d627cce82fbb5d2e87",+        "cNEnbfF2fcxmmCLWqMAaq6fxJvVkwMbyU3kCbpQznz4Z1j6TZDGb",+        "1397b0d4a03e1ab2c54dd9af99ce1ecbfb90c80a58886da95e1181a55703d96b",         {-            "isPrivkey": false,-            "chain": "test"+            "chain": "testnet4",+            "isCompressed": true,+            "isPrivkey": true         }     ],     [-        "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",-        "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",+        "93BcpCMKPmFCuY8bqS4k3HFrhJ1Afxi4uSsEeJFvX86GYW7PC7W",+        "d27d1b6ef55ca2e4d475b5276f2dbb85f7a6459dceeb89c67b776fd3bb974452",         {+            "chain": "signet",             "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "isPrivkey": true         }     ],     [-        "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",-        "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",+        "cUtwbyxoL1owPxUafgH2meEpydeywjhnTYv2mJaFHHchz39AaEgy",+        "da3ed4ef1647e1733ec076919cab6156077ed9532e7c365acc425747e198b3e1",         {+            "chain": "signet",             "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "isPrivkey": true         }     ],     [-        "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",-        "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",+        "927zPWny2SiNaUmHF5NnGQXQWDwbByfFzXGgu88j91ZoutSosvE",+        "468e0284f230153db8687d8ec23db079a5b67d72ca04174b3867b13e4ea9945e",         {+            "chain": "regtest",             "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "isPrivkey": true         }     ],     [-        "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",-        "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",+        "cRez45VGSp5EXNqm89K3NJJPSKKapJg5Kbw3atxr2337x2gtgYed",+        "798d87586cffbe8c545ab374454e403b1eb831501ebe89f3c3b02f3137bd7b46",         {+            "chain": "regtest",             "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "isPrivkey": true         }     ],     [-        "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",-        "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",+        "bc1qhxt04s5xnpy0kxw4x99n5hpdf5pmtzpqs52es2",+        "0014b996fac2869848fb19d5314b3a5c2d4d03b58820",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",-        "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",+        "bc1qgc9ljrvdf2e0zg9rmmq86xklqwfys7r6wptjlacdgrcdc7sa6ggqu4rrxf",+        "0020460bf90d8d4ab2f120a3dec07d1adf039248787a70572ff70d40f0dc7a1dd210",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",-        "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",+        "bc1pve739yap4uxjvfk0jrey69078u0gasm2nwvv483ec6zkzulgw9xqu4w9fd",+        "5120667d1293a1af0d2626cf90f24d15fe3f1e8ec36a9b98ca9e39c6856173e8714c",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",-        "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",+        "bc1zmjtqxkzs89",+        "5202dc96",         {+            "chain": "main",             "isPrivkey": false,-            "chain": "test"+            "tryCaseFlip": true         }     ],     [-        "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",-        "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",+        "tb1ql4k5ayv7p7w0t0ge7tpntgpkgw53g2payxkszr",+        "0014fd6d4e919e0f9cf5bd19f2c335a03643a914283d",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "main"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",-        "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",+        "tb1q9jx3x2qqdpempxrcfgyrkjd5fzeacaqj4ua7cs7fe2sfd2wdaueq5wn26y",+        "00202c8d1328006873b098784a083b49b448b3dc7412af3bec43c9caa096a9cdef32",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "main"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",-        "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",+        "tb1pdswckwd9ym5yf5eyzg8j4jjwnzla8y0tf9cp7aasfkek0u29sz9qfr00yf",+        "51206c1d8b39a526e844d324120f2aca4e98bfd391eb49701f77b04db367f145808a",         {-            "isCompressed": false,-            "isPrivkey": true,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",-        "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",+        "tb1r0ecpfxg2udhtc556gqrpwwhk4sw3f0kc",+        "53107e7014990ae36ebc529a4006173af6ac",         {-            "isCompressed": true,-            "isPrivkey": true,-            "chain": "test"+            "chain": "testnet4",+            "isPrivkey": false,+            "tryCaseFlip": true         }     ],     [-        "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",-        "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",+        "tb1q6mwf89hnqhlu8txjgjfs4s7p93ugffn3k062ll",+        "0014d6dc9396f305ffc3acd244930ac3c12c7884a671",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",-        "a9145ece0cadddc415b1980f001785947120acdb36fc87",+        "tb1qafrjalu4d73dql0czau9j6z422434kef235mzljf48ckd5xz3sys09jm97",+        "0020ea472eff956fa2d07df8177859685552ab1adb295469b17e49a9f166d0c28c09",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "main"+            "tryCaseFlip": true         }     ],     [-        "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",-        "0014751e76e8199196d454941c45d1b3a323f1433bd6",+        "tb1pwst9qszjrhuv2e7as0flcq9gm698v6gdxzz9e87p07s8rssdx3zqklm3vf",+        "512074165040521df8c567dd83d3fc00a8de8a76690d30845c9fc17fa071c20d3444",         {+            "chain": "signet",             "isPrivkey": false,-            "chain": "main",             "tryCaseFlip": true         }     ],     [-        "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",-        "0014751e76e8199196d454941c45d1b3a323f1433bd6",+        "tb1r3ss76jtsuxe8c8c8lxsehnpak55ylrgr345pww076l536ahjr6jsydamx3",+        "53208c21ed4970e1b27c1f07f9a19bcc3db5284f8d038d681739fed7e91d76f21ea5",         {+            "chain": "signet",             "isPrivkey": false,+            "tryCaseFlip": true+        }+    ],+    [+        "bcrt1q65nhlm4hf2ptg3t264al57p7wjxj2c3s6kyt83",+        "0014d5277feeb74a82b4456ad57bfa783e748d256230",+        {             "chain": "regtest",+            "isPrivkey": false,             "tryCaseFlip": true         }     ],     [-        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",-        "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+        "bcrt1qawvc90lpytw3z3k9etdx54l0exq5f5sqfzu5e45kjnl6slwayeeqx2dyac",+        "0020eb9982bfe122dd1146c5cada6a57efc98144d20048b94cd69694ffa87ddd2672",         {+            "chain": "regtest",             "isPrivkey": false,-            "chain": "test",             "tryCaseFlip": true         }     ],     [-        "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",-        "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",+        "bcrt1p39a4s4vdcw9kqa8w2t0rp7aj8kfxyw7mce5sk5d70x6wnnmpvt7skf2kxy",+        "5120897b58558dc38b6074ee52de30fbb23d92623bdbc6690b51be79b4e9cf6162fd",         {+            "chain": "regtest",             "isPrivkey": false,-            "chain": "main",-            "tryCaseFlip": true,-            "disabled": true+            "tryCaseFlip": true         }     ],     [-        "bc1sw50qa3jx3s",-        "6002751e",+        "bcrt1s489d9fhmyel0vzfqsrmew4x7r80asuqesm5hgqacy35daflcyufh3j8cgdtflvt99ph05m",+        "6028a9cad2a6fb267ef6092080f79754de19dfd8701986e97403b82468dea7f8271378c8f843569fb165",         {+            "chain": "regtest",             "isPrivkey": false,+            "tryCaseFlip": true+        }+    ],+    [+        "1G9A9j6W8TLuh6dEeVwWeyibK1Uc5MfVFV",+        "76a914a614da54daacdb8861f451a0b7e3c27cdf8a099e88ac",+        {             "chain": "main",-            "tryCaseFlip": true,-            "disabled": true+            "isPrivkey": false         }     ],     [-        "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",-        "5210751e76e8199196d454941c45d1b3a323",+        "33GA3ZXbw5o5HeUrBEaqkWXFYYZmdxGRRP",+        "a914113ca1afeb49ff3abf176ffa19c2a2b4df19712a87",         {-            "isPrivkey": false,             "chain": "main",-            "tryCaseFlip": true,-            "disabled": true+            "isPrivkey": false         }     ],     [-        "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",-        "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+        "mwgS2HRbjyfYxFnR1nF9VKLvmdgMfFBmGq",+        "76a914b14ce7070b53cb0e4b5b5f6e253e876990aeca2e88ac",         {-            "isPrivkey": false,-            "chain": "test",-            "tryCaseFlip": true+            "chain": "testnet4",+            "isPrivkey": false         }     ],     [-        "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",-        "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+        "2MwBVrJQ76BdaGD76CTmou8cZzQYLpe4NqU",+        "a9142b2c149cde619eae3d7fe995243b76a3417541aa87",         {-            "isPrivkey": false,-            "chain": "regtest",-            "tryCaseFlip": true+            "chain": "testnet4",+            "isPrivkey": false+        }+    ],+    [+        "mfnJ8tEkqKNFE5YaHTXFxyHk2mnDK2fvDh",+        "76a91402e6cd77e649ad8b281271f158fc964ca3f66cb088ac",+        {+            "chain": "signet",+            "isPrivkey": false+        }+    ],+    [+        "2My83D67ir7K8PPzeT6mE2oth3ZwNTVRS9F",+        "a9144074d84d32ff62da7b1b3c61925b934bfeb34b0587",+        {+            "chain": "signet",+            "isPrivkey": false         }     ] ]
+ data/psbt_bip174.json view
@@ -0,0 +1,236 @@+{+ "description": "The test vectors of BIP 174: the invalid PSBTs, which hw must refuse, the valid ones, which hw must read and write back byte for byte, and the PSBTs of the walk-through of the roles (creator, updater, signer, combiner, finalizer). Copied from the BIP, which is BSD-2-Clause.",+ "source": "https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki",+ "invalid": [+  {+   "case": "Network transaction, not PSBT format",+   "hex": "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300",+   "base64": "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA=="+  },+  {+   "case": "PSBT missing outputs",+   "hex": "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+   "base64": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="+  },+  {+   "case": "PSBT where one input has a filled scriptSig in the unsigned tx",+   "hex": "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000",+   "base64": "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA="+  },+  {+   "case": "PSBT where inputs and outputs are provided but without an unsigned tx",+   "hex": "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+   "base64": "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="+  },+  {+   "case": "PSBT with duplicate keys in an input",+   "hex": "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000",+   "base64": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA"+  },+  {+   "case": "PSBT with invalid global transaction typed key",+   "hex": "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"+  },+  {+   "case": "PSBT with invalid input witness utxo typed key",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"+  },+  {+   "case": "PSBT with invalid pubkey length for input partial signature typed key",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="+  },+  {+   "case": "PSBT with invalid redeemscript typed key",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"+  },+  {+   "case": "PSBT with invalid witnessscript typed key",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"+  },+  {+   "case": "PSBT with invalid pubkey in input BIP 32 derivation paths typed key",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="+  },+  {+   "case": "PSBT with invalid non-witness utxo typed key",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  },+  {+   "case": "PSBT with invalid final scriptsig typed key",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  },+  {+   "case": "PSBT with invalid final script witness typed key",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  },+  {+   "case": "PSBT with invalid pubkey in output BIP 32 derivation paths typed key",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"+  },+  {+   "case": "PSBT with invalid input sighash type typed key",+   "hex": "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+   "base64": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"+  },+  {+   "case": "PSBT with invalid output redeemScript typed key",+   "hex": "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+   "base64": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"+  },+  {+   "case": "PSBT with invalid output witnessScript typed key",+   "hex": "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d06d57f8a8751ae00",+   "base64": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnQbVf4qHUa4A"+  },+  {+   "case": "PSBT with unsigned tx serialized with witness serialization format",+   "hex": "70736274ff01007802000000000101268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc78700b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000",+   "base64": "cHNidP8BAHgCAAAAAAEBJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAAP7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHALMuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"+  },+  {+   "case": "PSBT with an invalid value data due to its size being not the stated size",+   "hex": "70736274ff0100337401ff0700010000000100ff01000a73317428ff0000000001ff010301000001000000000000000076010000004100090000000000",+   "base64": "cHNidP8BADN0Af8HAAEAAAABAP8BAApzMXQo/wAAAAAB/wEDAQAAAQAAAAAAAAAAdgEAAABBAAkAAAAAAA=="+  }+ ],+ "valid": [+  {+   "case": "PSBT with one P2PKH input. Outputs are empty",+   "hex": "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000",+   "base64": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"+  },+  {+   "case": "PSBT with one P2PKH input and one P2SH-P2WPKH input. First input is signed and finalized. Outputs are empty",+   "hex": "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000",+   "base64": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA"+  },+  {+   "case": "PSBT with one P2PKH input which has a non-final scriptSig and has a sighash type specified. Outputs are empty",+   "hex": "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000",+   "base64": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA=="+  },+  {+   "case": "PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.",+   "hex": "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000",+   "base64": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="+  },+  {+   "case": "PSBT with one P2SH-P2WSH input of a 2-of-2 multisig, redeemScript, witnessScript, and keypaths are available. Contains one signature.",+   "hex": "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+   "base64": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA="+  },+  {+   "case": "PSBT with one P2WSH input of a 2-of-2 multisig. witnessScript, keypaths, and global xpubs are available. Contains no signatures. Outputs filled.",+   "hex": "70736274ff01005202000000019dfc6628c26c5899fe1bd3dc338665bfd55d7ada10f6220973df2d386dec12760100000000ffffffff01f03dcd1d000000001600147b3a00bfdc14d27795c2b74901d09da6ef133579000000004f01043587cf02da3fd0088000000097048b1ad0445b1ec8275517727c87b4e4ebc18a203ffa0f94c01566bd38e9000351b743887ee1d40dc32a6043724f2d6459b3b5a4d73daec8fbae0472f3bc43e20cd90c6a4fae000080000000804f01043587cf02da3fd00880000001b90452427139cd78c2cff2444be353cd58605e3e513285e528b407fae3f6173503d30a5e97c8adbc557dac2ad9a7e39c1722ebac69e668b6f2667cc1d671c83cab0cd90c6a4fae000080010000800001012b0065cd1d000000002200202c5486126c4978079a814e13715d65f36459e4d6ccaded266d0508645bafa6320105475221029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c88712103372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b52ae2206029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c887110d90c6a4fae0000800000008000000000220603372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b10d90c6a4fae0000800100008000000000002202039eff1f547a1d5f92dfa2ba7af6ac971a4bd03ba4a734b03156a256b8ad3a1ef910ede45cc500000080000000800100008000",+   "base64": "cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA"+  },+  {+   "case": "PSBT with unknown types in the inputs.",+   "hex": "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000af00102030405060708090f0102030405060708090a0b0c0d0e0f0000",+   "base64": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACvABAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="+  },+  {+   "case": "PSBT with `PSBT_GLOBAL_XPUB`.",+   "hex": "70736274ff01009d0100000002710ea76ab45c5cb6438e607e59cc037626981805ae9e0dfd9089012abb0be5350100000000ffffffff190994d6a8b3c8c82ccbcfb2fba4106aa06639b872a8d447465c0d42588d6d670000000000ffffffff0200e1f505000000001976a914b6bc2c0ee5655a843d79afedd0ccc3f7dd64340988ac605af405000000001600141188ef8e4ce0449eaac8fb141cbf5a1176e6a088000000004f010488b21e039e530cac800000003dbc8a5c9769f031b17e77fea1518603221a18fd18f2b9a54c6c8c1ac75cbc3502f230584b155d1c7f1cd45120a653c48d650b431b67c5b2c13f27d7142037c1691027569c503100008000000080000000800001011f00e1f5050000000016001433b982f91b28f160c920b4ab95e58ce50dda3a4a220203309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c47304402202d704ced830c56a909344bd742b6852dccd103e963bae92d38e75254d2bb424502202d86c437195df46c0ceda084f2a291c3da2d64070f76bf9b90b195e7ef28f77201220603309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c1827569c5031000080000000800000008000000000010000000001011f00e1f50500000000160014388fb944307eb77ef45197d0b0b245e079f011de220202c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b11047304402204cb1fb5f869c942e0e26100576125439179ae88dca8a9dc3ba08f7953988faa60220521f49ca791c27d70e273c9b14616985909361e25be274ea200d7e08827e514d01220602c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b1101827569c5031000080000000800000008000000000000000000000220202d20ca502ee289686d21815bd43a80637b0698e1fbcdbe4caed445f6c1a0a90ef1827569c50310000800000008000000080000000000400000000",+   "base64": "cHNidP8BAJ0BAAAAAnEOp2q0XFy2Q45gflnMA3YmmBgFrp4N/ZCJASq7C+U1AQAAAAD/////GQmU1qizyMgsy8+y+6QQaqBmObhyqNRHRlwNQliNbWcAAAAAAP////8CAOH1BQAAAAAZdqkUtrwsDuVlWoQ9ea/t0MzD991kNAmIrGBa9AUAAAAAFgAUEYjvjkzgRJ6qyPsUHL9aEXbmoIgAAAAATwEEiLIeA55TDKyAAAAAPbyKXJdp8DGxfnf+oVGGAyIaGP0Y8rmlTGyMGsdcvDUC8jBYSxVdHH8c1FEgplPEjWULQxtnxbLBPyfXFCA3wWkQJ1acUDEAAIAAAACAAAAAgAABAR8A4fUFAAAAABYAFDO5gvkbKPFgySC0q5XljOUN2jpKIgIDMJaA8zx9446mpHzU7NZvH1pJdHxv+4gI7QkDkkPjrVxHMEQCIC1wTO2DDFapCTRL10K2hS3M0QPpY7rpLTjnUlTSu0JFAiAthsQ3GV30bAztoITyopHD2i1kBw92v5uQsZXn7yj3cgEiBgMwloDzPH3jjqakfNTs1m8fWkl0fG/7iAjtCQOSQ+OtXBgnVpxQMQAAgAAAAIAAAACAAAAAAAEAAAAAAQEfAOH1BQAAAAAWABQ4j7lEMH63fvRRl9CwskXgefAR3iICAsd3Fh9z0LfHK57nveZQKT0T8JW8dlatH1Jdpf0uELEQRzBEAiBMsftfhpyULg4mEAV2ElQ5F5rojcqKncO6CPeVOYj6pgIgUh9JynkcJ9cOJzybFGFphZCTYeJb4nTqIA1+CIJ+UU0BIgYCx3cWH3PQt8crnue95lApPRPwlbx2Vq0fUl2l/S4QsRAYJ1acUDEAAIAAAACAAAAAgAAAAAAAAAAAAAAiAgLSDKUC7iiWhtIYFb1DqAY3sGmOH7zb5MrtRF9sGgqQ7xgnVpxQMQAAgAAAAIAAAACAAAAAAAQAAAAA"+  },+  {+   "case": "PSBT with global unsigned tx that has 0 inputs and 0 outputs",+   "hex": "70736274ff01000a0000000000000000000000",+   "base64": "cHNidP8BAAoAAAAAAAAAAAAAAA=="+  },+  {+   "case": "PSBT with 0 inputs",+   "hex": "70736274ff01004c020000000002d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000000",+   "base64": "cHNidP8BAEwCAAAAAALT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAAA"+  },+  {+   "case": "A Witness UTXO is provided for a non-witness input",+   "hex": "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac0000000000010122d3dff505000000001976a914d48ed3110b94014cb114bd32d6f4d066dc74256b88ac0001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000",+   "base64": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="+  },+  {+   "case": "redeemScript with non-witness UTXO does not match the scriptPubKey",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752af2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq8iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  },+  {+   "case": "redeemScript with witness UTXO does not match the scriptPubKey",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028900010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQABBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  },+  {+   "case": "witnessScript with witness UTXO does not match the redeemScript",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ad2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+   "base64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="+  }+ ],+ "roles": [+  {+   "case": "The creator's PSBT",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"+  },+  {+   "case": "The updater's PSBT, with the previous transactions, scripts and derivations",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The updater's PSBT with SIGHASH_ALL on every input",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The PSBT signed with the keys m/0'/0'/0' and m/0'/0'/2'",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The PSBT signed with the keys m/0'/0'/1' and m/0'/0'/3'",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The combiner's PSBT of the two signed ones",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The finalizer's PSBT",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "A PSBT with unknown key-value pairs, the first",+   "hex": "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000af00102030405060708090f0102030405060708090a0b0c0d0e0f000af00102030405060708090f0102030405060708090a0b0c0d0e0f000af00102030405060708090f0102030405060708090a0b0c0d0e0f00"+  },+  {+   "case": "A PSBT with unknown key-value pairs, the second",+   "hex": "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000af00102030405060708100f0102030405060708090a0b0c0d0e0f000af00102030405060708100f0102030405060708090a0b0c0d0e0f000af00102030405060708100f0102030405060708090a0b0c0d0e0f00"+  },+  {+   "case": "The combiner's PSBT of the two with unknown pairs, its keys in lexicographic order",+   "hex": "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000af00102030405060708090f0102030405060708090a0b0c0d0e0f0af00102030405060708100f0102030405060708090a0b0c0d0e0f000af00102030405060708090f0102030405060708090a0b0c0d0e0f0af00102030405060708100f0102030405060708090a0b0c0d0e0f000af00102030405060708090f0102030405060708090a0b0c0d0e0f0af00102030405060708100f0102030405060708090a0b0c0d0e0f00"+  },+  {+   "case": "A PSBT to combine, the first",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "A PSBT to combine, the second",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022038bf34353f6c65bd4043eab7eac2fdd3c14cc7b62dad6c28bfa9989526ab375a0220097bcc5719f9aa7e3354a69f6839631b1c594f88c8bb119dcda68ec310159cec81010304810000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022001cb78fed0c015af295785eddd0fcf7f2145d1f580164820e61cb568f8fe843f02205bcf90d39fc9224477422e9c2f96aca8f566915cefa4ecd4e5c52a99b5889291810103048100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  },+  {+   "case": "The combiner's PSBT of those two",+   "hex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022038bf34353f6c65bd4043eab7eac2fdd3c14cc7b62dad6c28bfa9989526ab375a0220097bcc5719f9aa7e3354a69f6839631b1c594f88c8bb119dcda68ec310159cec81010304810000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022001cb78fed0c015af295785eddd0fcf7f2145d1f580164820e61cb568f8fe843f02205bcf90d39fc9224477422e9c2f96aca8f566915cefa4ecd4e5c52a99b5889291810103048100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+  }+ ],+ "extracted": {+  "case": "The transaction the extractor gives for the finalizer's PSBT",+  "hex": "0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000"+ }+}
+ data/psbt_bip370.json view
@@ -0,0 +1,258 @@+{+ "invalid": [+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_VERSION set to 2",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAH7BAIAAAAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_TX_VERSION",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001020402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAECBAIAAAAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_FALLBACK_LOCKTIME",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001030402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAEDBAIAAAAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_INPUT_COUNT",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001040102000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAEEAQIAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_OUTPUT_COUNT",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001050102000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAEFAQIAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_GLOBAL_TX_MODIFIABLE",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc68850000000001060100000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAEGAQAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BCGsCRzBEAiAFJ1pIVzTgrh87lxI3WG8OctyFgz0njA5HTNIxEsD6XgIgawSMg868PEHQuTzH2nYYXO29Aw0AWwgBi+K5i7rL33sBIQN2DcygXzmX3GWykwYPfynxUUyMUnBI4SgCsEHU/DQKJwAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_IN_PREVIOUS_TXID",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a27010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc800220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gAIgIC1gH4SEamdV93a+AOPZ3o+xCsyTX7g8RfsBYtTK1at5IY9p2HPlQAAIABAACAAAAAgAAAAAAqAAAAACICA27+LCVWIZhlU7qdZcPdxkFlyhQ24FqjWkxusCRRz3ltGPadhz5UAACAAQAAgAAAAIABAAAAYgAAAAA="+  },+  {+   "case": "PSBTv0 but with PSBT_IN_OUTPUT_INDEX",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a27010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_IN_SEQUENCE",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a27011004ffffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonARAE/////wAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_IN_REQUIRED_TIME_LOCKTIME",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a270111048c8dc46200220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonAREEjI3EYgAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_IN_REQUIRED_HEIGHT_LOCKTIME",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a270112041027000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonARIEECcAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAAAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv0 but with PSBT_OUT_AMOUNT",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f00000000002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonACICAtYB+EhGpnVfd2vgDj2d6PsQrMk1+4PEX7AWLUytWreSGPadhz5UAACAAQAAgAAAAIAAAAAAKgAAAAEDCAAIry8AAAAAACICA27+LCVWIZhlU7qdZcPdxkFlyhQ24FqjWkxusCRRz3ltGPadhz5UAACAAQAAgAAAAIABAAAAYgAAAAA="+  },+  {+   "case": "PSBTv0 but with PSBT_OUT_SCRIPT",+   "hex": "70736274ff01007102000000010b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc80000000000feffffff020008af2f00000000160014c430f64c4756da310dbd1a085572ef299926272c8bbdeb0b00000000160014a07dac8ab6ca942d379ed795f835ba71c9cc688500000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e01086b02473044022005275a485734e0ae1f3b971237586f0e72dc85833d278c0e474cd23112c0fa5e02206b048c83cebc3c41d0b93cc7da76185cedbd030d005b08018be2b98bbacbdf7b012103760dcca05f3997dc65b293060f7f29f1514c8c527048e12802b041d4fc340a2700220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000104160014a07dac8ab6ca942d379ed795f835ba71c9cc6885002202036efe2c255621986553ba9d65c3ddc64165ca1436e05aa35a4c6eb02451cf796d18f69d873e540000800100008000000080010000006200000000",+   "base64": "cHNidP8BAHECAAAAAQsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAAAAAAD+////AgAIry8AAAAAFgAUxDD2TEdW2jENvRoIVXLvKZkmJyyLvesLAAAAABYAFKB9rIq2ypQtN57Xlfg1unHJzGiFAAAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEIawJHMEQCIAUnWkhXNOCuHzuXEjdYbw5y3IWDPSeMDkdM0jESwPpeAiBrBIyDzrw8QdC5PMfadhhc7b0DDQBbCAGL4rmLusvfewEhA3YNzKBfOZfcZbKTBg9/KfFRTIxScEjhKAKwQdT8NAonACICAtYB+EhGpnVfd2vgDj2d6PsQrMk1+4PEX7AWLUytWreSGPadhz5UAACAAQAAgAAAAIAAAAAAKgAAAAEEFgAUoH2sirbKlC03nteV+DW6ccnMaIUAIgIDbv4sJVYhmGVTup1lw93GQWXKFDbgWqNaTG6wJFHPeW0Y9p2HPlQAAIABAACAAAAAgAEAAABiAAAAAA=="+  },+  {+   "case": "PSBTv2 but with PSBT_GLOBAL_UNSIGNED_TX",+   "hex": "70736274ff0100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e00000000010204020000000103040000000001040101010501020106010701fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff0111048c8dc4620112041027000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQIEAgAAAAEDBAAAAAABBAEBAQUBAgEGAQcB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAARAE/v///wERBIyNxGIBEgQQJwAAACICAtYB+EhGpnVfd2vgDj2d6PsQrMk1+4PEX7AWLUytWreSGPadhz5UAACAAQAAgAAAAIAAAAAAKgAAAAEDCAAIry8AAAAAAQQWABTEMPZMR1baMQ29GghVcu8pmSYnLAAiAgLjb7/1PdU0Bwz4/TlmFGgPNXqbhdtzQL8c+nRdKtezQBj2nYc+VAAAgAEAAIAAAACAAQAAAGQAAAABAwiLvesLAAAAAAEEFgAUTdGTrJZKVqwbnhzKhFT+L0dPhRMA"+  },+  {+   "case": "PSBTv2 missing PSBT_GLOBAL_INPUT_COUNT",+   "hex": "70736274ff01020402000000010304000000000105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEFAQIB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAARAE/v///wAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 missing PSBT_GLOBAL_OUTPUT_COUNT",+   "hex": "70736274ff01020402000000010304000000000104010101fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAARAE/v///wAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 missing PSBT_GLOBAL_TX_VERSION",+   "hex": "70736274ff010401010105010201fb040200000000010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BBAEBAQUBAgH7BAIAAAAAAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAAAEDCAAIry8AAAAAAQQWABTEMPZMR1baMQ29GghVcu8pmSYnLAABAwiLvesLAAAAAAEEFgAUTdGTrJZKVqwbnhzKhFT+L0dPhRMA"+  },+  {+   "case": "PSBTv2 missing PSBT_IN_PREVIOUS_TXID",+   "hex": "70736274ff0102040200000001030400000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEPBAAAAAABEAT+////ACICAtYB+EhGpnVfd2vgDj2d6PsQrMk1+4PEX7AWLUytWreSGPadhz5UAACAAQAAgAAAAIAAAAAAKgAAAAEDCAAIry8AAAAAAQQWABTEMPZMR1baMQ29GghVcu8pmSYnLAAiAgLjb7/1PdU0Bwz4/TlmFGgPNXqbhdtzQL8c+nRdKtezQBj2nYc+VAAAgAEAAIAAAACAAQAAAGQAAAABAwiLvesLAAAAAAEEFgAUTdGTrJZKVqwbnhzKhFT+L0dPhRMA"+  },+  {+   "case": "PSBTv2 missing PSBT_IN_OUTPUT_INDEX",+   "hex": "70736274ff0102040200000001030400000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IARAE/v///wAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 missing PSBT_OUT_AMOUNT",+   "hex": "70736274ff0102040200000001030400000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAEQBP7///8AIgIC1gH4SEamdV93a+AOPZ3o+xCsyTX7g8RfsBYtTK1at5IY9p2HPlQAAIABAACAAAAAgAAAAAAqAAAAAQQWABTEMPZMR1baMQ29GghVcu8pmSYnLAAiAgLjb7/1PdU0Bwz4/TlmFGgPNXqbhdtzQL8c+nRdKtezQBj2nYc+VAAAgAEAAIAAAACAAQAAAGQAAAABAwiLvesLAAAAAAEEFgAUTdGTrJZKVqwbnhzKhFT+L0dPhRMA"+  },+  {+   "case": "PSBTv2 missing PSBT_OUT_SCRIPT",+   "hex": "70736274ff0102040200000001030400000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f0000000000220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAEQBP7///8AIgIC1gH4SEamdV93a+AOPZ3o+xCsyTX7g8RfsBYtTK1at5IY9p2HPlQAAIABAACAAAAAgAAAAAAqAAAAAQMIAAivLwAAAAAAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 with PSBT_IN_REQUIRED_TIME_LOCKTIME less than 500000000",+   "hex": "70736274ff01020402000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011104ff64cd1d00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAAREE/2TNHQAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 with PSBT_IN_REQUIRED_HEIGHT_LOCKTIME greater than or equal to 500000000",+   "hex": "70736274ff01020402000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f04000000000112040065cd1d00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAARIEAGXNHQAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "PSBTv2 with PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 0",+   "hex": "70736274ff010204020000000103040000000001040101010501020106010701fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff0111048c8dc4620112040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAQYBBwH7BAIAAAAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BDiALCtkhQZwchxlzXXLcc5+eqeBjjR/kwe7w+ZRAhIFfyAEPBAAAAAABEAT+////AREEjI3EYgESBAAAAAAAIgIC1gH4SEamdV93a+AOPZ3o+xCsyTX7g8RfsBYtTK1at5IY9p2HPlQAAIABAACAAAAAgAAAAAAqAAAAAQMIAAivLwAAAAABBBYAFMQw9kxHVtoxDb0aCFVy7ymZJicsACICAuNvv/U91TQHDPj9OWYUaA81epuF23NAvxz6dF0q17NAGPadhz5UAACAAQAAgAAAAIABAAAAZAAAAAEDCIu96wsAAAAAAQQWABRN0ZOslkpWrBueHMqEVP4vR0+FEwA="+  }+ ],+ "valid": [+  {+   "case": "1 input, 2 output PSBTv2, required fields only",+   "hex": "70736274ff01020402000000010401010105010201fb040200000000010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2",+   "hex": "70736274ff01020402000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAACICAtYB+EhGpnVfd2vgDj2d6PsQrMk1+4PEX7AWLUytWreSGPadhz5UAACAAQAAgAAAAIAAAAAAKgAAAAEDCAAIry8AAAAAAQQWABTEMPZMR1baMQ29GghVcu8pmSYnLAAiAgLjb7/1PdU0Bwz4/TlmFGgPNXqbhdtzQL8c+nRdKtezQBj2nYc+VAAAgAEAAIAAAACAAQAAAGQAAAABAwiLvesLAAAAAAEEFgAUTdGTrJZKVqwbnhzKhFT+L0dPhRMA"+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with PSBT_IN_SEQUENCE",+   "hex": "70736274ff01020402000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff00220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEAUgIAAAABwaolbiFLlqGCL5PeQr/ztfP/jQUZMG41FddRWl6AWxIAAAAAAP////8BGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgAAAAABAR8Yxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAQ4gCwrZIUGcHIcZc11y3HOfnqngY40f5MHu8PmUQISBX8gBDwQAAAAAARAE/v///wAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with PSBT_IN_SEQUENCE, and all locktime fields",+   "hex": "70736274ff0102040200000001030400000000010401010105010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff0111048c8dc4620112041027000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAEQBP7///8BEQSMjcRiARIEECcAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with Inputs Modifiable Flag (bit 0) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010101fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEBAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with Outputs Modifiable Flag (bit 1) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010201fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgECAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with Has SIGHASH_SINGLE Flag (bit 2) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010401fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEEAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with an undefined flag (bit 3) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010801fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEIAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with both Inputs Modifiable Flag (bit 0) and Outputs Modifiable Flag (bit 1) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010301fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEDAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with both Inputs Modifiable Flag (bit 0) and Has SIGHASH_SINGLE Flag (bit 2) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010501fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEFAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with both Outputs Modifiable Flag (bit 1) and Has SIGHASH_SINGLE FLag (bit 2) of PSBT_GLOBAL_TX_MODIFIABLE set",+   "hex": "70736274ff0102040200000001040101010501020106010601fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEGAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with all defined PSBT_GLOBAL_TX_MODIFIABLE flags set",+   "hex": "70736274ff0102040200000001040101010501020106010701fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgEHAfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with all possible PSBT_GLOBAL_TX_MODIFIABLE flags set",+   "hex": "70736274ff010204020000000104010101050102010601ff01fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f040000000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIBBgH/AfsEAgAAAAABAFICAAAAAcGqJW4hS5ahgi+T3kK/87Xz/40FGTBuNRXXUVpegFsSAAAAAAD/////ARjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4AAAAAAQEfGMaaOwAAAAAWABSwo68UQghBJpPKfRZoUrUtsK7wbgEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAAiAgLWAfhIRqZ1X3dr4A49nej7EKzJNfuDxF+wFi1MrVq3khj2nYc+VAAAgAEAAIAAAACAAAAAACoAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAIgIC42+/9T3VNAcM+P05ZhRoDzV6m4Xbc0C/HPp0XSrXs0AY9p2HPlQAAIABAACAAAAAgAEAAABkAAAAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "1 input, 2 output updated PSBTv2, with all PSBTv2 fields",+   "hex": "70736274ff010204020000000103040000000001040101010501020106010701fb0402000000000100520200000001c1aa256e214b96a1822f93de42bff3b5f3ff8d0519306e3515d7515a5e805b120000000000ffffffff0118c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e0000000001011f18c69a3b00000000160014b0a3af144208412693ca7d166852b52db0aef06e010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000011004feffffff0111048c8dc4620112041027000000220202d601f84846a6755f776be00e3d9de8fb10acc935fb83c45fb0162d4cad5ab79218f69d873e540000800100008000000080000000002a0000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c00220202e36fbff53dd534070cf8fd396614680f357a9b85db7340bf1cfa745d2ad7b34018f69d873e54000080010000800000008001000000640000000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAQYBBwH7BAIAAAAAAQBSAgAAAAHBqiVuIUuWoYIvk95Cv/O18/+NBRkwbjUV11FaXoBbEgAAAAAA/////wEYxpo7AAAAABYAFLCjrxRCCEEmk8p9FmhStS2wrvBuAAAAAAEBHxjGmjsAAAAAFgAUsKOvFEIIQSaTyn0WaFK1LbCu8G4BDiALCtkhQZwchxlzXXLcc5+eqeBjjR/kwe7w+ZRAhIFfyAEPBAAAAAABEAT+////AREEjI3EYgESBBAnAAAAIgIC1gH4SEamdV93a+AOPZ3o+xCsyTX7g8RfsBYtTK1at5IY9p2HPlQAAIABAACAAAAAgAAAAAAqAAAAAQMIAAivLwAAAAABBBYAFMQw9kxHVtoxDb0aCFVy7ymZJicsACICAuNvv/U91TQHDPj9OWYUaA81epuF23NAvxz6dF0q17NAGPadhz5UAACAAQAAgAAAAIABAAAAZAAAAAEDCIu96wsAAAAAAQQWABRN0ZOslkpWrBueHMqEVP4vR0+FEwA="+  }+ ],+ "locktime": [+  {+   "case": "No locktimes specified",+   "hex": "70736274ff01020402000000010401010105010201fb040200000000010e200b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8010f0400000000000103080008af2f000000000104160014c430f64c4756da310dbd1a085572ef299926272c000103088bbdeb0b0000000001041600144dd193ac964a56ac1b9e1cca8454fe2f474f851300",+   "locktime": 0,+   "base64": "cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA=="+  },+  {+   "case": "Fallback locktime of 0",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f040100000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f0400000000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 0,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA="+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000, Input 2 has no locktime fields",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f0400000000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 10000,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA"+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000, Input 2 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 9000",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f040000000001120428230000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 10000,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAESBCgjAAAAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA=="+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000, Input 2 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 9000 and PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc46201120428230000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 10000,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA="+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000 and PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048459, Input 2 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 9000 and PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000111048b8dc4620112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc46201120428230000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 10000,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiARIEKCMAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA"+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048459, Input 2 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 9000 and PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000111048b8dc46200010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc46201120428230000000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 1657048460,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA="+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000 and PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048459, Input 2 has PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000111048b8dc4620112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc462000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 1657048460,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA="+  },+  {+   "case": "Input 1 has no locktime fields, Input 2 has PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f040100000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc462000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": 1657048460,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAREEjI3EYgABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA"+  },+  {+   "case": "Input 1 has PSBT_IN_REQUIRED_HEIGHT_LOCKTIME of 10000, Input 2 has PSBT_IN_REQUIRED_TIME_LOCKTIME of 1657048460",+   "hex": "70736274ff0102040200000001030400000000010401020105010101fb040200000000010e200f758dbfbd4da7c16c8a3309c3c81e1100f561ea646db5b01752c485e1bdde9f010f04010000000112041027000000010e203a1b3b3c837d6489ea7a31d8e6c7dd503c001bef3e06958e7574808d68ca78a5010f04000000000111048c8dc462000103084f9335770000000001041600140b1352cacd03cf6aa1b7f3c8d6388671b34a5e1100",+   "locktime": null,+   "base64": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA=="+  }+ ]+}
+ data/psbt_bip371.json view
@@ -0,0 +1,93 @@+{+  "description": "Test vectors of BIP 371, Taproot fields in PSBT",+  "source": "https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki",+  "invalid": [+    {+      "case": "PSBT With PSBT_IN_TAP_INTERNAL_KEY key that is too long (incorrectly serialized as compressed DER)",+      "hex": "70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701172102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000000",+      "base64": "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARchAv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyAAAA"+    },+    {+      "case": "PSBT With PSBT_IN_TAP_KEY_SIG signature that is too short",+      "hex": "70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701133f173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc3475000000",+      "base64": "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARM/Fzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1AAAA"+    },+    {+      "case": "PSBT With PSBT_IN_TAP_KEY_SIG signature that is too long",+      "hex": "70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000",+      "base64": "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARNCFzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1FwGqAAAA"+    },+    {+      "case": "PSBT With PSBT_IN_TAP_BIP32_DERIVATION key that is too long (incorrectly serialized as compressed DER)",+      "hex": "70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000",+      "base64": "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXIhYC/jSQZMmNbiqFP6PJsSvYswShnBlcYO+n7iOTBG0/ojIZAHcrLadWAACAAQAAgAAAAIABAAAAAAAAAAAAAA=="+    },+    {+      "case": "PSBT With PSBT_OUT_TAP_INTERNAL_KEY key that is too long (incorrectly serialized as compressed DER)",+      "hex": "70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200",+      "base64": "cHNidP8BAH0CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Aoh7AQAAAAAAFgAUI4KHHH6EIaAAk/dU2RKB5nWHS59gawQqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAABBSEC/jSQZMmNbiqFP6PJsSvYswShnBlcYO+n7iOTBG0/ojIA"+    },+    {+      "case": "PSBT With PSBT_OUT_TAP_BIP32_DERIVATION key that is too long (incorrectly serialized as compressed DER)",+      "hex": "70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07570000220702fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da7560000800100008000000080010000000000000000",+      "base64": "cHNidP8BAH0CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Aoh7AQAAAAAAFgAUI4KHHH6EIaAAk/dU2RKB5nWHS59gawQqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAiBwL+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMhkAdystp1YAAIABAACAAAAAgAEAAAAAAAAAAA=="+    },+    {+      "case": "PSBT With PSBT_IN_TAP_SCRIPT_SIG key that is too long (incorrectly serialized as compressed DER)",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJCFAIssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20s2XDhX1P8DIL5UP1WD/qRm3YXK+AXNoqJkTrwdPQAsJQIl1aqNznMxonsD886NgvjLMC1mxbpOh6LtGBXJrLKej/3BsQXZkljKyzGjh+RK4pXjjcZzncQiFx6lm9JvNQ8sAAA=="+    },+    {+      "case": "PSBT With PSBT_IN_TAP_SCRIPT_SIG signature that is too long",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJBFCyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwlCiXVqo3OczGiewPzzo2C+MswLWbFuk6Hou0YFcmssp6P/cGxBdmSWMrLMaOH5ErileONxnOdxCIXHqWb0m81DywEBAAA="+    },+    {+      "case": "PSBT With PSBT_IN_TAP_SCRIPT_SIG signature that is too short",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093f89756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd430000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJBFCyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwk/iXVqo3OczGiewPzzo2C+MswLWbFuk6Hou0YFcmssp6P/cGxBdmSWMrLMaOH5ErileONxnOdxCIXHqWb0m81DAAA="+    },+    {+      "case": "PSBT With PSBT_IN_TAP_LEAF_SCRIPT Control block that is too long",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJjFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4fgAIyAssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20qzAAAA="+    },+    {+      "case": "PSBT With PSBT_IN_TAP_LEAF_SCRIPT Control block that is too short",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJhFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4SMgLLE6xoJI3oBqpqNlnPPAPraCHQnIEUpOho/r3oZbttKswAAA"+    }+  ],+  "valid": [+    {+      "case": "PSBT with one P2TR key only input with internal key and its derivation path",+      "hex": "70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000",+      "base64": "cHNidP8BAFICAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////AUjmBSoBAAAAFgAUdo4e60z0IIZgM/gKzv8PlyB0SWkAAAAAAAEBKwDyBSoBAAAAIlEgWiws9bUs8x+DrS6Npj/wMYPs2PYJx1EK6KSOA5EKB1chFv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyGQB3Ky2nVgAAgAEAAIAAAACAAQAAAAAAAAABFyD+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMgAiAgNrdyptt02HU8mKgnlY3mx4qzMSEJ830+AwRIQkLs5z2Bh3Ky2nVAAAgAEAAIAAAACAAAAAAAAAAAAA"+    },+    {+      "case": "PSBT with one P2TR key only input with internal key, its derivation path, and signature",+      "hex": "70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011340bb53ec917bad9d906af1ba87181c48b86ace5aae2b53605a725ca74625631476fc6f5baedaf4f2ee0f477f36f58f3970d5b8273b7e497b97af2e3f125c97af342116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000",+      "base64": "cHNidP8BAFICAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////AUjmBSoBAAAAFgAUdo4e60z0IIZgM/gKzv8PlyB0SWkAAAAAAAEBKwDyBSoBAAAAIlEgWiws9bUs8x+DrS6Npj/wMYPs2PYJx1EK6KSOA5EKB1cBE0C7U+yRe62dkGrxuocYHEi4as5aritTYFpyXKdGJWMUdvxvW67a9PLuD0d/NvWPOXDVuCc7fkl7l68uPxJcl680IRb+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMhkAdystp1YAAIABAACAAAAAgAEAAAAAAAAAARcg/jSQZMmNbiqFP6PJsSvYswShnBlcYO+n7iOTBG0/ojIAIgIDa3cqbbdNh1PJioJ5WN5seKszEhCfN9PgMESEJC7Oc9gYdystp1QAAIABAACAAAAAgAAAAAAAAAAAAA=="+    },+    {+      "case": "PSBT with one P2TR key only output with internal key and its derivation path",+      "hex": "70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000",+      "base64": "cHNidP8BAF4CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////AUjmBSoBAAAAIlEgg2mORYxmZOFZXXXaJZfeHiLul9eY5wbEwKS1qYI810MAAAAAAAEBKwDyBSoBAAAAIlEgWiws9bUs8x+DrS6Npj/wMYPs2PYJx1EK6KSOA5EKB1chFv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyGQB3Ky2nVgAAgAEAAIAAAACAAQAAAAAAAAABFyD+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMgABBSARJNp67JLM0GyVRWJkf0N7E4uVchqEvivyJ2u92rPmcSEHESTaeuySzNBslUViZH9DexOLlXIahL4r8idrvdqz5nEZAHcrLadWAACAAQAAgAAAAIAAAAAABQAAAAA="+    },+    {+      "case": "PSBT with one P2TR script path only input with dummy internal key, scripts, derivation paths for keys in the scripts, and merkle root",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgg2mORYxmZOFZXXXaJZfeHiLul9eY5wbEwKS1qYI810MAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJiFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4fgjICyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSrMBCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wJfG5v6l/3FP9XJEmZkIEOQG6YqhD1v35fZ4S8HQqabOIyBDILC/FvARtT6nvmFZJKp/J+XSmtIOoRVdhIZ2w7rRsqzAYhXBUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsDNlw4V9T/AyC+VD9Vg/6kZt2FyvgFzaKiZE68HT0ALCRFfLkkK98xFxPeFEfNgV85cWlxWMlop+0TfwgPzVuH4IyD6D3o87zsdDAps59JuF62gsuXJLRnvrUi0GFnLikUcqazAIRYssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20jkBzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwl3Ky2nVgAAgAEAAIACAACAAAAAAAAAAAAhFkMgsL8W8BG1Pqe+YVkkqn8n5dKa0g6hFV2EhnbDutGyOQERXy5JCvfMRcT3hRHzYFfOXFpcVjJaKftE38ID81bh+HcrLadWAACAAQAAgAEAAIAAAAAAAAAAACEWUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAFAHxGHl0hFvoPejzvOx0MCmzn0m4XraCy5cktGe+tSLQYWcuKRRypOQFvfWIFnpSXoaSiZ1admHbaYBAa/zjjUpubk5zn+RrpcHcrLadWAACAAQAAgAMAAIAAAAAAAAAAAAEXIFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAARgg8DYuL3Wm9CClvePrIh2WrmcgzyX4GJDJWx13WstRXmUAAQUgESTaeuySzNBslUViZH9DexOLlXIahL4r8idrvdqz5nEhBxEk2nrskszQbJVFYmR/Q3sTi5VyGoS+K/Ina73as+ZxGQB3Ky2nVgAAgAEAAIAAAACAAAAAAAUAAAAA"+    },+    {+      "case": "PSBT with one P2TR script path only output with dummy internal key, taproot tree, and script key derivation paths",+      "hex": "70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a010000002251200a8cbdc86de1ce1c0f9caeb22d6df7ced3683fe423e05d1e402a879341d6f6f5000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2320001052050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac001066f02c02220736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02ac02c02220631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969ac01c0222044faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c4273ac210744faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c42733901f06b798b92a10ed9a9d0bbfd3af173a53b1617da3a4159ca008216cd856b2e0e772b2da75600008001000080010000800000000003000000210750929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2107631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969390118ace409889785e0ea70ceebb8e1ca892a7a78eaede0f2e296cf435961a8f4ca772b2da756000080010000800200008000000000030000002107736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02390129a5b4915090162d759afd3fe0f93fa3326056d0b4088cb933cae7826cb8d82c772b2da7560000800100008003000080000000000300000000",+      "base64": "cHNidP8BAF4CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////AUjmBSoBAAAAIlEgCoy9yG3hzhwPnK6yLW33ztNoP+Qj4F0eQCqHk0HW9vUAAAAAAAEBKwDyBSoBAAAAIlEgWiws9bUs8x+DrS6Npj/wMYPs2PYJx1EK6KSOA5EKB1chFv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyGQB3Ky2nVgAAgAEAAIAAAACAAQAAAAAAAAABFyD+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMgABBSBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wAEGbwLAIiBzblcpAP4SUliaIUPI88efcaBBLSNTr3VelwHHgmlKAqwCwCIgYxxfO1gyuPvev7GXBM7rMjwh9A96JPQ9aO8MwmsSWWmsAcAiIET6pJoDON5IjI3//s37bzKfOAvVZu8gyN9tgT6rHEJzrCEHRPqkmgM43kiMjf/+zftvMp84C9Vm7yDI322BPqscQnM5AfBreYuSoQ7ZqdC7/Trxc6U7FhfaOkFZygCCFs2Fay4Odystp1YAAIABAACAAQAAgAAAAAADAAAAIQdQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wAUAfEYeXSEHYxxfO1gyuPvev7GXBM7rMjwh9A96JPQ9aO8MwmsSWWk5ARis5AmIl4Xg6nDO67jhyokqenjq7eDy4pbPQ1lhqPTKdystp1YAAIABAACAAgAAgAAAAAADAAAAIQdzblcpAP4SUliaIUPI88efcaBBLSNTr3VelwHHgmlKAjkBKaW0kVCQFi11mv0/4Pk/ozJgVtC0CIy5M8rngmy42Cx3Ky2nVgAAgAEAAIADAACAAAAAAAMAAAAA"+    },+    {+      "case": "PSBT with one P2TR script path only input with dummy internal key, scripts, script key derivation paths, merkle root, and script path signatures",+      "hex": "70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b0940bf818d9757d6ffeb538ba057fb4c1fc4e0f5ef186e765beb564791e02af5fd3d5e2551d4e34e33d86f276b82c99c79aed3f0395a081efcd2cc2c65dd7e693d7941144320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f840e1f1ab6fabfa26b236f21833719dc1d428ab768d80f91f9988d8abef47bfb863bb1f2a529f768c15f00ce34ec283cdc07e88f8428be28f6ef64043c32911811a4114fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca96f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae97040ec1f0379206461c83342285423326708ab031f0da4a253ee45aafa5b8c92034d8b605490f8cd13e00f989989b97e215faa36f12dee3693d2daccf3781c1757f66215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000",+      "base64": "cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgg2mORYxmZOFZXXXaJZfeHiLul9eY5wbEwKS1qYI810MAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJBFCyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwlAv4GNl1fW/+tTi6BX+0wfxOD17xhudlvrVkeR4Cr1/T1eJVHU404z2G8na4LJnHmu0/A5Wgge/NLMLGXdfmk9eUEUQyCwvxbwEbU+p75hWSSqfyfl0prSDqEVXYSGdsO60bIRXy5JCvfMRcT3hRHzYFfOXFpcVjJaKftE38ID81bh+EDh8atvq/omsjbyGDNxncHUKKt2jYD5H5mI2KvvR7+4Y7sfKlKfdowV8AzjTsKDzcB+iPhCi+KPbvZAQ8MpEYEaQRT6D3o87zsdDAps59JuF62gsuXJLRnvrUi0GFnLikUcqW99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwQOwfA3kgZGHIM0IoVCMyZwirAx8NpKJT7kWq+luMkgNNi2BUkPjNE+APmJmJuX4hX6o28S3uNpPS2szzeBwXV/ZiFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4fgjICyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSrMBCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wJfG5v6l/3FP9XJEmZkIEOQG6YqhD1v35fZ4S8HQqabOIyBDILC/FvARtT6nvmFZJKp/J+XSmtIOoRVdhIZ2w7rRsqzAYhXBUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsDNlw4V9T/AyC+VD9Vg/6kZt2FyvgFzaKiZE68HT0ALCRFfLkkK98xFxPeFEfNgV85cWlxWMlop+0TfwgPzVuH4IyD6D3o87zsdDAps59JuF62gsuXJLRnvrUi0GFnLikUcqazAIRYssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20jkBzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwl3Ky2nVgAAgAEAAIACAACAAAAAAAAAAAAhFkMgsL8W8BG1Pqe+YVkkqn8n5dKa0g6hFV2EhnbDutGyOQERXy5JCvfMRcT3hRHzYFfOXFpcVjJaKftE38ID81bh+HcrLadWAACAAQAAgAEAAIAAAAAAAAAAACEWUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAFAHxGHl0hFvoPejzvOx0MCmzn0m4XraCy5cktGe+tSLQYWcuKRRypOQFvfWIFnpSXoaSiZ1admHbaYBAa/zjjUpubk5zn+RrpcHcrLadWAACAAQAAgAMAAIAAAAAAAAAAAAEXIFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAARgg8DYuL3Wm9CClvePrIh2WrmcgzyX4GJDJWx13WstRXmUAAQUgESTaeuySzNBslUViZH9DexOLlXIahL4r8idrvdqz5nEhBxEk2nrskszQbJVFYmR/Q3sTi5VyGoS+K/Ina73as+ZxGQB3Ky2nVgAAgAEAAIAAAACAAAAAAAUAAAAA"+    }+  ]+}
+ data/psbt_complex_core32.json view
@@ -0,0 +1,18 @@+{+  "initial_psbt": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQkBBQEBAfsEAgAAAAABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAAAAAABEAT9////AAEOIMJi6QRo1JwW6r9Kr87PLtmwKnCwUESH0W8cGHLOG6F3AQ8EAQAAAAEQBP3///8AAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQCAAAAARAE/f///wABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAMAAAABEAT9////AAEOIMJi6QRo1JwW6r9Kr87PLtmwKnCwUESH0W8cGHLOG6F3AQ8EBAAAAAEQBP3///8AAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQFAAAAARAE/f///wABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAYAAAABEAT9////AAEOIMJi6QRo1JwW6r9Kr87PLtmwKnCwUESH0W8cGHLOG6F3AQ8EBwAAAAEQBP3///8AAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQIAAAAARAE/f///wABAwjAQcgXAAAAAAEEFgAUTpbJk/Pp7+I9+2XU/5tnno3B+iAA",+  "miner_psbt": "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",+  "p2pkh_psbt": "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",+  "p2sh_pk_psbt": "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",+  "p2sh_ms_1_psbt": "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",+  "p2sh_ms_2_psbt": "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",+  "p2sh_wsh_pk_psbt": "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",+  "p2sh_wsh_ms_1_psbt": "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",+  "p2sh_wsh_ms_2_psbt": "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",+  "p2wpkh_psbt": "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",+  "p2wsh_pk_psbt": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQkBBQEBAfsEAgAAAAABAP1pAQIAAAABPz5dcDEB9LnFHDVX0dkwtDerzFGAP2nG84OdyAh0IkIAAAAAAP3///8JgPD6AgAAAAAXqRQlQrA6Ceod9lph12rCbyZBc5a1YIeA8PoCAAAAACIAIE1OVO9cvLTONJrL6AcLBW4bEA7C8tgsU6Y+XacDF+w2gPD6AgAAAAAXqRT5gCB+A4ogbnOBYUu6csQuFPQ3IIfOXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOgPD6AgAAAAAWABQj6uONw0purjA/mHGojb1aylHKMIDw+gIAAAAAGXapFNMBrO5FOkmmFdmf/j4uUGO+uMafiKyA8PoCAAAAABepFMjUBciHkNetEhcC/LaTGsvhZh8Eh4Dw+gIAAAAAF6kUGemwG0Zf2IAM5g3yhpIRLnnb0j2HgPD6AgAAAAAiACD1lLiozt8M0sebDr76Epk+eHE9eKszRvqDRTc3Pg+JhGUAAAABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAAAAAABEAT9////AAEA/WkBAgAAAAE/Pl1wMQH0ucUcNVfR2TC0N6vMUYA/acbzg53ICHQiQgAAAAAA/f///wmA8PoCAAAAABepFCVCsDoJ6h32WmHXasJvJkFzlrVgh4Dw+gIAAAAAIgAgTU5U71y8tM40msvoBwsFbhsQDsLy2CxTpj5dpwMX7DaA8PoCAAAAABepFPmAIH4DiiBuc4FhS7pyxC4U9Dcgh85eLhIBAAAAFgAUOALpJK38yRF36+SrQQ2wFdt4Yo6A8PoCAAAAABYAFCPq443DSm6uMD+YcaiNvVrKUcowgPD6AgAAAAAZdqkU0wGs7kU6SaYV2Z/+Pi5QY764xp+IrIDw+gIAAAAAF6kUyNQFyIeQ160SFwL8tpMay+FmHwSHgPD6AgAAAAAXqRQZ6bAbRl/YgAzmDfKGkhEuedvSPYeA8PoCAAAAACIAIPWUuKjO3wzSx5sOvvoSmT54cT14qzNG+oNFNzc+D4mEZQAAAAEBK4Dw+gIAAAAAIgAgTU5U71y8tM40msvoBwsFbhsQDsLy2CxTpj5dpwMX7DYiAgMRHnLItYKx0TGdv6joFRXODEqBej/mGhevf+YICqN5fkcwRAIgDimVg2SvJW41L8wvBFNBZUVSWtODhizVt1JV2EwEXxICIGyjRQPQrP3r8TPCZv88dcetA9vkg20SxdXEHPtoT6/TAQEFIyEDER5yyLWCsdExnb+o6BUVzgxKgXo/5hoXr3/mCAqjeX6sIgYDER5yyLWCsdExnb+o6BUVzgxKgXo/5hoXr3/mCAqjeX4ECMXuzQEOIMJi6QRo1JwW6r9Kr87PLtmwKnCwUESH0W8cGHLOG6F3AQ8EAQAAAAEQBP3///8AAQD9aQECAAAAAT8+XXAxAfS5xRw1V9HZMLQ3q8xRgD9pxvODncgIdCJCAAAAAAD9////CYDw+gIAAAAAF6kUJUKwOgnqHfZaYddqwm8mQXOWtWCHgPD6AgAAAAAiACBNTlTvXLy0zjSay+gHCwVuGxAOwvLYLFOmPl2nAxfsNoDw+gIAAAAAF6kU+YAgfgOKIG5zgWFLunLELhT0NyCHzl4uEgEAAAAWABQ4AukkrfzJEXfr5KtBDbAV23hijoDw+gIAAAAAFgAUI+rjjcNKbq4wP5hxqI29WspRyjCA8PoCAAAAABl2qRTTAazuRTpJphXZn/4+LlBjvrjGn4isgPD6AgAAAAAXqRTI1AXIh5DXrRIXAvy2kxrL4WYfBIeA8PoCAAAAABepFBnpsBtGX9iADOYN8oaSES5529I9h4Dw+gIAAAAAIgAg9ZS4qM7fDNLHmw6++hKZPnhxPXirM0b6g0U3Nz4PiYRlAAAAAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQCAAAAARAE/f///wABAP1pAQIAAAABPz5dcDEB9LnFHDVX0dkwtDerzFGAP2nG84OdyAh0IkIAAAAAAP3///8JgPD6AgAAAAAXqRQlQrA6Ceod9lph12rCbyZBc5a1YIeA8PoCAAAAACIAIE1OVO9cvLTONJrL6AcLBW4bEA7C8tgsU6Y+XacDF+w2gPD6AgAAAAAXqRT5gCB+A4ogbnOBYUu6csQuFPQ3IIfOXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOgPD6AgAAAAAWABQj6uONw0purjA/mHGojb1aylHKMIDw+gIAAAAAGXapFNMBrO5FOkmmFdmf/j4uUGO+uMafiKyA8PoCAAAAABepFMjUBciHkNetEhcC/LaTGsvhZh8Eh4Dw+gIAAAAAF6kUGemwG0Zf2IAM5g3yhpIRLnnb0j2HgPD6AgAAAAAiACD1lLiozt8M0sebDr76Epk+eHE9eKszRvqDRTc3Pg+JhGUAAAABAR/OXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQDAAAAARAE/f///wABAP1pAQIAAAABPz5dcDEB9LnFHDVX0dkwtDerzFGAP2nG84OdyAh0IkIAAAAAAP3///8JgPD6AgAAAAAXqRQlQrA6Ceod9lph12rCbyZBc5a1YIeA8PoCAAAAACIAIE1OVO9cvLTONJrL6AcLBW4bEA7C8tgsU6Y+XacDF+w2gPD6AgAAAAAXqRT5gCB+A4ogbnOBYUu6csQuFPQ3IIfOXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOgPD6AgAAAAAWABQj6uONw0purjA/mHGojb1aylHKMIDw+gIAAAAAGXapFNMBrO5FOkmmFdmf/j4uUGO+uMafiKyA8PoCAAAAABepFMjUBciHkNetEhcC/LaTGsvhZh8Eh4Dw+gIAAAAAF6kUGemwG0Zf2IAM5g3yhpIRLnnb0j2HgPD6AgAAAAAiACD1lLiozt8M0sebDr76Epk+eHE9eKszRvqDRTc3Pg+JhGUAAAABAR+A8PoCAAAAABYAFCPq443DSm6uMD+YcaiNvVrKUcowAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQEAAAAARAE/f///wABAP1pAQIAAAABPz5dcDEB9LnFHDVX0dkwtDerzFGAP2nG84OdyAh0IkIAAAAAAP3///8JgPD6AgAAAAAXqRQlQrA6Ceod9lph12rCbyZBc5a1YIeA8PoCAAAAACIAIE1OVO9cvLTONJrL6AcLBW4bEA7C8tgsU6Y+XacDF+w2gPD6AgAAAAAXqRT5gCB+A4ogbnOBYUu6csQuFPQ3IIfOXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOgPD6AgAAAAAWABQj6uONw0purjA/mHGojb1aylHKMIDw+gIAAAAAGXapFNMBrO5FOkmmFdmf/j4uUGO+uMafiKyA8PoCAAAAABepFMjUBciHkNetEhcC/LaTGsvhZh8Eh4Dw+gIAAAAAF6kUGemwG0Zf2IAM5g3yhpIRLnnb0j2HgPD6AgAAAAAiACD1lLiozt8M0sebDr76Epk+eHE9eKszRvqDRTc3Pg+JhGUAAAABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAUAAAABEAT9////AAEA/WkBAgAAAAE/Pl1wMQH0ucUcNVfR2TC0N6vMUYA/acbzg53ICHQiQgAAAAAA/f///wmA8PoCAAAAABepFCVCsDoJ6h32WmHXasJvJkFzlrVgh4Dw+gIAAAAAIgAgTU5U71y8tM40msvoBwsFbhsQDsLy2CxTpj5dpwMX7DaA8PoCAAAAABepFPmAIH4DiiBuc4FhS7pyxC4U9Dcgh85eLhIBAAAAFgAUOALpJK38yRF36+SrQQ2wFdt4Yo6A8PoCAAAAABYAFCPq443DSm6uMD+YcaiNvVrKUcowgPD6AgAAAAAZdqkU0wGs7kU6SaYV2Z/+Pi5QY764xp+IrIDw+gIAAAAAF6kUyNQFyIeQ160SFwL8tpMay+FmHwSHgPD6AgAAAAAXqRQZ6bAbRl/YgAzmDfKGkhEuedvSPYeA8PoCAAAAACIAIPWUuKjO3wzSx5sOvvoSmT54cT14qzNG+oNFNzc+D4mEZQAAAAEOIMJi6QRo1JwW6r9Kr87PLtmwKnCwUESH0W8cGHLOG6F3AQ8EBgAAAAEQBP3///8AAQD9aQECAAAAAT8+XXAxAfS5xRw1V9HZMLQ3q8xRgD9pxvODncgIdCJCAAAAAAD9////CYDw+gIAAAAAF6kUJUKwOgnqHfZaYddqwm8mQXOWtWCHgPD6AgAAAAAiACBNTlTvXLy0zjSay+gHCwVuGxAOwvLYLFOmPl2nAxfsNoDw+gIAAAAAF6kU+YAgfgOKIG5zgWFLunLELhT0NyCHzl4uEgEAAAAWABQ4AukkrfzJEXfr5KtBDbAV23hijoDw+gIAAAAAFgAUI+rjjcNKbq4wP5hxqI29WspRyjCA8PoCAAAAABl2qRTTAazuRTpJphXZn/4+LlBjvrjGn4isgPD6AgAAAAAXqRTI1AXIh5DXrRIXAvy2kxrL4WYfBIeA8PoCAAAAABepFBnpsBtGX9iADOYN8oaSES5529I9h4Dw+gIAAAAAIgAg9ZS4qM7fDNLHmw6++hKZPnhxPXirM0b6g0U3Nz4PiYRlAAAAAQ4gwmLpBGjUnBbqv0qvzs8u2bAqcLBQRIfRbxwYcs4boXcBDwQHAAAAARAE/f///wABAP1pAQIAAAABPz5dcDEB9LnFHDVX0dkwtDerzFGAP2nG84OdyAh0IkIAAAAAAP3///8JgPD6AgAAAAAXqRQlQrA6Ceod9lph12rCbyZBc5a1YIeA8PoCAAAAACIAIE1OVO9cvLTONJrL6AcLBW4bEA7C8tgsU6Y+XacDF+w2gPD6AgAAAAAXqRT5gCB+A4ogbnOBYUu6csQuFPQ3IIfOXi4SAQAAABYAFDgC6SSt/MkRd+vkq0ENsBXbeGKOgPD6AgAAAAAWABQj6uONw0purjA/mHGojb1aylHKMIDw+gIAAAAAGXapFNMBrO5FOkmmFdmf/j4uUGO+uMafiKyA8PoCAAAAABepFMjUBciHkNetEhcC/LaTGsvhZh8Eh4Dw+gIAAAAAF6kUGemwG0Zf2IAM5g3yhpIRLnnb0j2HgPD6AgAAAAAiACD1lLiozt8M0sebDr76Epk+eHE9eKszRvqDRTc3Pg+JhGUAAAABDiDCYukEaNScFuq/Sq/Ozy7ZsCpwsFBEh9FvHBhyzhuhdwEPBAgAAAABEAT9////AAEDCMBByBcAAAAAAQQWABROlsmT8+nv4j37ZdT/m2eejcH6IAA=",+  "p2wsh_ms_1_psbt": "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",+  "p2wsh_ms_2_psbt": "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",+  "combined_psbt": "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",+  "complete_psbt": "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",+  "final_tx": "02000000000109c262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba17700000000232200208ef92d01ee4d21a0fe373a9193f54a6ca6dd7e6ea372419eff61f4df4ce878a5fdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba1770100000000fdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba17702000000232200206915504d39daff3ee46d0344a232b5b5f1cf6d8edef28d5c56e5eb587a05f109fdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba1770300000000fdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba1770400000000fdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba177050000006a47304402206df6b93d4582004bd193aa991806c438ce6ed701112cfb72a7104e5d588d671402202b7eba2a30572520e84e6dd85965fcc90988be6d6d74a1a37a476e6663d6141d012103e4a112141aa2d01dfb3d296525f141f4c7286811899c251ccaeec6b24502be2bfdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba17706000000fc0047304402205803f8eafce9801dff0d8bb5bbf63fc9854901d6e9d8b135013f3194b91640a702204f6080204c524eafbe3dc1631bd051d972220cad85fa837e1cdd0e35da7e94300147304402204b0d0b2771cecc0307c845f35a87058cba6ac5f76462d519ed0d57aac5b6b171022001921987ac79f7a5d4aa301fca5087bc8199b37d653b164cf465cb44bd94395d014c69522102866d95fc2dd854f4f10b47067d53699c15851d739b6592f6f73b84d35412ac4821030f6707089f90b5e971d2df0ed4c71ca714f780abe2ff6bdc9aa160a21c97608421037a5707a94eb6a2a1ca472482a194022d6f2cc93c338880c28107a8ea6e408c6753aefdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba177070000006c47304402205cce9df7bbb5591f8aef95683b0de12a262a3155ddf13f63518af51c58f52f09022068f557ee7c83db340c803f7e3adf40dc1d687300707f9ddbf8ac303f8c0836d801232103db0fb9dfe845fcb75e7ea69860dd2b62b306c3852f2f689fc900f1dc032229eeacfdffffffc262e90468d49c16eabf4aafcecf2ed9b02a70b0504487d16f1c1872ce1ba1770800000000fdffffff01c041c817000000001600144e96c993f3e9efe23dfb65d4ff9b679e8dc1fa2002473044022074f882b6b6ad42d7456cfc0411a35010e1cfaadb3b9c287bf4dd0a927f86a15f02201025945dceb94820944015de3d504c312254fc38f6785aeb227710641a02253c012321030742cfccedddaa05d6ecbdeb28600502ad1e206d04f1ecfc043d7943edf65b5eac0247304402200e29958364af256e352fcc2f0453416545525ad383862cd5b75255d84c045f1202206ca34503d0acfdebf133c266ff3c75c7ad03dbe4836d12c5d5c41cfb684fafd301232103111e72c8b582b1d1319dbfa8e81515ce0c4a817a3fe61a17af7fe6080aa3797eac0400473044022058e4e17d7158b40fbc45e9a380ec63c9b6561bd1774be92cecd4475e9a37f47602203e5eea2e3ef2655f89ac30fa3f73943e3bee68508ca8eee6c66606b73d6dec6b0147304402200b580c4d272c815c6d284b2f641b5bccdbe84b22a1fa16e192d15af326a92cab02206ea42679e631d12d8670290fdbf67ff75ba1dd69093802f021277076e50270e701695221024a2b2867b8001c837f2f630d8586ce3a44ac5dd97cbcd6cea3643cf4bea7b8c6210397b1bde16f21191a65e16e1f46a4551de996bffa1e4eb3f0f3f1746eb480ae232103dd416cd596de2c2ca0f6625980937df55fec8a249334619015b5eda94102919653ae0247304402205332257a5cbbc59b68f3e4047aaf443a29f1e11b0139314e5be20f6e71b1015c0220058b7ab57781dd49e9260dc9fc2d5ad14689cc49898a403bb52c16a26bfba6580121033b78899278a2a7e673b8c00c39eaf3e96360d3b1feccc31af855a93482c07d1f024730440220359d3f4e20ba8edc4f633cb29a3ed79dcbad99d6e87659d1651726307e65d1ad02204b0645546c818dd71c8bf29a18a5707f0c36c12570b56f3e2da7215b88c35c42012102d322804b1889ee5871055e7bf8e02a27ee681a37aeedaa3b29f369979d71e1a900000004004730440220452f1fc7a60738f533b7705048cc5a2fa7854e5b138f65f3f9b348964c67be02022075686c7832c39813f67a0bfd353469c82b0db3f62559c2228d4b0c039910182a014730440220133157c86687fd769fbc7f8ccad3cae323e3e7bae207e014a8ae5eec5d45cbda022065fe3abe7398316caab39f7308be55a4ef76636c969c1cb3097544b59b16b0c101695221026589320adb556a333eb2234d1836f571ed3362a27ac99b348dfba25303b6092821027e1606d96721baccdb0b0212c2a551cd0348305929d7e2eb66950c02bc2284bb2102ecf5876024a2778099ee8499119272c424080e04f80e10172d861c7844a5d77353ae00000000"+}
+ data/psbt_core32.json view
@@ -0,0 +1,8 @@+{+ "description": "Files Bitcoin Core 32.1 wrote on a regtest chain for a payment of 1 BTC from a coin of 50 BTC: the PSBT walletcreatefundedpsbt gives in version 0 and in version 2, the version 2 one signed by walletprocesspsbt without finalizing, then finalized, and the transaction finalizepsbt extracts. See the README of the haskoin-wallet vectors in test/data/psbt.",+ "prepared_v0": "cHNidP8BAHcCAAAAAZzolVrP9CarlPjUSHujJgVmsHiFB+Mm+Bafv63Mct7+AAAAAAD9////AgDh9QUAAAAAGXapFKvIhl67bbYllWAxY5ddm6o2RjLKiKw2CBAkAQAAABl2qRRFWqUMJmMUF3HrrCCW43gzkRIn1oisAAAAAAABAIcCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAGXapFMgNVSI+LxBhNjrdKnsDY8KFvymNiKwAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAAiBgMvZDcCIlbIVOAJDn1si2XpZgbvf0BTmeDwGHyTCXrA+Ri8Vg+VLAAAgAAAAIAAAACAAAAAAAAAAAAAACICA3by5wZFjddWc7oZuO/YQu/d2bwK5B4xM53/z5F7bbbrGLxWD5UsAACAAAAAgAAAAIABAAAAAAAAAAA=",+ "prepared_v2": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAIcCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAGXapFMgNVSI+LxBhNjrdKnsDY8KFvymNiKwAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAAiBgMvZDcCIlbIVOAJDn1si2XpZgbvf0BTmeDwGHyTCXrA+Ri8Vg+VLAAAgAAAAIAAAACAAAAAAAAAAAABDiCc6JVaz/Qmq5T41Eh7oyYFZrB4hQfjJvgWn7+tzHLe/gEPBAAAAAABEAT9////AAEDCADh9QUAAAAAAQQZdqkUq8iGXrtttiWVYDFjl12bqjZGMsqIrAAiAgN28ucGRY3XVnO6Gbjv2ELv3dm8CuQeMTOd/8+Re2226xi8Vg+VLAAAgAAAAIAAAACAAQAAAAAAAAABAwg2CBAkAQAAAAEEGXapFEVapQwmYxQXceusIJbjeDOREifWiKwA",+ "signed_v2": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAIcCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAGXapFMgNVSI+LxBhNjrdKnsDY8KFvymNiKwAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAAiAgMvZDcCIlbIVOAJDn1si2XpZgbvf0BTmeDwGHyTCXrA+UcwRAIgLeMOTixwPqb3Q/BHIdvx2UaMZSeREBHXQFd48xD3urQCIBoZ1qD0T7FwE+JTWqLJ4hni3HJwM6PvpMB+dAKL1PaeASIGAy9kNwIiVshU4AkOfWyLZelmBu9/QFOZ4PAYfJMJesD5GLxWD5UsAACAAAAAgAAAAIAAAAAAAAAAAAEOIJzolVrP9CarlPjUSHujJgVmsHiFB+Mm+Bafv63Mct7+AQ8EAAAAAAEQBP3///8AAQMIAOH1BQAAAAABBBl2qRSryIZeu222JZVgMWOXXZuqNkYyyoisACICA3by5wZFjddWc7oZuO/YQu/d2bwK5B4xM53/z5F7bbbrGLxWD5UsAACAAAAAgAAAAIABAAAAAAAAAAEDCDYIECQBAAAAAQQZdqkURVqlDCZjFBdx66wgluN4M5ESJ9aIrAA=",+ "finalized_v2": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAIcCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZgD/////AgDyBSoBAAAAGXapFMgNVSI+LxBhNjrdKnsDY8KFvymNiKwAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QAAAAABB2pHMEQCIC3jDk4scD6m90PwRyHb8dlGjGUnkRAR10BXePMQ97q0AiAaGdag9E+xcBPiU1qiyeIZ4txycDOj76TAfnQCi9T2ngEhAy9kNwIiVshU4AkOfWyLZelmBu9/QFOZ4PAYfJMJesD5AQ4gnOiVWs/0JquU+NRIe6MmBWaweIUH4yb4Fp+/rcxy3v4BDwQAAAAAARAE/f///wABAwgA4fUFAAAAAAEEGXapFKvIhl67bbYllWAxY5ddm6o2RjLKiKwAIgIDdvLnBkWN11Zzuhm479hC793ZvArkHjEznf/PkXtttusYvFYPlSwAAIAAAACAAAAAgAEAAAAAAAAAAQMINggQJAEAAAABBBl2qRRFWqUMJmMUF3HrrCCW43gzkRIn1oisAA==",+ "final_tx": "02000000019ce8955acff426ab94f8d4487ba3260566b0788507e326f8169fbfadcc72defe000000006a47304402202de30e4e2c703ea6f743f04721dbf1d9468c6527911011d7405778f310f7bab402201a19d6a0f44fb17013e2535aa2c9e219e2dc727033a3efa4c07e74028bd4f69e0121032f6437022256c854e0090e7d6c8b65e96606ef7f405399e0f0187c93097ac0f9fdffffff0200e1f505000000001976a914abc8865ebb6db62595603163975d9baa364632ca88ac36081024010000001976a914455aa50c2663141771ebac2096e37833911227d688ac00000000"+}
− 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/slip39_split.json view
@@ -0,0 +1,324 @@+[+ {+  "description": "1-of-1, 128 bits, no passphrase",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     1,+     1+    ]+   ]+  },+  "identifier": 7945,+  "extendable": true,+  "iterationExponent": 0,+  "passphrase": "",+  "masterSecret": "4142434445464748494a4b4c4d4e4f50",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [],+    "digest": ""+   }+  ],+  "mnemonics": [+   [+    "duckling evil academic academic cards jacket national hairy ancestor crunch muscle parcel award peanut guitar dryer eclipse peasant resident fact"+   ]+  ]+ },+ {+  "description": "2-of-3, 128 bits, passphrase TREZOR",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     2,+     3+    ]+   ]+  },+  "identifier": 42,+  "extendable": true,+  "iterationExponent": 1,+  "passphrase": "TREZOR",+  "masterSecret": "4142434445464748494a4b4c4d4e4f50",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [],+    "digest": "698c2ccb37ff81b7f5603f9b"+   }+  ],+  "mnemonics": [+   [+    "acid fawn academic acid club member living lawsuit sunlight pancake market lend peasant sunlight glad simple infant organize grant infant",+    "acid fawn academic agency bracelet transfer march argue wits exact dough parcel animal traveler dictate river military salt satisfy payment",+    "acid fawn academic always advocate either wine oasis solution goat toxic injury radar romantic shrimp check warmth fangs ending mineral"+   ]+  ]+ },+ {+  "description": "3-of-5, 128 bits, not extendable",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     3,+     5+    ]+   ]+  },+  "identifier": 42,+  "extendable": false,+  "iterationExponent": 1,+  "passphrase": "TREZOR",+  "masterSecret": "4142434445464748494a4b4c4d4e4f50",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [+     "58fb87176761c432c8410001e0454f62"+    ],+    "digest": "7235aa0b577e0ffceb3978f5"+   }+  ],+  "mnemonics": [+   [+    "acid extra academic acne blue wine buyer install steady scandal silent lilac easy adapt lecture fiction step nylon indicate umbrella",+    "acid extra academic agree body market fraction duckling laden often freshman temple scene year brother image vexed science disaster explain",+    "acid extra academic amazing already repeat husband escape evaluate metric equation crunch robin slow episode strategy grant diagnose rhythm minister",+    "acid extra academic arcade alto slice dining adorn union smear syndrome episode downtown declare sniff wits entrance hairy various debut",+    "acid extra academic axle diploma verdict marathon shrimp educate traveler random maximum corner crush crazy fatal together campus bedroom frost"+   ]+  ]+ },+ {+  "description": "3-of-5, 256 bits, exponent 2",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     3,+     5+    ]+   ]+  },+  "identifier": 32767,+  "extendable": true,+  "iterationExponent": 2,+  "passphrase": "pass phrase",+  "masterSecret": "4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [+     "1e83c5fa3334312d90c0f1dfffa3b191d8e38dbf7ccf5e3d0c974a88ce700563"+    ],+    "digest": "ba2957e47955e605c968472eb58339f2e2fd28917f72760ff7c88606"+   }+  ],+  "mnemonics": [+   [+    "zero wits academic acne adapt pants scramble trash depend aquatic curly element average island yoga medal broken inside theater diet voting deploy frozen veteran sharp source lying dive acid float voice slap unhappy",+    "zero wits academic agree airline adapt cards royal duckling universe friar fatal cylinder become admit loan vegan boring cards escape family task amazing work temple loyalty vanish western modify herald hour tricycle salon",+    "zero wits academic amazing antenna stay genre hospital stilt ruler juice facility story industry formal clothes flexible blimp painting describe tofu fishing critical beyond helpful remove march humidity heat muscle mailman likely kind",+    "zero wits academic arcade average jerky repeat endorse spew class answer echo warmth behavior pile dance quiet injury friendly entrance element repeat ladle bulge email type visual presence zero payment bike percent extra",+    "zero wits academic axle amazing ultimate python switch patrol hobo genre smith depict national phantom fatal clinic cowboy sunlight research exotic brother work depict acquire legs holy plunge random maiden realize walnut domestic"+   ]+  ]+ },+ {+  "description": "2-of-2, 160 bits",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     2,+     2+    ]+   ]+  },+  "identifier": 0,+  "extendable": true,+  "iterationExponent": 0,+  "passphrase": "",+  "masterSecret": "4142434445464748494a4b4c4d4e4f5051525354",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [],+    "digest": "c6058760ba014cea8b09057071391884"+   }+  ],+  "mnemonics": [+   [+    "academic again academic acid beyond focus device elevator alto clinic industry legal both relate identify story scroll emerald mortgage priest permit browser party",+    "academic again academic agency mailman usual enlarge airport living airport dream endorse laden usual arcade canyon being drove finger surprise hazard being exchange"+   ]+  ]+ },+ {+  "description": "2 of 3 groups: 1-of-1, 2-of-3, 3-of-5, 128 bits",+  "scheme": {+   "groupThreshold": 2,+   "groups": [+    [+     1,+     1+    ],+    [+     2,+     3+    ],+    [+     3,+     5+    ]+   ]+  },+  "identifier": 12345,+  "extendable": true,+  "iterationExponent": 0,+  "passphrase": "TREZOR",+  "masterSecret": "4142434445464748494a4b4c4d4e4f50",+  "random": [+   {+    "shares": [],+    "digest": "20520badd1c2f4add42f6f8b"+   },+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [],+    "digest": "4b5df7a7faee6e3110e7dd40"+   },+   {+    "shares": [+     "307499552909a562c0c8da3c50734567"+    ],+    "digest": "ab0c66efb073adb6bb838ea1"+   }+  ],+  "mnemonics": [+   [+    "garlic single acrobat leader anatomy fridge album sympathy paper mandate vegan secret adult identify python diagnose garlic idle tidy fridge"+   ],+   [+    "garlic single beard leaf champion news smirk extend mule grownup orange dismiss paces believe teacher learn crowd hazard rapids believe",+    "garlic single beard lily away emphasis hamster display legend square airport priority ceiling cradle smirk royal desktop junction enjoy jewelry",+    "garlic single beard lungs aircraft domestic lobe chubby jerky capital prisoner beam physics grant replace prospect blanket diminish minister spine"+   ],+   [+    "garlic single ceramic learn apart infant gray famous elder permit mama arcade dictate dream either desert forbid fumes hobo texture",+    "garlic single ceramic lips easel cluster focus viral phantom sharp darkness company blue armed phantom market epidemic grin problem execute",+    "garlic single ceramic luxury antenna example manager trial pencil geology pleasure wine transfer satisfy payment oven ruin ordinary smirk lunch",+    "garlic single ceramic march discuss cards recall exhaust element aspect auction snapshot senior vanish enemy coal soldier petition apart climate",+    "garlic single ceramic method blimp elegant lend minister warn exact guest mixture prayer ocean chest breathe alarm civil painting seafood"+   ]+  ]+ },+ {+  "description": "1 of 2 groups: 2-of-3 and 2-of-3, 256 bits, not extendable",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     2,+     3+    ],+    [+     2,+     3+    ]+   ]+  },+  "identifier": 1,+  "extendable": false,+  "iterationExponent": 1,+  "passphrase": "",+  "masterSecret": "4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [],+    "digest": "990df1d6f4a02cca605b9418b260b7c70dfad975780be420c2bc4cdf"+   },+   {+    "shares": [],+    "digest": "8588cc4bd3f26057b5095f8f1462aef1a6421db7917bfa1ad21164f9"+   }+  ],+  "mnemonics": [+   [+    "academic always academic echo alive river tolerate elite quiet nail diet unusual shaped arena quantity discuss rescue laden sympathy detect romp decision glance duration deal unusual pupal fishing universe national acid endorse smoking",+    "academic always academic email anxiety decision ladybug spit piece gesture traffic grant humidity downtown criminal calcium execute acne veteran junction ending dish sprinkle traffic elite gray legend example scout grant credit glen story",+    "academic always academic entrance aviation glasses slow hazard crisis domestic midst military describe client obesity camera fake ruler member saver keyboard chest beard wireless guard welcome spend angel browser voter brother texture crystal"+   ],+   [+    "academic always away echo admit race airline lunar picture freshman priority manager vexed tricycle cylinder violence coastal order dish mandate declare course hanger saver smug squeeze finance various training hormone dining security brother",+    "academic always away email anatomy raisin should reward jury timely unfold orbit inside welcome glad nylon hazard anatomy benefit pajamas sympathy smear bucket carbon desire fraction friar plunge become mortgage desert flexible adult",+    "academic always away entrance airport intend leaf voting rich profile critical smith ecology short rapids elegant cards industry hormone race equation genre magazine judicial browser single enforce darkness ancestor camera elite brother arena"+   ]+  ]+ },+ {+  "description": "4-of-4, 128 bits: two random shares",+  "scheme": {+   "groupThreshold": 1,+   "groups": [+    [+     4,+     4+    ]+   ]+  },+  "identifier": 100,+  "extendable": true,+  "iterationExponent": 0,+  "passphrase": "",+  "masterSecret": "30313233343536373839616263646566",+  "random": [+   {+    "shares": [],+    "digest": ""+   },+   {+    "shares": [+     "e34d5d16e5543909b2dd338dd0b8fc11",+     "60ddb24c0d605a48fcc89a42f4626e08"+    ],+    "digest": "2536c03e1df9d5b3b4cbe3ad"+   }+  ],+  "mnemonics": [+   [+    "acquire clay academic acquire dish exchange industry hormone filter dismiss coal custody example math elegant mental agency fitness jerky pumps",+    "acquire clay academic aide branch symbolic shaped royal flea forecast class skin cluster elegant software coal lend curly math glasses",+    "acquire clay academic ambition duke wrap thumb soul sack bracelet tofu seafood ranked beyond blanket loud emperor listen kind distance",+    "acquire clay academic arena depend iris process surface mild photo veteran mailman impulse display force bulge obtain campus downtown exchange"+   ]+  ]+ }+]
+ data/slip39_vectors.json view
@@ -0,0 +1,406 @@+[+  [+    "1. Valid mnemonic without sharing (128 bits)",+    [+      "duckling enlarge academic academic agency result length solution fridge kidney coal piece deal husband erode duke ajar critical decision keyboard"+    ],+    "bb54aac4b89dc868ba37d9cc21b2cece",+    "xprv9s21ZrQH143K4QViKpwKCpS2zVbz8GrZgpEchMDg6KME9HZtjfL7iThE9w5muQA4YPHKN1u5VM1w8D4pvnjxa2BmpGMfXr7hnRrRHZ93awZ"+  ],+  [+    "2. Mnemonic with invalid checksum (128 bits)",+    [+      "duckling enlarge academic academic agency result length solution fridge kidney coal piece deal husband erode duke ajar critical decision kidney"+    ],+    "",+    ""+  ],+  [+    "3. Mnemonic with invalid padding (128 bits)",+    [+      "duckling enlarge academic academic email result length solution fridge kidney coal piece deal husband erode duke ajar music cargo fitness"+    ],+    "",+    ""+  ],+  [+    "4. Basic sharing 2-of-3 (128 bits)",+    [+      "shadow pistol academic always adequate wildlife fancy gross oasis cylinder mustang wrist rescue view short owner flip making coding armed",+      "shadow pistol academic acid actress prayer class unknown daughter sweater depict flip twice unkind craft early superior advocate guest smoking"+    ],+    "b43ceb7e57a0ea8766221624d01b0864",+    "xprv9s21ZrQH143K2nNuAbfWPHBtfiSCS14XQgb3otW4pX655q58EEZeC8zmjEUwucBu9dPnxdpbZLCn57yx45RBkwJHnwHFjZK4XPJ8SyeYjYg"+  ],+  [+    "5. Basic sharing 2-of-3 (128 bits)",+    [+      "shadow pistol academic always adequate wildlife fancy gross oasis cylinder mustang wrist rescue view short owner flip making coding armed"+    ],+    "",+    ""+  ],+  [+    "6. Mnemonics with different identifiers (128 bits)",+    [+      "adequate smoking academic acid debut wine petition glen cluster slow rhyme slow simple epidemic rumor junk tracks treat olympic tolerate",+      "adequate stay academic agency agency formal party ting frequent learn upstairs remember smear leaf damage anatomy ladle market hush corner"+    ],+    "",+    ""+  ],+  [+    "7. Mnemonics with different iteration exponents (128 bits)",+    [+      "peasant leaves academic acid desert exact olympic math alive axle trial tackle drug deny decent smear dominant desert bucket remind",+      "peasant leader academic agency cultural blessing percent network envelope medal junk primary human pumps jacket fragment payroll ticket evoke voice"+    ],+    "",+    ""+  ],+  [+    "8. Mnemonics with mismatching group thresholds (128 bits)",+    [+      "liberty category beard echo animal fawn temple briefing math username various wolf aviation fancy visual holy thunder yelp helpful payment",+      "liberty category beard email beyond should fancy romp founder easel pink holy hairy romp loyalty material victim owner toxic custody",+      "liberty category academic easy being hazard crush diminish oral lizard reaction cluster force dilemma deploy force club veteran expect photo"+    ],+    "",+    ""+  ],+  [+    "9. Mnemonics with mismatching group counts (128 bits)",+    [+      "average senior academic leaf broken teacher expect surface hour capture obesity desire negative dynamic dominant pistol mineral mailman iris aide",+      "average senior academic agency curious pants blimp spew clothes slice script dress wrap firm shaft regular slavery negative theater roster"+    ],+    "",+    ""+  ],+  [+    "10. Mnemonics with greater group threshold than group counts (128 bits)",+    [+      "music husband acrobat acid artist finance center either graduate swimming object bike medical clothes station aspect spider maiden bulb welcome",+      "music husband acrobat agency advance hunting bike corner density careful material civil evil tactics remind hawk discuss hobo voice rainbow",+      "music husband beard academic black tricycle clock mayor estimate level photo episode exclude ecology papa source amazing salt verify divorce"+    ],+    "",+    ""+  ],+  [+    "11. Mnemonics with duplicate member indices (128 bits)",+    [+      "device stay academic always dive coal antenna adult black exceed stadium herald advance soldier busy dryer daughter evaluate minister laser",+      "device stay academic always dwarf afraid robin gravity crunch adjust soul branch walnut coastal dream costume scholar mortgage mountain pumps"+    ],+    "",+    ""+  ],+  [+    "12. Mnemonics with mismatching member thresholds (128 bits)",+    [+      "hour painting academic academic device formal evoke guitar random modern justice filter withdraw trouble identify mailman insect general cover oven",+      "hour painting academic agency artist again daisy capital beaver fiber much enjoy suitable symbolic identify photo editor romp float echo"+    ],+    "",+    ""+  ],+  [+    "13. Mnemonics giving an invalid digest (128 bits)",+    [+      "guilt walnut academic acid deliver remove equip listen vampire tactics nylon rhythm failure husband fatigue alive blind enemy teaspoon rebound",+      "guilt walnut academic agency brave hamster hobo declare herd taste alpha slim criminal mild arcade formal romp branch pink ambition"+    ],+    "",+    ""+  ],+  [+    "14. Insufficient number of groups (128 bits, case 1)",+    [+      "eraser senior beard romp adorn nuclear spill corner cradle style ancient family general leader ambition exchange unusual garlic promise voice"+    ],+    "",+    ""+  ],+  [+    "15. Insufficient number of groups (128 bits, case 2)",+    [+      "eraser senior decision scared cargo theory device idea deliver modify curly include pancake both news skin realize vitamins away join",+      "eraser senior decision roster beard treat identify grumpy salt index fake aviation theater cubic bike cause research dragon emphasis counter"+    ],+    "",+    ""+  ],+  [+    "16. Threshold number of groups, but insufficient number of members in one group (128 bits)",+    [+      "eraser senior decision shadow artist work morning estate greatest pipeline plan ting petition forget hormone flexible general goat admit surface",+      "eraser senior beard romp adorn nuclear spill corner cradle style ancient family general leader ambition exchange unusual garlic promise voice"+    ],+    "",+    ""+  ],+  [+    "17. Threshold number of groups and members in each group (128 bits, case 1)",+    [+      "eraser senior decision roster beard treat identify grumpy salt index fake aviation theater cubic bike cause research dragon emphasis counter",+      "eraser senior ceramic snake clay various huge numb argue hesitate auction category timber browser greatest hanger petition script leaf pickup",+      "eraser senior ceramic shaft dynamic become junior wrist silver peasant force math alto coal amazing segment yelp velvet image paces",+      "eraser senior ceramic round column hawk trust auction smug shame alive greatest sheriff living perfect corner chest sled fumes adequate",+      "eraser senior decision smug corner ruin rescue cubic angel tackle skin skunk program roster trash rumor slush angel flea amazing"+    ],+    "7c3397a292a5941682d7a4ae2d898d11",+    "xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV"+  ],+  [+    "18. Threshold number of groups and members in each group (128 bits, case 2)",+    [+      "eraser senior decision smug corner ruin rescue cubic angel tackle skin skunk program roster trash rumor slush angel flea amazing",+      "eraser senior beard romp adorn nuclear spill corner cradle style ancient family general leader ambition exchange unusual garlic promise voice",+      "eraser senior decision scared cargo theory device idea deliver modify curly include pancake both news skin realize vitamins away join"+    ],+    "7c3397a292a5941682d7a4ae2d898d11",+    "xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV"+  ],+  [+    "19. Threshold number of groups and members in each group (128 bits, case 3)",+    [+      "eraser senior beard romp adorn nuclear spill corner cradle style ancient family general leader ambition exchange unusual garlic promise voice",+      "eraser senior acrobat romp bishop medical gesture pumps secret alive ultimate quarter priest subject class dictate spew material endless market"+    ],+    "7c3397a292a5941682d7a4ae2d898d11",+    "xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV"+  ],+  [+    "20. Valid mnemonic without sharing (256 bits)",+    [+      "theory painting academic academic armed sweater year military elder discuss acne wildlife boring employer fused large satoshi bundle carbon diagnose anatomy hamster leaves tracks paces beyond phantom capital marvel lips brave detect luck"+    ],+    "989baf9dcaad5b10ca33dfd8cc75e42477025dce88ae83e75a230086a0e00e92",+    "xprv9s21ZrQH143K41mrxxMT2FpiheQ9MFNmWVK4tvX2s28KLZAhuXWskJCKVRQprq9TnjzzzEYePpt764csiCxTt22xwGPiRmUjYUUdjaut8RM"+  ],+  [+    "21. Mnemonic with invalid checksum (256 bits)",+    [+      "theory painting academic academic armed sweater year military elder discuss acne wildlife boring employer fused large satoshi bundle carbon diagnose anatomy hamster leaves tracks paces beyond phantom capital marvel lips brave detect lunar"+    ],+    "",+    ""+  ],+  [+    "22. Mnemonic with invalid padding (256 bits)",+    [+      "theory painting academic academic campus sweater year military elder discuss acne wildlife boring employer fused large satoshi bundle carbon diagnose anatomy hamster leaves tracks paces beyond phantom capital marvel lips facility obtain sister"+    ],+    "",+    ""+  ],+  [+    "23. Basic sharing 2-of-3 (256 bits)",+    [+      "humidity disease academic always aluminum jewelry energy woman receiver strategy amuse duckling lying evidence network walnut tactics forget hairy rebound impulse brother survive clothes stadium mailman rival ocean reward venture always armed unwrap",+      "humidity disease academic agency actress jacket gross physics cylinder solution fake mortgage benefit public busy prepare sharp friar change work slow purchase ruler again tricycle involve viral wireless mixture anatomy desert cargo upgrade"+    ],+    "c938b319067687e990e05e0da0ecce1278f75ff58d9853f19dcaeed5de104aae",+    "xprv9s21ZrQH143K3a4GRMgK8WnawupkwkP6gyHxRsXnMsYPTPH21fWwNcAytijtfyftqNfiaY8LgQVdBQvHZ9FBvtwdjC7LCYxjYruJFuLzyMQ"+  ],+  [+    "24. Basic sharing 2-of-3 (256 bits)",+    [+      "humidity disease academic always aluminum jewelry energy woman receiver strategy amuse duckling lying evidence network walnut tactics forget hairy rebound impulse brother survive clothes stadium mailman rival ocean reward venture always armed unwrap"+    ],+    "",+    ""+  ],+  [+    "25. Mnemonics with different identifiers (256 bits)",+    [+      "smear husband academic acid deadline scene venture distance dive overall parking bracelet elevator justice echo burning oven chest duke nylon",+      "smear isolate academic agency alpha mandate decorate burden recover guard exercise fatal force syndrome fumes thank guest drift dramatic mule"+    ],+    "",+    ""+  ],+  [+    "26. Mnemonics with different iteration exponents (256 bits)",+    [+      "finger trash academic acid average priority dish revenue academic hospital spirit western ocean fact calcium syndrome greatest plan losing dictate",+      "finger traffic academic agency building lilac deny paces subject threaten diploma eclipse window unknown health slim piece dragon focus smirk"+    ],+    "",+    ""+  ],+  [+    "27. Mnemonics with mismatching group thresholds (256 bits)",+    [+      "flavor pink beard echo depart forbid retreat become frost helpful juice unwrap reunion credit math burning spine black capital lair",+      "flavor pink beard email diet teaspoon freshman identify document rebound cricket prune headset loyalty smell emission skin often square rebound",+      "flavor pink academic easy credit cage raisin crazy closet lobe mobile become drink human tactics valuable hand capture sympathy finger"+    ],+    "",+    ""+  ],+  [+    "28. Mnemonics with mismatching group counts (256 bits)",+    [+      "column flea academic leaf debut extra surface slow timber husky lawsuit game behavior husky swimming already paper episode tricycle scroll",+      "column flea academic agency blessing garbage party software stadium verify silent umbrella therapy decorate chemical erode dramatic eclipse replace apart"+    ],+    "",+    ""+  ],+  [+    "29. Mnemonics with greater group threshold than group counts (256 bits)",+    [+      "smirk pink acrobat acid auction wireless impulse spine sprinkle fortune clogs elbow guest hush loyalty crush dictate tracks airport talent",+      "smirk pink acrobat agency dwarf emperor ajar organize legs slice harvest plastic dynamic style mobile float bulb health coding credit",+      "smirk pink beard academic alto strategy carve shame language rapids ruin smart location spray training acquire eraser endorse submit peaceful"+    ],+    "",+    ""+  ],+  [+    "30. Mnemonics with duplicate member indices (256 bits)",+    [+      "fishing recover academic always device craft trend snapshot gums skin downtown watch device sniff hour clock public maximum garlic born",+      "fishing recover academic always aircraft view software cradle fangs amazing package plastic evaluate intend penalty epidemic anatomy quarter cage apart"+    ],+    "",+    ""+  ],+  [+    "31. Mnemonics with mismatching member thresholds (256 bits)",+    [+      "evoke garden academic academic answer wolf scandal modern warmth station devote emerald market physics surface formal amazing aquatic gesture medical",+      "evoke garden academic agency deal revenue knit reunion decrease magazine flexible company goat repair alarm military facility clogs aide mandate"+    ],+    "",+    ""+  ],+  [+    "32. Mnemonics giving an invalid digest (256 bits)",+    [+      "river deal academic acid average forbid pistol peanut custody bike class aunt hairy merit valid flexible learn ajar very easel",+      "river deal academic agency camera amuse lungs numb isolate display smear piece traffic worthy year patrol crush fact fancy emission"+    ],+    "",+    ""+  ],+  [+    "33. Insufficient number of groups (256 bits, case 1)",+    [+      "wildlife deal beard romp alcohol space mild usual clothes union nuclear testify course research heat listen task location thank hospital slice smell failure fawn helpful priest ambition average recover lecture process dough stadium"+    ],+    "",+    ""+  ],+  [+    "34. Insufficient number of groups (256 bits, case 2)",+    [+      "wildlife deal decision scared acne fatal snake paces obtain election dryer dominant romp tactics railroad marvel trust helpful flip peanut theory theater photo luck install entrance taxi step oven network dictate intimate listen",+      "wildlife deal decision smug ancestor genuine move huge cubic strategy smell game costume extend swimming false desire fake traffic vegan senior twice timber submit leader payroll fraction apart exact forward pulse tidy install"+    ],+    "",+    ""+  ],+  [+    "35. Threshold number of groups, but insufficient number of members in one group (256 bits)",+    [+      "wildlife deal decision shadow analysis adjust bulb skunk muscle mandate obesity total guitar coal gravity carve slim jacket ruin rebuild ancestor numerous hour mortgage require herd maiden public ceiling pecan pickup shadow club",+      "wildlife deal beard romp alcohol space mild usual clothes union nuclear testify course research heat listen task location thank hospital slice smell failure fawn helpful priest ambition average recover lecture process dough stadium"+    ],+    "",+    ""+  ],+  [+    "36. Threshold number of groups and members in each group (256 bits, case 1)",+    [+      "wildlife deal ceramic round aluminum pitch goat racism employer miracle percent math decision episode dramatic editor lily prospect program scene rebuild display sympathy have single mustang junction relate often chemical society wits estate",+      "wildlife deal decision scared acne fatal snake paces obtain election dryer dominant romp tactics railroad marvel trust helpful flip peanut theory theater photo luck install entrance taxi step oven network dictate intimate listen",+      "wildlife deal ceramic scatter argue equip vampire together ruin reject literary rival distance aquatic agency teammate rebound false argue miracle stay again blessing peaceful unknown cover beard acid island language debris industry idle",+      "wildlife deal ceramic snake agree voter main lecture axis kitchen physics arcade velvet spine idea scroll promise platform firm sharp patrol divorce ancestor fantasy forbid goat ajar believe swimming cowboy symbolic plastic spelling",+      "wildlife deal decision shadow analysis adjust bulb skunk muscle mandate obesity total guitar coal gravity carve slim jacket ruin rebuild ancestor numerous hour mortgage require herd maiden public ceiling pecan pickup shadow club"+    ],+    "5385577c8cfc6c1a8aa0f7f10ecde0a3318493262591e78b8c14c6686167123b",+    "xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c"+  ],+  [+    "37. Threshold number of groups and members in each group (256 bits, case 2)",+    [+      "wildlife deal decision scared acne fatal snake paces obtain election dryer dominant romp tactics railroad marvel trust helpful flip peanut theory theater photo luck install entrance taxi step oven network dictate intimate listen",+      "wildlife deal beard romp alcohol space mild usual clothes union nuclear testify course research heat listen task location thank hospital slice smell failure fawn helpful priest ambition average recover lecture process dough stadium",+      "wildlife deal decision smug ancestor genuine move huge cubic strategy smell game costume extend swimming false desire fake traffic vegan senior twice timber submit leader payroll fraction apart exact forward pulse tidy install"+    ],+    "5385577c8cfc6c1a8aa0f7f10ecde0a3318493262591e78b8c14c6686167123b",+    "xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c"+  ],+  [+    "38. Threshold number of groups and members in each group (256 bits, case 3)",+    [+      "wildlife deal beard romp alcohol space mild usual clothes union nuclear testify course research heat listen task location thank hospital slice smell failure fawn helpful priest ambition average recover lecture process dough stadium",+      "wildlife deal acrobat romp anxiety axis starting require metric flexible geology game drove editor edge screw helpful have huge holy making pitch unknown carve holiday numb glasses survive already tenant adapt goat fangs"+    ],+    "5385577c8cfc6c1a8aa0f7f10ecde0a3318493262591e78b8c14c6686167123b",+    "xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c"+  ],+  [+    "39. Mnemonic with insufficient length",+    [+      "junk necklace academic academic acne isolate join hesitate lunar roster dough calcium chemical ladybug amount mobile glasses verify cylinder"+    ],+    "",+    ""+  ],+  [+    "40. Mnemonic with invalid master secret length",+    [+      "fraction necklace academic academic award teammate mouse regular testify coding building member verdict purchase blind camera duration email prepare spirit quarter"+    ],+    "",+    ""+  ],+  [+    "41. Valid mnemonics which can detect some errors in modular arithmetic",+    [+      "herald flea academic cage avoid space trend estate dryer hairy evoke eyebrow improve airline artwork garlic premium duration prevent oven",+      "herald flea academic client blue skunk class goat luxury deny presence impulse graduate clay join blanket bulge survive dish necklace",+      "herald flea academic acne advance fused brother frozen broken game ranked ajar already believe check install theory angry exercise adult"+    ],+    "ad6f2ad8b59bbbaa01369b9006208d9a",+    "xprv9s21ZrQH143K2R4HJxcG1eUsudvHM753BZ9vaGkpYCoeEhCQx147C5qEcupPHxcXYfdYMwJmsKXrHDhtEwutxTTvFzdDCZVQwHneeQH8ioH"+  ],+  [+    "42. Valid extendable mnemonic without sharing (128 bits)",+    [+      "testify swimming academic academic column loyalty smear include exotic bedroom exotic wrist lobe cover grief golden smart junior estimate learn"+    ],+    "1679b4516e0ee5954351d288a838f45e",+    "xprv9s21ZrQH143K2w6eTpQnB73CU8Qrhg6gN3D66Jr16n5uorwoV7CwxQ5DofRPyok5DyRg4Q3BfHfCgJFk3boNRPPt1vEW1ENj2QckzVLQFXu"+  ],+  [+    "43. Extendable basic sharing 2-of-3 (128 bits)",+    [+      "enemy favorite academic acid cowboy phrase havoc level response walnut budget painting inside trash adjust froth kitchen learn tidy punish",+      "enemy favorite academic always academic sniff script carpet romp kind promise scatter center unfair training emphasis evening belong fake enforce"+    ],+    "48b1a4b80b8c209ad42c33672bdaa428",+    "xprv9s21ZrQH143K4FS1qQdXYAFVAHiSAnjj21YAKGh2CqUPJ2yQhMmYGT4e5a2tyGLiVsRgTEvajXkxhg92zJ8zmWZas9LguQWz7WZShfJg6RS"+  ],+  [+    "44. Valid extendable mnemonic without sharing (256 bits)",+    [+      "impulse calcium academic academic alcohol sugar lyrics pajamas column facility finance tension extend space birthday rainbow swimming purple syndrome facility trial warn duration snapshot shadow hormone rhyme public spine counter easy hawk album"+    ],+    "8340611602fe91af634a5f4608377b5235fa2d757c51d720c0c7656249a3035f",+    "xprv9s21ZrQH143K2yJ7S8bXMiGqp1fySH8RLeFQKQmqfmmLTRwWmAYkpUcWz6M42oGoFMJRENmvsGQmunWTdizsi8v8fku8gpbVvYSiCYJTF1Y"+  ],+  [+    "45. Extendable basic sharing 2-of-3 (256 bits)",+    [+      "western apart academic always artist resident briefing sugar woman oven coding club ajar merit pecan answer prisoner artist fraction amount desktop mild false necklace muscle photo wealthy alpha category unwrap spew losing making",+      "western apart academic acid answer ancient auction flip image penalty oasis beaver multiple thunder problem switch alive heat inherit superior teaspoon explain blanket pencil numb lend punish endless aunt garlic humidity kidney observe"+    ],+    "8dc652d6d6cd370d8c963141f6d79ba440300f25c467302c1d966bff8f62300d",+    "xprv9s21ZrQH143K2eFW2zmu3aayWWd6MJZBG7RebW35fiKcoCZ6jFi6U5gzffB9McDdiKTecUtRqJH9GzueCXiQK1LaQXdgthS8DgWfC8Uu3z7"+  ]+]
+ data/slip39_wordlist.txt view
@@ -0,0 +1,1024 @@+academic+acid+acne+acquire+acrobat+activity+actress+adapt+adequate+adjust+admit+adorn+adult+advance+advocate+afraid+again+agency+agree+aide+aircraft+airline+airport+ajar+alarm+album+alcohol+alien+alive+alpha+already+alto+aluminum+always+amazing+ambition+amount+amuse+analysis+anatomy+ancestor+ancient+angel+angry+animal+answer+antenna+anxiety+apart+aquatic+arcade+arena+argue+armed+artist+artwork+aspect+auction+august+aunt+average+aviation+avoid+award+away+axis+axle+beam+beard+beaver+become+bedroom+behavior+being+believe+belong+benefit+best+beyond+bike+biology+birthday+bishop+black+blanket+blessing+blimp+blind+blue+body+bolt+boring+born+both+boundary+bracelet+branch+brave+breathe+briefing+broken+brother+browser+bucket+budget+building+bulb+bulge+bumpy+bundle+burden+burning+busy+buyer+cage+calcium+camera+campus+canyon+capacity+capital+capture+carbon+cards+careful+cargo+carpet+carve+category+cause+ceiling+center+ceramic+champion+change+charity+check+chemical+chest+chew+chubby+cinema+civil+class+clay+cleanup+client+climate+clinic+clock+clogs+closet+clothes+club+cluster+coal+coastal+coding+column+company+corner+costume+counter+course+cover+cowboy+cradle+craft+crazy+credit+cricket+criminal+crisis+critical+crowd+crucial+crunch+crush+crystal+cubic+cultural+curious+curly+custody+cylinder+daisy+damage+dance+darkness+database+daughter+deadline+deal+debris+debut+decent+decision+declare+decorate+decrease+deliver+demand+density+deny+depart+depend+depict+deploy+describe+desert+desire+desktop+destroy+detailed+detect+device+devote+diagnose+dictate+diet+dilemma+diminish+dining+diploma+disaster+discuss+disease+dish+dismiss+display+distance+dive+divorce+document+domain+domestic+dominant+dough+downtown+dragon+dramatic+dream+dress+drift+drink+drove+drug+dryer+duckling+duke+duration+dwarf+dynamic+early+earth+easel+easy+echo+eclipse+ecology+edge+editor+educate+either+elbow+elder+election+elegant+element+elephant+elevator+elite+else+email+emerald+emission+emperor+emphasis+employer+empty+ending+endless+endorse+enemy+energy+enforce+engage+enjoy+enlarge+entrance+envelope+envy+epidemic+episode+equation+equip+eraser+erode+escape+estate+estimate+evaluate+evening+evidence+evil+evoke+exact+example+exceed+exchange+exclude+excuse+execute+exercise+exhaust+exotic+expand+expect+explain+express+extend+extra+eyebrow+facility+fact+failure+faint+fake+false+family+famous+fancy+fangs+fantasy+fatal+fatigue+favorite+fawn+fiber+fiction+filter+finance+findings+finger+firefly+firm+fiscal+fishing+fitness+flame+flash+flavor+flea+flexible+flip+float+floral+fluff+focus+forbid+force+forecast+forget+formal+fortune+forward+founder+fraction+fragment+frequent+freshman+friar+fridge+friendly+frost+froth+frozen+fumes+funding+furl+fused+galaxy+game+garbage+garden+garlic+gasoline+gather+general+genius+genre+genuine+geology+gesture+glad+glance+glasses+glen+glimpse+goat+golden+graduate+grant+grasp+gravity+gray+greatest+grief+grill+grin+grocery+gross+group+grownup+grumpy+guard+guest+guilt+guitar+gums+hairy+hamster+hand+hanger+harvest+have+havoc+hawk+hazard+headset+health+hearing+heat+helpful+herald+herd+hesitate+hobo+holiday+holy+home+hormone+hospital+hour+huge+human+humidity+hunting+husband+hush+husky+hybrid+idea+identify+idle+image+impact+imply+improve+impulse+include+income+increase+index+indicate+industry+infant+inform+inherit+injury+inmate+insect+inside+install+intend+intimate+invasion+involve+iris+island+isolate+item+ivory+jacket+jerky+jewelry+join+judicial+juice+jump+junction+junior+junk+jury+justice+kernel+keyboard+kidney+kind+kitchen+knife+knit+laden+ladle+ladybug+lair+lamp+language+large+laser+laundry+lawsuit+leader+leaf+learn+leaves+lecture+legal+legend+legs+lend+length+level+liberty+library+license+lift+likely+lilac+lily+lips+liquid+listen+literary+living+lizard+loan+lobe+location+losing+loud+loyalty+luck+lunar+lunch+lungs+luxury+lying+lyrics+machine+magazine+maiden+mailman+main+makeup+making+mama+manager+mandate+mansion+manual+marathon+march+market+marvel+mason+material+math+maximum+mayor+meaning+medal+medical+member+memory+mental+merchant+merit+method+metric+midst+mild+military+mineral+minister+miracle+mixed+mixture+mobile+modern+modify+moisture+moment+morning+mortgage+mother+mountain+mouse+move+much+mule+multiple+muscle+museum+music+mustang+nail+national+necklace+negative+nervous+network+news+nuclear+numb+numerous+nylon+oasis+obesity+object+observe+obtain+ocean+often+olympic+omit+oral+orange+orbit+order+ordinary+organize+ounce+oven+overall+owner+paces+pacific+package+paid+painting+pajamas+pancake+pants+papa+paper+parcel+parking+party+patent+patrol+payment+payroll+peaceful+peanut+peasant+pecan+penalty+pencil+percent+perfect+permit+petition+phantom+pharmacy+photo+phrase+physics+pickup+picture+piece+pile+pink+pipeline+pistol+pitch+plains+plan+plastic+platform+playoff+pleasure+plot+plunge+practice+prayer+preach+predator+pregnant+premium+prepare+presence+prevent+priest+primary+priority+prisoner+privacy+prize+problem+process+profile+program+promise+prospect+provide+prune+public+pulse+pumps+punish+puny+pupal+purchase+purple+python+quantity+quarter+quick+quiet+race+racism+radar+railroad+rainbow+raisin+random+ranked+rapids+raspy+reaction+realize+rebound+rebuild+recall+receiver+recover+regret+regular+reject+relate+remember+remind+remove+render+repair+repeat+replace+require+rescue+research+resident+response+result+retailer+retreat+reunion+revenue+review+reward+rhyme+rhythm+rich+rival+river+robin+rocky+romantic+romp+roster+round+royal+ruin+ruler+rumor+sack+safari+salary+salon+salt+satisfy+satoshi+saver+says+scandal+scared+scatter+scene+scholar+science+scout+scramble+screw+script+scroll+seafood+season+secret+security+segment+senior+shadow+shaft+shame+shaped+sharp+shelter+sheriff+short+should+shrimp+sidewalk+silent+silver+similar+simple+single+sister+skin+skunk+slap+slavery+sled+slice+slim+slow+slush+smart+smear+smell+smirk+smith+smoking+smug+snake+snapshot+sniff+society+software+soldier+solution+soul+source+space+spark+speak+species+spelling+spend+spew+spider+spill+spine+spirit+spit+spray+sprinkle+square+squeeze+stadium+staff+standard+starting+station+stay+steady+step+stick+stilt+story+strategy+strike+style+subject+submit+sugar+suitable+sunlight+superior+surface+surprise+survive+sweater+swimming+swing+switch+symbolic+sympathy+syndrome+system+tackle+tactics+tadpole+talent+task+taste+taught+taxi+teacher+teammate+teaspoon+temple+tenant+tendency+tension+terminal+testify+texture+thank+that+theater+theory+therapy+thorn+threaten+thumb+thunder+ticket+tidy+timber+timely+ting+tofu+together+tolerate+total+toxic+tracks+traffic+training+transfer+trash+traveler+treat+trend+trial+tricycle+trip+triumph+trouble+true+trust+twice+twin+type+typical+ugly+ultimate+umbrella+uncover+undergo+unfair+unfold+unhappy+union+universe+unkind+unknown+unusual+unwrap+upgrade+upstairs+username+usher+usual+valid+valuable+vampire+vanish+various+vegan+velvet+venture+verdict+verify+very+veteran+vexed+victim+video+view+vintage+violence+viral+visitor+visual+vitamins+vocal+voice+volume+voter+voting+walnut+warmth+warn+watch+wavy+wealthy+weapon+webcam+welcome+welfare+western+width+wildlife+window+wine+wireless+wisdom+withdraw+wits+wolf+woman+work+worthy+wrap+wrist+writing+wrote+year+yelp+yield+yoga+zero
+ data/taproot_mainnet_spends.json view
@@ -0,0 +1,295 @@+{+  "description": "Taproot script path spends from Bitcoin mainnet blocks 840000 and 900000. Each entry is one input: the transaction id, the input index, the height of the block that holds the transaction, the output script of the coin it spends, and its witness items. The scripts are 34 to 635 bytes long, so their lengths cross the CompactSize boundaries at 128 (where a base-128 varint differs) and at 253.",+  "source": "Witnesses from the raw blocks 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5 (840000) and 000000000000000000010538edbfd2d5b809a33dd83f284aeea41c6d0d96968a (900000). Spent output scripts from the transaction API of blockstream.info, fetched on 2026-11-05.",+  "spends": [+    {+      "txid": "23913fd423afba15d42f85c38bf505608c10401afeb7264aec4925dfa53be807",+      "input": 0,+      "height": 840000,+      "scriptBytes": 57,+      "spentScript": "512060e41c6e17335a21dd76fc3127c4afdd9a7e006972cb3b2b3123b5b54cc7db36",+      "witness": [+        "f9884962f3fa775498074471f79718355434e516d84f28832021c0c5ea71498c35a2b27a25d7a9e3ac4f6158ee9bc740ee3da5c7cd322805befac822c062aab6",+        "046fff956c6627148a4d59222e77d151e44ed82ad6ac8323314cffb7f116846a",+        "a91407c7a31cb91bc40a3c6a49d6ce76ae7849d27dc08820cb1fdeffba50230a4afde2863a48b4e924758e3730065c944c59bda01492f88aac",+        "c055e3697e2c977fa80cd04be6abdd9c8238c8abd9428f5565bc72bf2b53325bf26ac19662386e822838c99160225a0a01c6b0eccc2034080038a614f52c1d7034"+      ]+    },+    {+      "txid": "05f8584cf4dbe34ef677f8f316fcac9e6e4ccb0e298d53fd21edaac7787660ee",+      "input": 0,+      "height": 840000,+      "scriptBytes": 34,+      "spentScript": "512003642f6114a2e45208c4f245abb5b6aaa9edbb6dce55454cd30ab9ab56a9a066",+      "witness": [+        "06f31eccce157d7aa4788d7b9238673ec777ca7b658a571532c9a99a4159a48bc0ac85ca32ca69ad87690d17acd74bb1b6cf490bce9fb552c5c0acd62e6c352b",+        "0063036f7264510a746578742f706c61696e000d3834303030302e6269746d617068",+        "c1c0918ac338897c6f8f3b723c05c629dff665a1d75a51da24bb79ea0f4bf597e5"+      ]+    },+    {+      "txid": "0984c007f1b479fd924a548821f3784d1bc13422138813ed2f27ad7e289a91f1",+      "input": 0,+      "height": 840000,+      "scriptBytes": 34,+      "spentScript": "5120fa6d72387476873a4e0b8f231357b9f41799e0d069e5b4358e36f7225baab849",+      "witness": [+        "e9808f80331b2d90cec7a6b38623720178b1d2131cb49af2a0950110dad87a8fd8f60d9f9cd5f7abda4501cd33187a712f3d15fe52802fc84199cce19a3394f6",+        "0063036f7264510a746578742f706c61696e000d3833393939352e6269746d617068",+        "c0c72952c171396617fbb3a4e0dbb8f4e92eb5835738b3ba0a4c6f0c11ef0c920b"+      ]+    },+    {+      "txid": "0a10edc679a549b4356944c829ffc6b32f058648c068b14d3cc977b6b2e27fee",+      "input": 0,+      "height": 840000,+      "scriptBytes": 34,+      "spentScript": "512003642cfc8d45ee49ead7ea6c975ede46260e9c4bb4063dfc0cb9e66f72bff0ab",+      "witness": [+        "fe51921f67ac2f30f09787f57bee60a82c927bf6b1e775ab025dfa295265ce985a3cd576b020e833faafee2f54aba126519b07bde0a518404731986b6128f857",+        "0063036f7264510a746578742f706c61696e000d3834303030302e6269746d617068",+        "c1c72952c171396617fbb3a4e0dbb8f4e92eb5835738b3ba0a4c6f0c11ef0c920b"+      ]+    },+    {+      "txid": "101cb8945486ec07bbeb42ce266f70e10307d564fd4ecff95fd10b223cb53fc7",+      "input": 0,+      "height": 840000,+      "scriptBytes": 34,+      "spentScript": "512055533cb5225d5799eea47d606d65bd399e37af39584ac54447b9ae35565da769",+      "witness": [+        "ddcea277c64630898efacfa97b0a37777f74e555e2fd21e312e943b6386161ca3a575f6141d8811a2aaf9091bccfb2b5b9c79721c6e61928a6e4161653218d79",+        "0063036f7264510a746578742f706c61696e000d3833393939392e6269746d617068",+        "c0c72952c171396617fbb3a4e0dbb8f4e92eb5835738b3ba0a4c6f0c11ef0c920b"+      ]+    },+    {+      "txid": "15c735a453523784efd266c7fa4e39d927a15049ab2071836c83be7b4765896f",+      "input": 0,+      "height": 840000,+      "scriptBytes": 34,+      "spentScript": "51202683b1d252fc867a720e7273e956b533fc20001af36bcb402dce94e12604aeaa",+      "witness": [+        "51f3e8b6e04e5526cb94a9578e31adcf0f0b723162e6b923c9fed3abf84117c3a08e0036a644f7e292640ee1f87a10916bcc595b8c10606b6c41e45b81f0c305",+        "0063036f7264510a746578742f706c61696e000d3833393939342e6269746d617068",+        "c1c72952c171396617fbb3a4e0dbb8f4e92eb5835738b3ba0a4c6f0c11ef0c920b"+      ]+    },+    {+      "txid": "4f4d8766cf284dd9c7cd18a0dde96c110106ce7ea426b9c09b87621e1489f150",+      "input": 0,+      "height": 900000,+      "scriptBytes": 128,+      "spentScript": "51201bc5020f194da1886b5c8a4f805b612840dcc9136dfcaee05111c2fe17d8f7c7",+      "witness": [+        "b828878ff7444245a41d7500940085a449832ca1b8637bf844488eb98549ff9a43406a9f416331c2f155d7d5ea02b325cd73e6a85e16266814c765b9bb12649b",+        "208cdb7108c1fe1e3b8cf5b31fabd97de90e24b1b04df207c9961ca27555177dc7ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38003a7b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a2269726f6e222c22616d74223a223130303030227d68",+        "c08cdb7108c1fe1e3b8cf5b31fabd97de90e24b1b04df207c9961ca27555177dc7"+      ]+    },+    {+      "txid": "772e2d998c73b2396f93e9c025e5918e73486a087ea42a07d89323c9c3fca774",+      "input": 1,+      "height": 840000,+      "scriptBytes": 128,+      "spentScript": "5120c357730f446f102aa3e848ce74e530aa27d1210bbbcecbf1a419445d34025739",+      "witness": [+        "ac6cabd922f3e858735448d5dd705211b79335989ab0d92302c0d8394f0a1da0079f91be69fe8a4d384307ff908ad9db875fd9031a7565b76a151a0e3f6be521",+        "2081142c852bf9a7cd2c5511c335e81367acdd102f5d37e36cd3c518b89127f7b1ac0063036f726401032181763d607e7599dde732fa2c3e7b53ff4c3d097ce89287fc276d8dfe9e88649202010b203e8870d05b6db4cbd949061307f3d0e47366a24c8f60bade59071df05e5b68f80102027803010d08f4b4f0f157e9d21868",+        "c081142c852bf9a7cd2c5511c335e81367acdd102f5d37e36cd3c518b89127f7b1"+      ]+    },+    {+      "txid": "bce9468d2d82ce15939638bbcde8a4c4b8fb9282d46b0abf223b7edd9ff97dcc",+      "input": 0,+      "height": 900000,+      "scriptBytes": 128,+      "spentScript": "5120835979691c2b98abc0a6487c982f747cbff98a63b385f60437ed5c24a7458522",+      "witness": [+        "23b6065ffd6b40ed591dd477fc151bc2f8a1484aeb6654ee9e1cf62f7f987e62e03237c1e3f54e4886527fca136b738a30f7c2fe3efbb50e55dfe3b4bf00f8ce",+        "20739e8e89dfb84872edcb0931728e19622f3476a2d38b240bc88c4b977b273b7cac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38003a7b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226d616f74222c22616d74223a223335393736227d68",+        "c1739e8e89dfb84872edcb0931728e19622f3476a2d38b240bc88c4b977b273b7c"+      ]+    },+    {+      "txid": "c2d8fb1feb8436c4a23ab04522510c6a54f3f8518cec2ff17a6143398bd46e92",+      "input": 0,+      "height": 900000,+      "scriptBytes": 128,+      "spentScript": "51209e231554cba4aa23d53a5323c0ae25de7c37e6473e65669198a5b4cd4c70ad40",+      "witness": [+        "1af798ea16606550f3e8452009dc1c2dadc607a1e1af1f1c8ef0531f530235de1df299f680d5150f69b614c3740f9d6d11b935c9bbe0ea5deb6374a66e40ab97",+        "20258bc1a90a3ed6da478acf8db6bc2d852566ece2399127c725bb9835c80560ceac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38003a7b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a22576c7a78222c22616d74223a22302e313438227d68",+        "c1258bc1a90a3ed6da478acf8db6bc2d852566ece2399127c725bb9835c80560ce"+      ]+    },+    {+      "txid": "e2cc60f881e3f705b5353838708573d7f22acff5c807d8027854a9219c5b094d",+      "input": 1,+      "height": 840000,+      "scriptBytes": 128,+      "spentScript": "5120fea2e969a623d23fc098a749a8ef13c19b0477448c284961dac63b671e57b9e8",+      "witness": [+        "80103dd98ed04f71a4c92030a503825203dd45f5c9f8389f2448aec2c052658fb2ab94cf15784a42e7bf0dcd34f1439c1ff494e465f9b5fc37bd8e76fe0864ad",+        "203323e19281856c11aca6d4e3089b65297dbcb115c45ac32261137e27b78e14e6ac0063036f7264010320633b1a3000e6147c66257a6c4c0279e10ee5bbf1e7838d859feb3172e88307a8010b20633b1a3000e6147c66257a6c4c0279e10ee5bbf1e7838d859feb3172e88307a80102021027010d096b025e08a74a58880268",+        "c13323e19281856c11aca6d4e3089b65297dbcb115c45ac32261137e27b78e14e6"+      ]+    },+    {+      "txid": "7b66f3ca78ae7a5f7d787f8ede379f8d1cd380bda75363c1f5c9dcfcceec31f5",+      "input": 1,+      "height": 840000,+      "scriptBytes": 129,+      "spentScript": "5120a629f7f6867af8e3d8694c7e25ec2b781e56cbf1cf65f038de15fe34c83a0e09",+      "witness": [+        "251d46d96914b732154ca9c1278529e3ca14714770e6a07a2c6d5f5b9d51f9b6e63101a2c875d42a8446fa2f66aac279e25fad797447d670eb7cc111c9e06b0d",+        "20ed247313597e5eadc95a09e6c34643fd4f6edd4dad046cc155d5609c05a81611ac0063036f726401032181763d607e7599dde732fa2c3e7b53ff4c3d097ce89287fc276d8dfe9e88649201010b2064ec82c41727c6dba0d9d61559436a657ae62cdb94462056c297331e868004d40102027803010d09c4102abce829ea8c4168",+        "c0ed247313597e5eadc95a09e6c34643fd4f6edd4dad046cc155d5609c05a81611"+      ]+    },+    {+      "txid": "549e90d115440b53f6153819a271c19252a407b1dedb5a36873cd3047045cf71",+      "input": 0,+      "height": 840000,+      "scriptBytes": 257,+      "spentScript": "51201ddee580cced8b45fb9ad0f9bf026307b9fa187860fb7555460a2ed09c262d81",+      "witness": [+        "2d9249a3dc373f039f8d601ddcd7fb43178c9dce9b2f2803253f8f423a58c92e84ee2e242153d0dd0b98bac274badf49abb8da11cec9bb601b04ca070cd0ba2e",+        "201c3c38c774d0a1bce82aa81acfb2b01cdad69cac59c717ce0ff70399913c8edfac0063036f7264010b20f12c8d3d725d8a2ea0c6cb42ca32fc026e761ac442e2410f46f27cad2688624b01020001054c97a2657469746c6574424954434f494e2057495a415244532052554e456b6465736372697074696f6e786d424954434f494ee280a257495a4152445320697320612072756e65207468617420656d6572676564206173206120686f6d61676520746f207468652069636f6e6963204d6167696320496e7465726e6574204d6f6e6579206d656d6520627920406d6176656e73626f742e200a010d08aa9e34cf3288cf51680063036f7264010202390568",+        "c01c3c38c774d0a1bce82aa81acfb2b01cdad69cac59c717ce0ff70399913c8edf"+      ]+    },+    {+      "txid": "e973a2c700b9db24e0412e93c462a4608668257449f2a5295024789c5aeb5d56",+      "input": 0,+      "height": 840000,+      "scriptBytes": 299,+      "spentScript": "51200bf46fa241831781abdc0cab7fa6c16617d90246b2a9379d591b5f18fcbc5cdb",+      "witness": [+        "5109c3ccd95ece9ec66db9922b546f21a0cdee8d394cd0379be19a128fada9711b6dd2fd4e0e3b6ddd9db2a5a42c8d494e1bc788cf0a69f1927cc069679fe15c",+        "201b08b979750e5aa590fc1550cd275cea759f66e2ef56d2dfbfd5fbbb0e97fb84ac0063036f7264010b2095aea7f0e278f3d7baceb6191fb0d1ac920a6f09ee4d4cb7a88f78a578ff0d3e01020001054ccda16a626974466c6f7765727378bf54686520666972737420426974636f696e20476f6f64732c2061206d696c6573746f6e6520666f7220746865204247502070726f746f636f6c2e2054696d656c65737320466f7274756e652c20466f72676564207769746820426974636f696e2c20466f726576657220426c6f6f6d696e672e2054686520556c74696d617465204769667420457870657269656e63652e536f6c656c792061696d696e6720746f20747265617420796f7520657863657074696f6e616c6c792077656c6c2e010d083b26ccb5c1fbd05168",+        "c01b08b979750e5aa590fc1550cd275cea759f66e2ef56d2dfbfd5fbbb0e97fb84"+      ]+    },+    {+      "txid": "3257291b92c8584dd9b3046d5c6ec0491f708c1459926a237e6ed3fa69564a74",+      "input": 0,+      "height": 840000,+      "scriptBytes": 309,+      "spentScript": "5120f126d35d3aaf88df1052baf48aa525e5d660ae3c50ba8ee7994ce68a20c01cd6",+      "witness": [+        "6e6ae8c7ef99a2ba25c7a8f9b562d56efb0d3c4805d528f899db9f335ece1dff7b8c34f2e24bea06db88a700a2c4c9fe2f78c05378fb50611fe1871092954082",+        "20ec6ee972865a84f7b96186c3486ed99ef0691879cedb8c3713a3ffb4b8fdcab3ac0063036f7264010109696d6167652f676966010b205982ec543d7f10926435eff1816daf297a491dccf794d5fea991bb62b9c4118901020001054cc6a767426974636f696e7077696c6c206e6576657220676f6e6e61704e6576657220676f6e6e61206769766566796f752075706f4e6576657220676f6e6e61206c657468796f7520646f776e764e6576657220676f6e6e612072756e2061726f756e646e616e642064657365727420796f75704e6576657220676f6e6e61206d616b6567796f75206372796f4e6576657220676f6e6e612073617967676f6f64627965764e6576657220676f6e6e612074656c6c2061206c69656c616e64206875727420796f75010d0c2d05ebcb654a9a2a14e15a010068",+        "c1ec6ee972865a84f7b96186c3486ed99ef0691879cedb8c3713a3ffb4b8fdcab3"+      ]+    },+    {+      "txid": "aadf4138a594d388c8412a1fa41f2e5848c084f5b561366eb6394230733f884e",+      "input": 0,+      "height": 840000,+      "scriptBytes": 324,+      "spentScript": "51201bc4bd561b0d28535536bf0e0869fe8dc73afd43ce82d1d7015b765efcc03866",+      "witness": [+        "ad0b2e5a8493f07e9d0003e47e815faeef614f2ba35694332d9264fe8ecf2ddc8f22323bc0369d6130057794965f174be5a5b2867d243e8bedad5e12ce2ccf97",+        "202e8936b0bd8a7eb08e9978c5a28ef1138ce21450e286b8707878297ad6b868fdac0063036f7264010117746578742f68746d6c3b636861727365743d7574662d38004cfe3c6d65746120636f6e74656e743d2277696474683d6465766963652d77696474682c6d696e696d756d2d7363616c653d302e31226e616d653d76696577706f72743e3c626f6479207374796c653d6d617267696e3a303b6865696768743a313030253e3c696d67207372633d2f636f6e74656e742f653734653338643733323464323362313833663635633062396235613637636232363632653739303164393838353835376638316234616535353938323430306930207374796c653d646973706c61793a626c6f636b3b6d617267696e3a6175746f3b696d6167652d72656e646572696e673a706978656c617465643b6865696768743a313030253e68",+        "c12e8936b0bd8a7eb08e9978c5a28ef1138ce21450e286b8707878297ad6b868fd"+      ]+    },+    {+      "txid": "d719670e98d4cd52c20dd712f9bf622ad2f0e22e683bf4f9743c4ef12fd47be5",+      "input": 0,+      "height": 840000,+      "scriptBytes": 324,+      "spentScript": "5120b700d9432c285f7230b942c651c3a264fd75e28b587a049a7a801327265ece89",+      "witness": [+        "78ef4664f8f45b327e2ae1fe042c696cd17c3a86aee80a81ab986cf31a11f9eb4764388e710814ae8e90ccfba350721fc08d896fa303178473458e826ee68fde",+        "206734d8bb4b3df36b54dceeb3ceb96f77b5d5dea8c1771130a66486fe4a2738d7ac0063036f7264010109696d6167652f706e67004d0b0189504e470d0a1a0a0000000d4948445200000018000000180806000000e0773df8000000d24944415478daed943b0ec3200c86197b809e84b997e8c0c8de6364e9d8c374e42cbd0417885211c9c898609b40b722fd8aacc4fefc2036e67f269e0d697e60e75cd64c5011986a14c2061f85a8829f85b4fa5de92ca470fabc97a6e8b75d8018a35a3d80222308605f6b56b2576bb300003e21844504e0d66800e0e7bddf011ce41020490ba86e0d37603c687cabb859a800f41d05a42a86003faf40020c5540ffe4de0a2a0897f5f37e65d7480ba4025008f8241ba49a05d71e10aee471bbec767a0e01e816c5994bc145c0c10a375cefbffe1b9a6b9faa1d040000000049454e44ae42608268",+        "c16734d8bb4b3df36b54dceeb3ceb96f77b5d5dea8c1771130a66486fe4a2738d7"+      ]+    },+    {+      "txid": "e83b52838da0fb8a43248d5a6bac0e5f92a3828ed06fc67fd3f424c105e7c0c5",+      "input": 0,+      "height": 840000,+      "scriptBytes": 328,+      "spentScript": "5120741b3b5468dd9f46d763789af490ec8803613ad10ed0d1cb24dc632c1003b9a8",+      "witness": [+        "e73f5f16726c5d745e436d67557b6932abfaf687c0964512d5686de94ddc2ea7262a547274da9b698adb94ce9614070fe9e1765f0da97f17a3b955a0663873f6",+        "20515d63619a4fa4effa6e40b529ffe5c9ffc7fbf9f59986fa9bf0f3065b5a3598ac0063036f7264010109696d6167652f706e67004d0f0189504e470d0a1a0a0000000d4948445200000018000000180806000000e0773df8000000017352474200aece1ce9000000c949444154484b63dcf077db7f061a02c6510b0885ee6810610d21d90be20c8f0d5e82e5080651a248348621f3df2cc519f420c3610064095e0b40866f7ff50ac3304f3131067c96206bc069012ec3619af159425410516201411f7c4d542394bcc1f2dcf36f911fc9301fdc4d7e08374479ae3c03321f9705047d0052404910d13c0e28b200947ab0015cc996e464aab3b40bc37cd9c21e8644052eb8f845311d86dc6df9f87332be4c46531f1093d1884a45b852122c0e282e2ab059404a1904f3054985ddc8b40000d940ca49ee9c43100000000049454e44ae42608268",+        "c0515d63619a4fa4effa6e40b529ffe5c9ffc7fbf9f59986fa9bf0f3065b5a3598"+      ]+    },+    {+      "txid": "892d79b89349afbd9744ececd2efdf839441ea579fa78d0084b4d1d4865b4d90",+      "input": 0,+      "height": 840000,+      "scriptBytes": 562,+      "spentScript": "51205b7c6fe4be270de2b9b66f0436bfa690faebf723781ffa349b4a6afb411505ea",+      "witness": [+        "633ef1d5bbc92a36679a06c989346ed716428f8c9f4addf5e1e2752007c936c00dc9cadbdd9415cd099e82d38b50c26f76cfb939103c737c1a297aa5b81ec566",+        "20f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65ac0063036f7264010109746578742f68746d6c004c713c736372697074207265746f7264696e616c3d3536207372633d222f636f6e74656e742f353463323038383061633264383939613135306462646235383534346164343262643861313835353537666166316432316237346664616438343761383339396930223e3c2f7363726970743e680063036f726401011e6170706c69636174696f6e2f6a736f6e3b636861727365743d7574662d38004d5d017b2270223a22766f7264222c2276223a312c227479223a22696e7363222c22636f6c223a2235313562353031623537653130306235313862386631646538316539343933303761343663633339353234643939316364396166343562383232346530653564222c22696964223a225265746f7264696e616c73222c227075626c223a2231455337623370636a527a4667796969714c7278484b546e43686535364c4a485937222c226e6f6e6365223a33382c226d696e746572223a2262633170706e3072326b787665786a72676b35776163767061706e733768396378356e36633861717a646b326a70726d7364396b337875736770756b7866222c22736967223a22484a73437074314a4d424b5933503539586437484f58576f4b33716d47657372666b38312b46526d422f386e4770456a317072762b7a6b5873424e3878554e6f564d5743446645517948473274412b564450766e71346f3d227d68",+        "c1f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65675a92934efa5e4869b2432a3e00abfc6819367af43949c2edc693b32d23ac05"+      ]+    },+    {+      "txid": "1230846b9b6d6b6df20c3907aabc486808b73f9632b98820d96c0ce0b464f5c8",+      "input": 0,+      "height": 840000,+      "scriptBytes": 564,+      "spentScript": "51202053fa063fae7fdd9bae16b4e5b1a9b3bfdecb37a4081f16f46ba64ad5fffd61",+      "witness": [+        "2258ee17dc5892894fa0ada2b0d610b66a46ba4d0478e0ac3fc011ffa6a926c20bb6369afab22c4f6a8714d98b576617031eede617f692fb319287c6dea6282b",+        "20f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65ac0063036f7264010109746578742f68746d6c004c723c736372697074207265746f7264696e616c3d363532207372633d222f636f6e74656e742f353463323038383061633264383939613135306462646235383534346164343262643861313835353537666166316432316237346664616438343761383339396930223e3c2f7363726970743e680063036f726401011e6170706c69636174696f6e2f6a736f6e3b636861727365743d7574662d38004d5e017b2270223a22766f7264222c2276223a312c227479223a22696e7363222c22636f6c223a2235313562353031623537653130306235313862386631646538316539343933303761343663633339353234643939316364396166343562383232346530653564222c22696964223a225265746f7264696e616c73222c227075626c223a2231455337623370636a527a4667796969714c7278484b546e43686535364c4a485937222c226e6f6e6365223a3533342c226d696e746572223a22626331703678636a6370737636363068683767776d6e6579683937736c6d706d6336677638646d6578686b37333675726e676772326478717a7a67636579222c22736967223a22484b61747a7649393538762f4467344d38536a4d4e796b5047365563696b79446c70614b4d503445357a574b6556492f37615a54353741476a735a78647153726c466a517935744b73454f42586f4a7878757377446d593d227d68",+        "c1f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65675a92934efa5e4869b2432a3e00abfc6819367af43949c2edc693b32d23ac05"+      ]+    },+    {+      "txid": "15026593a1d763984038102b03fe603c3b8ba7c00f37db886a392b4d1faf1cc6",+      "input": 0,+      "height": 840000,+      "scriptBytes": 564,+      "spentScript": "5120d7532ec5070903d66b713ed5ef1f27b52ace24a27ff989d0ea0c4a9a3b923db0",+      "witness": [+        "2b6681984894706428a162d7eb6ee977d688e2d739e9bd2c9456fdbc8d8844b430b37a5c787006896bf62355adc576c9588f5d6dd5902eab40d97b2fa6a54e47",+        "20f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65ac0063036f7264010109746578742f68746d6c004c723c736372697074207265746f7264696e616c3d363238207372633d222f636f6e74656e742f353463323038383061633264383939613135306462646235383534346164343262643861313835353537666166316432316237346664616438343761383339396930223e3c2f7363726970743e680063036f726401011e6170706c69636174696f6e2f6a736f6e3b636861727365743d7574662d38004d5e017b2270223a22766f7264222c2276223a312c227479223a22696e7363222c22636f6c223a2235313562353031623537653130306235313862386631646538316539343933303761343663633339353234643939316364396166343562383232346530653564222c22696964223a225265746f7264696e616c73222c227075626c223a2231455337623370636a527a4667796969714c7278484b546e43686535364c4a485937222c226e6f6e6365223a3531302c226d696e746572223a22626331707a6a68667a6e77656172767873716c786b796a73356b6635396d326139377764326c3464783874666b6b676171736461666b75737564786a6d6a222c22736967223a22473167755a334d7747765a707145347351766a68324d68686478482f444a515048635333334c514666473647472f5239436c473134355374766a626144326150394d5a74433938387744323748547949756c4e587533413d227d68",+        "c1f2d00e1cce0b839d2e197679cac7a15152ec244338032c7767f72c1332d49a65675a92934efa5e4869b2432a3e00abfc6819367af43949c2edc693b32d23ac05"+      ]+    },+    {+      "txid": "d5a7d82af0d390bc175ef349db1cb68d2e953e4ff0baf23cca484f37d3f909f9",+      "input": 0,+      "height": 840000,+      "scriptBytes": 531,+      "spentScript": "5120675033149d4e7a75c460cc0d6fb91c4c1faffa4201e06f787ef34317e7d9c5e2",+      "witness": [+        "fd9209780edecf1face42577a0d8075b92d379dc96f1a12cd4065f6213da19c5b4a77273b3aaf7ad5d3f677091b771209e6c9d89382fdc33edbb0b02b4675a34",+        "20be11c34ef16429688176b463333e09a20906c0ba7bfb28e9b18eb80a4787bfccac0063036f72645d08fe9fbf7864d36e22010109696d6167652f706e67004dd00189504e470d0a1a0a0000000d4948445200000080000000800103000000f9f0f3880000000467414d410000b18f0bfc6105000000017352474200aece1ce90000000970485973000012740000127401de661f7800000006504c5445000000000000a567b9cf0000000274524e53f801ddf1961b000001454944415448c7edd4418aa0301484e13f6491a547c851ec9bf45192a379941c21cb30486a167931ea0c0c03bd6a5a90920f1113eb895e073ff09de0dcc6754d06c7043fa033812421b5059b8454160409292f7012ea2c20099d77d8851a6ebe54260a55e284c6265448133a41e8706b218717ca61417142eb11524374f6052789733d53eaec9c8f2d62a7b93be448f3773822f50165a3840704ca7687fa861638e203fc843a6e3c3dc7fe0047fe03d21dfaff83c056ffe550b7757e0de47487b1967fc36bf9ef2d7c6df2df3ec3039a9f9f724278410db30e5750edcacfc258a50e3f2b65a5cb6e96ce6a697bcb6ec5ed58ec56ed138b64e56f63ce1a733c8ab3980394bd858d58c71792c480a8c65647d8986662c3e983380619d82dc6a803c962fc0cc05d81548070055205a2d4462075205d81a48cd715483ac72cffe2f3e737feade137d92bc6203349d4e80000000049454e44ae42608268",+        "c1be11c34ef16429688176b463333e09a20906c0ba7bfb28e9b18eb80a4787bfcc"+      ]+    },+    {+      "txid": "ec357ff474e061b1f58fe193cd30d77238a0c64c3058e4bf250830f719ab4d20",+      "input": 0,+      "height": 840000,+      "scriptBytes": 588,+      "spentScript": "512042e74a371e2bf52f07848ff7345aa2109d64af0e16ac09e826b1326c4a2451ca",+      "witness": [+        "d582c40703c3a0ff65bf2320857a8e5dfc7353777c5e933df90f60d332e6176d3de8e509bc4774ae1a04b989c64265e44865175d03e5b5f3fd3c4336a20396cb",+        "2016046b966266c4bbaa542549fe5f0a4d37493bac038100dd7698d8e8fd4f041bac0063036f72645d08231d4fc402b4250301010d696d6167652f7376672b786d6c004d05023c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f7376672220786d6c3a73706163653d227072657365727665222066696c6c3d226e6f6e6522207374796c653d2277696474683a313030253b6865696768743a31303025222076696577426f783d22302030203332203332223e3c7061746820643d224d30203068333276333248307a22207374796c653d2266696c6c3a233030303b7374726f6b652d77696474683a35302e373137353b7374726f6b652d6c696e656361703a726f756e64222f3e3c7061746820643d226d3133322e323238203130342e3432342e3531362e373935682e3331386c2d2e37383820312e32363168312e3032386c2e3738382d312e3236682e386c2d2e38393720312e3433342e38393720312e343334682d2e386c2d2e3738382d312e3236682d312e3032386c2e37383820312e3236682d2e3331386c2d2e3531362e37393668332e3139326c2e3439392d2e3736392d2e3931342d312e3436312e3931342d312e3436322d2e352d2e3736387a22207374796c653d2266696c6c3a236666663b7374726f6b652d77696474683a332e32313034383b7374726f6b652d6c696e656361703a726f756e6422207472616e73666f726d3d227472616e736c617465282d3439302e343332202d3338372e3129207363616c6528332e373739353329222f3e3c2f7376673e68",+        "c116046b966266c4bbaa542549fe5f0a4d37493bac038100dd7698d8e8fd4f041b"+      ]+    },+    {+      "txid": "02fa364c362d25d3081f65afdb8887ee00eb1ad370965c421d85cdb584521b45",+      "input": 0,+      "height": 840000,+      "scriptBytes": 635,+      "spentScript": "5120d6fcd726c160a99327c818135e7bb8bdf12e17e3d91fdf353db2e7e7c3188e23",+      "witness": [+        "b84c633db29bc13e7d27b0608b98c292c0934f770404cb936c56b4ec0be49f2ff2aef56e039832555e42fc9600e5c9e559e1685d05567bf5d8a740ef5437501a",+        "2052d184e797a4127e473b71410f01ce7a34e88dfaf4c8af5c7e055e01b287f5b5ac0063036f72645d0cc59bbde6585900d4ff67ef01010109696d6167652f706e67004d080289504e470d0a1a0a0000000d494844520000003d000000140803000000091fc5250000000373424954080808dbe14fe00000004b504c5445fffffff6f6f6fcfcfc8f8f8facacacc6c6c6e6e6e6000000c1c1c1a4a4a4ddddddb2b2b2979797eaeaead0d0d0d6d6d6121212efefef8383836a6a6af3f3f3bababa2323235555552a2a2ae1b303820000019449444154388da592eb72eb200c8405182f6082c1b7f4fd9fb40bb84e33cd999c4ef941e4b01f12d28afc75e949dcf2faa8e02d3d43867fa8547c4b031810e1451210c41bd811702df72a015814431b4189f630b7277a42188009e388c9e76d850f307bc3470896542f667991420f87e73ae7a3558e7a7b2d63919419ac9d5e61666974956012cdfa5ed08e42632ca24c9981efb4f2407cd0b60abfd3219fb4c5a65959149567567c56ee64c18386b6787af8963f3aad17162c3513f335cd2db38ee348173dded1baf062e9b7e3f9f5b2f5053f5d13d27fd15305c71f3486f6a3f8c451ba3d668e5d8a8157027ffebd767ab81b7daa38586bd1c7ee906aabccfee14a6653f9550d51f795fb8e4e03879366224a220a5ce9b4b33a559f384761086c76826665a0e626a6d3854157d1ba93ba86ce4cb4a9e3da292c018a6285243b6a3c7cbddb9f2abeefe0f96519c78f6a0f9583a664a8f91acdf4fade69a7d98da662858af7fa2b354e7bf8eaeeda9074d2fd90348dbf6e5dc5fba88a182b7af65b73c9d62750b7f2188cbee2edb117f5a5fcd3fa044cff126976b605840000000049454e44ae42608268",+        "c052d184e797a4127e473b71410f01ce7a34e88dfaf4c8af5c7e055e01b287f5b5"+      ]+    }+  ]+}
+ 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,44 +1,67 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.2.+-- This file has been generated from package.yaml by hpack version 0.39.6. -- -- see: https://github.com/sol/hpack------ hash: 9861fe70166471990a6ee8fcf42174a9d40cda5b4e28119073d56a221e78fee3  name:           haskoin-core-version:        0.18.0+version:        1.6.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 build-type:     Simple extra-source-files:+    data/block_100000.hex+    data/bip341.json+    data/bip39_vectors.json+    data/complex_psbt.json+    data/core_signed_txs.json     data/forkid_script_tests.json     data/forkid_sighash.json     data/key_io_invalid.json     data/key_io_valid.json-    data/rfc6979abc.json+    data/psbt_bip174.json+    data/psbt_bip370.json+    data/psbt_bip371.json+    data/psbt_complex_core32.json+    data/psbt_core32.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/slip39_split.json+    data/slip39_vectors.json+    data/taproot_mainnet_spends.json+    data/bip39_wordlist.txt+    data/slip39_wordlist.txt+    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: https://github.com/jprupp/haskoin-core.git  library   exposed-modules:@@ -51,17 +74,21 @@       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.Keys.Slip39       Haskoin.Crypto.Signature-      Haskoin.Keys-      Haskoin.Keys.Common-      Haskoin.Keys.Extended-      Haskoin.Keys.Mnemonic       Haskoin.Network+      Haskoin.Network.BlockFilter       Haskoin.Network.Bloom       Haskoin.Network.Common+      Haskoin.Network.CompactBlock+      Haskoin.Network.Constants+      Haskoin.Network.Data       Haskoin.Network.Message       Haskoin.Script       Haskoin.Script.Common@@ -72,8 +99,9 @@       Haskoin.Transaction.Builder.Sign       Haskoin.Transaction.Common       Haskoin.Transaction.Genesis-      Haskoin.Transaction.Partial+      Haskoin.Transaction.Psbt       Haskoin.Transaction.Segwit+      Haskoin.Transaction.Taproot       Haskoin.Util       Haskoin.Util.Arbitrary       Haskoin.Util.Arbitrary.Address@@ -85,8 +113,10 @@       Haskoin.Util.Arbitrary.Script       Haskoin.Util.Arbitrary.Transaction       Haskoin.Util.Arbitrary.Util+      Haskoin.Util.Helpers+      Haskoin.Util.Marshal   other-modules:-      Paths_haskoin_core+      Haskoin.Crypto.Keys.Extended.Internal   hs-source-dirs:       src   build-depends:@@ -94,28 +124,34 @@     , aeson >=1.4.6.0     , array >=0.5.4.0     , base >=4.9 && <5-    , base16-bytestring >=0.1.1.6+    , base16 >=2.0+    , base64 >=1.0+    , binary >=0.8.8+    , bytes >=0.17     , bytestring >=0.10.10.0-    , cereal >=0.5.8.1+    , 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     , hspec >=2.7.1-    , memory >=0.15.0     , mtl >=2.2.2     , murmur3 >=1.0.3     , network >=3.1.1.1+    , ram >=0.22.0     , safe >=0.3.18     , scientific >=0.3.6.2-    , secp256k1-haskell >=0.4.0+    , secp256k1-haskell >=2.0.0     , split >=0.2.3.3     , string-conversions >=0.4.0.1     , text >=1.2.3.0     , time >=1.9.3     , transformers >=0.5.6.2+    , unicode-data >=0.2+    , unicode-transforms >=0.4     , unordered-containers >=0.2.10.0     , vector >=0.12.1.2   default-language: Haskell2010@@ -129,13 +165,16 @@       Haskoin.AddressSpec       Haskoin.BlockSpec       Haskoin.Crypto.HashSpec+      Haskoin.Crypto.Keys.ExtendedSpec+      Haskoin.Crypto.Keys.MnemonicSpec+      Haskoin.Crypto.Keys.Slip39Spec+      Haskoin.Crypto.KeysSpec       Haskoin.Crypto.SignatureSpec-      Haskoin.Keys.ExtendedSpec-      Haskoin.Keys.MnemonicSpec-      Haskoin.KeysSpec       Haskoin.NetworkSpec       Haskoin.ScriptSpec-      Haskoin.Transaction.PartialSpec+      Haskoin.Transaction.PsbtSpec+      Haskoin.Transaction.TaprootSpec+      Haskoin.Transaction.VerdictSpec       Haskoin.TransactionSpec       Haskoin.UtilSpec       Paths_haskoin_core@@ -147,12 +186,16 @@     , aeson >=1.4.6.0     , array >=0.5.4.0     , base >=4.9 && <5-    , base16-bytestring >=0.1.1.6+    , base16 >=2.0+    , base64+    , binary >=0.8.8+    , bytes >=0.17     , bytestring >=0.10.10.0-    , cereal >=0.5.8.1+    , 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@@ -160,18 +203,20 @@     , hspec >=2.7.1     , lens >=4.18.1     , lens-aeson >=1.1-    , memory >=0.15.0     , mtl >=2.2.2     , murmur3 >=1.0.3     , network >=3.1.1.1+    , ram >=0.22.0     , safe >=0.3.18     , scientific >=0.3.6.2-    , secp256k1-haskell >=0.4.0+    , secp256k1-haskell >=2.0.0     , split >=0.2.3.3     , string-conversions >=0.4.0.1     , text >=1.2.3.0     , time >=1.9.3     , transformers >=0.5.6.2+    , unicode-data >=0.2+    , unicode-transforms >=0.4     , unordered-containers >=0.2.10.0     , vector >=0.12.1.2   default-language: Haskell2010
src/Haskoin.hs view
@@ -1,33 +1,31 @@-{-|-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+-- 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 Network-    , module Constants-    , module Block-    , module Transaction-    , module Script-    , module Keys-    , module Crypto-    , module Util-    ) where+  ( module Address,+    module Block,+    module Transaction,+    module Script,+    module Crypto,+    module Network,+    module Util,+  )+where -import           Haskoin.Address             as Address-import           Haskoin.Block               as Block-import           Haskoin.Constants           as Constants-import           Haskoin.Crypto              as Crypto-import           Haskoin.Keys                as Keys-import           Haskoin.Network             as Network-import           Haskoin.Script              as Script-import           Haskoin.Transaction         as Transaction-import           Haskoin.Util                as Util+import Haskoin.Address as Address+import Haskoin.Block as Block+import Haskoin.Crypto as Crypto+import Haskoin.Network as Network+import Haskoin.Script as Script+import Haskoin.Transaction as Transaction+import Haskoin.Util as Util
src/Haskoin/Address.hs view
@@ -1,237 +1,622 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase        #-}+{-# 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 PatternSynonyms #-}+{-# LANGUAGE NoFieldSelectors #-} -Base58, CashAddr, Bech32 address and WIF private key serialization support.--}+-- |+-- Module      : Haskoin.Address+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Base58, CashAddr and Bech32 address serialization support. module Haskoin.Address-    ( -- * Addresses-      Address(..)-    , isPubKeyAddress-    , isScriptAddress-    , isWitnessPubKeyAddress-    , isWitnessScriptAddress-    , addrToText-    , textToAddr-    , bech32ToAddr-    , cashToAddr-    , base58ToAddr-    , addrToJSON-    , addrToEncoding-    , addrFromJSON-    , pubKeyAddr-    , pubKeyWitnessAddr-    , pubKeyCompatWitnessAddr-    , p2pkhAddr-    , p2wpkhAddr-    , p2shAddr-    , p2wshAddr-    , inputAddress-    , outputAddress-    , addressToScript-    , addressToScriptBS-    , addressToOutput-    , payToScriptAddress-    , payToWitnessScriptAddress-    , payToNestedScriptAddress-    , scriptToAddress-    , scriptToAddressBS-    , module Haskoin.Address.Base58-    , module Haskoin.Address.Bech32-    , module Haskoin.Address.CashAddr-    ) where+  ( -- * Addresses+    Address+      ( PubKeyAddress,+        ScriptAddress,+        WitnessPubKeyAddress,+        WitnessScriptAddress,+        WitnessAddress,+        Script32Address,+        CashAddress+      ),+    isPubKeyAddress,+    isScriptAddress,+    isWitnessAddress,+    isWitnessPubKeyAddress,+    isWitnessScriptAddress,+    isScript32Address,+    isCashAddress,+    pubKeyAddress,+    scriptAddress,+    witnessAddress,+    witnessPubKeyAddress,+    witnessScriptAddress,+    script32Address,+    cashAddress,+    addressHash160,+    addressHash256,+    addressVersion,+    addressBytes,+    addrToText,+    addrToBase58,+    addrToBech32,+    addrToCash,+    textToAddr,+    bech32ToAddr,+    cashToAddr,+    base58ToAddr,+    pubKeyAddr,+    pubKeyWitnessAddr,+    pubKeyCompatWitnessAddr,+    p2pkhAddr,+    p2wpkhAddr,+    p2shAddr,+    p2wshAddr,+    p2shP2wpkhAddr,+    inputAddress,+    outputAddress,+    addressToScript,+    addressToScriptBS,+    addressToOutput,+    payToScriptAddress,+    payToWitnessScriptAddress,+    payToNestedScriptAddress,+    scriptToAddress,+    scriptToAddressBS,+    module Haskoin.Address.Base58,+    module Haskoin.Address.Bech32,+    module Haskoin.Address.CashAddr,+  )+where -import           Control.Applicative-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           Data.ByteString          (ByteString)-import qualified Data.ByteString          as B-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize           as S-import           Data.Text                (Text)-import           Data.Word                (Word8)-import           GHC.Generics             (Generic)-import           Haskoin.Address.Base58-import           Haskoin.Address.Bech32-import           Haskoin.Address.CashAddr-import           Haskoin.Constants-import           Haskoin.Crypto-import           Haskoin.Keys.Common-import           Haskoin.Script-import           Haskoin.Util+import Control.Applicative (Alternative ((<|>)))+import Control.DeepSeq (NFData)+import Control.Monad (guard, mfilter, when, (<=<))+import Crypto.Secp256k1+import Data.Aeson (ToJSON (toJSON), withText)+import Data.Aeson.Encoding (text)+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get (MonadGet (getByteString, getWord64be, getWord8), isEmpty, runGetS)+import Data.Bytes.Put (MonadPut (putByteString, putWord64be, putWord8), runPutS)+import Data.Bytes.Serial (Serial (..))+import Data.Hashable (Hashable)+import Data.Maybe (fromMaybe, isJust)+import Data.Serialize (Serialize (..))+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word8)+import GHC.Generics (Generic)+import GHC.Read (expectP)+import Haskoin.Address.Base58+import Haskoin.Address.Bech32+import Haskoin.Address.CashAddr+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.Standard+import Haskoin.Util+import Text.Read+  ( Lexeme (Ident, Punc),+    Read (readListPrec, readPrec),+    ReadPrec,+    lexP,+    parens,+    pfail,+    prec,+    readListPrecDefault,+    reset,+  )  -- | Address format for Bitcoin and Bitcoin Cash.+--+-- The 'WitnessAddress' and 'CashAddress' patterns can only be used to match.+-- Build those addresses with 'witnessAddress' and 'cashAddress', which check+-- the version and the payload length, and which return the dedicated+-- constructor when one exists. This keeps every address with a single+-- representation, so two addresses with the same text are always equal.+--+-- Two different addresses can still pay the same script. A token-aware+-- CashAddr (type 2 or 3) locks coins with the script of its plain form (type+-- 0 or 1), and the two are not equal. To find what an address was paid, look+-- it up by the script of 'addressToOutput', not by the address.+--+-- A value does not know its network. 'textToAddr' and the JSON parser only+-- return addresses of the network they are given. The constructors, 'Read'+-- and the binary form do not check that, so a value from one of those can be+-- of a kind its chain does not have. 'addrToText' gives 'Nothing' for such a+-- value. data Address-    -- | pay to public key hash (regular)-    = PubKeyAddress-          { getAddrHash160 :: !Hash160-          -- ^ RIPEMD160 hash of public key's SHA256 hash-          }-    -- | pay to script hash-    | ScriptAddress-          { getAddrHash160 :: !Hash160-          -- ^ RIPEMD160 hash of script's SHA256 hash-          }-    -- | pay to witness public key hash-    | WitnessPubKeyAddress-          { getAddrHash160 :: !Hash160-          -- ^ RIPEMD160 hash of public key's SHA256 hash-          }-    -- | pay to witness script hash-    | WitnessScriptAddress-          { getAddrHash256 :: !Hash256-          -- ^ HASH256 hash of script-          }-    -- | other witness address-    | WitnessAddress-         { getAddrVersion :: !Word8-         , getAddrData    :: !ByteString-         }-    deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)+  = -- | pay to public key hash (regular)+    PubKeyAddress+      { -- | RIPEMD160 hash of public key's SHA256 hash+        hash160 :: !Hash160+      }+  | -- | pay to script hash (160-bit)+    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+      }+  | -- | SegWit address with a witness version other than zero, such as+    -- Taproot. The version is between 1 and 16 and the program is between 2+    -- and 40 bytes long.+    OtherWitnessAddress+      { version :: !Word8,+        bytes :: !ByteString+      }+  | -- | pay to script hash (256-bit)+    Script32Address+      { -- | HASH256 hash of script+        hash256 :: !Hash256+      }+  | -- | CashAddr with a type or hash size that has no dedicated constructor.+    -- Token-aware addresses have type 2 (P2PKH) or 3 (P2SH). The type is+    -- below 16 and the hash has one of the sizes the standard allows.+    OtherCashAddress+      { version :: !Word8,+        bytes :: !ByteString+      }+  deriving+    (Eq, Ord, Generic, Show, Hashable, NFData) +-- | Reads the text that 'Show' writes. The two generic kinds go through+-- 'witnessAddress' and 'cashAddress', so a version or a length that those+-- refuse does not read, and neither does a value that has a dedicated+-- constructor.+instance Read Address where+  readPrec =+    parens . prec 11 $ do+      Ident name <- lexP+      expectP (Punc "{")+      addr <- case name of+        "PubKeyAddress" -> PubKeyAddress <$> field "hash160"+        "ScriptAddress" -> ScriptAddress <$> field "hash160"+        "WitnessPubKeyAddress" -> WitnessPubKeyAddress <$> field "hash160"+        "WitnessScriptAddress" -> WitnessScriptAddress <$> field "hash256"+        "Script32Address" -> Script32Address <$> field "hash256"+        "OtherWitnessAddress" -> generic isWitnessAddress witnessAddress+        "OtherCashAddress" -> generic isCashAddress cashAddress+        _ -> pfail+      expectP (Punc "}")+      return addr+    where+      field :: (Read a) => String -> ReadPrec a+      field name = do+        expectP (Ident name)+        expectP (Punc "=")+        reset readPrec+      generic isKind build = do+        v <- field "version"+        expectP (Punc ",")+        b <- field "bytes"+        maybe pfail return (mfilter isKind (build v b))+  readListPrec = readListPrecDefault++-- | Match a SegWit address with a witness version other than zero. Use+-- 'witnessAddress' to build one.+pattern WitnessAddress :: Word8 -> ByteString -> Address+pattern WitnessAddress v d <- OtherWitnessAddress v d++-- | Match a CashAddr that has no dedicated constructor, such as a token-aware+-- address. Use 'cashAddress' to build one.+pattern CashAddress :: Word8 -> ByteString -> Address+pattern CashAddress v d <- OtherCashAddress v d++{-# COMPLETE+  PubKeyAddress,+  ScriptAddress,+  WitnessPubKeyAddress,+  WitnessScriptAddress,+  WitnessAddress,+  Script32Address,+  CashAddress+  #-}++-- | Highest witness version defined by BIP-141.+maxWitnessVersion :: Word8+maxWitnessVersion = 16++-- | Witness program length limits from BIP-141.+minWitnessProgramLength, maxWitnessProgramLength :: Int+minWitnessProgramLength = 2+maxWitnessProgramLength = 40++-- | Highest CashAddr type. The type byte holds the type in four bits.+maxCashAddrType :: Word8+maxCashAddrType = 15++-- | Hash sizes that the CashAddr version byte can describe.+cashAddrHashLengths :: [Int]+cashAddrHashLengths = [20, 24, 28, 32, 40, 48, 56, 64]++-- | CashAddr types for token-aware addresses, introduced with CashTokens.+-- They lock coins with the same script as the plain P2PKH and P2SH types.+-- The type only tells the sender that the receiver can handle tokens.+tokenPubKeyType, tokenScriptType :: Word8+tokenPubKeyType = 2+tokenScriptType = 3++-- | Binary serialization for 'Address' type is not standard.+-- Do not expect other other software to understand these.+-- Use text format or output scripts for exchange.+instance Serial Address where+  serialize = \case+    PubKeyAddress k -> do+      putWord8 0x00+      serialize k+    ScriptAddress s -> do+      putWord8 0x01+      serialize s+    WitnessPubKeyAddress h -> do+      putWord8 0x02+      serialize h+    WitnessScriptAddress s -> do+      putWord8 0x03+      serialize s+    OtherWitnessAddress v d -> do+      putWord8 0x04+      putWord8 v+      putSized d+    Script32Address s -> do+      putWord8 0x05+      serialize s+    OtherCashAddress v d -> do+      putWord8 0x06+      putWord8 v+      putSized d++  deserialize =+    getWord8 >>= \case+      0x00 -> PubKeyAddress <$> deserialize+      0x01 -> ScriptAddress <$> deserialize+      0x02 -> WitnessPubKeyAddress <$> deserialize+      0x03 -> WitnessScriptAddress <$> deserialize+      0x04 -> do+        v <- getWord8+        d <- getSized maxWitnessProgramLength+        checked "SegWit" (witnessAddress v d)+      0x05 -> Script32Address <$> deserialize+      0x06 -> do+        v <- getWord8+        d <- getSized (maximum cashAddrHashLengths)+        checked "CashAddr" (cashAddress v d)+      b ->+        fail . T.unpack $+          "Could not decode address type byte: "+            <> encodeHex (B.singleton b)+    where+      checked kind = maybe (fail ("Invalid " <> kind <> " address")) return++-- | Write bytes behind their length. The length is a 64-bit word to stay+-- compatible with data serialized by earlier versions.+putSized :: (MonadPut m) => ByteString -> m ()+putSized d = do+  putWord64be (fromIntegral (B.length d))+  putByteString d++-- | Read bytes written by 'putSized'. The length is checked against a maximum+-- while it is still a 64-bit word, so a corrupt or hostile length can neither+-- overflow an 'Int' nor ask for a huge allocation.+getSized :: (MonadGet m) => Int -> m ByteString+getSized maxLen = do+  n <- getWord64be+  when (n > fromIntegral maxLen) $ fail "Address payload too long"+  getByteString (fromIntegral n)++instance Serialize Address where+  put = serialize+  get = deserialize++instance Binary Address where+  put = serialize+  get = deserialize+ -- | 'Address' pays to a public key hash. isPubKeyAddress :: Address -> Bool isPubKeyAddress PubKeyAddress {} = True-isPubKeyAddress _                = False+isPubKeyAddress _ = False --- | 'Address' pays to a script hash.+-- | 'Address' pays to a 160-bit script hash. isScriptAddress :: Address -> Bool isScriptAddress ScriptAddress {} = True-isScriptAddress _                = False+isScriptAddress _ = False  -- | 'Address' pays to a witness public key hash. Only valid for SegWit -- networks. isWitnessPubKeyAddress :: Address -> Bool isWitnessPubKeyAddress WitnessPubKeyAddress {} = True-isWitnessPubKeyAddress _                       = False+isWitnessPubKeyAddress _ = False +-- | 'Address' pays to witness script hash. SegWit only. isWitnessScriptAddress :: Address -> Bool isWitnessScriptAddress WitnessScriptAddress {} = True-isWitnessScriptAddress _                       = False+isWitnessScriptAddress _ = False +-- | 'Address' is another type of SegWit address, not covered above. isWitnessAddress :: Address -> Bool-isWitnessAddress WitnessAddress {} = True-isWitnessAddress _                 = False+isWitnessAddress OtherWitnessAddress {} = True+isWitnessAddress _ = False -addrToJSON :: Network -> Address -> Value-addrToJSON net a = toJSON (addrToText net a)+-- | 'Address' pays to a 256-bit script hash.+isScript32Address :: Address -> Bool+isScript32Address Script32Address {} = True+isScript32Address _ = False -addrToEncoding :: Network -> Address -> Encoding-addrToEncoding net = maybe null_ text . addrToText net+-- | 'Address' is another type of CashAddr, not covered above.+isCashAddress :: Address -> Bool+isCashAddress OtherCashAddress {} = True+isCashAddress _ = False --- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and--- 'Bech32'.-addrFromJSON :: Network -> Value -> Parser Address-addrFromJSON net =+-- | Smart constructor for P2PKH address.+pubKeyAddress :: Hash160 -> Address+pubKeyAddress = PubKeyAddress++-- | Smart constructor for P2SH address.+scriptAddress :: Hash160 -> Address+scriptAddress = ScriptAddress++-- | Smart constructor for P2WPKH address.+witnessPubKeyAddress :: Hash160 -> Address+witnessPubKeyAddress = WitnessPubKeyAddress++-- | Smart constructor for P2WSH address.+witnessScriptAddress :: Hash256 -> Address+witnessScriptAddress = WitnessScriptAddress++-- | Smart constructor for a SegWit address from its witness version and+-- program. Version zero programs must be 20 or 32 bytes long and become+-- 'WitnessPubKeyAddress' or 'WitnessScriptAddress'. Other versions keep the+-- program as it is, as long as it is between 2 and 40 bytes long.+witnessAddress :: Word8 -> ByteString -> Maybe Address+witnessAddress v bs = do+  guard $ v <= maxWitnessVersion+  guard $ B.length bs >= minWitnessProgramLength+  guard $ B.length bs <= maxWitnessProgramLength+  if v == 0+    then case B.length bs of+      20 -> WitnessPubKeyAddress <$> decodeHash bs+      32 -> WitnessScriptAddress <$> decodeHash bs+      _ -> Nothing+    else return $ OtherWitnessAddress v bs++-- | Smart constructor for P2SH32 address.+script32Address :: Hash256 -> Address+script32Address = Script32Address++-- | Smart constructor for a CashAddr from its type and hash. P2PKH and P2SH+-- types with a 20-byte hash become 'PubKeyAddress' and 'ScriptAddress', and+-- the P2SH type with a 32-byte hash becomes 'Script32Address'. Every other+-- combination that the standard allows is kept as it is.+cashAddress :: Word8 -> ByteString -> Maybe Address+cashAddress v b = do+  guard $ v <= maxCashAddrType+  guard $ B.length b `elem` cashAddrHashLengths+  case (v, B.length b) of+    (0, 20) -> PubKeyAddress <$> decodeHash b+    (1, 20) -> ScriptAddress <$> decodeHash b+    (1, 32) -> Script32Address <$> decodeHash b+    _ -> return $ OtherCashAddress v b++-- | Read a hash from bytes of the right length.+decodeHash :: (Serial a) => ByteString -> Maybe a+decodeHash = eitherToMaybe . runGetS deserialize++-- | The 160-bit hash an address pays to, if it pays to one. Token-aware+-- CashAddr addresses pay to the same hash as their plain counterparts.+addressHash160 :: Address -> Maybe Hash160+addressHash160 = \case+  PubKeyAddress h -> Just h+  ScriptAddress h -> Just h+  WitnessPubKeyAddress h -> Just h+  OtherCashAddress v b+    | (v == tokenPubKeyType || v == tokenScriptType) && B.length b == 20 ->+        decodeHash b+  _ -> Nothing++-- | The 256-bit hash an address pays to, if it pays to one. Token-aware+-- CashAddr addresses pay to the same hash as their plain counterparts.+addressHash256 :: Address -> Maybe Hash256+addressHash256 = \case+  WitnessScriptAddress h -> Just h+  Script32Address h -> Just h+  OtherCashAddress v b+    | v == tokenScriptType && B.length b == 32 ->+        decodeHash b+  _ -> Nothing++-- | The witness version of a SegWit address, or the CashAddr type of any+-- other address.+addressVersion :: Address -> Word8+addressVersion = \case+  PubKeyAddress _ -> 0+  ScriptAddress _ -> 1+  WitnessPubKeyAddress _ -> 0+  WitnessScriptAddress _ -> 0+  OtherWitnessAddress v _ -> v+  Script32Address _ -> 1+  OtherCashAddress v _ -> v++-- | The hash or witness program of an address, without any prefix.+addressBytes :: Address -> ByteString+addressBytes = \case+  PubKeyAddress h -> hashBytes h+  ScriptAddress h -> hashBytes h+  WitnessPubKeyAddress h -> hashBytes h+  WitnessScriptAddress h -> hashBytes h+  OtherWitnessAddress _ b -> b+  Script32Address h -> hashBytes h+  OtherCashAddress _ b -> b++-- | Raw bytes of a hash.+hashBytes :: (Serial a) => a -> ByteString+hashBytes = runPutS . serialize++-- | JSON encoding uses the text form of the address. Encoding an address that+-- has no text form on the network, such as a SegWit address on Bitcoin Cash,+-- is a programming error and throws an 'error'. A loud failure is better than+-- a silent @null@ in the output.+--+-- An address from 'outputAddress', 'scriptToAddress', 'scriptToAddressBS',+-- 'textToAddr' or the JSON parser of the same network always has a text form+-- there. One from a constructor or from the binary form may not. Check such+-- a value with 'addrToText' before encoding it.+instance MarshalJSON Network Address where+  marshalValue net = toJSON . addrTextOrError net+  marshalEncoding net = text . addrTextOrError 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.+addrTextOrError :: Network -> Address -> Text+addrTextOrError net a = fromMaybe (error msg) (addrToText net a)+  where+    msg =+      "Address " <> show a <> " has no text form on network " <> net.name++-- | Convert address to human-readable string. Uses 'CashAddr' on Bitcoin Cash+-- networks, and 'Base58' or 'Bech32' on Bitcoin networks. addrToText :: Network -> Address -> Maybe Text-addrToText net a@PubKeyAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise = cashAddrEncode net 0 (S.encode h)-addrToText net a@ScriptAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise =-        cashAddrEncode net 1 (S.encode h)-addrToText net WitnessPubKeyAddress {getAddrHash160 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))-addrToText net WitnessScriptAddress {getAddrHash256 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))-addrToText net WitnessAddress {getAddrVersion = v, getAddrData = d} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp v (B.unpack d)+addrToText net a+  | isJust net.cashAddrPrefix = addrToCash net a+  | otherwise = addrToBase58 net a <|> addrToBech32 net a +-- | Encode a P2PKH or P2SH address in the legacy 'Base58' format of the+-- network. Works on Bitcoin Cash networks too, for software that still needs+-- the legacy format there. Other address kinds have no 'Base58' form.+addrToBase58 :: Network -> Address -> Maybe Base58+addrToBase58 net = \case+  PubKeyAddress h -> Just $ base58 net.addrPrefix h+  ScriptAddress h -> Just $ base58 net.scriptPrefix h+  _ -> Nothing+  where+    base58 pfx h = encodeBase58Check . runPutS $ putWord8 pfx >> serialize h++-- | Encode a SegWit address in the 'Bech32' or 'Bech32m' format of the+-- network. Fails on networks without SegWit and for other address kinds.+addrToBech32 :: Network -> Address -> Maybe Bech32+addrToBech32 net a = do+  hrp <- net.bech32Prefix+  case a of+    WitnessPubKeyAddress h -> segwitEncode hrp 0 (B.unpack (hashBytes h))+    WitnessScriptAddress h -> segwitEncode hrp 0 (B.unpack (hashBytes h))+    OtherWitnessAddress v d -> segwitEncode hrp v (B.unpack d)+    _ -> Nothing++-- | Encode an address in the 'CashAddr' format of the network. Fails on+-- networks without CashAddr and for SegWit addresses.+addrToCash :: Network -> Address -> Maybe CashAddr+addrToCash net = \case+  PubKeyAddress h -> cashAddrEncode net 0 (hashBytes h)+  ScriptAddress h -> cashAddrEncode net 1 (hashBytes h)+  Script32Address h -> cashAddrEncode net 1 (hashBytes h)+  OtherCashAddress v b -> cashAddrEncode net v b+  _ -> Nothing+ -- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.+--+-- The text must be the address and nothing else. White space around it is+-- refused, where Bitcoin Core skips it around a 'Base58' address, so trim+-- text that comes from a user.+--+-- A result is a well-formed address of the network. It is not always one+-- that can be paid. A CashAddr of a type above 3, or with a hash that is not+-- 20 or 32 bytes long, decodes to an address that has no output script, and+-- Bitcoin Cash Node refuses it as a destination. 'addressToOutput' tells:+-- it gives 'Nothing' for those. 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 (S.decode bs)-        1 -> ScriptAddress <$> eitherToMaybe (S.decode bs)-        _ -> Nothing+cashToAddr net txt = uncurry cashAddress =<< cashAddrDecode net txt  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 (S.decode bs)-            32 -> WitnessScriptAddress <$> eitherToMaybe (S.decode bs)-            _  -> Nothing-        _ -> Just $ WitnessAddress ver bs+  hrp <- net.bech32Prefix+  (ver, prog) <- segwitDecode hrp txt+  witnessAddress ver (B.pack prog)  base58ToAddr :: Network -> Text -> Maybe Address-base58ToAddr net txt =-    eitherToMaybe . runGet (base58get net) =<< decodeBase58Check txt+base58ToAddr net txt = do+  guard $ T.length txt <= maxBase58AddressLength+  bs <- decodeBase58Check txt+  eitherToMaybe $ runGetS (base58get net) bs -base58get :: Network -> Get Address+-- | A 'Base58' address holds 25 bytes: a version byte, a 20-byte hash and a+-- 4-byte checksum. That never takes more than 35 digits, because 58^35 is+-- larger than 256^25. Longer strings are rejected before any decoding, since+-- the time to decode grows with the square of the length.+maxBase58AddressLength :: Int+maxBase58AddressLength = 35++base58get :: (MonadGet m) => Network -> m Address base58get net = do-    pfx <- getWord8-    addr <- S.get-    f pfx addr+  pfx <- getWord8+  addr <- deserialize+  isEmpty >>= \case+    True -> f pfx addr+    False -> fail "Address too long"   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 :: Network -> Putter Address-base58put net (PubKeyAddress h) = do-        putWord8 (getAddrPrefix net)-        put h-base58put net (ScriptAddress h) = do-        putWord8 (getScriptPrefix net)-        put h-base58put _ _ = error "Cannot serialize this address as Base58"+-- | HASH160 of a public key in its serialized form.+pubKeyHash :: Ctx -> PublicKey -> Hash160+pubKeyHash ctx = addressHash . marshal ctx  -- | Obtain a standard pay-to-public-key-hash address from a public key.-pubKeyAddr :: PubKeyI -> Address-pubKeyAddr = PubKeyAddress . addressHash . S.encode---- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.-p2pkhAddr :: Hash160 -> Address-p2pkhAddr = PubKeyAddress+pubKeyAddr :: Ctx -> PublicKey -> Address+pubKeyAddr ctx = p2pkhAddr . pubKeyHash ctx  -- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a--- public key.-pubKeyWitnessAddr :: PubKeyI -> Address-pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . S.encode+-- public key. Returns 'Nothing' for an uncompressed key. BIP-143 makes witness+-- spends with uncompressed keys non-standard, so coins sent to such an address+-- could not be spent through the normal network.+pubKeyWitnessAddr :: Ctx -> PublicKey -> Maybe Address+pubKeyWitnessAddr ctx pk = do+  guard pk.compress+  return . p2wpkhAddr $ pubKeyHash ctx pk  -- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.-pubKeyCompatWitnessAddr :: PubKeyI -> Address-pubKeyCompatWitnessAddr =-    p2shAddr .-    addressHash . encodeOutputBS . PayWitnessPKHash . addressHash . S.encode+-- Returns 'Nothing' for an uncompressed key, for the same reason as+-- 'pubKeyWitnessAddr'.+pubKeyCompatWitnessAddr :: Ctx -> PublicKey -> Maybe Address+pubKeyCompatWitnessAddr ctx pk = do+  guard pk.compress+  return . p2shP2wpkhAddr $ pubKeyHash ctx pk +-- | 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 -- 'Hash160'. p2wpkhAddr :: Hash160 -> Address@@ -245,65 +630,149 @@ p2wshAddr :: Hash256 -> Address p2wshAddr = WitnessScriptAddress +-- | Obtain the backwards-compatible SegWit P2SH-P2WPKH address of the+-- 'Hash160' of a compressed public key. It is the pay-to-script-hash address+-- of the witness program @OP_0@ followed by the push of the 20-byte hash,+-- which is 22 bytes long.+p2shP2wpkhAddr :: Hash160 -> Address+p2shP2wpkhAddr h =+  p2shAddr . addressHash $ B.pack [0x00, 0x14] <> hashBytes h+ -- | Compute a standard pay-to-script-hash (P2SH) address for an output script.-payToScriptAddress :: ScriptOutput -> Address-payToScriptAddress = p2shAddr . addressHash . encodeOutputBS+--+-- Fails when the redeem script is longer than 'maxRedeemScriptSize' (520)+-- bytes. Consensus limits a push to that size, and the redeem script is a+-- push of the spending input, so coins sent to such an address could never be+-- spent. A multisig of compressed keys fits up to 15 keys. Use+-- 'payToWitnessScriptAddress' or 'payToNestedScriptAddress' for more.+--+-- Also fails for a value that 'encodeOutput' cannot write, such as a multisig+-- that requires more signatures than it has keys.+payToScriptAddress :: Ctx -> ScriptOutput -> Either String Address+payToScriptAddress ctx =+  fmap p2shAddr . p2shScriptHash <=< encodeOutput ctx  -- | Compute a SegWit pay-to-witness-script-hash (P2WSH) address for an output -- script.-payToWitnessScriptAddress :: ScriptOutput -> Address-payToWitnessScriptAddress = p2wshAddr . sha256 . encodeOutputBS+--+-- Fails when the witness script is longer than 'maxWitnessScriptSize'+-- (10,000) bytes, which consensus could never execute, and for a value that+-- 'encodeOutput' cannot write. The relay policy of Bitcoin Core is stricter+-- than consensus: it does not relay the spend of a witness script above 3,600+-- bytes, nor one with an uncompressed key. This function does not check+-- either.+payToWitnessScriptAddress :: Ctx -> ScriptOutput -> Either String Address+payToWitnessScriptAddress ctx =+  fmap p2wshAddr . p2wshScriptHash <=< encodeOutput ctx --- | Compute a backwards-compatible SegWit P2SH-P2WSH address.-payToNestedScriptAddress :: ScriptOutput -> Address-payToNestedScriptAddress =-    p2shAddr . addressHash . encodeOutputBS . toP2WSH . encodeOutput+-- | Compute a backwards-compatible SegWit P2SH-P2WSH address: the+-- pay-to-script-hash address of the witness program of the script. Fails as+-- 'payToWitnessScriptAddress' does.+payToNestedScriptAddress :: Ctx -> ScriptOutput -> Either String Address+payToNestedScriptAddress ctx out = do+  h <- p2wshScriptHash =<< encodeOutput ctx out+  payToScriptAddress ctx (PayWitnessScriptHash h) --- | Encode an output script from an address. Will fail if using a--- pay-to-witness address on a non-SegWit network.-addressToOutput :: Address -> ScriptOutput+-- | Get the output script that locks coins sent to an address. Token-aware+-- CashAddr addresses use the same script as their plain counterparts. Other+-- CashAddr kinds have no known script, so the result is 'Nothing'.+--+-- The function does not know the network. It gives the script of a SegWit+-- address or of a 256-bit script hash address whatever chain the caller+-- works on. An address read with 'textToAddr' or from JSON always belongs to+-- its network. An address built with a constructor, or read from its binary+-- form, may not: check it with 'addrToText' of the network before paying it.+-- On Bitcoin, anyone who sees the redeem script can spend the script of a+-- 'Script32Address', and Bitcoin Cash does not have SegWit.+addressToOutput :: Address -> Maybe 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 -> Just (PayPKHash h)+    ScriptAddress h -> Just (PayScriptHash h)+    WitnessPubKeyAddress h -> Just (PayWitnessPKHash h)+    WitnessScriptAddress h -> Just (PayWitnessScriptHash h)+    OtherWitnessAddress v d -> Just (PayWitness v d)+    Script32Address h -> Just (PayScript32Hash h)+    OtherCashAddress v b -> cashAddressOutput v b +-- | Output script for a CashAddr kind without a dedicated constructor.+cashAddressOutput :: Word8 -> ByteString -> Maybe ScriptOutput+cashAddressOutput v b+  | v == tokenPubKeyType && len == 20 = PayPKHash <$> decodeHash b+  | v == tokenScriptType && len == 20 = PayScriptHash <$> decodeHash b+  | v == tokenScriptType && len == 32 = PayScript32Hash <$> decodeHash b+  | otherwise = Nothing+  where+    len = B.length b+ -- | Get output script AST for an 'Address'.-addressToScript :: Address -> Script-addressToScript = encodeOutput . addressToOutput+addressToScript :: Ctx -> Address -> Maybe Script+addressToScript ctx addr =+  addressToOutput addr >>= eitherToMaybe . encodeOutput ctx  -- | Encode address as output script in 'ByteString' form.-addressToScriptBS :: Address -> ByteString-addressToScriptBS = S.encode . addressToScript+addressToScriptBS :: Ctx -> Address -> Maybe ByteString+addressToScriptBS ctx addr = (runPutS . serialize) <$> addressToScript ctx addr --- | Decode an output script into an 'Address' if it has such representation.-scriptToAddress :: Script -> Either String Address-scriptToAddress =-    maybeToEither "Could not decode address" . outputAddress <=< decodeOutput+-- | Decode an output script into the 'Address' it has on a network, if it+-- has one there. See 'outputAddress'.+scriptToAddress :: Network -> Ctx -> Script -> Either String Address+scriptToAddress net ctx =+  maybeToEither e . outputAddress net 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+-- | Decode a serialized script into the 'Address' it has on a network, if it+-- has one there. See 'outputAddress'.+scriptToAddressBS :: Network -> Ctx -> ByteString -> Either String Address+scriptToAddressBS net ctx =+  maybeToEither e . outputAddress net 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+-- | Get the 'Address' that a 'ScriptOutput' has on a network.+--+-- A pay-to-public-key output has no address. Its coins are not spendable with+-- the input script of the P2PKH address of the same key, so reporting that+-- address would mislead.+--+-- An output only has an address on a network that defines its kind. A+-- witness program has none on a network without SegWit, such as Bitcoin+-- Cash. A 256-bit script hash (@OP_HASH256@, a push of 32 bytes, @OP_EQUAL@)+-- has one on the Bitcoin Cash networks only. On Bitcoin that script is a+-- plain hash lock, which Bitcoin Core reports as non-standard. Every address+-- this function returns has a text form on the network, so 'addrToText' and+-- the JSON encoder succeed for it.+outputAddress :: Network -> Ctx -> ScriptOutput -> Maybe Address+outputAddress net _ =+  \case+    PayPKHash h -> Just $ PubKeyAddress h+    PayScriptHash h -> Just $ ScriptAddress h+    PayWitnessPKHash h -> witness $ Just (WitnessPubKeyAddress h)+    PayWitnessScriptHash h -> witness $ Just (WitnessScriptAddress h)+    PayWitness v d -> witness $ witnessAddress v d+    PayScript32Hash h -> script32 $ Just (Script32Address h)+    _ -> Nothing+  where+    -- SegWit addresses exist where the network has a Bech32 prefix.+    witness a = guard (isJust net.bech32Prefix) >> a+    -- Pay-to-script-hash with a 256-bit hash exists where the network has+    -- CashAddr.+    script32 a = guard (isJust net.cashAddrPrefix) >> a --- | Infer the 'Address' of a 'ScriptInput'.-inputAddress :: ScriptInput -> Maybe Address-inputAddress =-    \case-        (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr key-        (ScriptHashInput _ rdm) -> Just $ payToScriptAddress rdm-        _ -> Nothing+-- | Infer the 'Address' that a 'ScriptInput' spends from. It is the address+-- of the public key or of the redeem script that the input carries.+--+-- 'decodeInput' only returns a redeem script in the form 'encodeOutput'+-- writes, so for a decoded input this is the hash of the redeem script bytes+-- of the input. There is no address for a redeem script above+-- 'maxRedeemScriptSize' bytes, since no valid input can carry one. The+-- function does not look at the output being spent, so it is a guess that a+-- caller should confirm against that output when it has it.+inputAddress :: Ctx -> ScriptInput -> Maybe Address+inputAddress ctx =+  \case+    (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr ctx key+    (ScriptHashInput _ rdm) -> eitherToMaybe $ payToScriptAddress ctx rdm+    (NestedWitnessInput rdm) -> eitherToMaybe $ payToScriptAddress ctx rdm+    _ -> Nothing
src/Haskoin/Address/Base58.hs view
@@ -1,36 +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+-- 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+  ( -- * Base58+    Base58,+    encodeBase58,+    decodeBase58,+    encodeBase58Check,+    decodeBase58Check,+  )+where -import           Control.Monad-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import qualified Data.ByteString.Char8   as C-import           Data.Maybe              (fromMaybe, isJust, listToMaybe)-import           Data.Serialize          as S-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import qualified Data.Text               as T-import           Haskoin.Crypto.Hash-import           Haskoin.Util-import           Numeric                 (readInt, showIntAtBase)+import Control.Monad (guard)+import Data.Array (Array, assocs, listArray, (!), (//))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (Serial (serialize))+import Data.Char (chr)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Bits (shiftL, (.|.))+import Data.Word (Word64, Word8)+import Haskoin.Crypto.Hash (checkSum32)+import Haskoin.Util.Helpers (integerToBS)+import Numeric (showIntAtBase)  -- | 'Base58' classic Bitcoin address format. type Base58 = Text@@ -39,65 +46,114 @@ b58Data :: ByteString b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" --- | Convert a number less than or equal to provided integer into a 'Base58'--- character.-b58 :: Int -> Char-b58 = C.index b58Data+b58Array :: Array Int Word8+b58Array = listArray (0, 57) (B.unpack b58Data) +-- | Value of each byte as a Base58 digit. Bytes outside the alphabet map to+-- 'Nothing'. Every byte of a multi-byte UTF-8 character is outside the+-- alphabet, so no non-ASCII character can ever be read as a digit.+b58InvArray :: Array Word8 (Maybe Int)+b58InvArray =+  listArray+    (minBound, maxBound)+    (repeat Nothing)+    // map swap (assocs b58Array)+  where+    swap (i, c) = (c, Just i)++-- | Convert a number less than 58 into a 'Base58' character.+b58 :: Int -> Word8+b58 = (b58Array !)+ -- | Convert a 'Base58' character into the number it represents.-b58' :: Char -> Maybe Int-b58' = flip C.elemIndex b58Data+b58' :: Word8 -> Maybe Int+b58' = (b58InvArray !)  -- | Encode an arbitrary-length 'Integer' into a 'Base58' string. Leading zeroes -- will not be part of the resulting string. encodeBase58I :: Integer -> Base58-encodeBase58I i = cs $ showIntAtBase 58 b58 i ""+encodeBase58I i+  | i < 0 = cs $ showIntAtBase 58 (chr . fromIntegral . b58) i ""+  | otherwise = E.decodeLatin1 . B.pack . map b58 $ chunks i []+  where+    -- Divide by 58^10 rather than by 58, so that a number of n digits takes+    -- n/10 divisions of a big number and the rest is machine arithmetic.+    chunk = 58 ^ (10 :: Int) :: Integer+    chunks n acc+      | n < chunk = top (fromInteger n) acc+      | otherwise =+          let (q, r) = n `quotRem` chunk+           in chunks q (digits (10 :: Int) (fromInteger r) acc)+    -- the digits of a chunk, ten of them with leading zeros+    digits 0 _ acc = acc+    digits k w acc =+      let (q, r) = w `quotRem` (58 :: Word64)+       in digits (k - 1) q (fromIntegral r : acc)+    -- the digits of the leading chunk, without leading zeros+    top w acc+      | w < 58 = fromIntegral w : acc+      | otherwise =+          let (q, r) = w `quotRem` (58 :: Word64)+           in top q (fromIntegral r : acc) --- | Decode a 'Base58' string into an arbitrary-length 'Integer'.-decodeBase58I :: Base58 -> Maybe Integer-decodeBase58I s =-    case go of-        Just (r,[]) -> Just r-        _           -> Nothing+-- | Decode the UTF-8 bytes of a 'Base58' string into an 'Integer'. Fails on+-- the first byte that is not a 'Base58' digit.+decodeBase58I :: ByteString -> Maybe Integer+decodeBase58I = B.foldl' step (Just 0)   where-    p  = isJust . b58'-    f  = fromMaybe e . b58'-    go = listToMaybe $ readInt 58 p f (cs s)-    e  = error "Could not decode base58"+    step acc w = do+      n <- acc+      d <- b58' w+      return (n * 58 + toInteger d)  -- | Encode an arbitrary 'ByteString' into a its 'Base58' representation, -- preserving leading zeroes. encodeBase58 :: ByteString -> Base58 encodeBase58 bs =-    l `mappend` r+  l <> r   where-    (z, b) = BS.span (== 0) bs-    l = cs $ BS.replicate (BS.length z) (BS.index b58Data 0) -- preserve leading 0's-    r | BS.null b = T.empty-      | otherwise = encodeBase58I $ bsToInteger b+    (z, b) = B.span (== 0) bs+    l = T.replicate (B.length z) "1" -- preserve leading 0's+    r+      | B.null b = T.empty+      | otherwise = encodeBase58I $ B.foldl' (\n w -> n `shiftL` 8 .|. toInteger w) 0 b --- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'.+-- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'. Returns 'Nothing' if+-- the input contains any character outside the 'Base58' alphabet.+--+-- The time this takes grows with the square of the length of the input,+-- since the digits are accumulated into one big number. Bound the length+-- before decoding text from an untrusted source. No Bitcoin address is+-- longer than 35 characters and no extended key longer than 112;+-- 'Haskoin.Address.textToAddr', 'Haskoin.Crypto.Keys.fromWif' and+-- 'Haskoin.Crypto.Keys.xPrvImport' apply those bounds themselves. decodeBase58 :: Base58 -> Maybe ByteString decodeBase58 t =-    BS.append prefix <$> r+  B.append prefix <$> r   where-    (z, b) = BS.span (== BS.index b58Data 0) (cs t)-    prefix = BS.replicate (BS.length z) 0 -- preserve leading 1's-    r | BS.null b = Just BS.empty-      | otherwise = integerToBS <$> decodeBase58I (cs b)+    (z, b) = B.span (== b58 0) (E.encodeUtf8 t)+    prefix = B.replicate (B.length z) 0 -- preserve leading 1's+    r+      | B.null b = Just B.empty+      | otherwise = integerToBS <$> decodeBase58I b  -- | Computes a checksum for the input 'ByteString' and encodes the input and -- the checksum as 'Base58'. encodeBase58Check :: ByteString -> Base58 encodeBase58Check bs =-    encodeBase58 $ BS.append bs $ encode $ checkSum32 bs+  (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.+-- returns 'Nothing' if the input string contains invalid 'Base58' characters,+-- if it is too short to hold a checksum, or if the checksum fails. The cost+-- grows with the square of the length, as for 'decodeBase58': bound the+-- length of untrusted input first. decodeBase58Check :: Base58 -> Maybe ByteString-decodeBase58Check bs = do-    rs <- decodeBase58 bs-    let (res, chk) = BS.splitAt (BS.length rs - 4) rs-    guard $ chk == encode (checkSum32 res)-    return res+decodeBase58Check t = do+  rs <- decodeBase58 t+  guard $ B.length rs >= checkSumLength+  let (res, chk) = B.splitAt (B.length rs - checkSumLength) rs+  guard $ chk == (runPutS . serialize . checkSum32) res+  return res+  where+    checkSumLength = 4
src/Haskoin/Address/Bech32.hs view
@@ -1,46 +1,67 @@+{-# 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+-- 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-    , Data-    , bech32Encode-    , bech32Decode-    , toBase32-    , toBase256-    , segwitEncode-    , segwitDecode-    , Word5(..)-    , word5-    , fromWord5-    ) where+  ( -- * Bech32+    HRP,+    Bech32,+    Bech32Encoding (..),+    bech32Const,+    Data,+    bech32Encode,+    bech32Decode,+    toBase32,+    toBase256,+    segwitEncode,+    segwitDecode,+    Word5 (..),+    word5,+    fromWord5,+  )+where -import           Control.Monad         (guard)-import           Data.Array            (Array, assocs, bounds, listArray, (!),-                                        (//))-import           Data.Bits             (Bits, testBit, unsafeShiftL,-                                        unsafeShiftR, xor, (.&.), (.|.))-import qualified Data.ByteString       as B-import           Data.Char             (toUpper)-import           Data.Foldable         (foldl')-import           Data.Functor.Identity (Identity, runIdentity)-import           Data.Ix               (Ix (..))-import           Data.Text             (Text)-import qualified Data.Text             as T-import qualified Data.Text.Encoding    as E-import           Data.Word             (Word8)+import Control.Monad (guard)+import Data.Array+  ( Array,+    assocs,+    bounds,+    listArray,+    (!),+    (//),+  )+import Data.Bits+  ( Bits,+    testBit,+    unsafeShiftL,+    unsafeShiftR,+    xor,+    (.&.),+    (.|.),+  )+import Data.ByteString qualified as B+import Data.Foldable (foldl')+import Data.Functor.Identity (Identity, runIdentity)+import Data.Ix (Ix (..))+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Word (Word8) +data Bech32Encoding = Bech32 | Bech32m+  deriving (Eq, Show, Ord, Enum)+ -- | Bech32 human-readable string. type Bech32 = Text @@ -50,28 +71,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)+newtype Word5+  = UnsafeWord5 Word8+  deriving (Eq, Ord)  instance Ix Word5 where-    range (UnsafeWord5 m, UnsafeWord5 n) = map UnsafeWord5 $ range (m, n)-    index (UnsafeWord5 m, UnsafeWord5 n) (UnsafeWord5 i) = index (m, n) i-    inRange (m, n) i = m <= i && i <= n+  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 #-}@@ -79,151 +100,193 @@ -- | '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'.+-- | Inverse of 'charset'. Only the lowercase characters are present. The+-- decoder checks that the string is ASCII and not mixed case, then lowercases+-- it, before looking characters up here. No character from another alphabet+-- can therefore stand in for a 'Bech32' character.+charsetInverse :: Array Char (Maybe Word5)+charsetInverse =+  listArray ('0', 'z') (repeat Nothing) // map swap (assocs charset)+  where+    swap (w, c) = (c, Just w)++-- | Convert a lowercase character to its five-bit value from 'charset'. charsetMap :: Char -> Maybe Word5 charsetMap c-    | inRange (bounds inv) upperC = inv ! upperC-    | otherwise = Nothing-  where-    upperC = toUpper c-    inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)-    swap (a, b) = (toUpper b, Just a)+  | inRange (bounds charsetInverse) c = charsetInverse ! c+  | otherwise = Nothing  -- | Calculate or validate 'Bech32' checksum. bech32Polymod :: [Word5] -> Word bech32Polymod values = foldl' go 1 values .&. 0x3fffffff   where     go chk value =-        foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]+      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+        -- Keep only the low 25 bits before shifting, as the reference+        -- implementation does, so the accumulator never grows past 30 bits.+        chk' = ((chk .&. 0x1ffffff) .<<. 5) `xor` fromWord5 value  -- | Convert human-readable part of 'Bech32' string into a list of five-bit -- words. bech32HRPExpand :: HRP -> [Word5] bech32HRPExpand hrp =-    map (UnsafeWord5 . (.>>. 5)) hrpBytes ++-    [UnsafeWord5 0] ++ map word5 hrpBytes+  map (word5 . (.>>. 5)) hrpBytes+    ++ [word5 (0 :: Word8)]+    ++ map word5 hrpBytes   where     hrpBytes = B.unpack $ E.encodeUtf8 hrp --- | Calculate checksum for a string of five-bit words.-bech32CreateChecksum :: HRP -> [Word5] -> [Word5]-bech32CreateChecksum hrp dat = [word5 (polymod .>>. i) | i <- [25,20 .. 0]]+bech32Const :: Bech32Encoding -> Word+bech32Const Bech32 = 0x00000001+bech32Const Bech32m = 0x2bc830a3++-- | Calculate Bech32 checksum for a string of five-bit words.+bech32CreateChecksum :: Bech32Encoding -> HRP -> [Word5] -> [Word5]+bech32CreateChecksum enc hrp dat = [word5 (polymod .>>. i) | i <- [25, 20 .. 0]]   where     values = bech32HRPExpand hrp ++ dat-    polymod =-        bech32Polymod (values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]) `xor` 1+    w5 = values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]+    polymod = bech32Polymod w5 `xor` bech32Const enc --- | Verify checksum for a human-readable part and string of five-bit words.-bech32VerifyChecksum :: HRP -> [Word5] -> Bool-bech32VerifyChecksum hrp dat = bech32Polymod (bech32HRPExpand hrp ++ dat) == 1+-- | Verify Bech32 checksum for a human-readable part and string of five-bit words.+bech32VerifyChecksum :: HRP -> [Word5] -> Maybe Bech32Encoding+bech32VerifyChecksum hrp dat =+  let poly = bech32Polymod (bech32HRPExpand hrp ++ dat)+   in if+        | poly == bech32Const Bech32 -> Just Bech32+        | poly == bech32Const Bech32m -> Just Bech32m+        | otherwise -> Nothing  -- | Maximum length of a Bech32 result. maxBech32Length :: Int maxBech32Length = 90 +-- | Number of five-bit words in a Bech32 checksum.+checksumLength :: Int+checksumLength = 6+ -- | Encode string of five-bit words into 'Bech32' using a provided -- human-readable part. Can fail if 'HRP' is invalid or result would be longer -- than 90 characters.-bech32Encode :: HRP -> [Word5] -> Maybe Bech32-bech32Encode hrp dat = do-    guard $ checkHRP hrp-    let dat' = dat ++ bech32CreateChecksum (T.toLower hrp) dat-        rest = map (charset !) dat'-        result = T.concat [T.toLower hrp, T.pack "1", T.pack rest]-    guard $ T.length result <= maxBech32Length-    return result+bech32Encode :: Bech32Encoding -> HRP -> [Word5] -> Maybe Bech32+bech32Encode enc hrp dat = do+  guard $ checkHRP hrp+  -- The constructor of 'Word5' can hold any byte, and only 0 to 31 have a+  -- character.+  guard $ all (\(UnsafeWord5 w) -> w <= 31) dat+  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 +-- | BIP-173 allows only US-ASCII characters in the range 33 to 126 anywhere+-- in a 'Bech32' string.+isBech32Char :: Char -> Bool+isBech32Char c = c >= '\x21' && c <= '\x7e'+ -- | 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+checkHRP hrp = not (T.null hrp) && T.all isBech32Char hrp  -- | Decode human-readable 'Bech32' string into a human-readable part and a -- string of five-bit words.-bech32Decode :: Bech32 -> Maybe (HRP, [Word5])+bech32Decode :: Bech32 -> 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-    guard $ bech32VerifyChecksum hrp' dat'-    return (hrp', take (T.length dat - 6) dat')+  guard $ T.length bech32 <= maxBech32Length+  guard $ T.all isBech32Char bech32+  -- The string must be all uppercase or all lowercase, never a mix.+  guard $ T.toUpper bech32 == bech32 || lowerBech32 == bech32+  let (hrp, dat) = T.breakOnEnd "1" lowerBech32+  guard $ T.length dat >= checksumLength+  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 - checksumLength) dat')   where     lowerBech32 = T.toLower bech32  type Pad f = Int -> Int -> Word -> [[Word]] -> f [[Word]]  yesPadding :: Pad Identity-yesPadding _ 0 _ result        = return result+yesPadding _ 0 _ result = return result yesPadding _ _ padValue result = return $ [padValue] : result {-# INLINE yesPadding #-}  noPadding :: Pad Maybe noPadding frombits bits padValue result = do-    guard $ bits < frombits && padValue == 0-    return result+  guard $ bits < frombits && padValue == 0+  return result {-# INLINE noPadding #-}  -- | Big endian conversion of a bytestring from base \(2^{frombits}\) to base -- \(2^{tobits}\). {frombits} and {twobits} must be positive and -- \(2^{frombits}\) and \(2^{tobits}\) must be smaller than the size of Word. -- Every value in 'dat' must be strictly smaller than \(2^{frombits}\).-convertBits :: Functor f => [Word] -> Int -> Int -> Pad f -> f [Word]+convertBits :: (Functor f) => [Word] -> Int -> Int -> Pad f -> f [Word] convertBits dat frombits tobits pad = concat . reverse <$> go dat 0 0 []   where     go [] acc bits result =-        let padValue = (acc .<<. (tobits - bits)) .&. maxv-        in pad frombits bits padValue result-    go (value:dat') acc bits result =-        go dat' acc' (bits' `rem` tobits) (result' : result)+      let padValue = (acc .<<. (tobits - bits)) .&. maxv+       in pad frombits bits padValue result+    go (value : dat') acc bits result =+      go dat' acc' (bits' `rem` tobits) (result' : result)       where-        acc' = (acc .<<. frombits) .|. fromIntegral value+        -- Only the low (frombits + tobits - 1) bits of the accumulator are+        -- ever read, so masking to them keeps it from overflowing on long+        -- inputs.+        acc' = ((acc .<<. frombits) .|. fromIntegral value) .&. maxAcc         bits' = bits + frombits         result' =-            [ (acc' .>>. b) .&. maxv-            | b <- [bits' - tobits,bits' - 2 * tobits .. 0]-            ]+          [ (acc' .>>. b) .&. maxv+          | b <- [bits' - tobits, bits' - 2 * tobits .. 0]+          ]     maxv = (1 .<<. tobits) - 1+    maxAcc = (1 .<<. (frombits + tobits - 1)) - 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.+-- | Convert from five-bit word string to eight-bit word string. Fails if the+-- padding bits are not zero, or if there are enough of them to hold a whole+-- five-bit word, because the input is then not a canonical encoding. 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 :: Word8 -> Data -> Bool-segwitCheck witver witprog =-    witver <= 16 &&-    if witver == 0-        then length witprog == 20 || length witprog == 32-        else length witprog >= 2 && length witprog <= 40+segwitCheck :: Bech32Encoding -> Word8 -> Data -> Bool+segwitCheck enc witver witprog =+  witver <= 16+    && if witver == 0+      then enc == Bech32 && (length witprog == 20 || length witprog == 32)+      else enc == Bech32m && (length witprog >= 2 && length witprog <= 40)  -- | Decode SegWit 'Bech32' address from a string and expected human-readable part. segwitDecode :: HRP -> Bech32 -> Maybe (Word8, Data) segwitDecode hrp addr = do-    (hrp', dat) <- bech32Decode addr-    guard $ (hrp == hrp') && not (null dat)-    let (UnsafeWord5 witver:datBase32) = dat-    decoded <- toBase256 datBase32-    guard $ segwitCheck witver decoded-    return (witver, decoded)+  (enc, hrp', dat) <- bech32Decode addr+  guard $ hrp == hrp'+  (witver, datBase32) <- case dat of+    [] -> Nothing+    (v : rest) -> Just (fromWord5 v, rest)+  decoded <- toBase256 datBase32+  guard $ segwitCheck enc witver decoded+  return (witver, decoded)  -- | Encode 'Data' as a SegWit 'Bech32' address. Needs human-readable part and -- witness program version. segwitEncode :: HRP -> Word8 -> Data -> Maybe Text segwitEncode hrp witver witprog = do-    guard $ segwitCheck witver witprog-    bech32Encode hrp $ UnsafeWord5 witver : toBase32 witprog+  guard $ segwitCheck enc witver witprog+  bech32Encode enc hrp $ word5 witver : toBase32 witprog+  where+    enc = if witver == 0 then Bech32 else Bech32m
src/Haskoin/Address/CashAddr.hs view
@@ -1,199 +1,298 @@+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Support for Bitcoin Cash (BCH) CashAddr format. module Haskoin.Address.CashAddr-    ( -- * CashAddr-      CashPrefix-    , CashVersion-    , CashAddr-    , Cash32-    , cashAddrDecode-    , cashAddrEncode-    , cash32decodeType-    , cash32encodeType-    , cash32decode-    , cash32encode-    ) where+  ( -- * CashAddr+    CashPrefix,+    CashVersion,+    CashAddr,+    Cash32,+    cashAddrDecode,+    cashAddrEncode,+    cash32decodeType,+    cash32encodeType,+    cash32decode,+    cash32encode,+    decodeVersionByte,+    encodeVersionByte,+  )+where -import           Control.Monad-import           Data.Bits-import           Data.ByteString       (ByteString)-import qualified Data.ByteString       as B-import qualified Data.ByteString.Char8 as C-import           Data.Char-import           Data.List-import           Data.Text             (Text)-import qualified Data.Text             as T-import qualified Data.Text.Encoding    as E-import           Data.Word-import           Haskoin.Constants-import           Haskoin.Util+import Control.Monad (guard)+import Data.Bits+  ( Bits+      ( shiftL,+        shiftR,+        testBit,+        xor,+        (.&.),+        (.|.)+      ),+  )+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as C+import Data.Char (isAscii, isAsciiLower, isAsciiUpper, ord)+import Data.List (elemIndex, foldl')+import Data.Maybe (fromMaybe)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding (decodeLatin1, decodeUtf8, encodeUtf8)+import Data.Word (Word64, Word8)+import Haskoin.Network.Data (Network (cashAddrPrefix))+import Haskoin.Util.Helpers (convertBits)  -- | 'CashAddr' prefix, usually shown before the colon in addresses, but sometimes -- omitted. It is used in the checksum calculation to avoid parsing an address -- from the wrong network. type CashPrefix = Text --- | 'CashAddr' version, until new address schemes appear it will be zero.+-- | 'CashAddr' type. Zero is P2PKH and one is P2SH. Two and three are their+-- token-aware forms, introduced with CashTokens. type CashVersion = Word8  -- | High level 'CashAddr' human-reabale string, with explicit or implicit prefix. type CashAddr = Text --- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'. It--- need not encode a valid address but any binary data.+-- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'.+-- It need not encode a valid address but any binary data. type Cash32 = Text  -- | Symbols for encoding 'Cash32' data in human-readable strings. charset :: String charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" --- | Get the 32-bit number associated with this 'Cash32' character.-base32char :: Char -> Maybe Word8-base32char = fmap fromIntegral . (`elemIndex` charset)+-- | Number of five-bit characters in a 'Cash32' checksum.+checkSumLength :: Int+checkSumLength = 8 +-- | An ASCII letter of either case.+isAsciiLetter :: Char -> Bool+isAsciiLetter c = isAsciiLower c || isAsciiUpper c++-- | The longest data part of a 'CashAddr', in characters, checksum included.+-- The largest hash is 64 bytes. With the version byte that is 65 bytes, or+-- 520 bits, which take 104 characters of five bits. The checksum adds+-- 'checkSumLength' characters: 104 + 8 = 112.+maxCashAddrDataLength :: Int+maxCashAddrDataLength = 112++-- | The value of each byte as a 'Cash32' character, or 0xff for a byte that+-- is not one. Only lowercase letters and digits are in the table.+base32table :: ByteString+base32table =+  B.pack [maybe 0xff fromIntegral (elemIndex (toEnum i) charset) | i <- [0 .. 255]]++-- | Turn lowercase 'Cash32' characters into their five-bit values. Fails if+-- a byte is not a 'Cash32' character.+base32values :: ByteString -> Maybe ByteString+base32values dat = do+  let values = B.map (B.index base32table . fromIntegral) dat+  guard (B.all (/= 0xff) values)+  return values++-- | A 'CashAddr' string must be all lowercase or all uppercase. A mix of both+-- must be rejected. Only ASCII is allowed, so no character from another+-- alphabet can stand in for a letter.+singleCase :: Text -> Bool+singleCase t = T.all isAscii t && (T.toUpper t == t || T.toLower t == t)+ -- | High-Level: decode 'CashAddr' string if it is valid for the -- provided 'Network'. Prefix may be omitted from the string.+--+-- A string longer than the prefix of the network, the colon and+-- 'maxCashAddrDataLength' characters is refused before any other work. No+-- valid address is longer, since the largest hash the format has is 64 bytes. 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+  guard (T.length ca <= T.length epfx + 1 + maxCashAddrDataLength)+  guard (singleCase ca)+  let (cpfx, cdat) = T.breakOnEnd ":" (T.toLower ca)+  guard (T.null cpfx || T.init cpfx == epfx)+  (dpfx, ver, bs) <- cash32decodeType (epfx <> ":" <> cdat)+  guard (dpfx == epfx)+  return (ver, bs)  -- | High-Level: encode 'CashAddr' string for the provided network and hash. -- Fails if the 'CashVersion' or length of hash 'ByteString' is invalid. cashAddrEncode :: Network -> CashVersion -> ByteString -> Maybe CashAddr cashAddrEncode net cv bs = do-    pfx <- getCashAddrPrefix net-    cash32encodeType pfx cv bs+  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.+-- version byte before the 'ByteString' that encodes type and length. The+-- string must be the canonical encoding of its payload: padding bits must be+-- zero and there must be fewer than five of them. cash32decodeType :: Cash32 -> Maybe (CashPrefix, CashVersion, ByteString)-cash32decodeType ca' = do-    guard (T.toUpper ca' == ca' || ca == ca')-    (dpfx, bs) <- cash32decode ca-    guard (not (B.null bs))-    let vb = B.head bs-        pay = B.tail bs-    (ver, len) <- decodeVersionByte vb-    guard (B.length pay == len)-    return (dpfx, ver, pay)-  where-    ca = T.toLower ca'+cash32decodeType ca = do+  (dpfx, groups) <- cash32decodeGroups ca+  bs <- toBase256 groups+  (vb, pay) <- B.uncons bs+  (ver, len) <- decodeVersionByte vb+  guard (B.length pay == len)+  return (dpfx, ver, pay)  -- | 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+  cash32encode pfx pl  -- | Low-Level: decode 'Cash32' string. 'CashPrefix' must be part of the string.--- No version or hash length validation is performed.+-- No version or hash length validation is performed. The data may be any+-- sequence of five-bit groups, so bits left over after the last whole byte+-- are dropped. 'cash32decodeType' is the layer that enforces canonical+-- padding.+--+-- The prefix must be one or more letters, and the string has one colon, as+-- in the decoder of Bitcoin Cash Node. There is no limit on the length here,+-- since the data is arbitrary. The time and the memory grow in proportion to+-- the length. cash32decode :: Cash32 -> Maybe (CashPrefix, ByteString) cash32decode text = do-    let bs = C.map toLower bs'-    guard (C.map toUpper bs' == bs' || bs == bs')-    let (pfx', dat) = C.breakEnd (== ':') bs-    pfx <--        if B.null pfx' || pfx' == C.singleton ':'-            then Nothing-            else Just (B.init pfx')-    b32 <- B.pack <$> mapM base32char (C.unpack dat)-    let px = B.map (.&. 0x1f) pfx-        pd = px <> B.singleton 0 <> b32-        cs = cash32Polymod pd-        bb = B.take (B.length b32 - 8) b32-    guard (verifyCash32Polymod cs)-    let out = toBase256 bb-    return (E.decodeUtf8 pfx, out)-  where-    bs' = E.encodeUtf8 text+  (pfx, groups) <- cash32decodeGroups text+  return (pfx, wholeBytes groups) +-- | Check the checksum of a 'Cash32' string and return its prefix and its+-- data as five-bit groups, one per byte, without the checksum.+cash32decodeGroups :: Cash32 -> Maybe (CashPrefix, ByteString)+cash32decodeGroups text = do+  guard (singleCase text)+  let bs = encodeUtf8 (T.toLower text)+      (pfx', dat) = C.breakEnd (== ':') bs+  pfx <-+    if B.null pfx' || pfx' == C.singleton ':'+      then Nothing+      else Just (B.init pfx')+  -- Letters only: no digit and no second colon. The text is lowercase here.+  guard (C.all isAsciiLower pfx)+  b32 <- base32values dat+  guard (B.length b32 >= checkSumLength)+  let px = B.map (.&. 0x1f) pfx+      pd = px <> B.singleton 0 <> b32+      cs = cash32Polymod pd+  guard (verifyCash32Polymod cs)+  return (decodeUtf8 pfx, B.take (B.length b32 - checkSumLength) b32)+ -- | 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+-- arbitrary data, of any length.+--+-- The prefix must be one or more ASCII letters, which is what 'cash32decode'+-- and Bitcoin Cash Node read back. It is written in lowercase, so that the+-- string is never of mixed case. Any other prefix fails.+cash32encode :: CashPrefix -> ByteString -> Maybe Cash32+cash32encode prefix bs = do+  guard (not (T.null prefix) && T.all isAsciiLetter prefix)+  let pfx = T.toLower prefix+  let b32 = toBase32 bs+      px = B.map (.&. 0x1f) (encodeUtf8 pfx)+      pd = px <> B.singleton 0 <> b32 <> B.replicate checkSumLength 0+      cs = cash32Polymod pd+      c32 = B.map (B.index charsetBytes . fromIntegral) (b32 <> cs)+  return (pfx <> ":" <> decodeLatin1 c32) +-- | 'charset' as bytes, to look up characters in constant time.+charsetBytes :: ByteString+charsetBytes = C.pack charset+ -- | 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+toBase32 bs = fst $ B.unfoldrN groups group 0+  where+    groups = (B.length bs * 8 + 4) `div` 5+    byte i+      | i < B.length bs = fromIntegral (B.index bs i) :: Word+      | otherwise = 0+    -- the five bits that start this many bits into the input, from the two+    -- bytes that hold them, padded with zeros after the end+    group bit =+      let i = bit `shiftR` 3+          pair = (byte i `shiftL` 8) .|. byte (i + 1)+       in Just (fromIntegral ((pair `shiftR` (11 - (bit .&. 7))) .&. 0x1f), bit + 5) --- | Convert base of 'ByteString' from five to eight bits per byte. Ignore--- padding to be symmetric with respect to 'toBase32' function.-toBase256 :: ByteString -> ByteString+-- | Convert five-bit groups to the bytes they fill completely, dropping any+-- bits left over at the end.+wholeBytes :: ByteString -> ByteString+wholeBytes groups =+  B.take (5 * B.length groups `div` 8) . B.pack . map fromIntegral . fromMaybe [] $+    convertBits True 5 8 (map fromIntegral (B.unpack groups))++-- | Convert base of 'ByteString' from five to eight bits per byte. Fails if+-- the padding bits are not zero, or if there are enough of them to hold a+-- whole five-bit group, because the input is then not the canonical encoding+-- of its payload.+toBase256 :: ByteString -> Maybe ByteString toBase256 =-    B.pack .-    map fromIntegral . fst . convertBits False 5 8 . map fromIntegral . B.unpack+  fmap (B.pack . map fromIntegral)+    . 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.+-- larger than four 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 :: Int]]   where     polymod = B.foldl' outer (1 :: Word64) v `xor` 1     outer c d =-        let c0 = (fromIntegral (c `shiftR` 35) :: Word8)-            c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d-        in foldl' (inner c0) c' (zip [0 ..] generator)-    generator =-        [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470]-    inner c0 c (b, g)-        | c0 `testBit` b = c `xor` g-        | otherwise = c+      let c0 = c `shiftR` 35+          c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d+       in g 4 0x1e4f43e470 c0+            . g 3 0xae2eabe2a8 c0+            . g 2 0xf33e5fb3c4 c0+            . g 1 0x79b76d99e2 c0+            $ g 0 0x98f2bc8e61 c0 c'+    g :: Int -> Word64 -> Word64 -> Word64 -> Word64+    g b gen c0 c+      | c0 `testBit` b = c `xor` gen+      | otherwise = c  -- | Validate that polymod 'ByteString' (eight bytes) is equal to zero. verifyCash32Polymod :: ByteString -> Bool-verifyCash32Polymod = (== B.replicate 8 0)+verifyCash32Polymod = (== B.replicate checkSumLength 0)
src/Haskoin/Block.hs view
@@ -1,19 +1,21 @@-{-|-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+-- 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+  ( module Haskoin.Block.Common,+    module Haskoin.Block.Headers,+    module Haskoin.Block.Merkle,+  )+where -import           Haskoin.Block.Common-import           Haskoin.Block.Headers-import           Haskoin.Block.Merkle+import Haskoin.Block.Common+import Haskoin.Block.Headers+import Haskoin.Block.Merkle
src/Haskoin/Block/Common.hs view
@@ -1,60 +1,63 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Common data types and functions to handle blocks from the block chain. module Haskoin.Block.Common-    ( -- * Blocks-      Block(..)-    , BlockHeight-    , Timestamp-    , BlockHeader(..)-    , headerHash-    , BlockLocator-    , GetBlocks(..)-    , GetHeaders(..)-    , BlockHeaderCount-    , BlockHash(..)-    , blockHashToHex-    , hexToBlockHash-    , Headers(..)-    , decodeCompact-    , encodeCompact-    ) where+  ( -- * Blocks+    Block (..),+    BlockHeight,+    Timestamp,+    BlockHeader (..),+    headerHash,+    BlockLocator,+    GetBlocks (..),+    GetHeaders (..),+    BlockHeaderCount,+    BlockHash (..),+    blockHashToHex,+    hexToBlockHash,+    Headers (..),+    decodeCompact,+    encodeCompact,+  )+where -import           Control.DeepSeq-import           Control.Monad              (forM_, liftM2, mzero, replicateM)-import           Data.Aeson                 (FromJSON (..), ToJSON (..),-                                             Value (..), object, toJSON,-                                             withObject, withText, (.:), (.=))-import           Data.Aeson.Encoding        (pairs, unsafeToEncoding)-import           Data.Bits                  (shiftL, shiftR, (.&.), (.|.))-import qualified Data.ByteString            as B-import           Data.ByteString.Builder    (char7)-import           Data.Hashable              (Hashable)-import           Data.Maybe                 (fromMaybe)-import           Data.Serialize             (Serialize, decode, encode, get,-                                             put)-import           Data.Serialize.Get         (getWord32le)-import           Data.Serialize.Put         (Put, putWord32le)-import           Data.String                (IsString, fromString)-import           Data.String.Conversions    (cs)-import           Data.Text                  (Text)-import           Data.Word                  (Word32)-import           GHC.Generics               (Generic)-import           Haskoin.Crypto.Hash-import           Haskoin.Network.Common-import           Haskoin.Transaction.Common-import           Haskoin.Util-import qualified Text.Read                  as R+import Control.DeepSeq+import Control.Monad+import Data.Aeson+import Data.Aeson.Encoding+import Data.Binary (Binary (..))+import Data.Bits+import Data.ByteString qualified as B+import Data.ByteString.Lazy qualified as L+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable+import Data.Maybe+import Data.Serialize (Serialize (..))+import Data.String+import Data.String.Conversions+import Data.Text (Text)+import Data.Word+import GHC.Generics+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Transaction.Common+import Haskoin.Util.Helpers+import Text.Read qualified as R  -- | Height of a block in the block chain, starting at 0 for Genesis. type BlockHeight = Word32@@ -64,154 +67,206 @@  -- | 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+    txs <- getVarIntList deserialize+    return $ Block header txs+  serialize (Block h txs) = do+    serialize h+    putVarIntList serialize txs+ instance Serialize Block where-    get = do-        header <- get-        (VarInt c) <- get-        txs <- replicateM (fromIntegral c) get-        return $ Block header txs-    put (Block h txs) = do-        put h-        putVarInt $ length txs-        forM_ txs put+  get = deserialize+  put = serialize +instance Binary Block where+  get = deserialize+  put = serialize+ instance ToJSON Block where-    toJSON (Block h t) = object ["header" .= h, "transactions" .= t]-    toEncoding (Block h t) = pairs $ "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, Serialize, NFData)+newtype BlockHash = BlockHash {get :: Hash256}+  deriving (Eq, Ord, Generic, Hashable, Serial, NFData) +instance Serialize BlockHash where+  put = serialize+  get = deserialize++instance Binary BlockHash where+  put = serialize+  get = deserialize+ instance Show BlockHash where-    showsPrec _ = shows . blockHashToHex+  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 (B.reverse (encode h)) <> char7 '"'+  toJSON = String . blockHashToHex+  toEncoding (BlockHash h) = reversedHexEncoding h  -- | Block hashes are reversed with respect to the in-memory byte order in a -- block hash when displayed. blockHashToHex :: BlockHash -> Text-blockHashToHex (BlockHash h) = encodeHex (B.reverse (encode h))+blockHashToHex (BlockHash h) = reversedHex h  -- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are--- reversed as normal.+-- reversed as normal. The text must hold exactly 32 bytes of hex. hexToBlockHash :: Text -> Maybe BlockHash-hexToBlockHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- eitherToMaybe (decode bs)-    return $ BlockHash h+hexToBlockHash = fmap BlockHash . hexToReversedHash +-- | Hex of a 256-bit hash in display order, which is the reverse of its byte+-- order in memory. Block hashes and Merkle roots are shown this way.+reversedHex :: Hash256 -> Text+reversedHex = encodeHex . B.reverse . runPutS . serialize++-- | JSON encoding of a 256-bit hash in display order.+reversedHexEncoding :: Hash256 -> Encoding+reversedHexEncoding = hexEncoding . L.reverse . runPutL . serialize++-- | Parse hex in display order into a 256-bit hash. Any input that is not+-- exactly 32 bytes of hex is rejected instead of being truncated.+hexToReversedHash :: Text -> Maybe Hash256+hexToReversedHash hex = do+  bs <- decodeHex hex+  guard (B.length bs == 32)+  eitherToMaybe $ runGetS deserialize $ B.reverse bs+ -- | 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'.+--+-- The JSON form shows the previous block hash and the Merkle root in display+-- order, like the @getblockheader@ RPC call of Bitcoin Core. data BlockHeader = BlockHeader-    { blockVersion   :: !Word32 --  4 bytes+  { version :: !Word32, --  4 bytes+     -- | hash of the previous block (parent)-    , prevBlock      :: !BlockHash -- 32 bytes+    prev :: !BlockHash, -- 32 bytes+     -- | root of the merkle tree of transactions-    , merkleRoot     :: !Hash256 -- 32 bytes+    merkle :: !Hash256, -- 32 bytes+     -- | unix timestamp-    , blockTimestamp :: !Timestamp --  4 bytes+    timestamp :: !Timestamp, --  4 bytes+     -- | difficulty target-    , blockBits      :: !Word32 --  4 bytes+    bits :: !Word32, --  4 bytes+     -- | random nonce-    , bhNonce        :: !Word32 --  4 bytes-    }-    deriving (Eq, Ord, Show, Read, Generic, Hashable, NFData)-                                                 -- 80 bytes+    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 (encode m)-            , "timestamp" .= t-            , "bits" .= b-            , "nonce" .= n-            ]-    toEncoding (BlockHeader v p m t b n) =-        pairs-            ( "version" .= v-           <> "prevblock" .= p-           <> "merkleroot" .= encodeHex (encode m)-           <> "timestamp" .= t-           <> "bits" .= b-           <> "nonce" .= n-            )+  toJSON (BlockHeader v p m t b n) =+    object+      [ "version" .= v,+        "prevblock" .= p,+        "merkleroot" .= reversedHex 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` reversedHexEncoding 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 . decode =<<) . decodeHex+  parseJSON =+    withObject "BlockHeader" $ \o ->+      BlockHeader+        <$> o .: "version"+        <*> o .: "prevblock"+        <*> (merkleRoot =<< o .: "merkleroot")+        <*> o .: "timestamp"+        <*> o .: "bits"+        <*> o .: "nonce"+    where+      merkleRoot = maybe (fail "Invalid Merkle root") return . hexToReversedHash +instance Serial BlockHeader where+  deserialize = do+    v <- getWord32le+    p <- deserialize+    m <- deserialize+    t <- getWord32le+    b <- getWord32le+    n <- getWord32le+    return+      BlockHeader+        { version = v,+          prev = p,+          merkle = m,+          timestamp = t,+          bits = b,+          nonce = n+        }+  serialize (BlockHeader v p m bt bb n) = do+    putWord32le v+    serialize p+    serialize m+    putWord32le bt+    putWord32le bb+    putWord32le n++instance Binary BlockHeader where+  put = serialize+  get = deserialize+ instance Serialize BlockHeader where-    get = do-        v <- getWord32le-        p <- get-        m <- get-        t <- getWord32le-        b <- getWord32le-        n <- getWord32le-        return-            BlockHeader-                { blockVersion = v-                , prevBlock = p-                , merkleRoot = m-                , blockTimestamp = t-                , blockBits = b-                , bhNonce = n-                }-    put (BlockHeader v p m bt bb n) = do-        putWord32le v-        put p-        put m-        putWord32le bt-        putWord32le bb-        putWord32le n+  put = serialize+  get = deserialize  -- | Compute hash of 'BlockHeader'. headerHash :: BlockHeader -> BlockHash-headerHash = BlockHash . doubleSHA256 . encode+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.@@ -228,66 +283,94 @@ -- block hash, at at the tip of the chain, or after 500 entries, whichever comes -- earlier. data GetBlocks = GetBlocks-    { getBlocksVersion  :: !Word32+  { version :: !Word32,     -- | block locator object-    , getBlocksLocator  :: !BlockLocator+    locator :: !BlockLocator,     -- | hash of the last desired block-    , getBlocksHashStop :: !BlockHash-    }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize GetBlocks where-    get = GetBlocks <$> getWord32le <*> (repList =<< get) <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get-    put (GetBlocks v xs h) = putGetBlockMsg v xs h+    stop :: !BlockHash+  }+  deriving (Eq, Show, Read, Generic, NFData) -putGetBlockMsg :: Word32 -> BlockLocator -> BlockHash -> Put-putGetBlockMsg v xs h = do+-- | A locator of more than 'maxLocatorSize' hashes fails the parse. Bitcoin+-- Core and Bitcoin Cash Node disconnect a peer that sends one.+instance Serial GetBlocks where+  deserialize =+    GetBlocks+      <$> getWord32le+      <*> getVarIntListMax "getblocks" maxLocatorSize deserialize+      <*> deserialize+  serialize (GetBlocks v xs h) = do     putWord32le v-    putVarInt $ length xs-    forM_ xs put-    put h+    putVarIntList serialize xs+    serialize h +instance Serialize GetBlocks where+  put = serialize+  get = deserialize+ -- | Similar to the 'GetBlocks' message type but for retrieving block headers -- only. The response to a 'GetHeaders' request is a 'Headers' message -- containing a list of block headers. A maximum of 2000 block headers can be -- returned. 'GetHeaders' is used by simplified payment verification (SPV) -- clients to exclude block contents when synchronizing the block chain. data GetHeaders = GetHeaders-    { getHeadersVersion  :: !Word32+  { version :: !Word32,     -- | block locator object-    , getHeadersBL       :: !BlockLocator+    locator :: !BlockLocator,     -- | hash of the last desired block header-    , getHeadersHashStop :: !BlockHash-    }-    deriving (Eq, Show, Read, Generic, NFData)+    stop :: !BlockHash+  }+  deriving (Eq, Show, Read, Generic, NFData) +-- | A locator of more than 'maxLocatorSize' hashes fails the parse. Bitcoin+-- Core and Bitcoin Cash Node disconnect a peer that sends one.+instance Serial GetHeaders where+  deserialize =+    GetHeaders+      <$> getWord32le+      <*> getVarIntListMax "getheaders" maxLocatorSize deserialize+      <*> deserialize+  serialize (GetHeaders v xs h) = do+    putWord32le v+    putVarIntList serialize xs+    serialize h+ instance Serialize GetHeaders where-    get = GetHeaders <$> getWord32le <*> (repList =<< get) <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get-    put (GetHeaders v xs h) = putGetBlockMsg v xs h+  put = serialize+  get = deserialize +instance Binary GetHeaders where+  put = serialize+  get = deserialize+ -- | 'BlockHeader' type with a transaction count as 'VarInt' type BlockHeaderCount = (BlockHeader, VarInt)  -- | The 'Headers' type is used to return a list of block headers in -- response to a 'GetHeaders' message. newtype Headers = Headers-    { -- | list of block headers with transaction count-      headersList :: [BlockHeaderCount]-    } deriving (Eq, Show, Read, Generic, NFData)+  { -- | list of block headers with transaction count+    list :: [BlockHeaderCount]+  }+  deriving (Eq, Show, Read, Generic, NFData) +-- | A message of more than 'maxHeadersSize' headers fails the parse, as soon+-- as the count is read. Bitcoin Core and Bitcoin Cash Node check the count+-- before they read the headers, and punish a peer that sends more.+instance Serial Headers where+  deserialize =+    Headers <$> getVarIntListMax "headers" maxHeadersSize (liftM2 (,) deserialize deserialize)+  serialize (Headers xs) =+    putVarIntList (\(a, b) -> serialize a >> serialize b) xs+ instance Serialize Headers where-    get = Headers <$> (repList =<< get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action = liftM2 (,) get get-    put (Headers xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a, b) -> put a >> put b+  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@@ -299,7 +382,10 @@ -- \[ -- N = -1^{sign} \times mantissa \times 256^{exponent-3} -- \]-decodeCompact :: Word32 -> (Integer, Bool) -- ^ true means overflow+decodeCompact ::+  Word32 ->+  -- | true means overflow+  (Integer, Bool) decodeCompact nCompact = (if neg then res * (-1) else res, over)   where     nSize :: Int@@ -307,40 +393,51 @@     nWord' :: Word32     nWord' = nCompact .&. 0x007fffff     nWord :: Word32-    nWord | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))-          | otherwise = nWord'+    nWord+      | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))+      | otherwise = nWord'     res :: Integer-    res | nSize <= 3 = fromIntegral nWord-        | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))+    res+      | nSize <= 3 = fromIntegral nWord+      | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))     neg = nWord /= 0 && (nCompact .&. 0x00800000) /= 0-    over = nWord /= 0 && (nSize > 34 ||-                          nWord > 0xff && nSize > 33 ||-                          nWord > 0xffff && nSize > 32)+    over =+      nWord /= 0+        && ( nSize > 34+               || nWord > 0xff && nSize > 33+               || nWord > 0xffff && nSize > 32+           )  -- | Encode an 'Integer' to the compact number format used in the difficulty--- target of a block.+-- target of a block. The exponent is a single byte, so numbers that need more+-- than 255 bytes cannot be encoded and raise an error, as the assertion in+-- Bitcoin Core does. encodeCompact :: Integer -> Word32-encodeCompact i = nCompact+encodeCompact i+  | nSize > 255 = error "encodeCompact: number too large for the compact format"+  | otherwise = nCompact   where     i' = abs i     neg = i < 0     nSize' :: Int-    nSize' = let f 0 = 0-                 f n = 1 + f (n `shiftR` 8)-             in f i'+    nSize' =+      let f 0 = 0+          f n = 1 + f (n `shiftR` 8)+       in f i'     nCompact''' :: Word32     nCompact'''-        | nSize' <= 3 = fromIntegral $ (low64 .&. i') `shiftL` (8 * (3 - nSize'))-        | otherwise = fromIntegral $ low64 .&. (i' `shiftR` (8 * (nSize' - 3)))+      | 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'+    nCompact+      | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000+      | otherwise = nCompact'     low64 :: Integer     low64 = 0xffffffffffffffff
src/Haskoin/Block/Headers.hs view
@@ -1,902 +1,1107 @@-{-# 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.Bits                   (shiftL, shiftR, (.&.))-import qualified Data.ByteString             as B-import           Data.ByteString.Short       (ShortByteString, fromShort,-                                              toShort)-import           Data.Function               (on)-import           Data.Hashable-import           Data.HashMap.Strict         (HashMap)-import qualified Data.HashMap.Strict         as HashMap-import           Data.List                   (sort, sortBy)-import           Data.Maybe                  (fromMaybe, listToMaybe)-import           Data.Serialize              as S (Serialize (..), decode,-                                                   encode, get, put)-import           Data.Serialize.Get          as S-import           Data.Serialize.Put          as S-import           Data.Typeable               (Typeable)-import           Data.Word                   (Word32, Word64)-import           GHC.Generics                (Generic)-import           Haskoin.Block.Common-import           Haskoin.Constants-import           Haskoin.Crypto-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 Serialize BlockNode where-    get = do-        nodeHeader <- S.get-        nodeHeight <- getWord32le-        nodeWork <- S.get-        if nodeHeight == 0-            then do-                let nodeSkip = headerHash nodeHeader-                return BlockNode {..}-            else do-                nodeSkip <- S.get-                return BlockNode {..}-    put bn = do-        put $ nodeHeader bn-        putWord32le $ nodeHeight bn-        put $ nodeWork bn-        case nodeHeight bn of-            0 -> return ()-            _ -> put $ nodeSkip bn--instance Eq BlockNode where-    (==) = (==) `on` nodeHeader--instance Ord BlockNode where-    compare = compare `on` nodeHeight---- | Memory-based header tree.-data HeaderMemory = HeaderMemory-    { memoryHeaderMap  :: !BlockMap-    , memoryBestHeader :: !BlockNode-    } deriving (Eq, Typeable, Show, Read, Generic, Hashable, NFData)---- | Typeclass for block header chain storage monad.-class Monad m => BlockHeaders m where-    -- | Add a new 'BlockNode' to the chain. Does not validate.-    addBlockHeader :: BlockNode -> m ()-    -- | Get a 'BlockNode' associated with a 'BlockHash'.-    getBlockHeader :: BlockHash -> m (Maybe BlockNode)-    -- | Locate the 'BlockNode' for the highest block in the chain-    getBestBlockHeader :: m BlockNode-    -- | Set the highest block in the chain.-    setBestBlockHeader :: BlockNode -> m ()-    -- | Add a continuous bunch of block headers the chain. Does not validate.-    addBlockHeaders :: [BlockNode] -> m ()-    addBlockHeaders = mapM_ addBlockHeader--instance Monad m => BlockHeaders (StateT HeaderMemory m) where-    addBlockHeader = modify . addBlockHeaderMemory-    getBlockHeader bh = getBlockHeaderMemory bh <$> State.get-    getBestBlockHeader = gets memoryBestHeader-    setBestBlockHeader bn = modify $ \s -> s { memoryBestHeader = bn }---- | Initialize memory-based chain.-initialChain :: Network -> HeaderMemory-initialChain net = HeaderMemory-    { memoryHeaderMap = genesisMap net-    , memoryBestHeader = genesisNode net-    }---- | Initialize map for memory-based chain.-genesisMap :: Network -> BlockMap-genesisMap net =-    HashMap.singleton-        (shortBlockHash (headerHash (getGenesisHeader net)))-        (toShort (encode (genesisNode net)))---- | Add block header to memory block map.-addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory-addBlockHeaderMemory bn s@HeaderMemory{..} =-    let bm' = addBlockToMap bn memoryHeaderMap-    in s { memoryHeaderMap = bm' }---- | Get block header from memory block map.-getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode-getBlockHeaderMemory bh HeaderMemory {..} = do-    bs <- shortBlockHash bh `HashMap.lookup` memoryHeaderMap-    eitherToMaybe . decode $ fromShort bs---- | Calculate short block hash taking eight non-zero bytes from the 16-byte--- hash. This function will take the bytes that are not on the zero-side of the--- hash, making colissions between short block hashes difficult.-shortBlockHash :: BlockHash -> ShortBlockHash-shortBlockHash = either error id . decode . B.take 8 . encode---- | Add a block to memory-based block map.-addBlockToMap :: BlockNode -> BlockMap -> BlockMap-addBlockToMap node =-    HashMap.insert-    (shortBlockHash $ headerHash $ nodeHeader node)-    (toShort $ encode node)---- | Get the ancestor of the provided 'BlockNode' at the specified--- 'BlockHeight'.-getAncestor :: BlockHeaders m-            => BlockHeight-            -> BlockNode-            -> m (Maybe BlockNode)-getAncestor height node-    | height > nodeHeight node = return Nothing-    | otherwise = go node-  where-    e1 = error "Could not get skip header"-    e2 = error "Could not get previous block header"-    go walk-        | nodeHeight walk > height =-            let heightSkip = skipHeight (nodeHeight walk)-                heightSkipPrev = skipHeight (nodeHeight walk - 1)-             in if not (isGenesis walk) &&-                   (heightSkip == height ||-                    (heightSkip > height &&-                     not-                         (heightSkipPrev < heightSkip - 2 &&-                          heightSkipPrev >= height)))-                    then do-                        walk' <- fromMaybe e1 <$> getBlockHeader (nodeSkip walk)-                        go walk'-                    else do-                        walk' <--                            fromMaybe e2 <$>-                            getBlockHeader (prevBlock (nodeHeader walk))-                        go walk'-        | otherwise = return $ Just walk---- | Is the provided 'BlockNode' the Genesis block?-isGenesis :: BlockNode -> Bool-isGenesis 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-              -> Timestamp       -- ^ current time-              -> [BlockHeader]-              -> m (Either String [BlockNode])-connectBlocks _ _ [] = return $ Right []-connectBlocks net t bhs@(bh:_) =-    runExceptT $ do-        unless (chained bhs) $-            throwError "Blocks to connect do not form a chain"-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        bb <- lift getBestBlockHeader-        go par [] bb par pars bhs >>= \case-            bns@(bn:_) -> do-                lift $ addBlockHeaders bns-                let bb' = chooseBest bn bb-                when (bb' /= bb) $ lift $ setBestBlockHeader bb'-                return bns-            _ -> undefined-  where-    chained (h1:h2:hs) = headerHash h1 == prevBlock h2 && chained (h2 : hs)-    chained _          = True-    skipit lbh ls par-        | sh == nodeHeight lbh = return lbh-        | sh < nodeHeight lbh = do-            skM <- lift $ getAncestor sh lbh-            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        | otherwise = do-            let sn = ls !! fromIntegral (nodeHeight par - sh)-            when (nodeHeight sn /= sh) $-                throwError "BUG: Node height not right in skip"-            return sn-      where-        sh = skipHeight (nodeHeight par + 1)-    go _ acc _ _ _ [] = return acc-    go lbh acc bb par pars (h:hs) = do-        sk <- skipit lbh acc par-        bn <- ExceptT . return $ validBlock net t bb par pars h sk-        go lbh (bn : acc) (chooseBest bn bb) bn (take 10 $ par : pars) hs---- | Block's parent. If the block header is in the store, its parent must also--- be there. No block header get deleted or pruned from the store.-parentBlock :: BlockHeaders m-            => BlockHeader-            -> m (Maybe BlockNode)-parentBlock bh = getBlockHeader (prevBlock bh)---- | Validate and connect single block header to the block chain. Return 'Left'--- if fails to be validated.-connectBlock ::-       BlockHeaders m-    => Network-    -> Timestamp -- ^ current time-    -> BlockHeader-    -> m (Either String BlockNode)-connectBlock net t bh =-    runExceptT $ do-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        skM <- lift $ getAncestor (skipHeight (nodeHeight par + 1)) par-        sk <--            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        bb <- lift getBestBlockHeader-        bn <- ExceptT . return $ validBlock net t bb par pars bh sk-        let bb' = chooseBest bb bn-        lift $ addBlockHeader bn-        when (bb /= bb') . lift $ setBestBlockHeader bb'-        return bn---- | Validate this block header. Build a 'BlockNode' if successful.-validBlock :: Network-           -> Timestamp     -- ^ current time-           -> BlockNode     -- ^ best block-           -> BlockNode     -- ^ immediate parent-           -> [BlockNode]   -- ^ 10 parents above-           -> BlockHeader   -- ^ header to validate-           -> BlockNode     -- ^ skip node (black magic)-           -> Either String BlockNode-validBlock net t bb par pars bh sk = do-    let mt = medianTime . map (blockTimestamp . nodeHeader) $ par : pars-        nt = blockTimestamp bh-        hh = headerHash bh-        nv = blockVersion bh-        ng = nodeHeight par + 1-        aw = nodeWork par + headerWork bh-    unless (isValidPOW net bh) $-        Left $ "Proof of work failed: " ++ show (headerHash bh)-    unless (nt <= t + 2 * 60 * 60) $-        Left $ "Invalid header timestamp: " ++ show nt-    unless (nt >= mt) $-        Left $ "Block timestamp too early: " ++ show nt-    unless (afterLastCP net (nodeHeight bb) ng) $-        Left $ "Rewriting pre-checkpoint chain: " ++ show ng-    unless (validCP net ng hh) $-        Left $ "Rejected checkpoint: " ++ show ng-    unless (bip34 net ng hh) $-        Left $ "Rejected BIP-34 block: " ++ show hh-    unless (validVersion net ng nv) $-        Left $ "Invalid block version: " ++ show nv-    return BlockNode { nodeHeader = bh-                     , nodeHeight = ng-                     , nodeWork = aw-                     , nodeSkip = headerHash $ nodeHeader sk-                     }---- | Return the median of all provided timestamps. Can be unsorted. Error on--- empty list.-medianTime :: [Timestamp] -> Timestamp-medianTime ts-    | null ts = error "Cannot compute median time of empty header list"-    | otherwise = sort ts !! (length ts `div` 2)---- | Calculate the height of the skip (magic) block that corresponds to the--- given height. The block hash of the ancestor at that height will be placed on--- the 'BlockNode' structure to help locate ancestors at any height quickly.-skipHeight :: BlockHeight -> BlockHeight-skipHeight height-    | height < 2 = 0-    | height .&. 1 /= 0 = invertLowestOne (invertLowestOne $ height - 1) + 1-    | otherwise = invertLowestOne height---- | Part of the skip black magic calculation.-invertLowestOne :: BlockHeight -> BlockHeight-invertLowestOne height = height .&. (height - 1)---- | Get a number of parents for the provided block.-getParents :: BlockHeaders m-           => Int-           -> BlockNode-           -> m [BlockNode]   -- ^ starts from immediate parent-getParents = getpars []-  where-    getpars acc 0 _ = return $ reverse acc-    getpars acc 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-        -> BlockHeight  -- ^ new child height-        -> BlockHash    -- ^ new child hash-        -> Bool-validCP net height newChildHash =-    case lookup height (getCheckpoints net) of-        Just cpHash -> cpHash == newChildHash-        Nothing     -> True---- | New block height above the last checkpoint imported. Used to prevent a--- reorg below the highest checkpoint that was already imported.-afterLastCP :: Network-            -> BlockHeight  -- ^ best height-            -> BlockHeight  -- ^ new imported block height-            -> Bool-afterLastCP net bestHeight newChildHeight =-    case lM of-        Just l  -> l < newChildHeight-        Nothing -> True-  where-    lM =-        listToMaybe . reverse $-        [c | (c, _) <- getCheckpoints net, c <= bestHeight]---- | This block should be at least version 2 (BIP34). Block height must be--- included in the coinbase transaction to prevent non-unique transaction--- hashes.-bip34 :: Network-      -> BlockHeight  -- ^ new child height-      -> BlockHash    -- ^ new child hash-      -> Bool-bip34 net height hsh-    | fst (getBip34Block net) == 0 = True-    | fst (getBip34Block net) == height = snd (getBip34Block net) == hsh-    | otherwise = True---- | Check if the provided block height and version are valid.-validVersion :: Network-             -> BlockHeight  -- ^ new child height-             -> Word32       -- ^ new child version-             -> Bool-validVersion net height version-    | version < 2 = height < fst (getBip34Block net)-    | version < 3 = height < getBip66Height net-    | version < 4 = height < getBip65Height net-    | otherwise = True---- | Find last block with normal, as opposed to minimum difficulty (for test--- networks).-lastNoMinDiff :: BlockHeaders m => Network -> BlockNode -> m BlockNode-lastNoMinDiff _ 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 b-        | otherwise = return a--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-             -> BlockHeader  -- ^ last block in previous retarget (parent)-             -> Timestamp    -- ^ timestamp of first block in previous retarget-             -> Word32-calcNextWork net header time-    | getPowNoRetargetting net = blockBits header-    | new > getPowLimit net = encodeCompact (getPowLimit net)-    | otherwise = encodeCompact new-  where-    s = blockTimestamp header - time-    n | s < getTargetTimespan net `div` 4 = getTargetTimespan net `div` 4-      | s > getTargetTimespan net * 4 = getTargetTimespan net * 4-      | otherwise = s-    l = fst $ decodeCompact $ blockBits header-    new = l * fromIntegral n `div` fromIntegral (getTargetTimespan net)---- | Returns True if the difficulty target (bits) of the header is valid and the--- proof of work of the header matches the advertised difficulty target. This--- function corresponds to the function @CheckProofOfWork@ from @bitcoind@ in--- @main.cpp@.-isValidPOW :: Network -> BlockHeader -> Bool-isValidPOW net h-    | target <= 0 || over || target > getPowLimit net = False-    | otherwise = blockPOW (headerHash h) <= fromIntegral target-  where-    (target, over) = decodeCompact $ blockBits h---- | Returns the proof of work of a block header hash as an 'Integer' number.-blockPOW :: BlockHash -> Integer-blockPOW =  bsToInteger . B.reverse . encode---- | Returns the work represented by this block. Work is defined as the number--- of tries needed to solve a block in the average case with respect to the--- target.-headerWork :: BlockHeader -> Integer-headerWork bh = largestHash `div` (target + 1)-  where-    target      = fst $ decodeCompact $ blockBits bh-    largestHash = 1 `shiftL` 256---- | Number of blocks on average between difficulty cycles (2016 blocks).-diffInterval :: Network -> Word32-diffInterval net = getTargetTimespan net `div` getTargetSpacing net---- | Compare two blocks to get the best.-chooseBest :: BlockNode -> BlockNode -> BlockNode-chooseBest b1 b2 | nodeWork b1 == nodeWork b2 =-                       if nodeHeight b1 >= nodeHeight b2-                       then b1-                       else b2-                 | nodeWork b1 > nodeWork b2 = b1-                 | otherwise = b2---- | Get list of blocks for a block locator.-blockLocatorNodes :: BlockHeaders m => BlockNode -> m [BlockNode]-blockLocatorNodes best =-    reverse <$> go [] best 1-  where-    e1 = error "Could not get ancestor"-    go loc bn n =-        let loc' = bn : loc-            n' = if length loc' > 10-                 then n * 2-                 else 1-        in if nodeHeight bn < n'-           then do a <- fromMaybe e1 <$> getAncestor 0 bn-                   return $ a : loc'-           else do let h = nodeHeight bn - n'-                   bn' <- fromMaybe e1 <$> getAncestor h bn-                   go loc' bn' n'---- | Get block locator.-blockLocator :: BlockHeaders m => BlockNode -> m BlockLocator-blockLocator bn = map (headerHash . nodeHeader) <$> blockLocatorNodes bn---- | Become rich beyond your wildest dreams.-mineBlock :: Network -> Word32 -> BlockHeader -> BlockHeader-mineBlock net seed h =-    head-        [ j-        | i <- (+ seed) <$> [0 .. maxBound]-        , let j = h {bhNonce = i}-        , isValidPOW net j-        ]---- | Generate and append new blocks (mining). Only practical in regtest network.-appendBlocks ::-       Network-    -> Word32 -- ^ random seed-    -> BlockHeader-    -> Int-    -> [BlockHeader]-appendBlocks _ _ _ 0 = []-appendBlocks net seed bh i =-    bh' : appendBlocks net seed bh' (i - 1)-  where-    bh' = mineBlock net seed bh-        { prevBlock = headerHash bh-          -- Just to make it different in every header-        , merkleRoot = sha256 $ encode seed-        }---- | Find the last common block ancestor between provided block headers.-splitPoint :: BlockHeaders m => BlockNode -> BlockNode -> m BlockNode-splitPoint l r = do-    let h = min (nodeHeight l) (nodeHeight r)-    ll <- fromMaybe e <$> getAncestor h l-    lr <- fromMaybe e <$> getAncestor h r-    f ll lr-  where-    e = error "BUG: Could not get ancestor at lowest height"-    f ll lr =-        if ll == lr-            then return lr-            else do-                let h = nodeHeight ll - 1-                pl <- fromMaybe e <$> getAncestor h ll-                pr <- fromMaybe e <$> getAncestor h lr-                f pl pr---- | Generate the entire Genesis block for 'Network'.-genesisBlock :: Network -> Block-genesisBlock net = Block (getGenesisHeader net) [genesisTx]---- | Compute block subsidy at particular height.-computeSubsidy :: Network -> BlockHeight -> Word64-computeSubsidy net height =-    let halvings = height `div` getHalvingInterval net-        ini = 50 * 100 * 1000 * 1000-     in if halvings >= 64-            then 0-            else ini `shiftR` fromIntegral halvings+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Block.Headers+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Block chain header synchronization and proof-of-work consensus functions.+module Haskoin.Block.Headers+  ( -- * Block Headers+    BlockNode (..),+    BlockHeaders (..),+    BlockWork,+    genesisNode,+    genesisBlock,+    isGenesis,+    chooseBest,++    -- ** Header Store+    parentBlock,+    getParents,+    getAncestor,+    splitPoint,+    connectBlocks,+    connectBlock,+    blockLocator,++    -- ** Header Memory Store+    HeaderMemory (..),+    ShortBlockHash,+    BlockMap,+    shortBlockHash,+    initialChain,+    genesisMap,++    -- ** Validation+    validBlock,+    validCP,+    afterLastCP,+    bip34,+    validVersion,+    validTimeWarp,+    medianTime,+    mtp,++    -- ** Difficulty+    nextWorkRequired,+    nextPowWorkRequired,+    calcNextWork,+    lastNoMinDiff,+    diffInterval,+    nextEdaWorkRequired,+    nextDaaWorkRequired,+    computeTarget,+    getSuitableBlock,+    nextAsertWorkRequired,+    asertAnchor,+    computeAsertBits,+    isValidPOW,+    blockPOW,+    headerWork,++    -- ** Helper Functions+    blockLocatorNodes,+    skipHeight,+    appendBlocks,+    mineBlock,+    computeSubsidy,+    firstGreaterOrEqual,+    lastSmallerOrEqual,+  )+where++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.Int (Int32)+import Data.List (sort)+import Data.Maybe (fromMaybe, isJust)+import Data.Ord (comparing)+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.Util+import Numeric (showHex)++-- | 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, NFData)++-- | Hashes the header only, so that two nodes that are equal hash alike.+instance Hashable BlockNode where+  hashWithSalt salt = hashWithSalt salt . (.header)++instance Serial BlockNode where+  deserialize = do+    header <- deserialize+    height <- getWord32le+    work <- getInteger+    -- Chain work is a 256-bit unsigned number in Bitcoin Core.+    when (work < 0 || work >= 2 ^ (256 :: Int)) $+      fail "BlockNode: chain work out of range"+    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++-- | Two nodes are the same when they hold the same header.+instance Eq BlockNode where+  (==) = (==) `on` (.header)++-- | Nodes are ordered by height, then by header, so that equal nodes compare+-- as equal and nodes at the same height still have a total order.+instance Ord BlockNode where+  compare = comparing (\bn -> (bn.height, bn.header))++-- | 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. The map is keyed by eight bytes+-- of the hash, so the node found there is returned only when its header has+-- the hash that was asked for.+getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode+getBlockHeaderMemory bh s = do+  bs <- shortBlockHash bh `HashMap.lookup` s.blocks+  node <- eitherToMaybe . runGetS deserialize $ fromShort bs+  guard (headerHash node.header == bh)+  return node++-- | 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 into the+-- block chain. Each header is validated against the headers before it and+-- added to the store as it passes, so a failure keeps the headers that came+-- before it and makes the last of them the best block when it has the most+-- work. Return 'Left' on failure with error information, otherwise the new+-- nodes with the newest one first.+--+-- A header that is already in the store is not validated again: its stored+-- node is returned, as @AcceptBlockHeader@ does in Bitcoin Core.+--+-- What bounds the headers a peer can add to the store:+--+-- * Once the best block has reached a checkpoint, a new header at or below+--   that height is refused.+-- * Once the best block has the 'minimumChainWork' of the network, a new+--   header whose chain has less work is refused. Such a header can only fork+--   from far below the tip, where proof of work was cheap.+--+-- Neither applies while the node is still syncing below them. Bitcoin Core+-- stores no header from a peer before its chain has shown the minimum work.+-- This function has no such phase, so a caller that syncs from untrusted+-- peers should not keep headers for good before the best block has that+-- work.+connectBlocks ::+  (BlockHeaders m) =>+  Network ->+  -- | current time+  Timestamp ->+  [BlockHeader] ->+  m (Either String [BlockNode])+connectBlocks _ _ [] = return $ Right []+connectBlocks net t bhs+  | not (chained bhs) = return $ Left "Blocks to connect do not form a chain"+  | otherwise = do+      best <- getBestBlockHeader+      (result, nodes, best') <- go best [] bhs+      when (best' /= best) $ setBestBlockHeader best'+      return $ either Left (const (Right nodes)) result+  where+    chained (h1 : h2 : hs) = headerHash h1 == h2.prev && chained (h2 : hs)+    chained _ = True+    go best nodes [] = return (Right (), nodes, best)+    go best nodes (bh : rest) =+      connectHeader net t best bh >>= \case+        Left err -> return (Left err, nodes, best)+        Right node -> go (chooseBest best node) (node : nodes) rest++-- | 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+    best <- lift getBestBlockHeader+    node <- ExceptT $ connectHeader net t best bh+    let best' = chooseBest best node+    when (best' /= best) $ lift $ setBestBlockHeader best'+    return node++-- | Validate one header against the store and add it to the store. The best+-- block passed in is only used by the checkpoint rule and is not updated.+connectHeader ::+  (BlockHeaders m) =>+  Network ->+  Timestamp ->+  BlockNode ->+  BlockHeader ->+  m (Either String BlockNode)+connectHeader net t best bh =+  getBlockHeader (headerHash bh) >>= \case+    Just known -> return (Right known)+    Nothing -> connectNewHeader net t best bh++-- | Validate a header that is not in the store yet, and add it.+connectNewHeader ::+  (BlockHeaders m) =>+  Network ->+  Timestamp ->+  BlockNode ->+  BlockHeader ->+  m (Either String BlockNode)+connectNewHeader net t best bh =+  runExceptT $ do+    par <-+      maybeToExceptT+        "Could not get parent block"+        (MaybeT (parentBlock bh))+    pars <- lift $ getParents 10 par+    skipNode <-+      lift (getAncestor (skipHeight (par.height + 1)) par) >>= \case+        Just sk -> return sk+        Nothing ->+          throwError $+            "BUG: Could not get skip for block "+              ++ show (headerHash par.header)+    requiredBits <- lift $ nextWorkRequired net par bh+    node <- ExceptT . return $ validBlock net t best par pars bh skipNode requiredBits+    lift $ addBlockHeader node+    return node++-- | Validate this block header. Build a 'BlockNode' if successful. The checks+-- follow @CheckBlockHeader@ and @ContextualCheckBlockHeader@ in Bitcoin Core.+validBlock ::+  Network ->+  -- | current time+  Timestamp ->+  -- | best block+  BlockNode ->+  -- | immediate parent+  BlockNode ->+  -- | 10 parents above+  [BlockNode] ->+  -- | header to validate+  BlockHeader ->+  -- | skip node (black magic)+  BlockNode ->+  -- | difficulty bits the chain requires, from 'nextWorkRequired'+  Word32 ->+  Either String BlockNode+validBlock net t bb par pars bh sk requiredBits = 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 (bh.bits == requiredBits) $+    Left $+      "Incorrect difficulty bits: " ++ showBits bh.bits ++ ", expected " ++ showBits requiredBits+  unless (toInteger nt <= toInteger t + 2 * 60 * 60) $+    Left $+      "Invalid header timestamp: " ++ show nt+  -- The timestamp must be strictly above the median time past.+  unless (nt > mt) $+    Left $+      "Block timestamp too early: " ++ show nt+  unless (validTimeWarp net ng par.header.timestamp nt) $+    Left $+      "Block timestamp too early for retarget: " ++ show nt+  -- Once the best chain has the minimum chain work, a header with less can+  -- only be on a fork from far below the tip.+  unless (aw >= net.minimumChainWork || bb.work < net.minimumChainWork) $+    Left $+      "Header chain has less work than the minimum chain work: " ++ show ng+  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+      }+  where+    showBits b = "0x" ++ showHex b ""++-- | Return the median of all provided timestamps. Can be unsorted. For an+-- even count the upper of the two middle values is taken, as Bitcoin Core+-- does. 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)++-- | Median time past of a block: the median of the timestamps of the block and+-- its ten closest ancestors, or fewer near genesis. This is+-- @GetMedianTimePast@ in Bitcoin Core.+mtp :: (BlockHeaders m) => BlockNode -> m Timestamp+mtp bn = do+  pars <- getParents 10 bn+  return $ medianTime $ map (.header.timestamp) (bn : pars)++-- | 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 [c | (c, _) <- net.checkpoints, c <= bestHeight] of+    [] -> True+    reached -> maximum reached < newChildHeight++-- | 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+  | height /= net.bip34Height = True+  | otherwise = maybe True (== hsh) net.bip34Hash++-- | Check if the provided block height and version are valid. The three+-- conditions are independent, as in @ContextualCheckBlockHeader@ of Bitcoin+-- Core and Bitcoin Cash Node: a version below 2 is refused from the BIP34+-- height, below 3 from the BIP66 height and below 4 from the BIP65 height,+-- whatever the order of those heights on the network. The version is a+-- signed 32-bit number, so values with the top bit set are negative and+-- count as below version 2.+validVersion ::+  Network ->+  -- | new child height+  BlockHeight ->+  -- | new child version+  Word32 ->+  Bool+validVersion net height version =+  not+    ( (signedVersion < 2 && height >= net.bip34Height)+        || (signedVersion < 3 && height >= net.bip66Height)+        || (signedVersion < 4 && height >= net.bip65Height)+    )+  where+    signedVersion = fromIntegral version :: Int32++-- | BIP94 time warp rule: the first block of a retarget period may not be more+-- than 600 seconds earlier than its parent.+validTimeWarp ::+  Network ->+  -- | new child height+  BlockHeight ->+  -- | parent timestamp+  Timestamp ->+  -- | new child timestamp+  Timestamp ->+  Bool+validTimeWarp net height parent child+  | net.bip94 && height `mod` diffInterval net == 0 =+      toInteger child + 600 >= toInteger parent+  | 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++-- | Test network rule: a block more than twice the target spacing after its+-- parent may be mined at the minimum difficulty.+minDiffGap :: Network -> BlockNode -> BlockHeader -> Bool+minDiffGap net par bh =+  toInteger bh.timestamp > toInteger par.header.timestamp + 2 * toInteger net.targetSpacing++-- | Returns the work required on a block header given the previous block. This+-- corresponds to @GetNextWorkRequired@ in Bitcoin Core and Bitcoin Cash Node.+nextWorkRequired ::+  (BlockHeaders m) =>+  Network ->+  BlockNode ->+  BlockHeader ->+  m Word32+nextWorkRequired net par bh+  -- Bitcoin Cash Node never retargets on regtest, before picking an algorithm.+  | usesCashRules && net.powNoRetarget = return par.header.bits+  | otherwise =+      asertAnchor net par >>= \case+        Just anchor -> nextAsertWorkRequired net anchor par bh+        Nothing+          | activeAt net.daaHeight -> nextDaaWorkRequired net par bh+          | activeAt net.edaHeight -> nextEdaWorkRequired net par bh+          | otherwise -> nextPowWorkRequired net par bh+  where+    usesCashRules =+      isJust net.edaHeight || isJust net.daaHeight || isJust net.asertActivationTime+    activeAt = maybe False (\h -> par.height + 1 >= h)++-- | Find out the next amount of work required according to the Emergency+-- Difficulty Adjustment (EDA) algorithm from Bitcoin Cash. This corresponds to+-- @GetNextEDAWorkRequired@ in Bitcoin Cash Node.+nextEdaWorkRequired ::+  (BlockHeaders m) => Network -> BlockNode -> BlockHeader -> m Word32+nextEdaWorkRequired net par bh+  | (par.height + 1) `mod` diffInterval net == 0 =+      nextPowWorkRequired net par bh+  | net.minDiffBlocks =+      if minDiffGap net par bh+        then return powLimitBits+        else (.header.bits) <$> lastNoMinDiff net par+  | par.header.bits == powLimitBits =+      return powLimitBits+  | otherwise = do+      par6 <- fromMaybe e1 <$> getAncestor (par.height - 6) par+      parMtp <- mtp par+      par6Mtp <- mtp par6+      -- If producing the last six blocks took less than twelve hours the+      -- difficulty stays. Otherwise the target grows by a quarter.+      if toInteger parMtp - toInteger par6Mtp < 12 * 3600+        then return par.header.bits+        else do+          let (target, _) = decodeCompact par.header.bits+              target' = target + (target `shiftR` 2)+          return $ encodeCompact (min net.powLimit target')+  where+    powLimitBits = encodeCompact net.powLimit+    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. This corresponds to+-- @GetNextCashWorkRequired@ in Bitcoin Cash Node. The parent must be at or+-- above the difficulty interval height.+nextDaaWorkRequired ::+  (BlockHeaders m) => Network -> BlockNode -> BlockHeader -> m Word32+nextDaaWorkRequired net par bh+  | net.minDiffBlocks && minDiffGap net par bh = 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"++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)++-- | Anchor block of the aserti3-2d algorithm for the chain that ends at the+-- given block: the earliest ancestor, or the block itself, whose median time+-- past reaches the activation time. The anchor is the last block whose+-- difficulty came from the previous algorithm. Returns 'Nothing' when the+-- block's own median time past is still below the activation time, in which+-- case the previous algorithm applies to its children.+asertAnchor :: (BlockHeaders m) => Network -> BlockNode -> m (Maybe BlockNode)+asertAnchor net tip =+  case net.asertActivationTime of+    Nothing -> return Nothing+    Just activation -> do+      tipMtp <- mtp tip+      if tipMtp < activation+        then return Nothing+        else Just <$> search activation 0 tip.height+  where+    -- The median time past never decreases along a chain, so a binary search+    -- over the heights finds the first block that reaches the activation+    -- time. The upper bound always satisfies it.+    search activation lo hi+      | lo >= hi = ancestorAt lo+      | otherwise = do+          let mid = lo + (hi - lo) `div` 2+          midMtp <- mtp =<< ancestorAt mid+          if midMtp >= activation+            then search activation lo mid+            else search activation (mid + 1) hi+    ancestorAt h = fromMaybe e <$> getAncestor h tip+    e = error "asertAnchor: could not get ancestor"++-- | Find the next amount of work required according to the aserti3-2d+-- algorithm. This corresponds to @GetNextASERTWorkRequired@ in Bitcoin Cash+-- Node.+nextAsertWorkRequired ::+  (BlockHeaders m) =>+  Network ->+  -- | anchor block, see 'asertAnchor'+  BlockNode ->+  -- | parent of the new block+  BlockNode ->+  -- | new block header+  BlockHeader ->+  m Word32+nextAsertWorkRequired net anchor par bh+  | net.minDiffBlocks && minDiffGap net par bh = return (encodeCompact net.powLimit)+  | otherwise = do+      -- The clock starts at the anchor's parent. A genesis anchor has no+      -- parent, so its own timestamp is used, as Bitcoin Cash Node does.+      anchorParentTime <-+        if isGenesis anchor+          then return anchor.header.timestamp+          else (.header.timestamp) . fromMaybe e <$> getBlockHeader anchor.header.prev+      -- The formula is evaluated at the parent, as GetNextASERTWorkRequired+      -- does: the time and the height are the parent's. A block's own+      -- timestamp never changes its own difficulty. The minimum-difficulty+      -- rule above is the only use of the new header.+      let timeDiff = toInteger par.header.timestamp - toInteger anchorParentTime+          heightDiff = toInteger par.height - toInteger anchor.height+      return $+        computeAsertBits net.powLimit net.asertHalfLife anchor.header.bits timeDiff heightDiff+  where+    e = error "Could not get parent of ASERT anchor block"++idealBlockTime :: Integer+idealBlockTime = 10 * 60++rBits :: Int+rBits = 16++radix :: Integer+radix = 1 `shiftL` rBits++-- | The aserti3-2d formula: the target of the anchor block, doubled for every+-- half-life that the chain runs behind its ideal schedule and halved for every+-- half-life it runs ahead, in 16.16 fixed point with a cubic approximation of+-- the exponential.+computeAsertBits ::+  -- | proof-of-work limit (largest target) of the network+  Integer ->+  -- | half-life in seconds+  Integer ->+  -- | bits of the anchor block+  Word32 ->+  -- | seconds from the anchor's parent to the parent of the new block+  Integer ->+  -- | blocks from the anchor to the parent of the new block+  Integer ->+  Word32+computeAsertBits powLimit halflife anchorBits timeDiff heightDiff+  | target == 0 = encodeCompact 1+  | target > powLimit = encodeCompact powLimit+  | otherwise = encodeCompact target+  where+    anchorTarget = fst (decodeCompact anchorBits)+    -- Exponent in 16.16 fixed point. The reference C++ code truncates toward+    -- zero, hence quot.+    expo = ((timeDiff - idealBlockTime * (heightDiff + 1)) * radix) `quot` halflife+    -- Whole half-lives (an arithmetic shift, so rounded down) and the+    -- fractional part in [0, radix).+    shifts = expo `shiftR` rBits+    frac = expo - shifts * radix+    -- 2 ^ (frac / radix) in 16.16 fixed point.+    factor =+      radix+        + ( (195766423245049 * frac + 971821376 * frac ^ (2 :: Int) + 5127 * frac ^ (3 :: Int) + 2 ^ (47 :: Int))+              `shiftR` (rBits * 3)+          )+    scaled = anchorTarget * factor+    shifted+      | shifts < 0 = scaled `shiftR` fromInteger (negate shifts)+      | otherwise = scaled `shiftL` fromInteger shifts+    target = shifted `shiftR` rBits++-- | Compute Bitcoin Cash DAA target for a new block, like @ComputeTarget@ in+-- Bitcoin Cash Node.+computeTarget ::+  Network ->+  -- | first suitable block of the window+  BlockNode ->+  -- | last suitable block of the window+  BlockNode ->+  Integer+computeTarget net firstBlock lastBlock =+  let spacing = toInteger net.targetSpacing+      work = (lastBlock.work - firstBlock.work) * spacing+      actual = toInteger lastBlock.header.timestamp - toInteger firstBlock.header.timestamp+      clamped = max (72 * spacing) (min (288 * spacing) actual)+      work' = work `div` clamped+   in -- (2^256 - work) / work, as the unsigned negation in the reference code+      (2 ^ (256 :: Int) - work') `div` work'++-- | Get suitable block for Bitcoin Cash DAA computation: the block with the+-- median timestamp among the given block, its parent and its grandparent. On+-- equal timestamps the same block as Bitcoin Cash Node's sorting network is+-- chosen.+getSuitableBlock :: (BlockHeaders m) => BlockNode -> m BlockNode+getSuitableBlock par = do+  unless (par.height >= 3) $ error "Block height is less than three"+  parents <- getParents 2 par+  case parents of+    [parent, grandparent] -> return $ medianOfThree grandparent parent par+    _ -> error "Could not get the two parents of the block"+  where+    later x y = x.header.timestamp > y.header.timestamp+    -- Oldest first, swapping only on a strictly later timestamp.+    medianOfThree b0 b1 b2 =+      let (c0, c2) = if later b0 b2 then (b2, b0) else (b0, b2)+          (d0, d1) = if later c0 b1 then (b1, c0) else (c0, b1)+          (e1, _) = if later d1 c2 then (c2, d1) else (d1, c2)+       in const e1 d0++-- | 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 minDiffGap net par bh+            then return $ encodeCompact net.powLimit+            else do+              d <- lastNoMinDiff net par+              return d.header.bits+        else return par.header.bits+  | otherwise = do+      -- First block of the retarget period that ends with the parent.+      f <- fromMaybe e1 <$> getAncestor (par.height - (diffInterval net - 1)) par+      -- BIP94 bases the retarget on the first block of the period, which+      -- cannot use the min-difficulty exception, instead of the last one.+      let h+            | net.bip94 = (par.header :: BlockHeader) {bits = f.header.bits}+            | otherwise = par.header+      return $ calcNextWork net h f.header.timestamp+  where+    e1 = error "Could not get ancestor for block header"++-- | Computes the work required for the first block in a new retarget period.+-- The actual timespan is limited to a quarter and four times the target+-- timespan. A negative timespan, when the last block is dated before the+-- first, is limited like a short one.+calcNextWork ::+  Network ->+  -- | last block in previous retarget (parent)+  BlockHeader ->+  -- | timestamp of first block in previous retarget+  Timestamp ->+  Word32+calcNextWork net header firstTime+  | net.powNoRetarget = header.bits+  | new > net.powLimit = encodeCompact net.powLimit+  | otherwise = encodeCompact new+  where+    timespan = toInteger net.targetTimespan+    actual = toInteger header.timestamp - toInteger firstTime+    clamped = max (timespan `div` 4) (min (timespan * 4) actual)+    new = fst (decodeCompact header.bits) * clamped `div` timespan++-- | 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. A header whose bits are negative, overflow or are zero has no work,+-- like @GetBlockProof@ in Bitcoin Core.+headerWork :: BlockHeader -> Integer+headerWork bh+  | target <= 0 || over = 0+  | otherwise = largestHash `div` (target + 1)+  where+    (target, over) = 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: the one with more accumulated work.+-- When the work is equal the first argument is kept, whatever the heights,+-- so callers pass the block they already have first and the block that was+-- seen first stays the best. This is the order of+-- @CBlockIndexWorkComparator@ in Bitcoin Core: chain work, then arrival.+chooseBest :: BlockNode -> BlockNode -> BlockNode+chooseBest b1 b2+  | b2.work > b1.work = b2+  | otherwise = b1++-- | Get list of blocks for a block locator: the ten blocks below the best, then+-- blocks at exponentially growing distances, then genesis. This is+-- @LocatorEntries@ in Bitcoin Core.+blockLocatorNodes :: (BlockHeaders m) => BlockNode -> m [BlockNode]+blockLocatorNodes best =+  reverse <$> go [] (0 :: Int) best 1+  where+    e1 = error "Could not get ancestor"+    go acc count node step+      | isGenesis node = return (node : acc)+      | otherwise = do+          let acc' = node : acc+              count' = count + 1+              height = if node.height > step then node.height - step else 0+              step' = if count' > 10 then step * 2 else step+          ancestor <- fromMaybe e1 <$> getAncestor height node+          go acc' count' ancestor step'++-- | 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 =+  case filter (isValidPOW net) [h {nonce = i} | i <- (+ seed) <$> [0 .. maxBound]] of+    (mined : _) -> mined+    [] -> error "mineBlock: no nonce satisfies the target"++-- | Generate and append new blocks (mining). Only practical in regtest network.+-- Every block is dated one target spacing after its parent, so the chain+-- passes the median time past rule.+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+          { -- Version 4 passes the version rules of BIP34, BIP65 and BIP66+            -- at any height. Regtest applies them from height 1.+            version = 4,+            prev = headerHash bh,+            timestamp = bh.timestamp + net.targetSpacing,+            -- 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 -> Block+genesisBlock net = Block net.genesisHeader [net.genesisTx]++-- | 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,255 +1,413 @@ {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Haskoin.Block.Merkle-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-} -Function to deal with Merkle trees inside 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-    , PartialMerkleTree-    , buildMerkleRoot-    , merkleBlockTxs-    , testMerkleRoot-    , buildPartialMerkle-    , decodeMerkleFlags-    , encodeMerkleFlags-    , calcTreeHeight-    , calcTreeWidth-    , hash2-    , calcHash-    , traverseAndBuild-    , traverseAndExtract-    , extractMatches-    , splitIn-    , boolsToWord8-    ) where+  ( -- * Merkle Blocks+    MerkleBlock (..),+    MerkleRoot,+    FlagBits,+    PartialMerkleTree,+    buildMerkleRoot,+    merkleBlockTxs,+    testMerkleRoot,+    buildPartialMerkle,+    extractMatches,+    extractIndexedMatches,+    merkleBlockIndexedTxs,+    maxMerkleFlagBits,+    decodeMerkleFlags,+    encodeMerkleFlags,+    calcTreeHeight,+    calcTreeWidth,+    hash2,+    calcHash,+    splitIn,+    boolsToWord8,+  )+where -import           Control.DeepSeq-import           Control.Monad              (forM_, replicateM, when)-import           Data.Bits-import qualified Data.ByteString            as BS-import           Data.Either                (isRight)-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize             (Serialize, encode, get, put)-import           Data.Serialize.Get         (getWord32le, getWord8)-import           Data.Serialize.Put         (putWord32le, putWord8)-import           Data.Word                  (Word32, Word8)-import           GHC.Generics-import           Haskoin.Block.Common-import           Haskoin.Constants-import           Haskoin.Crypto.Hash-import           Haskoin.Network.Common-import           Haskoin.Transaction.Common+import Control.DeepSeq+import Control.Monad (replicateM, when)+import Data.Binary (Binary (..))+import Data.Bits+import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either (isRight)+import Data.Hashable+import Data.Maybe (listToMaybe)+import Data.Serialize (Serialize (..))+import qualified Data.Vector as V+import Data.Word (Word32, Word8)+import GHC.Generics+import Haskoin.Block.Common+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Transaction.Common  -- | Hash of the block's Merkle root.-type MerkleRoot        = Hash256+type MerkleRoot = Hash256  -- | Bits that are used to rebuild partial merkle tree transaction hash list.-type FlagBits          = [Bool]+type FlagBits = [Bool]  -- | Partial Merkle tree for a filtered block. type PartialMerkleTree = [Hash256]  -- | Filtered block: a block with a partial Merkle tree that only includes the -- transactions that pass a bloom filter that was negotiated.-data MerkleBlock =-    MerkleBlock {-                -- | block header-                  merkleHeader    :: !BlockHeader-                -- | total number of transactions in block-                , merkleTotalTxns :: !Word32-                -- | hashes in depth-first order-                , mHashes         :: !PartialMerkleTree-                -- | bits to rebuild partial merkle tree-                , mFlags          :: !FlagBits-                } deriving (Eq, Show, Read, Generic, Hashable, NFData)+data MerkleBlock = MerkleBlock+  { -- | block header+    header :: !BlockHeader,+    -- | total number of transactions in block+    txn :: !Word32,+    -- | hashes in depth-first order+    hashes :: !PartialMerkleTree,+    -- | bits to rebuild partial merkle tree+    flags :: !FlagBits+  }+  deriving (Eq, Show, Read, Generic, Hashable, NFData) -instance Serialize MerkleBlock where+-- | The parse fails for a merkle block that no verifier accepts because of+-- its size alone: more hashes than transactions, or more flag bytes than+-- 'maxMerkleFlagBits' allows. Both are refused by @ExtractMatches@ of+-- @CPartialMerkleTree@ in Bitcoin Core and Bitcoin Cash Node, the first as+-- such and the second as unused flag bits. Checking them here, before the+-- hashes and the flags are read, bounds the memory that a peer can make the+-- parser use. The flag bytes are kept packed and unpacked on demand.+instance Serial MerkleBlock where+  deserialize = do+    header <- deserialize+    ntx <- getWord32le+    VarInt hashCount <- deserialize+    when (hashCount > maxListSize) $+      fail ("List count too large: " ++ show hashCount)+    when (hashCount > fromIntegral ntx) $+      fail $+        "Merkle block with more hashes than transactions: "+          ++ show hashCount+          ++ " hashes, "+          ++ show ntx+          ++ " transactions"+    hashes <- replicateM (fromIntegral hashCount) deserialize+    VarInt flagCount <- deserialize+    let maxBits = maxMerkleFlagBits (fromIntegral ntx) (fromIntegral hashCount)+    when (toInteger flagCount > (maxBits + 7) `div` 8) $+      fail $+        "Merkle block with more flag bytes than its tree can use: "+          ++ show flagCount+          ++ " bytes, at most "+          ++ show ((maxBits + 7) `div` 8)+    flagBytes <- getByteString (fromIntegral flagCount)+    -- The copy lets go of the message buffer. The list of bits is built when+    -- it is asked for.+    return $ MerkleBlock header ntx hashes (decodeMerkleFlags (B.unpack (B.copy flagBytes))) -    get = do-        header <- get-        ntx    <- getWord32le-        (VarInt matchLen) <- get-        hashes <- replicateM (fromIntegral matchLen) get-        (VarInt flagLen)  <- get-        ws <- replicateM (fromIntegral flagLen) getWord8-        return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)+  serialize (MerkleBlock h ntx hashes flags) = do+    serialize h+    putWord32le ntx+    putVarIntList serialize hashes+    putVarIntList putWord8 (encodeMerkleFlags flags) -    put (MerkleBlock h ntx hashes flags) = do-        put h-        putWord32le ntx-        putVarInt $ length hashes-        forM_ hashes put-        let ws = encodeMerkleFlags flags-        putVarInt $ length ws-        forM_ ws putWord8+instance Binary MerkleBlock where+  put = serialize+  get = deserialize --- | Unpack Merkle flags into 'FlagBits' structure.+instance Serialize MerkleBlock where+  put = serialize+  get = deserialize++-- | Largest number of flag bits that a valid partial Merkle tree can use,+-- for a block of the given number of transactions and a proof of the given+-- number of hashes. A proof spends one flag bit on each tree node it visits.+-- Every node it visits lies on the path from the root to one of its hashes,+-- which has at most the tree height plus one nodes, and it cannot visit more+-- nodes than the tree has.+maxMerkleFlagBits ::+  -- | number of transactions in the block+  Integer ->+  -- | number of hashes in the proof+  Integer ->+  Integer+maxMerkleFlagBits ntx hashCount+  | ntx <= 0 || hashCount <= 0 = 0+  | otherwise = min (hashCount * (height + 1)) nodes+  where+    widths = takeWhile (> 1) (iterate (\w -> (w + 1) `div` 2) ntx)+    -- one level per width above one, and the root+    height = toInteger (length widths)+    nodes = sum widths + 1++-- | Unpack Merkle flags into 'FlagBits' structure. The least significant bit+-- of each byte comes first. decodeMerkleFlags :: [Word8] -> FlagBits-decodeMerkleFlags ws =-    [ b | p <- [ 0 .. length ws * 8 - 1 ]-        , b <- [ testBit (ws !! (p `div` 8)) (p `mod` 8) ]-    ]+decodeMerkleFlags = concatMap (\w -> map (testBit w) [0 .. 7]) --- | Pack Merkle flags from 'FlagBits'.+-- | Pack Merkle flags from 'FlagBits'. The last byte is padded with zero bits. encodeMerkleFlags :: FlagBits -> [Word8] encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs  -- | Computes the height of a Merkle tree.-calcTreeHeight :: Int -- ^ number of transactions (leaf nodes)-               -> Int -- ^ height of the merkle tree-calcTreeHeight ntx | ntx < 2 = 0-                   | even ntx  = 1 + calcTreeHeight (ntx `div` 2)-                   | otherwise = calcTreeHeight $ ntx + 1+calcTreeHeight ::+  -- | number of transactions (leaf nodes)+  Int ->+  -- | height of the merkle tree+  Int+calcTreeHeight ntx+  | ntx < 2 = 0+  | even ntx = 1 + calcTreeHeight (ntx `div` 2)+  | otherwise = calcTreeHeight $ ntx + 1  -- | Computes the width of a Merkle tree at a specific height. The transactions -- are at height 0.-calcTreeWidth :: Int -- ^ number of transactions (leaf nodes)-              -> Int -- ^ height at which we want to compute the width-              -> Int -- ^ width of the Merkle tree+calcTreeWidth ::+  -- | number of transactions (leaf nodes)+  Int ->+  -- | height at which we want to compute the width+  Int ->+  -- | width of the Merkle tree+  Int calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h --- | Computes the root of a Merkle tree from a list of leaf node hashes.-buildMerkleRoot :: [TxHash]   -- ^ transaction hashes (leaf nodes)-                -> MerkleRoot -- ^ root of the Merkle tree-buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs+-- | Every level of a Merkle tree, leaves first and root last. Each level+-- lists its nodes from left to right. A level with an odd number of nodes+-- pairs its last node with itself to build the level above.+type MerkleLevels = [V.Vector Hash256] +-- | Build all levels of the Merkle tree over the given leaves, in linear+-- time. Also report whether some node combines two identical hashes, which+-- Bitcoin Core calls a mutated tree (CVE-2012-2459). The last node of an odd+-- level pairing with itself does not count as a duplicate.+merkleLevels :: [Hash256] -> (MerkleLevels, Bool)+merkleLevels leaves = go (V.fromList leaves) False []+  where+    go level mutated above+      | V.length level <= 1 = (reverse (level : above), mutated)+      | otherwise = go (V.fromList parents) (mutated || duplicated) (level : above)+      where+        count = V.length level+        pairs = [(level V.! i, level V.! min (i + 1) (count - 1)) | i <- [0, 2 .. count - 1]]+        parents = [hash2 l r | (l, r) <- pairs]+        duplicated = or [level V.! i == level V.! (i + 1) | i <- [0, 2 .. count - 2]]++-- | Computes the root of a Merkle tree from a list of leaf node hashes. Fails+-- for an empty list, since a block has at least its coinbase, and for a+-- mutated tree, in which two sibling nodes are identical (CVE-2012-2459). A+-- block with duplicate transactions is invalid, and its mutated tree has the+-- same root as the valid block it was derived from.+buildMerkleRoot ::+  -- | transaction hashes (leaf nodes)+  [TxHash] ->+  -- | root of the Merkle tree+  Either String MerkleRoot+buildMerkleRoot [] = Left "buildMerkleRoot: a block needs at least one transaction"+buildMerkleRoot txs+  | mutated = Left "buildMerkleRoot: duplicate transaction hashes (mutated tree)"+  | otherwise = Right (V.head (last levels))+  where+    (levels, mutated) = merkleLevels (map (.get) txs)+ -- | Concatenate and compute double SHA256. hash2 :: Hash256 -> Hash256 -> Hash256-hash2 a b = doubleSHA256 $ encode a `BS.append` encode b+hash2 a b = doubleSHA256 $ runPutS (serialize a) <> runPutS (serialize b) --- | Computes the hash of a specific node in a Merkle tree.-calcHash :: Int       -- ^ height of the node-         -> Int       -- ^ position of the node (0 for the leftmost node)-         -> [TxHash]  -- ^ transaction hashes (leaf nodes)-         -> Hash256   -- ^ hash of the node at the specified position+-- | Computes the hash of a specific node in a Merkle tree, or 'Nothing' when+-- the tree has no node at that height and position. Builds the whole tree,+-- so use 'buildMerkleRoot' or 'buildPartialMerkle' for more than one node.+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+  Maybe 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 || null txs = Nothing+  | otherwise = listToMaybe (drop height levels) >>= (V.!? pos)   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+    (levels, _) = merkleLevels (map (.get) txs)  -- | Build a partial Merkle tree. Provide a list of tuples with all transaction -- hashes in the block, and whether the transaction is to be included in the -- partial tree. Returns a flag bits structure and the computed partial Merkle -- tree. buildPartialMerkle ::-       [(TxHash, Bool)] -- ^ transaction hash and whether to include-    -> (FlagBits, PartialMerkleTree) -- ^ flag bits and partial Merkle tree-buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs+  -- | transaction hash and whether to include+  [(TxHash, Bool)] ->+  -- | flag bits and partial Merkle tree+  (FlagBits, PartialMerkleTree)+buildPartialMerkle [] = ([], [])+buildPartialMerkle txs = traverseAndBuild levels matches (length levels - 1) 0+  where+    (levels, _) = merkleLevels (map ((.get) . fst) txs)+    matches = matchLevels (map snd txs) --- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'--- above.-traverseAndBuild ::-       Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)-traverseAndBuild height pos txs-    | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters"-    | height == 0 || not match = ([match], [calcHash height pos t])-    | otherwise = (match : lb ++ rb, lh ++ rh)+-- | For every level of the tree, whether each node has a matching transaction+-- somewhere below it. Shaped like the levels of 'merkleLevels'.+matchLevels :: [Bool] -> [V.Vector Bool]+matchLevels leafMatches = go (V.fromList leafMatches) []   where-    t = map fst txs-    s = pos `shiftL` height-    e = min (length txs) $ (pos + 1) `shiftL` height-    match = any snd $ take (e - s) $ drop s txs-    (lb, lh) = traverseAndBuild (height - 1) (pos * 2) txs-    (rb, rh)-        | (pos * 2 + 1) < calcTreeWidth (length txs) (height - 1) =-            traverseAndBuild (height - 1) (pos * 2 + 1) txs-        | otherwise = ([], [])+    go level above+      | V.length level <= 1 = reverse (level : above)+      | otherwise = go (V.fromList parents) (level : above)+      where+        count = V.length level+        parents = [level V.! i || level V.! min (i + 1) (count - 1) | i <- [0, 2 .. count - 1]] --- | Helper function to extract transaction hashes from partial Merkle tree.-traverseAndExtract :: Int -> Int -> Int -> FlagBits -> PartialMerkleTree-                   -> Maybe (MerkleRoot, [TxHash], Int, Int)-traverseAndExtract height pos ntx flags hashes-    | null flags               = Nothing-    | height == 0 || not match = leafResult-    | isNothing leftM          = Nothing-    | (pos*2+1) >= calcTreeWidth ntx (height-1) =-        Just (hash2 lh lh, lm, lcf+1, lch)-    | isNothing rightM         = Nothing-    | otherwise =-        Just (hash2 lh rh, lm ++ rm, lcf+rcf+1, lch+rch)+-- | Depth-first walk that records the partial Merkle tree, like+-- @CPartialMerkleTree::TraverseAndBuild@ in Bitcoin Core. A node with no match+-- below it is stored as a single hash. A node with a match is descended into.+traverseAndBuild ::+  MerkleLevels -> [V.Vector Bool] -> Int -> Int -> (FlagBits, PartialMerkleTree)+traverseAndBuild levels matches height pos+  | height == 0 || not match = ([match], [node])+  | otherwise = (match : leftBits ++ rightBits, leftHashes ++ rightHashes)   where-    leafResult-        | null hashes = Nothing-        | otherwise = Just (h, [ TxHash h | height == 0 && match ], 1, 1)-    (match:fs) = flags-    (h:_)     = hashes-    leftM  = traverseAndExtract (height-1) (pos*2) ntx fs hashes-    (lh,lm,lcf,lch) = fromMaybe e leftM-    rightM = traverseAndExtract (height-1) (pos*2+1) ntx-                (drop lcf fs) (drop lch hashes)-    (rh,rm,rcf,rch) = fromMaybe e rightM-    e = error "traverseAndExtract: unexpected error extracting a Maybe value"+    match = (matches !! height) V.! pos+    node = (levels !! height) V.! pos+    (leftBits, leftHashes) = traverseAndBuild levels matches (height - 1) (pos * 2)+    (rightBits, rightHashes)+      | pos * 2 + 1 < V.length (levels !! (height - 1)) =+          traverseAndBuild levels matches (height - 1) (pos * 2 + 1)+      | otherwise = ([], []) +-- | Depth-first walk that rebuilds a node of the partial Merkle tree, like+-- @CPartialMerkleTree::TraverseAndExtract@ in Bitcoin Core. Returns the hash+-- of the node, the matching transactions below it with their positions in+-- the block, and the flags and hashes that are still unused.+traverseAndExtract ::+  Int ->+  Int ->+  Int ->+  FlagBits ->+  PartialMerkleTree ->+  Either String (Hash256, [(Int, TxHash)], FlagBits, PartialMerkleTree)+traverseAndExtract height pos ntx flags hashes =+  case flags of+    [] -> Left "extractMatches: ran out of flag bits"+    (parentOfMatch : flags')+      | height == 0 || not parentOfMatch ->+          -- a leaf, or a subtree without matches: its hash is stored as is+          case hashes of+            [] -> Left "extractMatches: ran out of hashes"+            (h : hashes') ->+              Right (h, [(pos, TxHash h) | height == 0 && parentOfMatch], flags', hashes')+      | otherwise -> do+          (left, leftMatches, flags'', hashes'') <-+            traverseAndExtract (height - 1) (pos * 2) ntx flags' hashes+          if pos * 2 + 1 < calcTreeWidth ntx (height - 1)+            then do+              (right, rightMatches, flags''', hashes''') <-+                traverseAndExtract (height - 1) (pos * 2 + 1) ntx flags'' hashes''+              -- Two branches never hash the same set of distinct transactions.+              when (left == right) $+                Left "extractMatches: identical left and right branches (mutated tree)"+              return (hash2 left right, leftMatches ++ rightMatches, flags''', hashes''')+            else return (hash2 left left, leftMatches, flags'', hashes'')+ -- | Extracts the matching hashes from a partial merkle tree. This will return -- the list of transaction hashes that have been included (set to true) in--- a call to 'buildPartialMerkle'.-extractMatches :: Network-               -> FlagBits-               -> PartialMerkleTree-               -> Int -- ^ number of transaction at height 0 (leaf nodes)-               -> Either String (MerkleRoot, [TxHash])-               -- ^ Merkle root and list of matching transaction hashes-extractMatches net flags hashes ntx-    | ntx == 0 = Left-        "extractMatches: number of transactions can not be 0"-    | ntx > getMaxBlockSize net `div` 60 = Left-        "extractMatches: number of transactions excessively high"-    | length hashes > ntx = Left-        "extractMatches: More hashes provided than the number of transactions"-    | length flags < length hashes = Left-        "extractMatches: At least one bit per node and one bit per hash"-    | isNothing resM = Left-        "extractMatches: traverseAndExtract failed"-    | (nBitsUsed+7) `div` 8 /= (length flags+7) `div` 8 = Left-        "extractMatches: All bits were not consumed"-    | nHashUsed /= length hashes = Left $-        "extractMatches: All hashes were not consumed: " ++ show nHashUsed-    | otherwise = return (merkRoot, matches)-  where-    resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes-    (merkRoot, matches, nBitsUsed, nHashUsed) = fromMaybe e resM-    e = error "extractMatches: unexpected error extracting a Maybe value"+-- a call to 'buildPartialMerkle'. See 'extractIndexedMatches' for what this+-- does not prove.+extractMatches ::+  Network ->+  FlagBits ->+  PartialMerkleTree ->+  -- | number of transaction at height 0 (leaf nodes)+  Int ->+  -- | Merkle root and list of matching transaction hashes+  Either String (MerkleRoot, [TxHash])+extractMatches net flags hashes ntx =+  fmap (map snd) <$> extractIndexedMatches net flags hashes ntx +-- | Like 'extractMatches', and gives the position of each matching+-- transaction in the block, as @ExtractMatches@ does in Bitcoin Core.+--+-- The number of transactions is a claim of whoever made the proof. Nothing+-- here can check it, and it sets the depth of the tree. A 64-byte+-- transaction hashes like an inner node of the tree, so a proof that claims+-- twice as many transactions can present the two halves of such a+-- transaction as two matches (CVE-2017-12842). A verifier that knows the+-- block compares the count with it, as Bitcoin Core does. One that does not+-- can ask for a proof of the coinbase transaction, which is always at+-- position 0, and require the same number of transactions in both proofs.+extractIndexedMatches ::+  Network ->+  FlagBits ->+  PartialMerkleTree ->+  -- | number of transaction at height 0 (leaf nodes)+  Int ->+  -- | Merkle root, and the matching transactions with their positions+  Either String (MerkleRoot, [(Int, TxHash)])+extractIndexedMatches net flags hashes ntx+  | ntx == 0 =+      Left "extractMatches: number of transactions can not be 0"+  | ntx > net.maxBlockSize `div` 60 =+      Left "extractMatches: number of transactions excessively high"+  | length hashes > ntx =+      Left "extractMatches: More hashes provided than the number of transactions"+  | length flags < length hashes =+      Left "extractMatches: At least one bit per node and one bit per hash"+  | otherwise = do+      (root, matches, unusedFlags, unusedHashes) <-+        traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes+      let bitsUsed = length flags - length unusedFlags+      -- Only the padding of the last flag byte may stay unused.+      when ((bitsUsed + 7) `div` 8 /= (length flags + 7) `div` 8) $+        Left "extractMatches: All bits were not consumed"+      when (not (null unusedHashes)) $+        Left "extractMatches: All hashes were not consumed"+      return (root, matches)+ -- | Helper function to split a list in chunks 'Int' length. Last chunk may be--- smaller.+-- smaller. A chunk length below one gives the whole list as one chunk. splitIn :: Int -> [a] -> [[a]] splitIn _ [] = []-splitIn c xs = xs1 : splitIn c xs2+splitIn c xs+  | c < 1 = [xs]+  | otherwise = xs1 : splitIn c xs2   where     (xs1, xs2) = splitAt c xs  -- | Pack up to eight bools in a byte. boolsToWord8 :: [Bool] -> Word8 boolsToWord8 [] = 0-boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])+boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0 .. 7]) --- | Get matching transactions from Merkle block.+-- | Get matching transactions from Merkle block. The partial tree must+-- rebuild the Merkle root of the header that came in the same message. That+-- is all this checks: the caller has to validate the header itself, its proof+-- of work and its place in the chain, and the transaction count is a claim,+-- see 'extractIndexedMatches'. 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 = map snd <$> merkleBlockIndexedTxs net b --- | Check if Merkle block root is valid against the block header.+-- | Like 'merkleBlockTxs', with the position of each matching transaction+-- in the block.+merkleBlockIndexedTxs :: Network -> MerkleBlock -> Either String [(Int, TxHash)]+merkleBlockIndexedTxs net b = do+  (root, ths) <- extractIndexedMatches 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. See+-- 'merkleBlockTxs' for what that does not prove. testMerkleRoot :: Network -> MerkleBlock -> Bool testMerkleRoot net = isRight . merkleBlockTxs net
− src/Haskoin/Constants.hs
@@ -1,562 +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-    ( -- * Constants-      Network(..)-    , btc-    , btcTest-    , btcRegTest-    , bch-    , bchTest-    , bchRegTest-    , allNets-    , netByName-    , netByIdent-    ) where--import           Control.DeepSeq-import           Data.ByteString      (ByteString)-import           Data.List-import           Data.Maybe-import           Data.Serialize-import           Data.String-import           Data.Text            (Text)-import           Data.Word            (Word32, Word64, Word8)-import           GHC.Generics         (Generic)-import           Haskoin.Block.Common-import           Text.Read---- | Version of Haskoin Core package.-versionString :: IsString a => a-#ifdef CURRENT_PACKAGE_VERSION-versionString = CURRENT_PACKAGE_VERSION-#else-versionString = "Unavailable"-#endif---- | Constants for network.-data Network = Network-    { -- | lowercase alphanumeric and dashes-      getNetworkName              :: !String-      -- | network Haskell identifier-    , getNetworkIdent             :: !String-      -- | prefix for 'Base58' P2PKH addresses-    , getAddrPrefix               :: !Word8-      -- | prefix for 'Base58' P2SH addresses-    , getScriptPrefix             :: !Word8-      -- | prefix for WIF private key-    , getSecretPrefix             :: !Word8-      -- | prefix for extended public key-    , getExtPubKeyPrefix          :: !Word32-      -- | prefix for extended private key-    , getExtSecretPrefix          :: !Word32-      -- | network magic-    , getNetworkMagic             :: !Word32-      -- | genesis block header-    , getGenesisHeader            :: !BlockHeader-      -- | maximum block size in bytes-    , getMaxBlockSize             :: !Int-      -- | maximum amount of satoshi-    , getMaxSatoshi               :: !Word64-      -- | user agent string-    , getHaskoinUserAgent         :: !ByteString-      -- | default port for P2P connections-    , getDefaultPort              :: !Int-      -- | allow min difficulty blocks (testnet)-    , getAllowMinDifficultyBlocks :: !Bool-      -- | do not retarget difficulty (regtest)-    , getPowNoRetargetting        :: !Bool-      -- | proof-of-work target higest possible value-    , getPowLimit                 :: !Integer-      -- | block at which BIP34 activates-    , getBip34Block               :: !(BlockHeight, BlockHash)-      -- | block at which BIP65 activates-    , getBip65Height              :: !BlockHeight-      -- | block at which BIP66 activates-    , getBip66Height              :: !BlockHeight-      -- | time between difficulty retargets-    , getTargetTimespan           :: !Word32-      -- | time between blocks-    , getTargetSpacing            :: !Word32-      -- | checkpoints-    , getCheckpoints              :: ![(BlockHeight, BlockHash)]-      -- | BIP44 derivation path root-    , getBip44Coin                :: !Word32-      -- | peer-to-peer network seeds-    , getSeeds                    :: ![String]-      -- | fork id for replay protection-    , getSigHashForkId            :: !(Maybe Word32)-      -- | EDA start block height-    , getEdaBlockHeight           :: !(Maybe Word32)-      -- | DAA start block height-    , getDaaBlockHeight           :: !(Maybe Word32)-      -- | 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, Generic, NFData)--instance Serialize Network where-    put net =-        putWord32be $ getNetworkMagic net-    get = do-        magic <- getWord32be-        case find ((== magic) . getNetworkMagic) allNets of-            Nothing  -> fail $ "Network magic unknown: " <> show magic-            Just net -> return net--instance Show Network where-    show = getNetworkIdent--instance Read Network where-    readPrec = do-        Ident str <- lexP-        maybe pfail return (netByIdent str)--instance IsString Network where-    fromString = fromMaybe (error "Network name invalid") . netByName---- | Query known networks by name.-netByName :: String -> Maybe Network-netByName str = find ((== str) . getNetworkName) allNets---- | Query known networks by Haskell identifier.-netByIdent :: String -> Maybe Network-netByIdent str = find ((== str) . getNetworkIdent) allNets---- | Bitcoin SegWit network. Symbol: BTC.-btc :: Network-btc =-    Network-    { getNetworkName = "btc"-    , getNetworkIdent = "btc"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xf9beb4d9-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent =-          "/haskoin-btc:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-          ]-    , getSeeds =-          [ "seed.bitcoin.sipa.be" -- Pieter Wuille-          , "dnsseed.bluematt.me" -- Matt Corallo-          , "dnsseed.bitcoin.dashjr.org" -- Luke Dashjr-          , "seed.bitcoinstats.com" -- Chris Decker-          , "seed.bitcoin.jonasschnelli.ch" -- Jonas Schnelli-          , "seed.btc.petertodd.org" -- Peter Todd-          , "seed.bitcoin.sprovoost.nl" -- Sjors Provoost-          , "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"-    , getNetworkIdent = "btcTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0x0b110907-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoin.jonasschnelli.ch"-          , "seed.tbtc.petertodd.org"-          , "seed.testnet.bitcoin.sprovoost.nl"-          , "testnet-seed.bluematt.me"-          ]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , 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"-    , getNetworkIdent = "btcRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xfabfb5da-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints = []-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , 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"-    , getNetworkIdent = "bch"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xe3e1f3e8-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-            -- UAHF fork block.-          , ( 478559-            , "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec")-            -- Nov, 13 DAA activation block.-          , ( 504031-            , "0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c")-          ]-    , getSeeds =-          [ "seed.bitcoinabc.org"-          , "seed-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.-bchTest :: Network-bchTest =-    Network-    { getNetworkName = "bchtest"-    , getNetworkIdent = "bchTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xf4e5f3f4-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-            -- UAHF fork block.-          , ( 1155876-            , "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5")-            -- Nov, 13. DAA activation block.-          , ( 1188697-            , "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoinabc.org"-          , "testnet-seed-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"-    , getNetworkIdent = "bchRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xdab5bffa-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 0-            , "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")-          ]-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Just 0-    , 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, 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Hashing functions and ECDSA signatures. module Haskoin.Crypto-    ( module Hash-    , module Signature-    , module Secp256k1-    ) where+  ( module Secp256k1,+    module Hash,+    module Keys,+    module Signature,+  )+where -import           Crypto.Secp256k1         as Secp256k1-import           Haskoin.Crypto.Hash      as Hash-import           Haskoin.Crypto.Signature as Signature+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,202 +1,259 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Haskoin.Crypto.Hash-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoFieldSelectors #-} -Hashing functions and corresponding data types. Uses functions from the-cryptonite library.--}+-- |+-- 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(getHash512)-    , Hash256(getHash256)-    , Hash160(getHash160)-    , CheckSum32(getCheckSum32)-    , sha512-    , sha256-    , ripemd160-    , sha1-    , doubleSHA256-    , addressHash-    , checkSum32-    , hmac512-    , hmac256-    , split512-    , join512-    ) where+  ( -- * Hashes+    Hash512 (get),+    Hash256 (get),+    Hash160 (get),+    CheckSum32 (get),+    sha512,+    sha256,+    ripemd160,+    sha1,+    doubleSHA256,+    addressHash,+    checkSum32,+    hmac512,+    hmac256,+    split512,+    join512,+    initTaggedHash,+  )+where -import           Control.DeepSeq-import           Crypto.Hash             (RIPEMD160 (..), SHA1 (..),-                                          SHA256 (..), SHA512 (..), hashWith)-import           Crypto.MAC.HMAC         (HMAC, hmac)-import           Data.ByteArray          (ByteArrayAccess)-import qualified Data.ByteArray          as BA-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.ByteString.Short   (ShortByteString)-import qualified Data.ByteString.Short   as BSS-import           Data.Either             (fromRight)-import           Data.Hashable           (Hashable)-import           Data.Serialize          (Serialize (..), decode)-import qualified Data.Serialize.Get      as Get-import qualified Data.Serialize.Put      as Put-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           Data.Word               (Word32)-import           GHC.Generics            (Generic)-import           Haskoin.Util-import           Text.Read               as R+import Control.DeepSeq+import Control.Monad (guard)+import Crypto.Hash+import Crypto.MAC.HMAC (HMAC, hmac)+import Data.Binary (Binary (..))+import Data.ByteArray (ByteArrayAccess, convert)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Short (ShortByteString, fromShort, toShort)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial (Serial (..))+import Data.Either (fromRight)+import Data.Function (on)+import Data.Hashable (Hashable)+import Data.Serialize (Serialize (..))+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import Data.Void (Void)+import Data.Word (Word32)+import GHC.Generics (Generic)+import Haskoin.Util.Helpers+import Haskoin.Util.Marshal+import Text.Read as R  -- | 'Word32' wrapped for type-safe 32-bit checksums. newtype CheckSum32 = CheckSum32-    { getCheckSum32 :: Word32-    } deriving (Eq, Ord, Serialize, 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++instance Binary CheckSum32 where+  put = serialize+  get = deserialize++-- | Decode a hex string that must hold exactly the given number of bytes. The+-- text forms of the hash types use it so that a hash can never have the wrong+-- length.+decodeHexBytes :: Int -> String -> Maybe ByteString+decodeHexBytes n str = do+  bs <- decodeHex (cs str)+  guard (B.length bs == n)+  return bs+ -- | 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) (decodeHexBytes 64 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) (decodeHexBytes 32 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) (decodeHexBytes 20 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 = maybe e (Hash512 . toShort) . decodeHexBytes 64+    where+      e = error "Could not decode hash from hex string" +instance Serial Hash512 where+  deserialize = Hash512 . toShort <$> getByteString 64+  serialize = putByteString . fromShort . (.get)+ instance Serialize Hash512 where-    get = Hash512 <$> Get.getShortByteString 64-    put = Put.putShortByteString . getHash512+  put = serialize+  get = deserialize +instance Binary Hash512 where+  put = serialize+  get = deserialize+ instance IsString Hash256 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    32 -> Hash256 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"+  fromString = maybe e (Hash256 . toShort) . decodeHexBytes 32+    where+      e = error "Could not decode hash from hex string" +instance Serial Hash256 where+  deserialize = Hash256 . toShort <$> getByteString 32+  serialize = putByteString . fromShort . (.get)+ instance Serialize Hash256 where-    get = Hash256 <$> Get.getShortByteString 32-    put = Put.putShortByteString . getHash256+  put = serialize+  get = deserialize +instance Binary Hash256 where+  put = serialize+  get = deserialize+ instance IsString Hash160 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    20 -> Hash160 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"+  fromString = maybe e (Hash160 . toShort) . decodeHexBytes 20+    where+      e = error "Could not decode hash from hex string" +instance Serial Hash160 where+  deserialize = Hash160 . toShort <$> getByteString 20+  serialize = putByteString . fromShort . (.get)+ instance Serialize Hash160 where-    get = Hash160 <$> Get.getShortByteString 20-    put = Put.putShortByteString . getHash160+  put = serialize+  get = deserialize +instance Binary Hash160 where+  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 = fromRight (error "Could not decode bytes as CheckSum32")-             . decode-             . BS.take 4-             . BA.convert-             . hashWith SHA256-             . hashWith SHA256+checkSum32 :: (ByteArrayAccess b) => b -> CheckSum32+checkSum32 =+  fromRight (error "Could not decode bytes as CheckSum32")+    . runGetS deserialize+    . B.take 4+    . convert+    . hashWith SHA256+    . hashWith SHA256  {- HMAC -}  -- | Computes HMAC over SHA-512. hmac512 :: ByteString -> ByteString -> Hash512 hmac512 key msg =-    Hash512 $ 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)+join512 (a, b) = Hash512 (toShort (a.get `app` b.get))+  where+    app = B.append `on` fromShort++-- | Initialize tagged hash specified in BIP340+--+-- @since 0.21.0+initTaggedHash ::+  -- | Hash tag+  ByteString ->+  Context SHA256+initTaggedHash tag =+  (`hashUpdates` [hashedTag, hashedTag]) $+    hashInit @SHA256+  where+    hashedTag = hashWith SHA256 tag
+ src/Haskoin/Crypto/Keys.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Keys+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- ECDSA private and public keys, extended keys (BIP-32) and mnemonic sentences+-- (BIP-39).+module Haskoin.Crypto.Keys+  ( module Haskoin.Crypto.Keys.Common,+    module Haskoin.Crypto.Keys.Extended,+    module Haskoin.Crypto.Keys.Mnemonic,+  )+where++import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Crypto.Keys.Mnemonic
+ src/Haskoin/Crypto/Keys/Common.hs view
@@ -0,0 +1,259 @@+{-# 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,+    decodePubKey,+    derivePublicKey,+    wrapSecKey,+    decodeSecKey,+    fromMiniKey,+    tweakPubKey,+    tweakSecKey,++    -- ** Private Key Wallet Import Format (WIF)+    fromWif,+    toWif,+  )+where++import Control.DeepSeq+import Control.Monad (guard, mzero, unless, (<=<))+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 Data.Text qualified as T+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 $ decodePubKey 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++-- | Decode a public key from bytes that must hold the key and nothing else:+-- 33 bytes starting with 0x02 or 0x03, or 65 bytes starting with 0x04. A key+-- pushed in a script, a witness or a PSBT record is the whole item, so use+-- this function, or 'unmarshal', for those. 'marshalGet' reads a key from the+-- start of a longer stream and leaves the rest.+--+-- A hybrid key, 65 bytes starting with 0x06 or 0x07, is refused. Consensus+-- takes that form, but the relay rules of Bitcoin Core do not, and no wallet+-- makes one.+decodePubKey :: Ctx -> ByteString -> Either String PublicKey+decodePubKey ctx = runGetS $ do+  k <- marshalGet ctx+  done <- isEmpty+  unless done $ fail "Trailing bytes after public key"+  return k++-- | 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, Generic, NFData)++-- | The secret is not shown. A key that ends up in a log, an error message+-- or a REPL session must not give its coins away. There is no 'Read'+-- instance for the same reason: use 'toWif' and 'fromWif' to write a key+-- out and read it back.+--+-- This protects a 'PrivateKey', not the 'SecKey' inside it. The 'Show'+-- instance of 'SecKey' comes from the secp256k1 binding and prints the 32+-- bytes in hex, so @show privateKey.key@ gives the secret away. A record of+-- your own that holds a 'SecKey' and derives 'Show' prints it too. Keep keys+-- in 'PrivateKey' or 'XPrvKey' where they may be shown.+instance Show PrivateKey where+  showsPrec d p =+    showParen (d > 10) $+      showString "PrivateKey {key = <redacted>, compress = "+        . shows p.compress+        . showString "}"++instance Serial PrivateKey where+  serialize p = do+    putByteString p.key.get+    serialize p.compress+  deserialize = do+    bs <- getByteString 32+    c <- deserialize+    case decodeSecKey bs of+      Nothing -> fail "Secret key is zero or not below the curve order"+      Just k -> return PrivateKey {key = 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++-- | Build a secret key from its 32 big-endian bytes. Fails when there are not+-- 32 bytes, and when the value is zero or not below the curve order: a secret+-- key is a number from 1 to n-1. Every form of a private key in this library+-- is read through this function: WIF, mini keys, extended keys and the binary+-- form.+--+-- The checks are those of 'Crypto.Secp256k1.secKey', which this function+-- calls. Version 2.0.0 of the binding checks the range. Earlier versions only+-- checked the length, and this package requires 2.0.0 or later.+decodeSecKey :: ByteString -> Maybe SecKey+decodeSecKey = secKey++-- | Tweak a private key.+tweakSecKey :: Ctx -> SecKey -> Hash256 -> Maybe SecKey+tweakSecKey ctx k =+  tweakAddSecKey ctx k <=< tweak . runPutS . serialize++-- | Decode a Casascius mini private key, as the Bitcoin wiki describes the+-- format (<https://en.bitcoin.it/wiki/Mini_private_key_format>):+--+-- * 30 characters, or the 22 characters of the first series of coins+-- * every character from the Base58 alphabet, the first one the letter @S@+-- * SHA-256 of the key followed by @?@ starts with a zero byte+--+-- The private key is SHA-256 of the mini key, and its public key is+-- uncompressed. A string that breaks one of these rules is not a mini key,+-- even when its check byte happens to be zero, which one string in 256 has.+fromMiniKey :: ByteString -> Maybe PrivateKey+fromMiniKey bs = do+  guard (BS.length bs `elem` [22, 30])+  guard (BS.head bs == letterS)+  guard (BS.all (`BS.elem` base58Alphabet) bs)+  guard (BS.head checkHash == 0x00)+  wrapSecKey False <$> (decodeSecKey . runPutS . serialize . sha256) bs+  where+    checkHash = runPutS $ serialize $ sha256 $ bs `BS.append` "?"+    letterS = 0x53+    base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"++-- | Longest Base58 string a WIF can take. A WIF holds at most 38 bytes with+-- its checksum, which never need more than 52 digits because 58^52 is larger+-- than 256^38. Longer strings are rejected before decoding, since the time to+-- decode Base58 grows with the square of the length.+maxWifLength :: Int+maxWifLength = 52++-- | Decode private key from WIF (wallet import format) string. Fails on a+-- string of another network, of the wrong length, or holding a key that is+-- zero or not below the curve order.+fromWif :: Network -> Base58 -> Maybe PrivateKey+fromWif net wif = do+  guard (T.length wif <= maxWifLength)+  bs <- decodeBase58Check wif+  (prefix, rest) <- BS.uncons bs+  -- Check that this is a private key+  guard (prefix == net.secretPrefix)+  case BS.length rest of+    -- Uncompressed format+    32 -> wrapSecKey False <$> decodeSecKey rest+    -- Compressed format: the key is followed by a 0x01 flag byte+    33 -> do+      guard $ BS.last rest == 0x01+      wrapSecKey True <$> decodeSecKey (BS.init rest)+    -- 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,1261 @@+{-# 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 PatternSynonyms #-}+{-# 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 (Deriv, (:|), (:/)),+    AnyDeriv,+    HardDeriv,+    SoftDeriv,+    HardOrAny,+    AnyOrSoft,+    DerivPath,+    HardPath,+    SoftPath,+    Bip32PathIndex (..),+    derivePath,+    derivePubPath,+    tryDerivePath,+    tryDerivePubPath,+    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 (foldM, guard, mzero, unless, when, (<=<))+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.Bifunctor (first)+import Data.Bits (clearBit, setBit, testBit)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Char (isDigit)+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),+    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, NFData, Hashable)++-- | The chain code and the key are not shown. Together they give every key+-- below this one, so a value that ends up in a log or an error message+-- must not reveal them. There is no 'Read' instance for the same reason:+-- use 'xPrvExport' and 'xPrvImport' to write a key out and read it back.+--+-- The @key@ field is a 'SecKey', whose own 'Show' instance prints the secret+-- in hex. Do not show that field by itself.+instance Show XPrvKey where+  showsPrec d k =+    showParen (d > 10) $+      showString "XPrvKey {depth = "+        . shows k.depth+        . showString ", parent = "+        . shows k.parent+        . showString ", index = "+        . shows k.index+        . showString ", chain = <redacted>, key = <redacted>}"++instance Marshal Network XPrvKey where+  marshalGet net = do+    ver <- getWord32be+    unless (ver == net.xPrvPrefix) $+      fail "Get: Invalid version for extended private key"+    d <- getWord8+    p <- deserialize+    i <- getWord32be+    checkMasterFields d p i+    XPrvKey d p i <$> 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"+    d <- getWord8+    p <- deserialize+    i <- getWord32be+    checkMasterFields d p i+    XPubKey d p i <$> deserialize <*> getCompressedPubKey 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++-- | Read the public key of an extended key. BIP32 serializes it in 33 bytes,+-- the compressed form. The 65-byte form is refused, so that an extended key+-- has one binary form of 78 bytes.+getCompressedPubKey :: (MonadGet m) => Ctx -> m PubKey+getCompressedPubKey ctx = do+  bs <- getByteString 33+  unless (B.head bs `elem` [0x02, 0x03]) $+    fail "Extended key must hold a compressed public key"+  case importPubKey ctx bs of+    Nothing -> fail "Extended key holds an invalid public key"+    Just k -> return k++-- | A key at depth 0 is a master key. BIP32 requires its parent fingerprint+-- and child index to be zero; test vector 5 lists keys that break this rule+-- as invalid.+checkMasterFields :: (MonadGet m) => Word8 -> Fingerprint -> KeyIndex -> m ()+checkMasterFields depth parent index =+  when (depth == 0 && (parent /= Fingerprint 0 || index /= 0)) $+    fail "Extended key at depth 0 must have a zero parent fingerprint and index"++-- | Build a BIP32 compatible extended private key from a seed. This will+-- produce a root node (@depth=0@ and @parent=0@).+--+-- The seed must be 16 to 64 bytes long: BIP32 takes a seed "between 128 and+-- 512 bits", and Bitcoin Core refuses any other length. A shorter seed, such+-- as the empty string left by a failed read of entropy, would give a master+-- key that anyone can compute.+--+-- Throws 'DerivationException' for a seed of another length, and in the+-- vanishingly unlikely case that the seed gives a key outside the curve order,+-- which BIP32 declares invalid.+makeXPrvKey :: ByteString -> XPrvKey+makeXPrvKey bs+  | B.length bs < 16 || B.length bs > 64 =+      throw $+        DerivationException+          "Invalid seed: its length must be between 16 and 64 bytes"+  | otherwise = XPrvKey 0 (Fingerprint 0) 0 c k+  where+    (p, c) = split512 $ hmac512 "Bitcoin seed" bs+    k = fromMaybe err (decodeSecKey (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 = orThrow (tryPrvSubKey ctx xkey child)++-- | 'prvSubKey' with its failures as an error value.+tryPrvSubKey :: Ctx -> XPrvKey -> KeyIndex -> Either String XPrvKey+tryPrvSubKey ctx xkey child = do+  unless (child < 0x80000000) $ Left hardIndexError+  depth <- nextDepth xkey.depth+  k <-+    maybeToEither "Invalid prvSubKey derivation" $+      tweakSecKey ctx xkey.key a+  return $ XPrvKey depth (xPrvFP ctx xkey) child c k+  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++-- | 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 = orThrow (tryPubSubKey ctx xKey child)++-- | 'pubSubKey' with its failures as an error value.+tryPubSubKey :: Ctx -> XPubKey -> KeyIndex -> Either String XPubKey+tryPubSubKey ctx xKey child = do+  unless (child < 0x80000000) $ Left hardIndexError+  depth <- nextDepth xKey.depth+  pK <-+    maybeToEither "Invalid pubSubKey derivation" $+      tweakPubKey ctx xKey.key a+  return $ XPubKey depth (xPubFP ctx xKey) child c pK+  where+    m = B.append (exportPubKey ctx True xKey.key) (runPutS $ serialize child)+    (a, c) = split512 $ hmac512 (runPutS $ serialize xKey.chain) m++-- | 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 = orThrow (tryHardSubKey ctx xkey child)++-- | 'hardSubKey' with its failures as an error value.+tryHardSubKey :: Ctx -> XPrvKey -> KeyIndex -> Either String XPrvKey+tryHardSubKey ctx xkey child = do+  unless (child < 0x80000000) $ Left hardIndexError+  depth <- nextDepth xkey.depth+  k <-+    maybeToEither "Invalid hardSubKey derivation" $+      tweakSecKey ctx xkey.key a+  return $ XPrvKey depth (xPrvFP ctx xkey) i c k+  where+    i = setBit child 31+    m = B.append (bsPadPrvKey xkey.key) (runPutS $ serialize i)+    (a, c) = split512 $ hmac512 (runPutS $ serialize xkey.chain) m++-- | Turn the failure of a derivation into a 'DerivationException'. Every+-- failure of a derivation function that does not return 'Either' throws that+-- exception, so that a caller can catch them all at once.+orThrow :: Either String a -> a+orThrow = either (throw . DerivationException) id++-- | The error for a child index with the hard bit set. The derivation+-- functions take the index without the bit, below 2^31, and 'hardSubKey'+-- sets the bit itself.+hardIndexError :: String+hardIndexError = "Invalid child derivation index"++-- | The error for a derivation below depth 255.+depthError :: String+depthError = "Maximum derivation depth exceeded"++-- | Depth of a child key. BIP32 stores the depth in one byte, so a key at+-- depth 255 can have no children. Bitcoin Core refuses to derive one+-- (@CExtKey::Derive@ and @CExtPubKey::Derive@ return false), and so does this+-- library.+nextDepth :: Word8 -> Either String Word8+nextDepth 255 = Left depthError+nextDepth d = Right (d + 1)++-- | 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. Extended+-- public keys are always compressed, so the address always exists.+xPubWitnessAddr :: Ctx -> XPubKey -> Address+xPubWitnessAddr ctx xkey = p2wpkhAddr (xPubKeyHash ctx xkey)++-- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended+-- public key. Extended public keys are always compressed, so the address+-- always exists.+xPubCompatWitnessAddr :: Ctx -> XPubKey -> Address+xPubCompatWitnessAddr ctx xkey =+  p2shP2wpkhAddr (xPubKeyHash ctx xkey)++-- | HASH160 of the compressed public key of an extended public key.+xPubKeyHash :: Ctx -> XPubKey -> Hash160+xPubKeyHash ctx xkey = addressHash (marshal 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, if the checksum fails, if the+-- payload is not exactly 78 bytes, or if the key fails the checks of BIP32.+xPrvImport :: Network -> Base58 -> Maybe XPrvKey+xPrvImport net =+  eitherToMaybe . unmarshal net <=< decodeExtendedKey++-- | Decodes a BIP32 encoded extended public key. This function will fail if+-- invalid base 58 characters are detected, if the checksum fails, if the+-- payload is not exactly 78 bytes, or if the key fails the checks of BIP32.+xPubImport :: Network -> Ctx -> Base58 -> Maybe XPubKey+xPubImport net ctx =+  eitherToMaybe . unmarshal (net, ctx) <=< decodeExtendedKey++-- | Size in bytes of a serialized extended key, without its checksum.+extendedKeySize :: Int+extendedKeySize = 78++-- | Longest Base58 string an extended key can take. The 82 bytes of key and+-- checksum never need more than 112 digits because 58^112 is larger than+-- 256^82. Longer strings are rejected before decoding, since the time to decode+-- Base58 grows with the square of the length.+maxExtendedKeyLength :: Int+maxExtendedKeyLength = 112++-- | Decode the Base58Check form of an extended key into its 78 bytes. A payload+-- of another size is refused, as Bitcoin Core does, so that trailing bytes or+-- an uncompressed public key cannot slip through.+decodeExtendedKey :: Base58 -> Maybe ByteString+decodeExtendedKey t = do+  guard (Text.length t <= maxExtendedKeyLength)+  bs <- decodeBase58Check t+  guard (B.length bs == extendedKeySize)+  return bs++-- | 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 pay-to-script-hash 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, and the keys of the redeem+-- script are sorted as BIP 67 defines.+--+-- Fails when /m/ is not between 1 and the number of keys, and when there are+-- more than 15 keys. The redeem script of 16 compressed keys is 547 bytes+-- (3 + 16 × 34), and a pay-to-script-hash redeem script can have at most 520+-- ('maxRedeemScriptSize'), so coins sent to such an address could never be+-- spent. For 16 to 20 keys, build a pay-to-witness-script-hash address from+-- the redeem script with 'payToWitnessScriptAddress'.+deriveMSAddr ::+  Ctx -> [XPubKey] -> Int -> KeyIndex -> Either String (Address, RedeemScript)+deriveMSAddr ctx keys m i = do+  addr <- payToScriptAddress ctx rdm+  return (addr, 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. Fails as 'deriveMSAddr' does.+deriveMSAddrs ::+  Ctx ->+  [XPubKey] ->+  Int ->+  KeyIndex ->+  Either String [(Address, RedeemScript, KeyIndex)]+deriveMSAddrs ctx keys m start = do+  -- Whether an address can be made depends on the number of keys and on m,+  -- not on the index. One check therefore covers the whole list, which stays+  -- lazy and endless.+  _ <- deriveMSAddr ctx keys m start+  return+    [ (a, rdm, i)+      | i <- cycleIndex start,+        Right (a, rdm) <- [deriveMSAddr ctx keys m i]+    ]++-- | Helper function to go through derivation indices. Throws+-- 'DerivationException' for an index with the hard bit set.+cycleIndex :: KeyIndex -> [KeyIndex]+cycleIndex i+  | i == 0 = cycle [0 .. 0x7fffffff]+  | i < 0x80000000 = cycle $ [i .. 0x7fffffff] ++ [0 .. (i - 1)]+  | otherwise = throw $ DerivationException $ "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+--+-- The index of a step is below 2^31, for a hardened step as well: '(:|)'+-- stands for the hard bit. Building a step with a larger index throws+-- 'DerivationException'. A path can therefore not hold an index that its+-- text and binary forms cannot write.+data DerivPathI t where+  HardStep :: (HardOrAny t) => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+  SoftStep :: (AnyOrSoft t) => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+  Deriv :: DerivPathI t++-- | A hardened step. The index is given without the hard bit.+pattern (:|) :: () => (HardOrAny t) => DerivPathI t -> KeyIndex -> DerivPathI t+pattern next :| i <- HardStep next i+  where+    next :| i = HardStep next (checkedIndex i)++-- | A soft step.+pattern (:/) :: () => (AnyOrSoft t) => DerivPathI t -> KeyIndex -> DerivPathI t+pattern next :/ i <- SoftStep next i+  where+    next :/ i = SoftStep next (checkedIndex i)++{-# COMPLETE (:|), (:/), Deriv #-}++-- | The index of a path step, which must be below 2^31.+checkedIndex :: KeyIndex -> KeyIndex+checkedIndex i+  | i < 0x80000000 = i+  | otherwise = throw (DerivationException hardIndexError)++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: a soft path sorts before a hard one, as it does when+  -- comparing the index lists of 'pathToList', where the hard bit is set.+  (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. Throws+-- 'DerivationException' when the path cannot be followed, for example when it+-- leads below depth 255. Use 'tryDerivePath' for a path that comes from+-- outside, such as the path of a PSBT record.+derivePath :: Ctx -> DerivPathI t -> XPrvKey -> XPrvKey+derivePath ctx path = orThrow . tryDerivePath ctx path++-- | Derive a public key from a soft derivation path. Throws+-- 'DerivationException' when the path cannot be followed. Use+-- 'tryDerivePubPath' for a path that comes from outside.+derivePubPath :: Ctx -> SoftPath -> XPubKey -> XPubKey+derivePubPath ctx path = orThrow . tryDerivePubPath ctx path++-- | Derive a private key from a derivation path, with an error value when the+-- path cannot be followed. BIP32 stores the depth of a key in one byte, so a+-- path that leads below depth 255 has no key. That is checked before any key+-- is derived, so a path that is far too long costs nothing.+tryDerivePath :: Ctx -> DerivPathI t -> XPrvKey -> Either String XPrvKey+tryDerivePath ctx path key = do+  checkPathDepth key.depth steps+  foldM step key steps+  where+    steps = pathSteps path+    step k (hard, i)+      | hard = tryHardSubKey ctx k i+      | otherwise = tryPrvSubKey ctx k i++-- | Derive a public key from a soft derivation path, with an error value when+-- the path cannot be followed. See 'tryDerivePath'.+tryDerivePubPath :: Ctx -> SoftPath -> XPubKey -> Either String XPubKey+tryDerivePubPath ctx path key = do+  checkPathDepth key.depth steps+  foldM (tryPubSubKey ctx) key (map snd steps)+  where+    steps = pathSteps path++-- | The steps of a path from the key down. Each step says whether it is+-- hardened, and gives its index without the hard bit.+pathSteps :: DerivPathI t -> [(Bool, KeyIndex)]+pathSteps =+  reverse . go+  where+    go (next :| i) = (True, i) : go next+    go (next :/ i) = (False, i) : go next+    go Deriv = []++-- | Fail when a key at this depth has no descendant that many steps below.+checkPathDepth :: Word8 -> [a] -> Either String ()+checkPathDepth depth steps+  | length steps > 255 - fromIntegral depth = Left depthError+  | otherwise = Right ()++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. The element is+-- a decimal number below 2^31, followed by @'@, @h@ or @H@ for a hardened+-- index. BIP32 itself writes a hardened index with a subscript H, as in+-- @m\/0H\/1@. Bitcoin Core and the descriptors of BIP380 take @'@ and @h@+-- only, so write one of those two for text that other software reads.+-- 'pathToStr' writes @'@. The number is read as an 'Integer' so that a value+-- too large for 32 bits cannot wrap around.+parseBip32PathIndex :: String -> Maybe Bip32PathIndex+parseBip32PathIndex segment =+  case span isDigit segment of+    ("", _) -> Nothing+    (digits, "") -> Bip32SoftIndex <$> parseIndex digits+    (digits, [marker])+      | marker `elem` hardMarkers -> Bip32HardIndex <$> parseIndex digits+    _ -> Nothing+  where+    hardMarkers = "'hH" :: String+    parseIndex digits = do+      let n = read digits :: Integer+      guard (is31Bit n)+      return (fromInteger n)++-- | 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 (fmap Bip32HardIndex . readIndex) (numberToInteger n)+      s =+        parens $ do+          Ident "Bip32SoftIndex" <- lexP+          Number n <- lexP+          maybe pfail (fmap Bip32SoftIndex . readIndex) (numberToInteger n)++-- | Read a derivation index that was lexed as a number, refusing values that+-- do not fit in 31 bits instead of wrapping them around.+readIndex :: Integer -> ReadPrec KeyIndex+readIndex i+  | is31Bit i = return (fromInteger i)+  | otherwise = pfail++-- | 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, 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, public derivations with a hard segment, and a path that leads+-- below depth 255 return an error value. The function does not throw.+applyPath :: Ctx -> ParsedPath -> XKey -> Either String XKey+applyPath ctx path key =+  first ("applyPath: " <>) $+    case (path, key) of+      (ParsedPrv _, XPrv k n) -> (`XPrv` n) <$> derivePrv k+      (ParsedPrv _, XPub {}) -> Left "Invalid public key"+      (ParsedPub _, XPrv k n) -> (`XPub` n) . deriveXPubKey ctx <$> derivePrv k+      (ParsedPub _, XPub k n) -> (`XPub` n) <$> derivePub k+      -- For empty parsed paths, we take a hint from the provided key+      (ParsedEmpty _, XPrv k n) -> (`XPrv` n) <$> derivePrv k+      (ParsedEmpty _, XPub k n) -> (`XPub` n) <$> derivePub k+  where+    derivePrv = tryDerivePath ctx path.get+    derivePub k = do+      soft <- maybeToEither "Invalid hard derivation" (toSoft path.get)+      tryDerivePubPath ctx soft k++{- 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. Fails as 'deriveMSAddr' does.+derivePathMSAddr ::+  Ctx ->+  [XPubKey] ->+  SoftPath ->+  Int ->+  KeyIndex ->+  Either String (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. Fails as 'deriveMSAddr' does.+derivePathMSAddrs ::+  Ctx ->+  [XPubKey] ->+  SoftPath ->+  Int ->+  KeyIndex ->+  Either String [(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 decodeSecKey bs of+    Nothing -> fail "Secret key is zero or not below the curve order"+    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,82 @@+{-# 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 (unless)+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.ByteString qualified as B+import Data.Bytes.Serial (Serial (..))+import Data.Hashable (Hashable)+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++-- | Read a fingerprint from exactly eight hex digits.+textToFingerprint :: Text -> Either String Fingerprint+textToFingerprint t = do+  bs <- maybe (Left "Fingerprint: invalid hex") Right (decodeHex t)+  unless (B.length bs == 4) $ Left "Fingerprint: expected 4 bytes"+  S.decode bs++instance Show Fingerprint where+  show = show . Text.unpack . encodeHex . S.encode++instance Read Fingerprint where+  readPrec = readPrec >>= either fail pure . textToFingerprint++instance IsString Fingerprint where+  fromString =+    either (error . ("Fingerprint literal: " <>)) id+      . textToFingerprint+      . Text.pack++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,2356 @@+{-# 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.+--+-- The sentence and the passphrase may come from an untrusted source, so the+-- work done on them is bounded. A word longer than any word of the list is+-- never normalised, and a passphrase must be in the Stream-Safe Text Format+-- of Unicode. No error text holds a word of the sentence.+module Haskoin.Crypto.Keys.Mnemonic+  ( -- * Mnemonic Sentences+    Entropy,+    Mnemonic,+    Passphrase,+    Seed,+    toMnemonic,+    fromMnemonic,+    mnemonicToSeed,+    wordList,+    wordListMap,+  )+where++import Control.Monad (unless, 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.Char (isAscii)+import Data.List+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Text.Normalize (NormalizationMode (NFKD), normalize)+import Data.Vector (Vector, (!))+import Data.Vector qualified as V+import Haskoin.Util+import Unicode.Char qualified as UC++-- | 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.+--+-- This is a plain 'ByteString'. Its 'Show' instance prints the bytes, so+-- keep it out of logs and error messages.+type Entropy = ByteString++-- | Human-readable mnemonic sentence.+--+-- This is a plain 'Text', and so is 'Passphrase'. The compiler does not+-- tell the two apart, so mind the order of the arguments of+-- 'mnemonicToSeed': the passphrase comes first. The 'Show' instance prints+-- the sentence in full, so keep it out of logs and error messages.+type Mnemonic = Text++-- | Optional passphrase for mnemnoic sentence. It may hold any text that+-- is in the Stream-Safe Text Format of Unicode, see 'mnemonicToSeed'.+type Passphrase = Text++-- | Seed for a private key from a mnemonic sentence. A plain 'ByteString',+-- which 'Show' prints in full.+type Seed = ByteString++-- | Mnemonic key checksum.+type Checksum = ByteString++-- | Paremeters for PBKDF2 function.+pbkdfParams :: Parameters+pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}++-- | Smallest entropy BIP39 allows: 128 bits, which gives 12 words.+minEntropyBytes :: Int+minEntropyBytes = 16++-- | Largest entropy BIP39 allows: 256 bits, which gives 24 words.+maxEntropyBytes :: Int+maxEntropyBytes = 32++-- | Word counts of the sentences BIP39 allows, for 128 to 256 bits of entropy.+validWordCounts :: [Int]+validWordCounts = [12, 15, 18, 21, 24]++-- | The most letters of a word of the English list. NFKD never makes a+-- text shorter, so a word of the sentence with more characters than this+-- cannot be a word of the list, whatever its normal form is.+maxWordLength :: Int+maxWordLength = 8++-- | The most combining characters a passphrase may hold in a row once it+-- is normalised: the limit of the Stream-Safe Text Format of Unicode,+-- definition UAX15-D3 of Unicode Standard Annex 15. The annex chose 30 as+-- "significantly beyond what is required for any linguistic or technical+-- usage".+maxCombiningRun :: Int+maxCombiningRun = 30++-- | Provide initial 'Entropy' as a 'ByteString' of 16 to 32 bytes, in steps+-- of 4 bytes, as BIP39 allows. Output a 'Mnemonic' sentence of 12 to 24 words.+toMnemonic :: Entropy -> Either String Mnemonic+toMnemonic ent = do+  when (remainder /= 0) $+    Left "toMnemonic: entropy must be multiples of 4 bytes"+  when (B.length ent < minEntropyBytes) $+    Left "toMnemonic: minimum entropy is 16 bytes (128 bits)"+  when (B.length ent > maxEntropyBytes) $+    Left "toMnemonic: maximum entropy is 32 bytes (256 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.+--+-- Each word is NFKD-normalised before it is looked up, as BIP39 requires.+-- Only sentences of 12, 15, 18, 21 or 24 words are accepted. The words may+-- be separated by any amount of white space.+--+-- The sentence can come from an untrusted source, so the work is linear in+-- its length: the words are counted first, and a word longer than any word+-- of the list is refused without being normalised. An error names the+-- position of a wrong word, counted from 1, and never the word itself or a+-- checksum value, since the sentence is a secret and errors end up in logs.+fromMnemonic :: Mnemonic -> Either String Entropy+fromMnemonic ms = do+  when (T.null ms) $+    Left "fromMnemonic: empty mnemonic"+  unless (word_count `elem` validWordCounts) $+    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"+  return ms_ent+  where+    -- The sentence is split before anything is normalised. White space+    -- stays white space under NFKD and nothing is reordered across it, so+    -- the words are those of the normalised sentence.+    ms_words = T.words ms+    word_count = length ms_words+    (ent_len, cs_len) = (word_count * 11) `quotRem` 32++-- | 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++-- | The 512-bit 'Seed' of a sentence and a passphrase that are both in+-- NFKD already: PBKDF2-HMAC-SHA512 of the sentence, salted with "mnemonic"+-- followed by the passphrase, 2048 rounds.+normalizedToSeed :: Passphrase -> Mnemonic -> Seed+normalizedToSeed 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. The sentence and the passphrase are+-- NFKD-normalised as BIP39 requires, so a passphrase with accented letters+-- gives the same seed here as in other wallets.+--+-- The passphrase can be of any length. It is refused when its normal form+-- would hold more than 30 combining characters in a row, which no real+-- text does. That is the limit of the Stream-Safe Text Format of Unicode+-- (Unicode Standard Annex 15, UAX15-D3). Without it the time to normalise+-- such a run grows with the square of its length, so that 32 KB of text+-- from an untrusted source took half a minute.+mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed+mnemonicToSeed pf ms = do+  ent <- fromMnemonic ms+  -- The sentence is written again from its entropy, so it is in lower-case+  -- ASCII with single spaces, which is its NFKD form+  mnm <- toMnemonic ent+  pf_nfkd <- normalizePassphrase pf+  return $ normalizedToSeed pf_nfkd mnm++-- | The NFKD form of a passphrase, or an error when the passphrase is not+-- in the Stream-Safe Text Format.+normalizePassphrase :: Passphrase -> Either String Passphrase+normalizePassphrase pf+  -- NFKD leaves ASCII as it is+  | T.all isAscii pf = Right pf+  | exceedsCombiningRun maxCombiningRun pf =+      Left $+        "mnemonicToSeed: the passphrase has more than "+          ++ show maxCombiningRun+          ++ " combining characters in a row"+  | otherwise = Right (normalize NFKD pf)++-- | Whether the NFKD form of a text holds more combining characters in a+-- row than the limit. The text is not normalised as a whole to find out,+-- since that is the costly step. Each character is decomposed on its own:+-- normalising only reorders the combining characters inside a run, so the+-- runs of the decomposed characters are as long as those of the normal+-- form. A combining character is what the normaliser treats as one: a+-- character with a canonical combining class other than zero.+exceedsCombiningRun :: Int -> Text -> Bool+exceedsCombiningRun limit = go 0 . T.unpack+  where+    go :: Int -> String -> Bool+    go _ [] = False+    go run (c : rest)+      -- An ASCII character is its own decomposition and ends a run+      | isAscii c = go 0 rest+      | otherwise = case extend run (decomposition c) of+          Nothing -> True+          Just run' -> go run' rest+    -- The length of the run at the end of the characters given, when the+    -- run before them had the length given. Nothing when a run passes the+    -- limit.+    extend :: Int -> String -> Maybe Int+    extend run [] = Just run+    extend run (x : xs)+      | not (UC.isCombining x) = extend 0 xs+      | run >= limit = Nothing+      | otherwise = extend (run + 1) xs+    decomposition c = T.unpack (normalize NFKD (T.singleton c))++-- | Get indices of words in word list. An error names the positions of the+-- words that are not in the list, counted from 1.+getIndices :: [Text] -> Either String [Int]+getIndices ws+  | null missing = return $ catMaybes found+  | otherwise = Left $ "fromMnemonic: " ++ wordsAt missing ++ " not in the list"+  where+    found = map wordIndex ws+    missing = [n | (n, Nothing) <- zip [1 :: Int ..] found]++-- | The index of a word of the sentence in the word list, looked up in its+-- NFKD form. A word longer than any word of the list is not normalised.+wordIndex :: Text -> Maybe Int+wordIndex w+  | T.compareLength w maxWordLength == GT = Nothing+  | otherwise = M.lookup (normalize NFKD w) wordListMap++-- | The start of a sentence about the words at the positions given: "word+-- 3 is", "words 3 and 7 are", "words 1, 3 and 7 are".+wordsAt :: [Int] -> String+wordsAt positions = case reverse (map show positions) of+  [] -> "no word is"+  [one] -> "word " ++ one ++ " is"+  final : before -> "words " ++ intercalate ", " (reverse before) ++ " and " ++ final ++ " are"++-- | 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/Keys/Slip39.hs view
@@ -0,0 +1,1898 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Crypto.Keys.Slip39+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Shamir's secret-sharing for mnemonic codes, SLIP-0039. A master secret is+-- encrypted with a passphrase and split into shares, of which a chosen+-- number rebuild it. Each share is a mnemonic of the SLIP-0039 word list,+-- with a checksum that catches any three wrong words. The split has two+-- levels: the encrypted master secret is shared among groups, and each+-- group's share among its members. A plain T-of-N split is one group with+-- a member threshold of T and N members.+--+-- The specification is at+-- <https://github.com/satoshilabs/slips/blob/master/slip-0039.md>. This+-- module follows it section by section: the field GF(256), the Lagrange+-- interpolation, the splitting and recovery of a secret with its digest,+-- the format of a share mnemonic, the RS1024 checksum, the encryption of+-- the master secret with the passphrase, and the generation and combination+-- of the shares.+--+-- The master secret of a BIP-0032 wallet is its seed, the bytes fed to+-- HMAC-SHA512 to derive the master key. A SLIP-0039 share is not a BIP-0039+-- mnemonic and cannot be converted into one.+--+-- A share is a secret. The 'Show' instances of this module leave the share+-- values out, and an error names the position of a wrong word, never the+-- word. A mnemonic can come from an untrusted source, so its length is+-- checked before anything else: a share has at most 1023 words.+--+-- The field arithmetic looks bytes of the secret up in tables, as the+-- reference implementation does, so its timing depends on them. Hardware+-- wallets use constant-time code. Do not split or combine secrets where an+-- attacker can time the operation closely.+module Haskoin.Crypto.Keys.Slip39+  ( -- * Shares+    Share (..),+    encodeShare,+    decodeShare,++    -- * Generating shares+    Scheme (..),+    generateMnemonics,+    generateShares,+    generateSharesFrom,+    generateSharesWith,+    SplitRandom (..),+    splitRandomSizes,++    -- * Combining shares+    combineMnemonics,+    combineShares,+    EncryptedMasterSecret (..),++    -- * The passphrase+    encryptMasterSecret,+    decryptMasterSecret,+    decryptMasterSecretBytes,++    -- * Shamir's secret sharing+    splitSecret,+    recoverSecret,+    interpolate,+    secretIndex,+    digestIndex,++    -- * The field GF(256)+    gfMul,+    gfInv,+    gfDiv,++    -- * The checksum+    rs1024Polymod,+    rs1024Checksum,+    rs1024Verify,+    customizationString,++    -- * The word list+    slip39WordList,+    slip39WordListMap,+    mnemonicToIndices,+    indicesToMnemonic,+  )+where++import Control.Monad (foldM, forM, forM_, replicateM, unless, when)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE)+import Crypto.Hash (SHA256)+import Crypto.KDF.PBKDF2 (Parameters (..), fastPBKDF2_SHA256)+import Crypto.MAC.HMAC (HMAC, hmac)+import Data.Array.Unboxed (UArray, listArray, (!))+import Data.Bits (shiftL, shiftR, testBit, xor, (.&.), (.|.))+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Char (isAscii, ord)+import Data.List (nub, transpose)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as M+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 Data.Word (Word8)+import System.Entropy (getEntropy)++{- Shares -}++-- | A share, as its mnemonic carries it. The identifier is a random 15-bit+-- value, the same on every share of a split. The iteration exponent sets+-- the PBKDF2 iterations of the passphrase to 10000 times 2 to its power.+-- The indices are the x values of the share at each level, 0 to 15, and the+-- thresholds and the group count are the actual numbers, 1 to 16, not the+-- values the mnemonic encodes, which are one less.+data Share = Share+  { identifier :: !Int,+    extendable :: !Bool,+    iterationExponent :: !Int,+    groupIndex :: !Int,+    groupThreshold :: !Int,+    groupCount :: !Int,+    memberIndex :: !Int,+    memberThreshold :: !Int,+    -- | The share value: f(x) of each byte of the secret shared+    value :: !ByteString+  }+  deriving (Eq, Ord)++-- | The value of a share is secret, so it is not shown.+instance Show Share where+  showsPrec d s =+    showParen (d > 10) $+      showString "Share {identifier = "+        . shows s.identifier+        . showString ", extendable = "+        . shows s.extendable+        . showString ", iterationExponent = "+        . shows s.iterationExponent+        . showString ", groupIndex = "+        . shows s.groupIndex+        . showString ", groupThreshold = "+        . shows s.groupThreshold+        . showString ", groupCount = "+        . shows s.groupCount+        . showString ", memberIndex = "+        . shows s.memberIndex+        . showString ", memberThreshold = "+        . shows s.memberThreshold+        . showString ", value = <redacted>}"++-- | The master secret encrypted with its passphrase, with what its+-- decryption needs besides the passphrase. This is what the shares share.+data EncryptedMasterSecret = EncryptedMasterSecret+  { identifier :: !Int,+    extendable :: !Bool,+    iterationExponent :: !Int,+    ciphertext :: !ByteString+  }+  deriving (Eq)++-- | The ciphertext is secret, so it is not shown.+instance Show EncryptedMasterSecret where+  showsPrec d e =+    showParen (d > 10) $+      showString "EncryptedMasterSecret {identifier = "+        . shows e.identifier+        . showString ", extendable = "+        . shows e.extendable+        . showString ", iterationExponent = "+        . shows e.iterationExponent+        . showString ", ciphertext = <redacted>}"++-- | How a master secret is split: how many groups are needed to rebuild+-- it, and, for each group, how many of its members are needed and how many+-- members it has. A plain T-of-N split is @Scheme 1 [(T, N)]@.+data Scheme = Scheme+  { groupThreshold :: !Int,+    groups :: ![(Int, Int)]+  }+  deriving (Eq, Show)++{- Constants of the format -}++-- | The index at which the shared secret is stored, f(255).+secretIndex :: Word8+secretIndex = 255++-- | The index at which the digest of the shared secret is stored, f(254).+digestIndex :: Word8+digestIndex = 254++-- | The bytes of the digest at the head of the digest share.+digestBytes :: Int+digestBytes = 4++-- | The most shares a secret can be split into, and the most groups: the+-- indices have four bits.+maxShares :: Int+maxShares = 16++-- | The fewest bytes of a master secret: 128 bits.+minSecretBytes :: Int+minSecretBytes = 16++-- | The words of a mnemonic that are not share value: four of header and+-- three of checksum.+metadataWords :: Int+metadataWords = 7++-- | The header words of a mnemonic: the identifier, the flag, the exponent,+-- the indices, the thresholds and the group count, 40 bits in all.+headerWords :: Int+headerWords = 4++-- | The fewest words of a mnemonic: the metadata and 13 words of value for+-- a secret of 128 bits.+minMnemonicWords :: Int+minMnemonicWords = 20++-- | The most words of a mnemonic. The checksum is a Reed-Solomon code over+-- GF(1024), whose code words have at most 1023 symbols, the "up to a+-- thousand words" of the specification. Beyond that length the checksum no+-- longer promises to catch any three wrong words.+maxMnemonicWords :: Int+maxMnemonicWords = 1023++-- | The most bytes of a share value that fit a mnemonic: the 1016 words+-- left by the metadata hold 10160 bits.+maxShareValueBytes :: Int+maxShareValueBytes = ((maxMnemonicWords - metadataWords) * 10) `div` 8++{- The field GF(256) -}++-- | The powers of the generator x + 1, the byte 3, in the field of the AES+-- cipher: bytes as polynomials modulo x^8 + x^4 + x^3 + x + 1. The table+-- holds 3^i for i from 0 to 254.+expTable :: UArray Int Word8+expTable = listArray (0, 254) (map fromIntegral (take 255 (iterate timesGenerator (1 :: Int))))+  where+    -- Multiply by x + 1, then reduce by the polynomial, 0x11B+    timesGenerator p =+      let q = (p `shiftL` 1) `xor` p+       in if testBit q 8 then q `xor` 0x11B else q++-- | The logarithm of each non-zero byte to the base 3: the inverse of+-- 'expTable'. The entry of zero, which has no logarithm, is unused.+logTable :: UArray Word8 Int+logTable = listArray (0, 255) [M.findWithDefault 0 b logs | b <- [0 .. 255]]+  where+    logs = M.fromList [(expTable ! i, i) | i <- [0 .. 254]]++-- | The product of two bytes in GF(256).+gfMul :: Word8 -> Word8 -> Word8+gfMul a b+  | a == 0 || b == 0 = 0+  | otherwise = expTable ! ((logTable ! a + logTable ! b) `mod` 255)++-- | The inverse of a byte in GF(256); zero has none.+gfInv :: Word8 -> Maybe Word8+gfInv 0 = Nothing+gfInv a = Just (expTable ! ((255 - logTable ! a) `mod` 255))++-- | The quotient of two bytes in GF(256); nothing when dividing by zero.+gfDiv :: Word8 -> Word8 -> Maybe Word8+gfDiv a b = gfMul a <$> gfInv b++{- Shamir's secret sharing -}++-- | The value at the index x of the polynomials through the points given,+-- one polynomial per byte of the values, by Lagrange interpolation. The+-- points must have distinct indices and values of one length.+interpolate :: Word8 -> [(Word8, ByteString)] -> Either String ByteString+interpolate x points = do+  when (null points) $ Left "There are no shares to interpolate"+  when (length (nub indices) /= length indices) $ Left "The shares do not have distinct indices"+  when (length (nub (map (B.length . snd) points)) /= 1) $ Left "The shares do not have the same length"+  weights <- mapM basis indices+  -- Each byte of the result is the sum over the points of the point's byte+  -- times its basis polynomial at x; the sum of the field is xor+  return $ B.pack [foldl' xor 0 (zipWith gfMul weights column) | column <- transpose (map (B.unpack . snd) points)]+  where+    indices = map fst points+    -- The Lagrange basis polynomial of the point at xi, evaluated at x: the+    -- product over the other points of (x - xj) / (xi - xj), where the+    -- difference of the field is xor. The indices are distinct, so no+    -- division is by zero.+    basis xi = foldM (factor xi) 1 [xj | xj <- indices, xj /= xi]+    factor xi acc xj =+      maybe (Left "The shares do not have distinct indices") (Right . gfMul acc) $+        gfDiv (x `xor` xj) (xi `xor` xj)++-- | The random values a split draws: the T - 2 random shares, each as long+-- as the secret, and the random part of the digest share, four bytes+-- shorter. A split with a threshold of 1 draws none.+data SplitRandom = SplitRandom+  { randomShares :: ![ByteString],+    digestRandom :: !ByteString+  }+  deriving (Eq)++-- | The random shares are shares of the secret, and the random part is+-- most of the digest share, so neither is shown.+instance Show SplitRandom where+  showsPrec d _ =+    showParen (d > 10) $+      showString "SplitRandom {randomShares = <redacted>, digestRandom = <redacted>}"++-- | The sizes of the random values a split of a secret of the given length+-- draws at the given threshold: how many random shares, their length, and+-- the length of the random part of the digest share.+splitRandomSizes :: Int -> Int -> (Int, Int, Int)+splitRandomSizes threshold secretLength+  | threshold <= 1 = (0, secretLength, 0)+  | otherwise = (threshold - 2, secretLength, secretLength - digestBytes)++-- | The first four bytes of HMAC-SHA256 keyed with the random part of the+-- digest share, of the shared secret.+digestOf :: ByteString -> ByteString -> ByteString+digestOf randomPart secret = B.take digestBytes (hmacSha256 randomPart secret)++hmacSha256 :: ByteString -> ByteString -> ByteString+hmacSha256 key msg = BA.convert (hmac key msg :: HMAC SHA256)++-- | SplitSecret of the specification: the shares of a secret at the given+-- threshold and count, with their indices from 0, computed from the random+-- values given. With a threshold of 1 every share is the secret itself.+-- Otherwise the random shares take the first indices, the digest share+-- sits at 254 and the secret at 255, and the remaining shares are the+-- polynomials through those points.+splitSecret :: Int -> Int -> ByteString -> SplitRandom -> Either String [(Word8, ByteString)]+splitSecret threshold count secret random = do+  when (threshold < 1) $ Left "The threshold must be at least 1"+  when (threshold > count) $ Left "The threshold is greater than the number of shares"+  when (count > maxShares) $ Left ("The number of shares is greater than " <> show maxShares)+  checkSecretLength "secret" secret+  let (randomCount, _, randomPartLength) = splitRandomSizes threshold (B.length secret)+  unless (length random.randomShares == randomCount) $+    Left ("A split with a threshold of " <> show threshold <> " takes " <> show randomCount <> " random shares")+  unless (all ((== B.length secret) . B.length) random.randomShares) $+    Left "A random share must be as long as the secret"+  unless (B.length random.digestRandom == randomPartLength) $+    Left ("The random part of the digest share must have " <> show randomPartLength <> " bytes")+  if threshold == 1+    then return [(i, secret) | i <- indicesUpTo count]+    else do+      let digestShare = digestOf random.digestRandom secret <> random.digestRandom+          randomPoints = zip (indicesUpTo randomCount) random.randomShares+          basePoints = randomPoints ++ [(digestIndex, digestShare), (secretIndex, secret)]+      computed <- forM (drop randomCount (indicesUpTo count)) $ \i -> (,) i <$> interpolate i basePoints+      return (randomPoints ++ computed)+  where+    indicesUpTo n = take n [0 ..]++-- | RecoverSecret of the specification: the secret the shares were split+-- from at the given threshold. With a threshold of 1 it is the first share.+-- Otherwise the secret and the digest share are interpolated at their+-- indices, and the digest must match the secret.+recoverSecret :: Int -> [(Word8, ByteString)] -> Either String ByteString+recoverSecret threshold shares =+  case shares of+    [] -> Left "There are no shares to recover the secret from"+    (_, first) : _+      | threshold == 1 -> Right first+      | otherwise -> do+          secret <- interpolate secretIndex shares+          digestShare <- interpolate digestIndex shares+          let (digest, randomPart) = B.splitAt digestBytes digestShare+          -- compared in constant time, since the digest is of the secret+          unless (BA.constEq digest (digestOf randomPart secret)) $ Left "The digest of the secret does not match"+          return secret++{- The checksum -}++-- | The polymod of the RS1024 code over the values given, each a word of+-- the mnemonic as its 10-bit index, after the characters of the+-- customization string.+rs1024Polymod :: [Int] -> Int+rs1024Polymod = foldl' step 1+  where+    step chk v =+      let top = chk `shiftR` 20+          shifted = ((chk .&. 0xfffff) `shiftL` 10) `xor` v+       in foldl' (\c (i, g) -> if testBit top i then c `xor` g else c) shifted (zip [0 ..] generator)+    generator =+      [ 0xe0e040,+        0x1c1c080,+        0x3838100,+        0x7070200,+        0xe0e0009,+        0x1c0c2412,+        0x38086c24,+        0x3090fc48,+        0x21b1f890,+        0x3f3f120+      ]++-- | The customization string of the checksum, fed to the polymod before+-- the data: "shamir", or "shamir_extendable" when the extendable flag is+-- set.+customizationString :: Bool -> [Int]+customizationString ext = map ord (if ext then "shamir_extendable" else "shamir")++-- | The three checksum words of the data words given, under the+-- customization string.+rs1024Checksum :: [Int] -> [Int] -> [Int]+rs1024Checksum cs values = [(polymod `shiftR` (10 * (2 - i))) .&. 1023 | i <- [0 .. 2]]+  where+    polymod = rs1024Polymod (cs ++ values ++ [0, 0, 0]) `xor` 1++-- | Whether the words given, the data words and their checksum, check out+-- under the customization string.+rs1024Verify :: [Int] -> [Int] -> Bool+rs1024Verify cs values = rs1024Polymod (cs ++ values) == 1++{- Share mnemonics -}++-- | The mnemonic of a share: its header, its value padded to whole words,+-- and the checksum, as words of the list. A share whose fields are out of+-- range is refused, and so is a value of more than 1270 bytes, whose+-- mnemonic would be longer than the 1023 words the checksum covers.+encodeShare :: Share -> Either String Text+encodeShare share = do+  checkShare share+  when (B.length share.value > maxShareValueBytes) $+    Left ("The share value must have at most " <> show maxShareValueBytes <> " bytes, not " <> show (B.length share.value))+  let dataWords = headerOf share ++ valueWords share.value+      checksum = rs1024Checksum (customizationString share.extendable) dataWords+  indicesToMnemonic (dataWords ++ checksum)++-- | The share a mnemonic carries. The words are looked up in lower case.+-- The mnemonic must have at least 20 and at most 1023 words, a length that+-- leaves at most 8 bits of padding, a valid checksum, zero padding bits,+-- and a group threshold no greater than its group count.+--+-- The words are counted before anything else is done with them, so the+-- work on a mnemonic from an untrusted source is linear in its length.+decodeShare :: Text -> Either String Share+decodeShare mnemonic = do+  let mnemonicWords = T.words mnemonic+      count = length mnemonicWords+      valueWordCount = count - metadataWords+  when (count > maxMnemonicWords) $+    Left ("The mnemonic has " <> show count <> " words, but a share has at most " <> show maxMnemonicWords)+  indices <- wordsToIndices mnemonicWords+  -- The padded value is a whole number of 10-bit words; its length modulo+  -- 16 is the padding, since the value has a multiple of 16 bits+  let padding = (10 * valueWordCount) `mod` 16+  when (count < minMnemonicWords) $+    Left ("The mnemonic has " <> show count <> " words, but a share has at least " <> show minMnemonicWords)+  when (padding > 8) $+    Left ("The mnemonic has " <> show count <> " words, which is not a valid share length")+  let (header, rest) = splitAt headerWords indices+      (valueIndices, checksum) = splitAt valueWordCount rest+      headerInt = indicesToInteger header+      ext = testBit headerInt 24+  unless (rs1024Verify (customizationString ext) (header ++ valueIndices ++ checksum)) $+    Left "The checksum of the mnemonic is invalid"+  let share =+        Share+          { identifier = field 25 32767,+            extendable = ext,+            iterationExponent = field 20 15,+            groupIndex = field 16 15,+            groupThreshold = field 12 15 + 1,+            groupCount = field 8 15 + 1,+            memberIndex = field 4 15,+            memberThreshold = field 0 15 + 1,+            value = B.empty+          }+      field offset mask = fromIntegral ((headerInt `shiftR` offset) .&. mask)+  when (share.groupThreshold > share.groupCount) $+    Left "The group threshold is greater than the group count"+  let valueBytes = (10 * valueWordCount - padding) `div` 8+      valueInt = indicesToInteger valueIndices+  when (valueInt >= 2 ^ (8 * valueBytes)) $+    Left "The padding bits of the share value are not zero"+  return share {value = integerToBytes valueBytes valueInt}++-- | The header of a share as four words: the identifier, the flag, the+-- exponent, the group index, the group threshold less one, the group count+-- less one, the member index and the member threshold less one, 40 bits.+headerOf :: Share -> [Int]+headerOf share = integerToIndices headerWords headerInt+  where+    headerInt =+      (toInteger share.identifier `shiftL` 25)+        .|. (if share.extendable then 1 `shiftL` 24 else 0)+        .|. (toInteger share.iterationExponent `shiftL` 20)+        .|. (toInteger share.groupIndex `shiftL` 16)+        .|. (toInteger (share.groupThreshold - 1) `shiftL` 12)+        .|. (toInteger (share.groupCount - 1) `shiftL` 8)+        .|. (toInteger share.memberIndex `shiftL` 4)+        .|. toInteger (share.memberThreshold - 1)++-- | A share value as words: its bits, left-padded with zeros to a multiple+-- of ten.+valueWords :: ByteString -> [Int]+valueWords bytes = integerToIndices wordCount (bytesToInteger bytes)+  where+    wordCount = (8 * B.length bytes + 9) `div` 10++-- | Every field of a share checked against its range, so that an encoded+-- share is a valid mnemonic and a combined set is one the format allows.+checkShare :: Share -> Either String ()+checkShare share = do+  checkRange "identifier" 0 32767 share.identifier+  checkRange "iteration exponent" 0 15 share.iterationExponent+  checkRange "group index" 0 (maxShares - 1) share.groupIndex+  checkRange "group threshold" 1 maxShares share.groupThreshold+  checkRange "group count" 1 maxShares share.groupCount+  when (share.groupThreshold > share.groupCount) $+    Left "The group threshold is greater than the group count"+  checkRange "member index" 0 (maxShares - 1) share.memberIndex+  checkRange "member threshold" 1 maxShares share.memberThreshold+  checkSecretLength "share value" share.value++-- | A count with its noun: "1 share", "2 shares".+counted :: Int -> String -> String+counted n noun = show n <> " " <> noun <> (if n == 1 then "" else "s")++checkRange :: String -> Int -> Int -> Int -> Either String ()+checkRange what lo hi v =+  unless (v >= lo && v <= hi) $+    Left ("The " <> what <> " must be between " <> show lo <> " and " <> show hi <> ", not " <> show v)++-- | A secret, or a share of one, has at least 128 bits and a multiple of+-- 16 bits.+checkSecretLength :: String -> ByteString -> Either String ()+checkSecretLength what bytes = do+  when (B.length bytes < minSecretBytes) $+    Left ("The " <> what <> " must have at least " <> show minSecretBytes <> " bytes, not " <> show (B.length bytes))+  when (odd (B.length bytes)) $+    Left ("The " <> what <> " must have an even number of bytes, not " <> show (B.length bytes))++{- The passphrase -}++-- | The master secret encrypted with the passphrase under the identifier,+-- the extendable flag and the iteration exponent given, by the four-round+-- Feistel network of the specification. The passphrase must be printable+-- ASCII, code points 32 to 126; use the empty passphrase for none.+encryptMasterSecret :: Int -> Bool -> Int -> Text -> ByteString -> Either String EncryptedMasterSecret+encryptMasterSecret identifier ext iterExponent passphrase secret = do+  checkRange "identifier" 0 32767 identifier+  checkRange "iteration exponent" 0 15 iterExponent+  checkSecretLength "master secret" secret+  passphraseBytes <- checkPassphrase passphrase+  return+    EncryptedMasterSecret+      { identifier = identifier,+        extendable = ext,+        iterationExponent = iterExponent,+        ciphertext = feistel [0, 1, 2, 3] identifier ext iterExponent passphraseBytes secret+      }++-- | The bytes of a passphrase, which must be printable ASCII: "the+-- passphrase MUST be a string containing only printable ASCII characters+-- (code points 32-126)", says the specification.+checkPassphrase :: Text -> Either String ByteString+checkPassphrase passphrase+  | T.all printableAscii passphrase = Right (E.encodeUtf8 passphrase)+  | otherwise = Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+  where+    printableAscii c = ord c >= 32 && ord c <= 126++-- | The master secret of an encrypted one under a passphrase: the Feistel+-- rounds in reverse. Any passphrase gives a master secret, so a wrong one+-- is not detected, as the specification intends.+--+-- The passphrase must be printable ASCII, as the specification requires+-- and as on encryption. A passphrase with other characters has several+-- encodings that look alike on screen, such as "é" as one code point or as+-- "e" with a combining accent, and each one would open a different wallet.+-- To recover shares made by software that allowed such a passphrase, use+-- 'decryptMasterSecretBytes' with the exact bytes that software used.+decryptMasterSecret :: Text -> EncryptedMasterSecret -> Either String ByteString+decryptMasterSecret passphrase ems = do+  passphraseBytes <- checkPassphrase passphrase+  decryptMasterSecretBytes passphraseBytes ems++-- | 'decryptMasterSecret' with the passphrase as bytes, which are not+-- checked, as the @decrypt@ function of the reference implementation takes+-- it. This is for shares made by software that allowed a passphrase+-- outside printable ASCII: the caller has to know which bytes that+-- software fed to the cipher, for instance the UTF-8 encoding of the NFKD+-- form of the passphrase. Get the encrypted master secret from+-- 'combineShares'.+decryptMasterSecretBytes :: ByteString -> EncryptedMasterSecret -> Either String ByteString+decryptMasterSecretBytes passphraseBytes ems = do+  checkRange "identifier" 0 32767 ems.identifier+  checkRange "iteration exponent" 0 15 ems.iterationExponent+  checkSecretLength "encrypted master secret" ems.ciphertext+  return $ feistel [3, 2, 1, 0] ems.identifier ems.extendable ems.iterationExponent passphraseBytes ems.ciphertext++-- | The Feistel network of the specification over the rounds given, in+-- that order: the halves L and R become (R, L xor F(i, R)) each round, and+-- the result is R || L. The round function is PBKDF2-HMAC-SHA256 of the+-- round number followed by the passphrase, salted with the salt prefix+-- and R, with 2500 times 2 to the exponent iterations. The passphrase is+-- given as its bytes.+feistel :: [Word8] -> Int -> Bool -> Int -> ByteString -> ByteString -> ByteString+feistel rounds identifier ext iterExponent passphrase secret = right <> left+  where+    half = B.length secret `div` 2+    (left, right) = foldl' step (B.take half secret, B.drop half secret) rounds+    step (l, r) i = (r, B.pack (B.zipWith xor l (roundFunction i r)))+    roundFunction i r =+      fastPBKDF2_SHA256+        Parameters {iterCounts = 2500 `shiftL` iterExponent, outputLength = half}+        (B.cons i passphrase)+        (saltPrefix <> r)+    -- Without the extendable flag the identifier salts the passphrase, so+    -- another split of the same secret gives another master secret+    saltPrefix+      | ext = B.empty+      | otherwise = "shamir" <> integerToBytes 2 (toInteger identifier)++{- Generating shares -}++-- | The shares of a master secret under a scheme, with the random values+-- given: the identifier, then one 'SplitRandom' for the split among the+-- groups and one for each group's split among its members, in that order.+-- GenerateShares of the specification, with the identifier and the+-- extendable flag as arguments. The shares come as one list per group.+generateSharesWith :: Scheme -> Int -> Bool -> Int -> Text -> ByteString -> [SplitRandom] -> Either String [[Share]]+generateSharesWith scheme identifier ext iterExponent passphrase secret randoms = do+  checkScheme scheme+  ems <- encryptMasterSecret identifier ext iterExponent passphrase secret+  (groupRandom, memberRandoms) <- case randoms of+    (g : ms) | length ms == groupCount -> Right (g, ms)+    _ ->+      Left $+        "A scheme of " <> show groupCount <> " groups takes " <> show (groupCount + 1)+          <> " sets of random values: one for the split among the groups, then one per group"+  groupShares <- splitSecret scheme.groupThreshold groupCount ems.ciphertext groupRandom+  forM (zip3 scheme.groups groupShares memberRandoms) $ \((threshold, members), (gi, groupSecret), random) -> do+    memberShares <- splitSecret threshold members groupSecret random+    return+      [ Share+          { identifier = identifier,+            extendable = ext,+            iterationExponent = iterExponent,+            groupIndex = fromIntegral gi,+            groupThreshold = scheme.groupThreshold,+            groupCount = groupCount,+            memberIndex = fromIntegral mi,+            memberThreshold = threshold,+            value = v+          }+        | (mi, v) <- memberShares+      ]+  where+    groupCount = length scheme.groups++-- | A scheme has 1 to 16 groups, a group threshold within them, and in each+-- group a member threshold within 1 to 16 members. A group whose member+-- threshold is 1 has one member: more would differ only in their index and+-- give no security, so the specification refuses them.+checkScheme :: Scheme -> Either String ()+checkScheme scheme = do+  when (null scheme.groups) $ Left "A scheme has at least one group"+  when (length scheme.groups > maxShares) $ Left ("A scheme has at most " <> show maxShares <> " groups")+  checkRange "group threshold" 1 (length scheme.groups) scheme.groupThreshold+  forM_ (zip [1 :: Int ..] scheme.groups) $ \(i, (threshold, members)) -> do+    let group = "group " <> show i+    checkRange ("member count of " <> group) 1 maxShares members+    checkRange ("member threshold of " <> group) 1 members threshold+    when (threshold == 1 && members > 1) $+      Left ("A group with a member threshold of 1 has one member, but " <> group <> " has " <> show members)++-- | The thresholds of the splits of a scheme, in the order the random+-- values are drawn: the groups first, then the members of each group.+schemeThresholds :: Scheme -> [Int]+schemeThresholds scheme = scheme.groupThreshold : map fst scheme.groups++-- | The shares of a master secret under a scheme, with a random identifier+-- and random values from the operating system: 'getEntropy' of the entropy+-- package, which reads the @getrandom@ system call or @\/dev\/urandom@ on+-- Unix and the system's generator on Windows. The specification asks for a+-- source "suitable for generating cryptographic keys".+--+-- Everything is checked before anything is drawn. A secret of more than+-- 1270 bytes can be split, but its shares are too long for 'encodeShare'.+generateShares :: (MonadIO m) => Scheme -> Bool -> Int -> Text -> ByteString -> m (Either String [[Share]])+generateShares = generateSharesFrom (liftIO . getEntropy)++-- | 'generateShares' with the source of random bytes as its first+-- argument: a function from a number of bytes to that many random bytes.+-- Use it to draw from a generator of your own, which must be fit for keys.+-- With @getRandomBytes@ of crypton it runs in any @MonadRandom@. Mind that+-- in 'IO' that function takes its bytes from the RDRAND instruction of the+-- processor alone when the processor has one, and does not mix in the+-- operating system's entropy.+--+-- The source is only asked for whole values, never for zero bytes, and+-- only after the scheme, the exponent, the passphrase and the secret have+-- passed their checks.+generateSharesFrom ::+  (Monad m) =>+  -- | the source of random bytes+  (Int -> m ByteString) ->+  Scheme ->+  Bool ->+  Int ->+  Text ->+  ByteString ->+  m (Either String [[Share]])+generateSharesFrom randomBytes scheme ext iterExponent passphrase secret = runExceptT $ do+  ExceptT . return $ do+    checkScheme scheme+    checkRange "iteration exponent" 0 15 iterExponent+    checkSecretLength "master secret" secret+    _ <- checkPassphrase passphrase+    return ()+  idBytes <- draw 2+  let identifier = fromIntegral (bytesToInteger idBytes .&. 0x7fff)+  randoms <- mapM drawSplitRandom (schemeThresholds scheme)+  ExceptT . return $ generateSharesWith scheme identifier ext iterExponent passphrase secret randoms+  where+    -- The bytes asked of the source, which must give as many as asked+    draw 0 = return B.empty+    draw n = do+      bytes <- lift (randomBytes n)+      unless (B.length bytes == n) $+        throwE ("The source of random bytes gave " <> show (B.length bytes) <> " bytes where " <> show n <> " were asked for")+      return bytes+    -- The random values of one split: the random shares, then the random+    -- part of the digest share+    drawSplitRandom threshold = do+      let (count, shareLength, randomPartLength) = splitRandomSizes threshold (B.length secret)+      shares <- replicateM count (draw shareLength)+      randomPart <- draw randomPartLength+      return SplitRandom {randomShares = shares, digestRandom = randomPart}++-- | The share mnemonics of a master secret under a scheme, one list per+-- group: 'generateShares' encoded.+generateMnemonics :: (MonadIO m) => Scheme -> Bool -> Int -> Text -> ByteString -> m (Either String [[Text]])+generateMnemonics scheme ext iterExponent passphrase secret =+  (>>= traverse (traverse encodeShare)) <$> generateShares scheme ext iterExponent passphrase secret++{- Combining shares -}++-- | The encrypted master secret the shares rebuild. The shares must agree+-- on the identifier, the flag, the exponent, the group threshold, the group+-- count and the length of their values; they must come from exactly as+-- many groups as the group threshold; and in each group they must agree on+-- the member threshold, have distinct member indices and be exactly as many+-- as that threshold. Each group's secret is recovered from its members,+-- then the master secret from the groups.+combineShares :: [Share] -> Either String EncryptedMasterSecret+combineShares [] = Left "There are no shares to combine"+combineShares shares@(first : _) = do+  mapM_ checkShare shares+  agree "identifier" (.identifier)+  agree "extendable flag" (.extendable)+  agree "iteration exponent" (.iterationExponent)+  agree "group threshold" (.groupThreshold)+  agree "group count" (.groupCount)+  agree "length of their values" (B.length . (.value))+  let groups = M.toList (M.fromListWith (flip (++)) [(s.groupIndex, [s]) | s <- shares])+  unless (length groups == first.groupThreshold) $+    Left ("The shares come from " <> counted (length groups) "group" <> ", but the group threshold is " <> show first.groupThreshold)+  groupSecrets <- forM groups $ \(gi, members) -> do+    threshold <- case members of+      m : _ -> Right m.memberThreshold+      [] -> Left ("Group " <> show gi <> " has no shares") -- cannot happen: a group holds the share it was made for+    unless (all ((== threshold) . (.memberThreshold)) members) $+      Left ("The shares of group " <> show gi <> " do not have the same member threshold")+    forM_ (duplicates (map (.memberIndex) members)) $ \mi ->+      Left ("Group " <> show gi <> " has two shares with the member index " <> show mi)+    unless (length members == threshold) $+      Left ("Group " <> show gi <> " has " <> counted (length members) "share" <> ", but its member threshold is " <> show threshold)+    secret <- recoverSecret threshold [(fromIntegral m.memberIndex, m.value) | m <- members]+    return (fromIntegral gi, secret)+  ciphertext <- recoverSecret first.groupThreshold groupSecrets+  return+    EncryptedMasterSecret+      { identifier = first.identifier,+        extendable = first.extendable,+        iterationExponent = first.iterationExponent,+        ciphertext = ciphertext+      }+  where+    agree :: (Eq a) => String -> (Share -> a) -> Either String ()+    agree what field =+      unless (all ((== field first) . field) shares) $+        Left ("The shares do not have the same " <> what)+    duplicates xs = nub [x | (i, x) <- zip [0 :: Int ..] xs, x `elem` take i xs]++-- | The master secret the mnemonics rebuild, decrypted with the passphrase,+-- which must be printable ASCII (see 'decryptMasterSecret').+--+-- The cost of the decryption is set by the iteration exponent the shares+-- carry: 10000 times 2 to its power rounds of PBKDF2-HMAC-SHA256. The+-- exponent can be as high as 15, which is 327,680,000 rounds and about two+-- minutes of one processor core, where an exponent of 0 takes a few+-- milliseconds. The specification allows it. A program that reads shares+-- from an untrusted source should look at the @iterationExponent@ that+-- 'decodeShare' gives before it combines them.+combineMnemonics :: Text -> [Text] -> Either String ByteString+combineMnemonics passphrase mnemonics = do+  shares <- mapM decodeShare mnemonics+  ems <- combineShares shares+  decryptMasterSecret passphrase ems++{- Words and bits -}++-- | The indices of the words of a mnemonic in the list. The words are+-- separated by white space and matched without regard to the case of+-- their ASCII letters. An error names the position of the first word that+-- is not in the list, counted from 1, and not the word, since a share is a+-- secret.+mnemonicToIndices :: Text -> Either String [Int]+mnemonicToIndices = wordsToIndices . T.words++-- | The indices of words in the list, see 'mnemonicToIndices'.+wordsToIndices :: [Text] -> Either String [Int]+wordsToIndices ws = forM (zip [1 :: Int ..] ws) $ \(position, w) ->+  maybe (Left ("Word " <> show position <> " is not in the SLIP-39 word list")) Right (wordIndex w)++-- | The index of a word in the list. The words of the list are ASCII, so+-- a word with any other character is not one of them: lower-casing by the+-- Unicode rules would turn a look-alike such as U+212A KELVIN SIGN into a+-- letter.+wordIndex :: Text -> Maybe Int+wordIndex w+  | T.all isAscii w = M.lookup (T.toLower w) slip39WordListMap+  | otherwise = Nothing++-- | The mnemonic of word indices, the words separated by single spaces.+-- An index outside the list, 0 to 1023, is refused.+indicesToMnemonic :: [Int] -> Either String Text+indicesToMnemonic indices = T.unwords <$> mapM wordAt indices+  where+    wordAt i = maybe (Left ("The word index must be between 0 and 1023, not " <> show i)) Right (slip39WordList V.!? i)++-- | The big-endian number of a list of 10-bit indices.+indicesToInteger :: [Int] -> Integer+indicesToInteger = foldl' (\acc i -> (acc `shiftL` 10) .|. toInteger i) 0++-- | A number as the given count of 10-bit indices, big-endian.+integerToIndices :: Int -> Integer -> [Int]+integerToIndices count n = [fromIntegral ((n `shiftR` (10 * i)) .&. 1023) | i <- reverse [0 .. count - 1]]++-- | The big-endian number of a byte string.+bytesToInteger :: ByteString -> Integer+bytesToInteger = B.foldl' (\acc b -> (acc `shiftL` 8) .|. toInteger b) 0++-- | A number as the given count of bytes, big-endian.+integerToBytes :: Int -> Integer -> ByteString+integerToBytes count n = B.pack [fromIntegral ((n `shiftR` (8 * i)) .&. 255) | i <- reverse [0 .. count - 1]]++-- | The index of each word of the list.+slip39WordListMap :: Map Text Int+slip39WordListMap = M.fromList (zip (V.toList slip39WordList) [0 ..])++-- | The 1024 words of SLIP-0039, in order. The list is alphabetical, its+-- words have 4 to 8 letters and distinct first four letters.+slip39WordList :: Vector Text+slip39WordList =+  V.fromListN+    1024+    [ "academic",+      "acid",+      "acne",+      "acquire",+      "acrobat",+      "activity",+      "actress",+      "adapt",+      "adequate",+      "adjust",+      "admit",+      "adorn",+      "adult",+      "advance",+      "advocate",+      "afraid",+      "again",+      "agency",+      "agree",+      "aide",+      "aircraft",+      "airline",+      "airport",+      "ajar",+      "alarm",+      "album",+      "alcohol",+      "alien",+      "alive",+      "alpha",+      "already",+      "alto",+      "aluminum",+      "always",+      "amazing",+      "ambition",+      "amount",+      "amuse",+      "analysis",+      "anatomy",+      "ancestor",+      "ancient",+      "angel",+      "angry",+      "animal",+      "answer",+      "antenna",+      "anxiety",+      "apart",+      "aquatic",+      "arcade",+      "arena",+      "argue",+      "armed",+      "artist",+      "artwork",+      "aspect",+      "auction",+      "august",+      "aunt",+      "average",+      "aviation",+      "avoid",+      "award",+      "away",+      "axis",+      "axle",+      "beam",+      "beard",+      "beaver",+      "become",+      "bedroom",+      "behavior",+      "being",+      "believe",+      "belong",+      "benefit",+      "best",+      "beyond",+      "bike",+      "biology",+      "birthday",+      "bishop",+      "black",+      "blanket",+      "blessing",+      "blimp",+      "blind",+      "blue",+      "body",+      "bolt",+      "boring",+      "born",+      "both",+      "boundary",+      "bracelet",+      "branch",+      "brave",+      "breathe",+      "briefing",+      "broken",+      "brother",+      "browser",+      "bucket",+      "budget",+      "building",+      "bulb",+      "bulge",+      "bumpy",+      "bundle",+      "burden",+      "burning",+      "busy",+      "buyer",+      "cage",+      "calcium",+      "camera",+      "campus",+      "canyon",+      "capacity",+      "capital",+      "capture",+      "carbon",+      "cards",+      "careful",+      "cargo",+      "carpet",+      "carve",+      "category",+      "cause",+      "ceiling",+      "center",+      "ceramic",+      "champion",+      "change",+      "charity",+      "check",+      "chemical",+      "chest",+      "chew",+      "chubby",+      "cinema",+      "civil",+      "class",+      "clay",+      "cleanup",+      "client",+      "climate",+      "clinic",+      "clock",+      "clogs",+      "closet",+      "clothes",+      "club",+      "cluster",+      "coal",+      "coastal",+      "coding",+      "column",+      "company",+      "corner",+      "costume",+      "counter",+      "course",+      "cover",+      "cowboy",+      "cradle",+      "craft",+      "crazy",+      "credit",+      "cricket",+      "criminal",+      "crisis",+      "critical",+      "crowd",+      "crucial",+      "crunch",+      "crush",+      "crystal",+      "cubic",+      "cultural",+      "curious",+      "curly",+      "custody",+      "cylinder",+      "daisy",+      "damage",+      "dance",+      "darkness",+      "database",+      "daughter",+      "deadline",+      "deal",+      "debris",+      "debut",+      "decent",+      "decision",+      "declare",+      "decorate",+      "decrease",+      "deliver",+      "demand",+      "density",+      "deny",+      "depart",+      "depend",+      "depict",+      "deploy",+      "describe",+      "desert",+      "desire",+      "desktop",+      "destroy",+      "detailed",+      "detect",+      "device",+      "devote",+      "diagnose",+      "dictate",+      "diet",+      "dilemma",+      "diminish",+      "dining",+      "diploma",+      "disaster",+      "discuss",+      "disease",+      "dish",+      "dismiss",+      "display",+      "distance",+      "dive",+      "divorce",+      "document",+      "domain",+      "domestic",+      "dominant",+      "dough",+      "downtown",+      "dragon",+      "dramatic",+      "dream",+      "dress",+      "drift",+      "drink",+      "drove",+      "drug",+      "dryer",+      "duckling",+      "duke",+      "duration",+      "dwarf",+      "dynamic",+      "early",+      "earth",+      "easel",+      "easy",+      "echo",+      "eclipse",+      "ecology",+      "edge",+      "editor",+      "educate",+      "either",+      "elbow",+      "elder",+      "election",+      "elegant",+      "element",+      "elephant",+      "elevator",+      "elite",+      "else",+      "email",+      "emerald",+      "emission",+      "emperor",+      "emphasis",+      "employer",+      "empty",+      "ending",+      "endless",+      "endorse",+      "enemy",+      "energy",+      "enforce",+      "engage",+      "enjoy",+      "enlarge",+      "entrance",+      "envelope",+      "envy",+      "epidemic",+      "episode",+      "equation",+      "equip",+      "eraser",+      "erode",+      "escape",+      "estate",+      "estimate",+      "evaluate",+      "evening",+      "evidence",+      "evil",+      "evoke",+      "exact",+      "example",+      "exceed",+      "exchange",+      "exclude",+      "excuse",+      "execute",+      "exercise",+      "exhaust",+      "exotic",+      "expand",+      "expect",+      "explain",+      "express",+      "extend",+      "extra",+      "eyebrow",+      "facility",+      "fact",+      "failure",+      "faint",+      "fake",+      "false",+      "family",+      "famous",+      "fancy",+      "fangs",+      "fantasy",+      "fatal",+      "fatigue",+      "favorite",+      "fawn",+      "fiber",+      "fiction",+      "filter",+      "finance",+      "findings",+      "finger",+      "firefly",+      "firm",+      "fiscal",+      "fishing",+      "fitness",+      "flame",+      "flash",+      "flavor",+      "flea",+      "flexible",+      "flip",+      "float",+      "floral",+      "fluff",+      "focus",+      "forbid",+      "force",+      "forecast",+      "forget",+      "formal",+      "fortune",+      "forward",+      "founder",+      "fraction",+      "fragment",+      "frequent",+      "freshman",+      "friar",+      "fridge",+      "friendly",+      "frost",+      "froth",+      "frozen",+      "fumes",+      "funding",+      "furl",+      "fused",+      "galaxy",+      "game",+      "garbage",+      "garden",+      "garlic",+      "gasoline",+      "gather",+      "general",+      "genius",+      "genre",+      "genuine",+      "geology",+      "gesture",+      "glad",+      "glance",+      "glasses",+      "glen",+      "glimpse",+      "goat",+      "golden",+      "graduate",+      "grant",+      "grasp",+      "gravity",+      "gray",+      "greatest",+      "grief",+      "grill",+      "grin",+      "grocery",+      "gross",+      "group",+      "grownup",+      "grumpy",+      "guard",+      "guest",+      "guilt",+      "guitar",+      "gums",+      "hairy",+      "hamster",+      "hand",+      "hanger",+      "harvest",+      "have",+      "havoc",+      "hawk",+      "hazard",+      "headset",+      "health",+      "hearing",+      "heat",+      "helpful",+      "herald",+      "herd",+      "hesitate",+      "hobo",+      "holiday",+      "holy",+      "home",+      "hormone",+      "hospital",+      "hour",+      "huge",+      "human",+      "humidity",+      "hunting",+      "husband",+      "hush",+      "husky",+      "hybrid",+      "idea",+      "identify",+      "idle",+      "image",+      "impact",+      "imply",+      "improve",+      "impulse",+      "include",+      "income",+      "increase",+      "index",+      "indicate",+      "industry",+      "infant",+      "inform",+      "inherit",+      "injury",+      "inmate",+      "insect",+      "inside",+      "install",+      "intend",+      "intimate",+      "invasion",+      "involve",+      "iris",+      "island",+      "isolate",+      "item",+      "ivory",+      "jacket",+      "jerky",+      "jewelry",+      "join",+      "judicial",+      "juice",+      "jump",+      "junction",+      "junior",+      "junk",+      "jury",+      "justice",+      "kernel",+      "keyboard",+      "kidney",+      "kind",+      "kitchen",+      "knife",+      "knit",+      "laden",+      "ladle",+      "ladybug",+      "lair",+      "lamp",+      "language",+      "large",+      "laser",+      "laundry",+      "lawsuit",+      "leader",+      "leaf",+      "learn",+      "leaves",+      "lecture",+      "legal",+      "legend",+      "legs",+      "lend",+      "length",+      "level",+      "liberty",+      "library",+      "license",+      "lift",+      "likely",+      "lilac",+      "lily",+      "lips",+      "liquid",+      "listen",+      "literary",+      "living",+      "lizard",+      "loan",+      "lobe",+      "location",+      "losing",+      "loud",+      "loyalty",+      "luck",+      "lunar",+      "lunch",+      "lungs",+      "luxury",+      "lying",+      "lyrics",+      "machine",+      "magazine",+      "maiden",+      "mailman",+      "main",+      "makeup",+      "making",+      "mama",+      "manager",+      "mandate",+      "mansion",+      "manual",+      "marathon",+      "march",+      "market",+      "marvel",+      "mason",+      "material",+      "math",+      "maximum",+      "mayor",+      "meaning",+      "medal",+      "medical",+      "member",+      "memory",+      "mental",+      "merchant",+      "merit",+      "method",+      "metric",+      "midst",+      "mild",+      "military",+      "mineral",+      "minister",+      "miracle",+      "mixed",+      "mixture",+      "mobile",+      "modern",+      "modify",+      "moisture",+      "moment",+      "morning",+      "mortgage",+      "mother",+      "mountain",+      "mouse",+      "move",+      "much",+      "mule",+      "multiple",+      "muscle",+      "museum",+      "music",+      "mustang",+      "nail",+      "national",+      "necklace",+      "negative",+      "nervous",+      "network",+      "news",+      "nuclear",+      "numb",+      "numerous",+      "nylon",+      "oasis",+      "obesity",+      "object",+      "observe",+      "obtain",+      "ocean",+      "often",+      "olympic",+      "omit",+      "oral",+      "orange",+      "orbit",+      "order",+      "ordinary",+      "organize",+      "ounce",+      "oven",+      "overall",+      "owner",+      "paces",+      "pacific",+      "package",+      "paid",+      "painting",+      "pajamas",+      "pancake",+      "pants",+      "papa",+      "paper",+      "parcel",+      "parking",+      "party",+      "patent",+      "patrol",+      "payment",+      "payroll",+      "peaceful",+      "peanut",+      "peasant",+      "pecan",+      "penalty",+      "pencil",+      "percent",+      "perfect",+      "permit",+      "petition",+      "phantom",+      "pharmacy",+      "photo",+      "phrase",+      "physics",+      "pickup",+      "picture",+      "piece",+      "pile",+      "pink",+      "pipeline",+      "pistol",+      "pitch",+      "plains",+      "plan",+      "plastic",+      "platform",+      "playoff",+      "pleasure",+      "plot",+      "plunge",+      "practice",+      "prayer",+      "preach",+      "predator",+      "pregnant",+      "premium",+      "prepare",+      "presence",+      "prevent",+      "priest",+      "primary",+      "priority",+      "prisoner",+      "privacy",+      "prize",+      "problem",+      "process",+      "profile",+      "program",+      "promise",+      "prospect",+      "provide",+      "prune",+      "public",+      "pulse",+      "pumps",+      "punish",+      "puny",+      "pupal",+      "purchase",+      "purple",+      "python",+      "quantity",+      "quarter",+      "quick",+      "quiet",+      "race",+      "racism",+      "radar",+      "railroad",+      "rainbow",+      "raisin",+      "random",+      "ranked",+      "rapids",+      "raspy",+      "reaction",+      "realize",+      "rebound",+      "rebuild",+      "recall",+      "receiver",+      "recover",+      "regret",+      "regular",+      "reject",+      "relate",+      "remember",+      "remind",+      "remove",+      "render",+      "repair",+      "repeat",+      "replace",+      "require",+      "rescue",+      "research",+      "resident",+      "response",+      "result",+      "retailer",+      "retreat",+      "reunion",+      "revenue",+      "review",+      "reward",+      "rhyme",+      "rhythm",+      "rich",+      "rival",+      "river",+      "robin",+      "rocky",+      "romantic",+      "romp",+      "roster",+      "round",+      "royal",+      "ruin",+      "ruler",+      "rumor",+      "sack",+      "safari",+      "salary",+      "salon",+      "salt",+      "satisfy",+      "satoshi",+      "saver",+      "says",+      "scandal",+      "scared",+      "scatter",+      "scene",+      "scholar",+      "science",+      "scout",+      "scramble",+      "screw",+      "script",+      "scroll",+      "seafood",+      "season",+      "secret",+      "security",+      "segment",+      "senior",+      "shadow",+      "shaft",+      "shame",+      "shaped",+      "sharp",+      "shelter",+      "sheriff",+      "short",+      "should",+      "shrimp",+      "sidewalk",+      "silent",+      "silver",+      "similar",+      "simple",+      "single",+      "sister",+      "skin",+      "skunk",+      "slap",+      "slavery",+      "sled",+      "slice",+      "slim",+      "slow",+      "slush",+      "smart",+      "smear",+      "smell",+      "smirk",+      "smith",+      "smoking",+      "smug",+      "snake",+      "snapshot",+      "sniff",+      "society",+      "software",+      "soldier",+      "solution",+      "soul",+      "source",+      "space",+      "spark",+      "speak",+      "species",+      "spelling",+      "spend",+      "spew",+      "spider",+      "spill",+      "spine",+      "spirit",+      "spit",+      "spray",+      "sprinkle",+      "square",+      "squeeze",+      "stadium",+      "staff",+      "standard",+      "starting",+      "station",+      "stay",+      "steady",+      "step",+      "stick",+      "stilt",+      "story",+      "strategy",+      "strike",+      "style",+      "subject",+      "submit",+      "sugar",+      "suitable",+      "sunlight",+      "superior",+      "surface",+      "surprise",+      "survive",+      "sweater",+      "swimming",+      "swing",+      "switch",+      "symbolic",+      "sympathy",+      "syndrome",+      "system",+      "tackle",+      "tactics",+      "tadpole",+      "talent",+      "task",+      "taste",+      "taught",+      "taxi",+      "teacher",+      "teammate",+      "teaspoon",+      "temple",+      "tenant",+      "tendency",+      "tension",+      "terminal",+      "testify",+      "texture",+      "thank",+      "that",+      "theater",+      "theory",+      "therapy",+      "thorn",+      "threaten",+      "thumb",+      "thunder",+      "ticket",+      "tidy",+      "timber",+      "timely",+      "ting",+      "tofu",+      "together",+      "tolerate",+      "total",+      "toxic",+      "tracks",+      "traffic",+      "training",+      "transfer",+      "trash",+      "traveler",+      "treat",+      "trend",+      "trial",+      "tricycle",+      "trip",+      "triumph",+      "trouble",+      "true",+      "trust",+      "twice",+      "twin",+      "type",+      "typical",+      "ugly",+      "ultimate",+      "umbrella",+      "uncover",+      "undergo",+      "unfair",+      "unfold",+      "unhappy",+      "union",+      "universe",+      "unkind",+      "unknown",+      "unusual",+      "unwrap",+      "upgrade",+      "upstairs",+      "username",+      "usher",+      "usual",+      "valid",+      "valuable",+      "vampire",+      "vanish",+      "various",+      "vegan",+      "velvet",+      "venture",+      "verdict",+      "verify",+      "very",+      "veteran",+      "vexed",+      "victim",+      "video",+      "view",+      "vintage",+      "violence",+      "viral",+      "visitor",+      "visual",+      "vitamins",+      "vocal",+      "voice",+      "volume",+      "voter",+      "voting",+      "walnut",+      "warmth",+      "warn",+      "watch",+      "wavy",+      "wealthy",+      "weapon",+      "webcam",+      "welcome",+      "welfare",+      "western",+      "width",+      "wildlife",+      "window",+      "wine",+      "wireless",+      "wisdom",+      "withdraw",+      "wits",+      "wolf",+      "woman",+      "work",+      "worthy",+      "wrap",+      "wrist",+      "writing",+      "wrote",+      "year",+      "yelp",+      "yield",+      "yoga",+      "zero"+    ]
src/Haskoin/Crypto/Signature.hs view
@@ -1,88 +1,168 @@+{-# 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+-- 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-    , signHash-    , verifyHashSig-    , isCanonicalHalfOrder-    , decodeStrictSig-    , exportSig-    ) where+  ( -- * Signatures+    signHash,+    hashToMsg,+    verifyHashSig,+    isCanonicalHalfOrder,+    decodeStrictSig,+    exportSig, -import           Control.Monad       (guard, unless, when)-import           Crypto.Secp256k1-import           Data.ByteString     (ByteString)-import qualified Data.ByteString     as BS-import           Data.Maybe          (fromMaybe, isNothing)-import           Data.Serialize      as S-import           Haskoin.Crypto.Hash-import           Numeric             (showHex)+    -- * Schnorr signatures of Bitcoin Cash+    verifyCashSchnorr,+  )+where +import Control.Monad (unless, when)+import Crypto.Secp256k1+import Data.Aeson+import Data.Aeson.Encoding+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Lazy qualified as L+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Maybe (catMaybes, fromMaybe)+import Data.Serialize (Serialize (..))+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 . encode+  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)-  where-    norm = fromMaybe s (normalizeSig s)+verifyHashSig :: Ctx -> Hash256 -> Sig -> PubKey -> Bool+verifyHashSig ctx h s p = verifySig ctx p (normalizeSig ctx s) (hashToMsg h) --- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.-getSig :: Get Sig-getSig = do-    l <--        lookAhead $ do-            t <- getWord8-            -- 0x30 is DER sequence type-            unless (t == 0x30) $-                fail $-                "Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"-            l <- getWord8-            when (l == 0x00) $ fail "Indeterminate form unsupported"-            when (l >= 0x80) $ fail "Multi-octect length not supported"-            return $ fromIntegral l+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 :: Putter Sig-putSig s = putByteString $ exportSig s+  marshalPut ctx s = putByteString $ exportSig ctx s --- | Is canonical half order.-isCanonicalHalfOrder :: Sig -> Bool-isCanonicalHalfOrder = isNothing . normalizeSig+instance MarshalJSON Ctx Sig where+  marshalValue ctx = String . encodeHex . exportSig ctx+  marshalEncoding ctx = hexEncoding . L.fromStrict . exportSig ctx+  -- Decoding applies the same rules as the binary form, 'decodeStrictSig'.+  -- The error message does not repeat the text. The text comes from outside+  -- and can be of any length.+  unmarshalValue ctx =+    withText "Sig" $ \t ->+      case decodeHex t >>= decodeStrictSig ctx of+        Nothing -> fail "Could not decode signature"+        Just s -> return s --- | 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+-- | Is the s component at most half the curve order? This is the low-S rule+-- that Bitcoin Core applies to the transactions it relays:+-- @SCRIPT_VERIFY_LOW_S@ is one of its standard flags. It is not a consensus+-- rule, so a block may hold a signature with a high s. The decoders of this+-- module do not apply it. Check it with this function where the relay rules+-- matter. 'signHash' always makes a signature that passes.+isCanonicalHalfOrder :: Ctx -> Sig -> Bool+isCanonicalHalfOrder = isLowS++-- | Decode a signature from its strict DER encoding, the encoding that BIP 66+-- made a consensus rule (@IsValidSignatureEncoding@ in Bitcoin Core, without+-- the hash type byte).+--+-- A signature whose r or s is zero, or not below the curve order, is refused+-- as well. BIP 66 allows such an encoding, but the signature can never+-- verify. 'importSig' applies both rules.+--+-- A high s is accepted, since BIP 66 allows it and such signatures are in the+-- block chain. Use 'isCanonicalHalfOrder' for the low-S relay rule.+decodeStrictSig :: Ctx -> ByteString -> Maybe Sig+decodeStrictSig = importSig++-- | Verify a Schnorr signature of Bitcoin Cash: 64 bytes, @r@ then @s@,+-- of a public key over a 32-byte digest.+--+-- Bitcoin Cash has had these in @OP_CHECKSIG@ since May 2019, where a+-- signature of 64 bytes and the hash type byte is one, and most of its+-- wallets make them. They are not the Schnorr signatures of BIP 340,+-- which Taproot uses: the key is a full public key and not its x+-- coordinate, the challenge is a plain SHA-256 of @r@, the compressed+-- key and the digest, and @R@ is told from its negation by its y+-- coordinate being a square. libsecp256k1 has no function for them, so+-- this builds the check out of the operations it has on points:+--+-- > e = SHA-256(r || compressed P || m) mod n+-- > R = s*G - e*P+-- > valid when R is not at infinity, x(R) = r, and y(R) is a square mod p+--+-- This is the check of @secp256k1_schnorr_sig_verify@ in Bitcoin Cash+-- Node, and the tests hold it to the test vectors of that code. It only+-- verifies: nothing here is secret, so it is not written to run in+-- constant time.+verifyCashSchnorr :: Ctx -> PubKey -> ByteString -> Hash256 -> Bool+verifyCashSchnorr ctx pub sig h+  | BS.length sig /= 64 = False+  | r >= fieldP || s >= orderN = False+  | otherwise = case combined of+      Nothing -> False+      Just point ->+        let (x, y) = BS.splitAt 32 (BS.drop 1 (exportPubKey ctx False point))+         in bsToInteger x == r && isSquare (bsToInteger y)+  where+    (rBytes, sBytes) = BS.splitAt 32 sig+    r = bsToInteger rBytes+    s = bsToInteger sBytes+    digest = runPutS (serialize h)+    e = bsToInteger (runPutS (serialize (sha256 (rBytes <> exportPubKey ctx True pub <> digest)))) `mod` orderN+    -- s*G, when s is not zero+    sG = derivePubKey ctx <$> secKey (word256 s)+    -- -e*P, when e is not zero+    eP = tweak (word256 ((orderN - e) `mod` orderN)) >>= tweakMulPubKey ctx pub+    -- their sum, which is nothing when it is the point at infinity+    combined = case catMaybes [sG, eP] of+      [] -> Nothing+      points -> combinePubKeys ctx points+    -- Euler's criterion+    isSquare y = powMod y ((fieldP - 1) `div` 2) fieldP == 1+    word256 n = let b = integerToBS n in BS.replicate (32 - BS.length b) 0 <> b+    fieldP = 2 ^ (256 :: Int) - 2 ^ (32 :: Int) - 977 :: Integer+    orderN = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 :: Integer+    powMod _ 0 _ = 1+    powMod b k m+      | even k = let half = powMod b (k `div` 2) m in half * half `mod` m+      | otherwise = b * powMod b (k - 1) m `mod` m
− 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,154 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# 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.Applicative     ((<|>))-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.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.ByteString.Builder (char7)-import           Data.Hashable-import           Data.Maybe              (fromMaybe)-import           Data.Serialize          (Serialize, decode, encode, get, put)-import           Data.Serialize.Get      (getByteString)-import           Data.Serialize.Put      (putByteString)-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           GHC.Generics            (Generic)-import           Haskoin.Address.Base58-import           Haskoin.Constants-import           Haskoin.Crypto.Hash-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 . decode <=< decodeHex $ cs str-      where-        e = error "Could not decode public key"--instance ToJSON PubKeyI where-    toJSON = String . encodeHex . encode-    toEncoding s = unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'--instance FromJSON PubKeyI where-    parseJSON = withText "PubKeyI" $-        maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex--instance Serialize PubKeyI where-    get = c <|> u-      where-        c = do-            bs <- getByteString 33-            guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True-        u = do-            bs <- getByteString 65-            guard $ BS.head bs == 0x04-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False--    put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)---- | Wrap a public key from secp256k1 library adding information about compression.-wrapPubKey :: Bool -> PubKey -> PubKeyI-wrapPubKey c p = PubKeyI p c---- | Derives a public key from a private key. This function will preserve--- compression flag.-derivePubKeyI :: SecKeyI -> PubKeyI-derivePubKeyI (SecKeyI d c) = PubKeyI (derivePubKey d) c---- | Tweak a public key.-tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey-tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)---- | Elliptic curve private key type with expected public key compression--- information. Compression information is stored in private key WIF formats and--- needs to be preserved to generate the correct address from the corresponding--- public key.-data SecKeyI = SecKeyI-    { secKeyData       :: !SecKey-    , secKeyCompressed :: !Bool-    } deriving (Eq, Show, Read, Generic, NFData)---- | Wrap private key with corresponding public key compression flag.-wrapSecKey :: Bool -> SecKey -> SecKeyI-wrapSecKey c d = SecKeyI d c---- | Tweak a private key.-tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)---- | Decode Casascius mini private keys (22 or 30 characters).-fromMiniKey :: ByteString -> Maybe SecKeyI-fromMiniKey bs = do-    guard checkShortKey-    wrapSecKey False <$> secKey (encode (sha256 bs))-  where-    checkHash = encode $ sha256 $ bs `BS.append` "?"-    checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00---- | 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,940 +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-    , 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.Bits               (clearBit, setBit, testBit)-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as B-import           Data.Either             (fromRight)-import           Data.List               (foldl')-import           Data.List.Split         (splitOn)-import           Data.Maybe              (fromMaybe)-import           Data.Serialize          as S (Serialize, decode, encode, get,-                                               put)-import           Data.Serialize.Get      (Get, getWord32be, getWord8, runGet)-import           Data.Serialize.Put      (Putter, putWord32be, putWord8, runPut)-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           Data.Typeable           (Typeable)-import           Data.Word               (Word32, Word8)-import           GHC.Generics            (Generic)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Crypto.Hash-import           Haskoin.Keys.Common-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---- | Fingerprint of parent-type Fingerprint = Word32---- | Data type representing an extended BIP32 private key. An extended key--- is a node in a tree of key derivations. It has a depth in the tree, a--- parent node and an index to differentiate it from other siblings.-data XPrvKey = XPrvKey-    { xPrvDepth  :: !Word8       -- ^ depth in the tree-    , xPrvParent :: !Fingerprint -- ^ fingerprint of parent-    , xPrvIndex  :: !KeyIndex    -- ^ derivation index-    , xPrvChain  :: !ChainCode   -- ^ chain code-    , xPrvKey    :: !SecKey      -- ^ private key of this node-    } deriving (Generic, Eq, Show, Read, NFData)--instance Serialize XPrvKey where-    put k = do-        putWord8 $ xPrvDepth k-        putWord32be $ xPrvParent k-        putWord32be $ xPrvIndex k-        put $ xPrvChain k-        putPadPrvKey $ xPrvKey k-    get =-        XPrvKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> getPadPrvKey--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-    { xPubDepth  :: !Word8     -- ^ depth in the tree-    , xPubParent :: !Fingerprint    -- ^ fingerprint of parent-    , xPubIndex  :: !KeyIndex  -- ^ derivation index-    , xPubChain  :: !ChainCode -- ^ chain code-    , xPubKey    :: !PubKey    -- ^ public key of this node-    } deriving (Generic, Eq, Show, Read, NFData)--instance Serialize XPubKey where-    put k = do-        putWord8 $ xPubDepth k-        putWord32be $ xPubParent k-        putWord32be $ xPubIndex k-        put $ xPubChain k-        put $ wrapPubKey True (xPubKey k)-    get =-        XPubKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> (pubKeyPoint <$> S.get)---- | 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 0 0 c k-  where-    (p, c) = split512 $ hmac512 "Bitcoin seed" bs-    k     = fromMaybe err (secKey (encode p))-    err   = throw $ DerivationException "Invalid seed"---- | Derive an extended public key from an extended private key. This function--- will preserve the depth, parent, index and chaincode fields of the extended--- private keys.-deriveXPubKey :: XPrvKey -> XPubKey-deriveXPubKey (XPrvKey d p i c k) = XPubKey d p i c (derivePubKey k)---- | Compute a private, soft child key derivation. A private soft derivation--- will allow the equivalent extended public key to derive the public key for--- this child. Given a parent key /m/ and a derivation index /i/, this function--- will compute /m\/i/.------ Soft derivations allow for more flexibility such as read-only wallets.--- However, care must be taken not the leak both the parent extended public key--- and one of the extended child private keys as this would compromise the--- extended parent private key.-prvSubKey :: XPrvKey  -- ^ extended parent private key-          -> KeyIndex -- ^ child derivation index-          -> XPrvKey  -- ^ extended child private key-prvSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) child c k-    | otherwise = error "Invalid child derivation index"-  where-    pK = xPubKey $ deriveXPubKey xkey-    m = B.append (exportPubKey True pK) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err = throw $ DerivationException "Invalid prvSubKey derivation"---- | Compute a public, soft child key derivation. Given a parent key /M/--- and a derivation index /i/, this function will compute /M\/i/.-pubSubKey :: XPubKey  -- ^ extended parent public key-          -> KeyIndex -- ^ child derivation index-          -> XPubKey  -- ^ extended child public key-pubSubKey xKey child-    | child >= 0 && child < 0x80000000 =-        XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK-    | otherwise = error "Invalid child derivation index"-  where-    m = B.append (exportPubKey True (xPubKey xKey)) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPubChain xKey) m-    pK = fromMaybe err $ tweakPubKey (xPubKey xKey) a-    err = throw $ DerivationException "Invalid pubSubKey derivation"---- | Compute a hard child key derivation. Hard derivations can only be computed--- for private keys. Hard derivations do not allow the parent public key to--- derive the child public keys. However, they are safer as a breach of the--- parent public key and child private keys does not lead to a breach of the--- parent private key. Given a parent key /m/ and a derivation index /i/, this--- function will compute /m\/i'/.-hardSubKey :: XPrvKey  -- ^ extended parent private key-           -> KeyIndex -- ^ child derivation index-           -> XPrvKey  -- ^ extended child private key-hardSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) i c k-    | otherwise = error "Invalid child derivation index"-  where-    i = setBit child 31-    m = B.append (bsPadPrvKey $ xPrvKey xkey) (encode i)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err = throw $ DerivationException "Invalid hardSubKey derivation"---- | Returns true if the extended private key was derived through a hard--- derivation.-xPrvIsHard :: XPrvKey -> Bool-xPrvIsHard k = testBit (xPrvIndex k) 31---- | Returns true if the extended public key was derived through a hard--- derivation.-xPubIsHard :: XPubKey -> Bool-xPubIsHard k = testBit (xPubIndex k) 31---- | Returns the derivation index of this extended private key without the hard--- bit set.-xPrvChild :: XPrvKey -> KeyIndex-xPrvChild k = clearBit (xPrvIndex k) 31---- | Returns the derivation index of this extended public key without the hard--- bit set.-xPubChild :: XPubKey -> KeyIndex-xPubChild k = clearBit (xPubIndex k) 31---- | Computes the key identifier of an extended private key.-xPrvID :: XPrvKey -> Hash160-xPrvID = xPubID . deriveXPubKey---- | Computes the key identifier of an extended public key.-xPubID :: XPubKey -> Hash160-xPubID = ripemd160 . encode . sha256 . exportPubKey True . xPubKey---- | Computes the key fingerprint of an extended private key.-xPrvFP :: XPrvKey -> Fingerprint-xPrvFP =-    fromRight err . decode . B.take 4 . encode . xPrvID-  where-    err = error "Could not decode xPrvFP"---- | Computes the key fingerprint of an extended public key.-xPubFP :: XPubKey -> Fingerprint-xPubFP =-    fromRight err . decode . B.take 4 . encode . xPubID-  where-    err = error "Could not decode xPubFP"---- | Compute a standard P2PKH address for an extended public key.-xPubAddr :: XPubKey -> Address-xPubAddr xkey = pubKeyAddr (wrapPubKey True (xPubKey xkey))---- | Compute a SegWit P2WPKH address for an extended public key.-xPubWitnessAddr :: XPubKey -> Address-xPubWitnessAddr xkey = pubKeyWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended--- public key.-xPubCompatWitnessAddr :: XPubKey -> Address-xPubCompatWitnessAddr xkey =-    pubKeyCompatWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Exports an extended private key to the BIP32 key export format ('Base58').-xPrvExport :: Network -> XPrvKey -> Base58-xPrvExport net = encodeBase58Check . runPut . putXPrvKey net---- | Exports an extended public key to the BIP32 key export format ('Base58').-xPubExport :: Network -> XPubKey -> Base58-xPubExport net = encodeBase58Check . runPut . putXPubKey net---- | Decodes a BIP32 encoded extended private key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPrvImport :: Network -> Base58 -> Maybe XPrvKey-xPrvImport net = eitherToMaybe . runGet (getXPrvKey net) <=< decodeBase58Check---- | Decodes a BIP32 encoded extended public key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPubImport :: Network -> Base58 -> Maybe XPubKey-xPubImport net = eitherToMaybe . runGet (getXPubKey net) <=< decodeBase58Check---- | Export an extended private key to WIF (Wallet Import Format).-xPrvWif :: Network -> XPrvKey -> Base58-xPrvWif net xkey = toWif net (wrapSecKey True (xPrvKey xkey))---- | Parse a binary extended private key.-getXPrvKey :: Network -> Get XPrvKey-getXPrvKey net = do-    ver <- getWord32be-    unless (ver == getExtSecretPrefix net) $ fail-        "Get: Invalid version for extended private key"-    S.get---- | Serialize an extended private key.-putXPrvKey :: Network -> Putter XPrvKey-putXPrvKey net k = do-    putWord32be $ getExtSecretPrefix net-    put k---- | Parse a binary extended public key.-getXPubKey :: Network -> Get XPubKey-getXPubKey net = do-    ver <- getWord32be-    unless (ver == getExtPubKeyPrefix net) $ fail-        "Get: Invalid version for extended public key"-    S.get---- | Serialize an extended public key.-putXPubKey :: Network -> Putter XPubKey-putXPubKey net k = do-    putWord32be $ getExtPubKeyPrefix net-    put 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 Serialize DerivPath where-    get = listToPath <$> S.get-    put = put . pathToList--instance Serialize HardPath where-    get = maybe mzero return . toHard . listToPath =<< S.get-    put = put . pathToList--instance Serialize SoftPath where-    get = maybe mzero return . toSoft . listToPath =<< S.get-    put = put . pathToList---- | Get a list of derivation indices from a derivation path.-pathToList :: DerivPathI t -> [KeyIndex]-pathToList =-    reverse . go-  where-    go (next :| i) = setBit i 31 : go next-    go (next :/ i) = i : go next-    go _           = []---- | Convert a list of derivation indices to a derivation path.-listToPath :: [KeyIndex] -> DerivPath-listToPath =-    go . reverse-  where-    go (i:is)-        | testBit i 31 = go is :| clearBit i 31-        | otherwise    = go is :/ i-    go [] = Deriv---- | Convert a derivation path to a human-readable string.-pathToStr :: DerivPathI t -> String-pathToStr p =-    case p of-        next :| i -> concat [ pathToStr next, "/", show i, "'" ]-        next :/ i -> concat [ pathToStr next, "/", show i ]-        Deriv     -> ""---- | Turn a derivation path into a hard derivation path. Will fail if the path--- contains soft derivations.-toHard :: DerivPathI t -> Maybe HardPath-toHard p = case p of-    next :| i -> (:| i) <$> toHard next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Turn a derivation path into a soft derivation path. Will fail if the path--- has hard derivations.-toSoft :: DerivPathI t -> Maybe SoftPath-toSoft p = case p of-    next :/ i -> (:/ i) <$> toSoft next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Make a derivation path generic.-toGeneric :: DerivPathI t -> DerivPath-toGeneric p = case p of-    next :/ i -> toGeneric next :/ i-    next :| i -> toGeneric next :| i-    Deriv     -> Deriv---- | Append two derivation paths together. The result will be a mixed--- derivation path.-(++/) :: DerivPathI t1 -> DerivPathI t2 -> DerivPath-(++/) p1 p2 =-    go id (toGeneric p2) $ toGeneric p1-  where-    go f p = case p of-        next :/ i -> go (f . (:/ i)) $ toGeneric next-        next :| i -> go (f . (:| i)) $ toGeneric next-        _         -> f---- | Derive a private key from a derivation path-derivePath :: DerivPathI t -> XPrvKey -> XPrvKey-derivePath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :| i -> go (f . flip hardSubKey i) next-        next :/ i -> go (f . flip prvSubKey i) next-        _         -> f---- | Derive a public key from a soft derivation path-derivePubPath :: SoftPath -> XPubKey -> XPubKey-derivePubPath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :/ i -> go (f . flip pubSubKey i) next-        _         -> f--instance Show DerivPath where-    showsPrec d p = showParen (d > 10) $-        showString "DerivPath " . shows (pathToStr p)--instance Read DerivPath where-    readPrec = parens $ do-        R.Ident "DerivPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ getParsedPath <$> parsePath str--instance Show HardPath where-    showsPrec d p = showParen (d > 10) $-        showString "HardPath " . shows (pathToStr p)--instance Read HardPath where-    readPrec = parens $ do-        R.Ident "HardPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseHard str--instance Show SoftPath where-    showsPrec d p = showParen (d > 10) $-        showString "SoftPath " . shows (pathToStr p)--instance Read SoftPath where-    readPrec = parens $ do-        R.Ident "SoftPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseSoft str--instance IsString ParsedPath where-    fromString =-        fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString DerivPath where-    fromString =-        getParsedPath . fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString HardPath where-    fromString =-        fromMaybe e . parseHard-      where-        e = error "Could not parse hard derivation path"--instance IsString SoftPath where-    fromString =-        fromMaybe e . parseSoft-      where-        e = error "Could not parse soft derivation path"--instance FromJSON ParsedPath where-    parseJSON = withText "ParsedPath" $ \str -> case parsePath $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON DerivPath where-    parseJSON = withText "DerivPath" $ \str -> case parsePath $ cs str of-        Just p -> return $ getParsedPath p-        _      -> mzero--instance FromJSON HardPath where-    parseJSON = withText "HardPath" $ \str -> case parseHard $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON SoftPath where-    parseJSON = withText "SoftPath" $ \str -> case parseSoft $ cs str of-        Just p -> return p-        _      -> mzero--instance ToJSON (DerivPathI t) where-    toJSON = A.String . cs . pathToStr-    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 :: Get SecKey-getPadPrvKey = do-    pad <- getWord8-    unless (pad == 0x00) $ fail "Private key must be padded with 0x00"-    S.get---- | Serialize HDW-specific private key.-putPadPrvKey :: Putter SecKey-putPadPrvKey p = putWord8 0x00 >> S.put p--bsPadPrvKey :: SecKey -> ByteString-bsPadPrvKey = runPut . putPadPrvKey
− src/Haskoin/Keys/Mnemonic.hs
@@ -1,516 +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 = ByteString---- | Seed for a private key from a mnemonic sentence.-type Seed = ByteString---- | Mnemonic key checksum.-type Checksum = ByteString---- | Paremeters for PBKDF2 function.-pbkdfParams :: Parameters-pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}---- | Provide intial 'Entropy' as a 'ByteString' of length multiple of 4 bytes.--- Output a 'Mnemonic' sentence.-toMnemonic :: Entropy -> Either String Mnemonic-toMnemonic ent = do-    when (BS.null ent) $-        Left "toMnemonic: entropy can not be empty"-    when (remainder /= 0) $-        Left "toMnemonic: entropy must be multiples of 4 bytes"-    when (cs_len > 16) $-        Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"-    return ms-  where-    (cs_len, remainder) = BS.length ent `quotRem` 4-    c = calcCS cs_len ent-    indices = bsToIndices $ ent `BS.append` c-    ms = T.unwords $ map (wl!) indices---- | Revert 'toMnemonic'. Do not use this to generate a 'Seed'. Instead use--- 'mnemonicToSeed'. This outputs the original 'Entropy' used to generate a--- 'Mnemonic' sentence.-fromMnemonic :: Mnemonic -> Either String Entropy-fromMnemonic ms = do-    when (T.null ms) $-        Left "fromMnemonic: empty mnemonic"-    when (word_count > 48) $-        Left $ "fromMnemonic: too many words: " ++ show word_count-    when (word_count `mod` 3 /= 0) $-        Left $ "fromMnemonic: wrong number of words:" ++ show word_count-    ms_bs <- indicesToBS =<< getIndices ms_words-    let (ms_ent, ms_cs) = BS.splitAt (ent_len * 4) ms_bs-        ms_cs_num = numCS cs_len ms_cs-        ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent-    when (ent_cs_num /= ms_cs_num) $-        Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num-    return ms_ent-  where-    ms_words = T.words ms-    word_count = length ms_words-    (ent_len, cs_len) = (word_count * 11) `quotRem` 32-    sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b---- | Compute 'Checksum'.-calcCS :: Int -> Entropy -> Checksum-calcCS len = getBits len . BA.convert . hashWith SHA256--numCS :: Int -> Entropy -> Integer-numCS len =-    shiftCS . bsToInteger-  where-    shiftCS = case 8 - len `mod` 8 of-        8 -> id-        x -> flip shiftR x---- | Turn an arbitrary sequence of characters into a 512-bit 'Seed'. Use--- 'mnemonicToSeed' to get a seed from a 'Mnemonic' sentence. Warning: Does not--- perform NFKD normalization.-anyToSeed :: Passphrase -> Mnemonic -> Seed-anyToSeed pf ms =-    fastPBKDF2_SHA512 pbkdfParams (E.encodeUtf8 ms) ("mnemonic" `mappend` pf)---- | Get a 512-bit 'Seed' from a 'Mnemonic' sentence. Will validate checksum.--- 'Passphrase' can be used to protect the 'Mnemonic'. Use an empty string as--- 'Passphrase' if none is required.-mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed-mnemonicToSeed pf ms = do-    ent <- fromMnemonic ms-    mnm <- toMnemonic ent-    return $ anyToSeed pf mnm---- | Get indices of words in word list.-getIndices :: [Text] -> Either String [Int]-getIndices ws-    | null n = return $ catMaybes i-    | otherwise = Left $ "getIndices: words not found: " ++ cs w-  where-    i = map (`M.lookup` wl') ws-    n = elemIndices Nothing i-    w = T.unwords $ map (ws !!) n---- | Turn a list of 11-bit numbers into a 'ByteString'-indicesToBS :: [Int] -> Either String ByteString-indicesToBS is = do-    when lrg $ Left "indicesToBS: index larger or equal than 2048"-    return . pad . integerToBS $ foldl' f 0 is `shiftL` shift_width-  where-    lrg = isJust $ find (>= 2048) is-    (q, r) = (length is * 11) `quotRem` 8-    shift_width =-        if r == 0-            then 0-            else 8 - r-    bl =-        if r == 0-            then q-            else q + 1 -- length of resulting ByteString-    pad bs = BS.append (BS.replicate (bl - BS.length bs) 0x00) bs-    f acc x = (acc `shiftL` 11) + fromIntegral x---- | Turn a 'ByteString' into a list of 11-bit numbers.-bsToIndices :: ByteString -> [Int]-bsToIndices bs =-    reverse . go q $ bsToInteger bs `shiftR` r-  where-    (q, r) = (BS.length bs * 8) `quotRem` 11-    go 0 _ = []-    go n i = fromIntegral (i `mod` 2048) : go (n - 1) (i `shiftR` 11)--wl' :: M.Map Text Int-wl' = V.ifoldr' (\i w m -> M.insert w i m) M.empty wl---- | Standard English dictionary from BIP-39 specification.-wl :: Vector Text-wl = V.fromListN 2048-    [ "abandon", "ability", "able", "about", "above", "absent"-    , "absorb", "abstract", "absurd", "abuse", "access", "accident"-    , "account", "accuse", "achieve", "acid", "acoustic", "acquire"-    , "across", "act", "action", "actor", "actress", "actual"-    , "adapt", "add", "addict", "address", "adjust", "admit"-    , "adult", "advance", "advice", "aerobic", "affair", "afford"-    , "afraid", "again", "age", "agent", "agree", "ahead"-    , "aim", "air", "airport", "aisle", "alarm", "album"-    , "alcohol", "alert", "alien", "all", "alley", "allow"-    , "almost", "alone", "alpha", "already", "also", "alter"-    , "always", "amateur", "amazing", "among", "amount", "amused"-    , "analyst", "anchor", "ancient", "anger", "angle", "angry"-    , "animal", "ankle", "announce", "annual", "another", "answer"-    , "antenna", "antique", "anxiety", "any", "apart", "apology"-    , "appear", "apple", "approve", "april", "arch", "arctic"-    , "area", "arena", "argue", "arm", "armed", "armor"-    , "army", "around", "arrange", "arrest", "arrive", "arrow"-    , "art", "artefact", "artist", "artwork", "ask", "aspect"-    , "assault", "asset", "assist", "assume", "asthma", "athlete"-    , "atom", "attack", "attend", "attitude", "attract", "auction"-    , "audit", "august", "aunt", "author", "auto", "autumn"-    , "average", "avocado", "avoid", "awake", "aware", "away"-    , "awesome", "awful", "awkward", "axis", "baby", "bachelor"-    , "bacon", "badge", "bag", "balance", "balcony", "ball"-    , "bamboo", "banana", "banner", "bar", "barely", "bargain"-    , "barrel", "base", "basic", "basket", "battle", "beach"-    , "bean", "beauty", "because", "become", "beef", "before"-    , "begin", "behave", "behind", "believe", "below", "belt"-    , "bench", "benefit", "best", "betray", "better", "between"-    , "beyond", "bicycle", "bid", "bike", "bind", "biology"-    , "bird", "birth", "bitter", "black", "blade", "blame"-    , "blanket", "blast", "bleak", "bless", "blind", "blood"-    , "blossom", "blouse", "blue", "blur", "blush", "board"-    , "boat", "body", "boil", "bomb", "bone", "bonus"-    , "book", "boost", "border", "boring", "borrow", "boss"-    , "bottom", "bounce", "box", "boy", "bracket", "brain"-    , "brand", "brass", "brave", "bread", "breeze", "brick"-    , "bridge", "brief", "bright", "bring", "brisk", "broccoli"-    , "broken", "bronze", "broom", "brother", "brown", "brush"-    , "bubble", "buddy", "budget", "buffalo", "build", "bulb"-    , "bulk", "bullet", "bundle", "bunker", "burden", "burger"-    , "burst", "bus", "business", "busy", "butter", "buyer"-    , "buzz", "cabbage", "cabin", "cable", "cactus", "cage"-    , "cake", "call", "calm", "camera", "camp", "can"-    , "canal", "cancel", "candy", "cannon", "canoe", "canvas"-    , "canyon", "capable", "capital", "captain", "car", "carbon"-    , "card", "cargo", "carpet", "carry", "cart", "case"-    , "cash", "casino", "castle", "casual", "cat", "catalog"-    , "catch", "category", "cattle", "caught", "cause", "caution"-    , "cave", "ceiling", "celery", "cement", "census", "century"-    , "cereal", "certain", "chair", "chalk", "champion", "change"-    , "chaos", "chapter", "charge", "chase", "chat", "cheap"-    , "check", "cheese", "chef", "cherry", "chest", "chicken"-    , "chief", "child", "chimney", "choice", "choose", "chronic"-    , "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle"-    , "citizen", "city", "civil", "claim", "clap", "clarify"-    , "claw", "clay", "clean", "clerk", "clever", "click"-    , "client", "cliff", "climb", "clinic", "clip", "clock"-    , "clog", "close", "cloth", "cloud", "clown", "club"-    , "clump", "cluster", "clutch", "coach", "coast", "coconut"-    , "code", "coffee", "coil", "coin", "collect", "color"-    , "column", "combine", "come", "comfort", "comic", "common"-    , "company", "concert", "conduct", "confirm", "congress", "connect"-    , "consider", "control", "convince", "cook", "cool", "copper"-    , "copy", "coral", "core", "corn", "correct", "cost"-    , "cotton", "couch", "country", "couple", "course", "cousin"-    , "cover", "coyote", "crack", "cradle", "craft", "cram"-    , "crane", "crash", "crater", "crawl", "crazy", "cream"-    , "credit", "creek", "crew", "cricket", "crime", "crisp"-    , "critic", "crop", "cross", "crouch", "crowd", "crucial"-    , "cruel", "cruise", "crumble", "crunch", "crush", "cry"-    , "crystal", "cube", "culture", "cup", "cupboard", "curious"-    , "current", "curtain", "curve", "cushion", "custom", "cute"-    , "cycle", "dad", "damage", "damp", "dance", "danger"-    , "daring", "dash", "daughter", "dawn", "day", "deal"-    , "debate", "debris", "decade", "december", "decide", "decline"-    , "decorate", "decrease", "deer", "defense", "define", "defy"-    , "degree", "delay", "deliver", "demand", "demise", "denial"-    , "dentist", "deny", "depart", "depend", "deposit", "depth"-    , "deputy", "derive", "describe", "desert", "design", "desk"-    , "despair", "destroy", "detail", "detect", "develop", "device"-    , "devote", "diagram", "dial", "diamond", "diary", "dice"-    , "diesel", "diet", "differ", "digital", "dignity", "dilemma"-    , "dinner", "dinosaur", "direct", "dirt", "disagree", "discover"-    , "disease", "dish", "dismiss", "disorder", "display", "distance"-    , "divert", "divide", "divorce", "dizzy", "doctor", "document"-    , "dog", "doll", "dolphin", "domain", "donate", "donkey"-    , "donor", "door", "dose", "double", "dove", "draft"-    , "dragon", "drama", "drastic", "draw", "dream", "dress"-    , "drift", "drill", "drink", "drip", "drive", "drop"-    , "drum", "dry", "duck", "dumb", "dune", "during"-    , "dust", "dutch", "duty", "dwarf", "dynamic", "eager"-    , "eagle", "early", "earn", "earth", "easily", "east"-    , "easy", "echo", "ecology", "economy", "edge", "edit"-    , "educate", "effort", "egg", "eight", "either", "elbow"-    , "elder", "electric", "elegant", "element", "elephant", "elevator"-    , "elite", "else", "embark", "embody", "embrace", "emerge"-    , "emotion", "employ", "empower", "empty", "enable", "enact"-    , "end", "endless", "endorse", "enemy", "energy", "enforce"-    , "engage", "engine", "enhance", "enjoy", "enlist", "enough"-    , "enrich", "enroll", "ensure", "enter", "entire", "entry"-    , "envelope", "episode", "equal", "equip", "era", "erase"-    , "erode", "erosion", "error", "erupt", "escape", "essay"-    , "essence", "estate", "eternal", "ethics", "evidence", "evil"-    , "evoke", "evolve", "exact", "example", "excess", "exchange"-    , "excite", "exclude", "excuse", "execute", "exercise", "exhaust"-    , "exhibit", "exile", "exist", "exit", "exotic", "expand"-    , "expect", "expire", "explain", "expose", "express", "extend"-    , "extra", "eye", "eyebrow", "fabric", "face", "faculty"-    , "fade", "faint", "faith", "fall", "false", "fame"-    , "family", "famous", "fan", "fancy", "fantasy", "farm"-    , "fashion", "fat", "fatal", "father", "fatigue", "fault"-    , "favorite", "feature", "february", "federal", "fee", "feed"-    , "feel", "female", "fence", "festival", "fetch", "fever"-    , "few", "fiber", "fiction", "field", "figure", "file"-    , "film", "filter", "final", "find", "fine", "finger"-    , "finish", "fire", "firm", "first", "fiscal", "fish"-    , "fit", "fitness", "fix", "flag", "flame", "flash"-    , "flat", "flavor", "flee", "flight", "flip", "float"-    , "flock", "floor", "flower", "fluid", "flush", "fly"-    , "foam", "focus", "fog", "foil", "fold", "follow"-    , "food", "foot", "force", "forest", "forget", "fork"-    , "fortune", "forum", "forward", "fossil", "foster", "found"-    , "fox", "fragile", "frame", "frequent", "fresh", "friend"-    , "fringe", "frog", "front", "frost", "frown", "frozen"-    , "fruit", "fuel", "fun", "funny", "furnace", "fury"-    , "future", "gadget", "gain", "galaxy", "gallery", "game"-    , "gap", "garage", "garbage", "garden", "garlic", "garment"-    , "gas", "gasp", "gate", "gather", "gauge", "gaze"-    , "general", "genius", "genre", "gentle", "genuine", "gesture"-    , "ghost", "giant", "gift", "giggle", "ginger", "giraffe"-    , "girl", "give", "glad", "glance", "glare", "glass"-    , "glide", "glimpse", "globe", "gloom", "glory", "glove"-    , "glow", "glue", "goat", "goddess", "gold", "good"-    , "goose", "gorilla", "gospel", "gossip", "govern", "gown"-    , "grab", "grace", "grain", "grant", "grape", "grass"-    , "gravity", "great", "green", "grid", "grief", "grit"-    , "grocery", "group", "grow", "grunt", "guard", "guess"-    , "guide", "guilt", "guitar", "gun", "gym", "habit"-    , "hair", "half", "hammer", "hamster", "hand", "happy"-    , "harbor", "hard", "harsh", "harvest", "hat", "have"-    , "hawk", "hazard", "head", "health", "heart", "heavy"-    , "hedgehog", "height", "hello", "helmet", "help", "hen"-    , "hero", "hidden", "high", "hill", "hint", "hip"-    , "hire", "history", "hobby", "hockey", "hold", "hole"-    , "holiday", "hollow", "home", "honey", "hood", "hope"-    , "horn", "horror", "horse", "hospital", "host", "hotel"-    , "hour", "hover", "hub", "huge", "human", "humble"-    , "humor", "hundred", "hungry", "hunt", "hurdle", "hurry"-    , "hurt", "husband", "hybrid", "ice", "icon", "idea"-    , "identify", "idle", "ignore", "ill", "illegal", "illness"-    , "image", "imitate", "immense", "immune", "impact", "impose"-    , "improve", "impulse", "inch", "include", "income", "increase"-    , "index", "indicate", "indoor", "industry", "infant", "inflict"-    , "inform", "inhale", "inherit", "initial", "inject", "injury"-    , "inmate", "inner", "innocent", "input", "inquiry", "insane"-    , "insect", "inside", "inspire", "install", "intact", "interest"-    , "into", "invest", "invite", "involve", "iron", "island"-    , "isolate", "issue", "item", "ivory", "jacket", "jaguar"-    , "jar", "jazz", "jealous", "jeans", "jelly", "jewel"-    , "job", "join", "joke", "journey", "joy", "judge"-    , "juice", "jump", "jungle", "junior", "junk", "just"-    , "kangaroo", "keen", "keep", "ketchup", "key", "kick"-    , "kid", "kidney", "kind", "kingdom", "kiss", "kit"-    , "kitchen", "kite", "kitten", "kiwi", "knee", "knife"-    , "knock", "know", "lab", "label", "labor", "ladder"-    , "lady", "lake", "lamp", "language", "laptop", "large"-    , "later", "latin", "laugh", "laundry", "lava", "law"-    , "lawn", "lawsuit", "layer", "lazy", "leader", "leaf"-    , "learn", "leave", "lecture", "left", "leg", "legal"-    , "legend", "leisure", "lemon", "lend", "length", "lens"-    , "leopard", "lesson", "letter", "level", "liar", "liberty"-    , "library", "license", "life", "lift", "light", "like"-    , "limb", "limit", "link", "lion", "liquid", "list"-    , "little", "live", "lizard", "load", "loan", "lobster"-    , "local", "lock", "logic", "lonely", "long", "loop"-    , "lottery", "loud", "lounge", "love", "loyal", "lucky"-    , "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics"-    , "machine", "mad", "magic", "magnet", "maid", "mail"-    , "main", "major", "make", "mammal", "man", "manage"-    , "mandate", "mango", "mansion", "manual", "maple", "marble"-    , "march", "margin", "marine", "market", "marriage", "mask"-    , "mass", "master", "match", "material", "math", "matrix"-    , "matter", "maximum", "maze", "meadow", "mean", "measure"-    , "meat", "mechanic", "medal", "media", "melody", "melt"-    , "member", "memory", "mention", "menu", "mercy", "merge"-    , "merit", "merry", "mesh", "message", "metal", "method"-    , "middle", "midnight", "milk", "million", "mimic", "mind"-    , "minimum", "minor", "minute", "miracle", "mirror", "misery"-    , "miss", "mistake", "mix", "mixed", "mixture", "mobile"-    , "model", "modify", "mom", "moment", "monitor", "monkey"-    , "monster", "month", "moon", "moral", "more", "morning"-    , "mosquito", "mother", "motion", "motor", "mountain", "mouse"-    , "move", "movie", "much", "muffin", "mule", "multiply"-    , "muscle", "museum", "mushroom", "music", "must", "mutual"-    , "myself", "mystery", "myth", "naive", "name", "napkin"-    , "narrow", "nasty", "nation", "nature", "near", "neck"-    , "need", "negative", "neglect", "neither", "nephew", "nerve"-    , "nest", "net", "network", "neutral", "never", "news"-    , "next", "nice", "night", "noble", "noise", "nominee"-    , "noodle", "normal", "north", "nose", "notable", "note"-    , "nothing", "notice", "novel", "now", "nuclear", "number"-    , "nurse", "nut", "oak", "obey", "object", "oblige"-    , "obscure", "observe", "obtain", "obvious", "occur", "ocean"-    , "october", "odor", "off", "offer", "office", "often"-    , "oil", "okay", "old", "olive", "olympic", "omit"-    , "once", "one", "onion", "online", "only", "open"-    , "opera", "opinion", "oppose", "option", "orange", "orbit"-    , "orchard", "order", "ordinary", "organ", "orient", "original"-    , "orphan", "ostrich", "other", "outdoor", "outer", "output"-    , "outside", "oval", "oven", "over", "own", "owner"-    , "oxygen", "oyster", "ozone", "pact", "paddle", "page"-    , "pair", "palace", "palm", "panda", "panel", "panic"-    , "panther", "paper", "parade", "parent", "park", "parrot"-    , "party", "pass", "patch", "path", "patient", "patrol"-    , "pattern", "pause", "pave", "payment", "peace", "peanut"-    , "pear", "peasant", "pelican", "pen", "penalty", "pencil"-    , "people", "pepper", "perfect", "permit", "person", "pet"-    , "phone", "photo", "phrase", "physical", "piano", "picnic"-    , "picture", "piece", "pig", "pigeon", "pill", "pilot"-    , "pink", "pioneer", "pipe", "pistol", "pitch", "pizza"-    , "place", "planet", "plastic", "plate", "play", "please"-    , "pledge", "pluck", "plug", "plunge", "poem", "poet"-    , "point", "polar", "pole", "police", "pond", "pony"-    , "pool", "popular", "portion", "position", "possible", "post"-    , "potato", "pottery", "poverty", "powder", "power", "practice"-    , "praise", "predict", "prefer", "prepare", "present", "pretty"-    , "prevent", "price", "pride", "primary", "print", "priority"-    , "prison", "private", "prize", "problem", "process", "produce"-    , "profit", "program", "project", "promote", "proof", "property"-    , "prosper", "protect", "proud", "provide", "public", "pudding"-    , "pull", "pulp", "pulse", "pumpkin", "punch", "pupil"-    , "puppy", "purchase", "purity", "purpose", "purse", "push"-    , "put", "puzzle", "pyramid", "quality", "quantum", "quarter"-    , "question", "quick", "quit", "quiz", "quote", "rabbit"-    , "raccoon", "race", "rack", "radar", "radio", "rail"-    , "rain", "raise", "rally", "ramp", "ranch", "random"-    , "range", "rapid", "rare", "rate", "rather", "raven"-    , "raw", "razor", "ready", "real", "reason", "rebel"-    , "rebuild", "recall", "receive", "recipe", "record", "recycle"-    , "reduce", "reflect", "reform", "refuse", "region", "regret"-    , "regular", "reject", "relax", "release", "relief", "rely"-    , "remain", "remember", "remind", "remove", "render", "renew"-    , "rent", "reopen", "repair", "repeat", "replace", "report"-    , "require", "rescue", "resemble", "resist", "resource", "response"-    , "result", "retire", "retreat", "return", "reunion", "reveal"-    , "review", "reward", "rhythm", "rib", "ribbon", "rice"-    , "rich", "ride", "ridge", "rifle", "right", "rigid"-    , "ring", "riot", "ripple", "risk", "ritual", "rival"-    , "river", "road", "roast", "robot", "robust", "rocket"-    , "romance", "roof", "rookie", "room", "rose", "rotate"-    , "rough", "round", "route", "royal", "rubber", "rude"-    , "rug", "rule", "run", "runway", "rural", "sad"-    , "saddle", "sadness", "safe", "sail", "salad", "salmon"-    , "salon", "salt", "salute", "same", "sample", "sand"-    , "satisfy", "satoshi", "sauce", "sausage", "save", "say"-    , "scale", "scan", "scare", "scatter", "scene", "scheme"-    , "school", "science", "scissors", "scorpion", "scout", "scrap"-    , "screen", "script", "scrub", "sea", "search", "season"-    , "seat", "second", "secret", "section", "security", "seed"-    , "seek", "segment", "select", "sell", "seminar", "senior"-    , "sense", "sentence", "series", "service", "session", "settle"-    , "setup", "seven", "shadow", "shaft", "shallow", "share"-    , "shed", "shell", "sheriff", "shield", "shift", "shine"-    , "ship", "shiver", "shock", "shoe", "shoot", "shop"-    , "short", "shoulder", "shove", "shrimp", "shrug", "shuffle"-    , "shy", "sibling", "sick", "side", "siege", "sight"-    , "sign", "silent", "silk", "silly", "silver", "similar"-    , "simple", "since", "sing", "siren", "sister", "situate"-    , "six", "size", "skate", "sketch", "ski", "skill"-    , "skin", "skirt", "skull", "slab", "slam", "sleep"-    , "slender", "slice", "slide", "slight", "slim", "slogan"-    , "slot", "slow", "slush", "small", "smart", "smile"-    , "smoke", "smooth", "snack", "snake", "snap", "sniff"-    , "snow", "soap", "soccer", "social", "sock", "soda"-    , "soft", "solar", "soldier", "solid", "solution", "solve"-    , "someone", "song", "soon", "sorry", "sort", "soul"-    , "sound", "soup", "source", "south", "space", "spare"-    , "spatial", "spawn", "speak", "special", "speed", "spell"-    , "spend", "sphere", "spice", "spider", "spike", "spin"-    , "spirit", "split", "spoil", "sponsor", "spoon", "sport"-    , "spot", "spray", "spread", "spring", "spy", "square"-    , "squeeze", "squirrel", "stable", "stadium", "staff", "stage"-    , "stairs", "stamp", "stand", "start", "state", "stay"-    , "steak", "steel", "stem", "step", "stereo", "stick"-    , "still", "sting", "stock", "stomach", "stone", "stool"-    , "story", "stove", "strategy", "street", "strike", "strong"-    , "struggle", "student", "stuff", "stumble", "style", "subject"-    , "submit", "subway", "success", "such", "sudden", "suffer"-    , "sugar", "suggest", "suit", "summer", "sun", "sunny"-    , "sunset", "super", "supply", "supreme", "sure", "surface"-    , "surge", "surprise", "surround", "survey", "suspect", "sustain"-    , "swallow", "swamp", "swap", "swarm", "swear", "sweet"-    , "swift", "swim", "swing", "switch", "sword", "symbol"-    , "symptom", "syrup", "system", "table", "tackle", "tag"-    , "tail", "talent", "talk", "tank", "tape", "target"-    , "task", "taste", "tattoo", "taxi", "teach", "team"-    , "tell", "ten", "tenant", "tennis", "tent", "term"-    , "test", "text", "thank", "that", "theme", "then"-    , "theory", "there", "they", "thing", "this", "thought"-    , "three", "thrive", "throw", "thumb", "thunder", "ticket"-    , "tide", "tiger", "tilt", "timber", "time", "tiny"-    , "tip", "tired", "tissue", "title", "toast", "tobacco"-    , "today", "toddler", "toe", "together", "toilet", "token"-    , "tomato", "tomorrow", "tone", "tongue", "tonight", "tool"-    , "tooth", "top", "topic", "topple", "torch", "tornado"-    , "tortoise", "toss", "total", "tourist", "toward", "tower"-    , "town", "toy", "track", "trade", "traffic", "tragic"-    , "train", "transfer", "trap", "trash", "travel", "tray"-    , "treat", "tree", "trend", "trial", "tribe", "trick"-    , "trigger", "trim", "trip", "trophy", "trouble", "truck"-    , "true", "truly", "trumpet", "trust", "truth", "try"-    , "tube", "tuition", "tumble", "tuna", "tunnel", "turkey"-    , "turn", "turtle", "twelve", "twenty", "twice", "twin"-    , "twist", "two", "type", "typical", "ugly", "umbrella"-    , "unable", "unaware", "uncle", "uncover", "under", "undo"-    , "unfair", "unfold", "unhappy", "uniform", "unique", "unit"-    , "universe", "unknown", "unlock", "until", "unusual", "unveil"-    , "update", "upgrade", "uphold", "upon", "upper", "upset"-    , "urban", "urge", "usage", "use", "used", "useful"-    , "useless", "usual", "utility", "vacant", "vacuum", "vague"-    , "valid", "valley", "valve", "van", "vanish", "vapor"-    , "various", "vast", "vault", "vehicle", "velvet", "vendor"-    , "venture", "venue", "verb", "verify", "version", "very"-    , "vessel", "veteran", "viable", "vibrant", "vicious", "victory"-    , "video", "view", "village", "vintage", "violin", "virtual"-    , "virus", "visa", "visit", "visual", "vital", "vivid"-    , "vocal", "voice", "void", "volcano", "volume", "vote"-    , "voyage", "wage", "wagon", "wait", "walk", "wall"-    , "walnut", "want", "warfare", "warm", "warrior", "wash"-    , "wasp", "waste", "water", "wave", "way", "wealth"-    , "weapon", "wear", "weasel", "weather", "web", "wedding"-    , "weekend", "weird", "welcome", "west", "wet", "whale"-    , "what", "wheat", "wheel", "when", "where", "whip"-    , "whisper", "wide", "width", "wife", "wild", "will"-    , "win", "window", "wine", "wing", "wink", "winner"-    , "winter", "wire", "wisdom", "wise", "wish", "witness"-    , "wolf", "woman", "wonder", "wood", "wool", "word"-    , "work", "world", "worry", "worth", "wrap", "wreck"-    , "wrestle", "wrist", "write", "wrong", "yard", "year"-    , "yellow", "you", "young", "youth", "zebra", "zero"-    , "zone", "zoo"-    ]
src/Haskoin/Network.hs view
@@ -1,21 +1,31 @@-{-|-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+-- 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 Common-    , module Message-    , module Bloom-    ) where+  ( module Data,+    module Constants,+    module Common,+    module Message,+    module Bloom,+    module CompactBlock,+    module BlockFilter,+  )+where -import           Haskoin.Network.Bloom   as Bloom-import           Haskoin.Network.Common  as Common-import           Haskoin.Network.Message as Message+import Haskoin.Network.BlockFilter as BlockFilter+import Haskoin.Network.Bloom as Bloom+import Haskoin.Network.Common as Common+import Haskoin.Network.CompactBlock as CompactBlock+import Haskoin.Network.Constants as Constants+import Haskoin.Network.Data as Data+import Haskoin.Network.Message as Message
+ src/Haskoin/Network/BlockFilter.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Network.BlockFilter+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Messages of client-side block filtering (BIP 157): @getcfilters@,+-- @cfilter@, @getcfheaders@, @cfheaders@, @getcfcheckpt@ and @cfcheckpt@.+-- The filters themselves are defined by BIP 158.+module Haskoin.Network.BlockFilter+  ( FilterType,+    basicFilterType,+    GetCFilters (..),+    CFilter (..),+    GetCFHeaders (..),+    CFHeaders (..),+    GetCFCheckpt (..),+    CFCheckpt (..),+  )+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.Serialize (Serialize (..))+import Data.Word (Word32, Word8)+import GHC.Generics (Generic)+import Haskoin.Block.Common (BlockHash)+import Haskoin.Crypto.Hash (Hash256)+import Haskoin.Network.Common++-- | Kind of filter, as defined by BIP 158.+type FilterType = Word8++-- | The basic filter type of BIP 158, the only one defined so far.+basicFilterType :: FilterType+basicFilterType = 0++-- | The @getcfilters@ message. It asks for the filters of a range of blocks.+-- BIP 157 limits a range to 1000 blocks.+data GetCFilters = GetCFilters+  { -- | kind of filter+    filterType :: !FilterType,+    -- | height of the first block of the range+    startHeight :: !Word32,+    -- | hash of the last block of the range+    stopHash :: !BlockHash+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial GetCFilters where+  deserialize = GetCFilters <$> getWord8 <*> getWord32le <*> deserialize+  serialize g = do+    putWord8 g.filterType+    putWord32le g.startHeight+    serialize g.stopHash++instance Binary GetCFilters where+  put = serialize+  get = deserialize++instance Serialize GetCFilters where+  put = serialize+  get = deserialize++-- | The @cfilter@ message: the filter of one block.+data CFilter = CFilter+  { -- | kind of filter+    filterType :: !FilterType,+    -- | hash of the block the filter is for+    blockHash :: !BlockHash,+    -- | the serialized filter+    filterBytes :: !ByteString+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial CFilter where+  deserialize = CFilter <$> getWord8 <*> deserialize <*> getVarIntBytes+  serialize c = do+    putWord8 c.filterType+    serialize c.blockHash+    putVarIntBytes c.filterBytes++instance Binary CFilter where+  put = serialize+  get = deserialize++instance Serialize CFilter where+  put = serialize+  get = deserialize++-- | The @getcfheaders@ message. It asks for the filter hashes of a range of+-- blocks, with the filter header before the range. BIP 157 limits a range to+-- 2000 blocks.+data GetCFHeaders = GetCFHeaders+  { -- | kind of filter+    filterType :: !FilterType,+    -- | height of the first block of the range+    startHeight :: !Word32,+    -- | hash of the last block of the range+    stopHash :: !BlockHash+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial GetCFHeaders where+  deserialize = GetCFHeaders <$> getWord8 <*> getWord32le <*> deserialize+  serialize g = do+    putWord8 g.filterType+    putWord32le g.startHeight+    serialize g.stopHash++instance Binary GetCFHeaders where+  put = serialize+  get = deserialize++instance Serialize GetCFHeaders where+  put = serialize+  get = deserialize++-- | The @cfheaders@ message, the answer to 'GetCFHeaders'. The filter header+-- of each block is the double SHA-256 of its filter hash and the previous+-- filter header.+data CFHeaders = CFHeaders+  { -- | kind of filter+    filterType :: !FilterType,+    -- | hash of the last block of the range+    stopHash :: !BlockHash,+    -- | filter header of the block before the range+    previousHeader :: !Hash256,+    -- | filter hash of every block of the range+    filterHashes :: ![Hash256]+  }+  deriving (Eq, Show, Generic, NFData)++-- | A message of more than 'maxFilterHashes' filter hashes fails the parse,+-- as BIP 157 requires.+instance Serial CFHeaders where+  deserialize =+    CFHeaders+      <$> getWord8+      <*> deserialize+      <*> deserialize+      <*> getVarIntListMax "cfheaders" maxFilterHashes deserialize+  serialize c = do+    putWord8 c.filterType+    serialize c.stopHash+    serialize c.previousHeader+    putVarIntList serialize c.filterHashes++instance Binary CFHeaders where+  put = serialize+  get = deserialize++instance Serialize CFHeaders where+  put = serialize+  get = deserialize++-- | The @getcfcheckpt@ message. It asks for the filter headers at every+-- thousandth block up to the given one.+data GetCFCheckpt = GetCFCheckpt+  { -- | kind of filter+    filterType :: !FilterType,+    -- | hash of the last block of the chain to cover+    stopHash :: !BlockHash+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial GetCFCheckpt where+  deserialize = GetCFCheckpt <$> getWord8 <*> deserialize+  serialize g = do+    putWord8 g.filterType+    serialize g.stopHash++instance Binary GetCFCheckpt where+  put = serialize+  get = deserialize++instance Serialize GetCFCheckpt where+  put = serialize+  get = deserialize++-- | The @cfcheckpt@ message, the answer to 'GetCFCheckpt'.+data CFCheckpt = CFCheckpt+  { -- | kind of filter+    filterType :: !FilterType,+    -- | hash of the last block of the chain covered+    stopHash :: !BlockHash,+    -- | filter headers at heights 1000, 2000, and so on+    filterHeaders :: ![Hash256]+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial CFCheckpt where+  deserialize = CFCheckpt <$> getWord8 <*> deserialize <*> getVarIntList deserialize+  serialize c = do+    putWord8 c.filterType+    serialize c.stopHash+    putVarIntList serialize c.filterHeaders++instance Binary CFCheckpt where+  put = serialize+  get = deserialize++instance Serialize CFCheckpt where+  put = serialize+  get = deserialize
src/Haskoin/Network/Bloom.hs view
@@ -1,61 +1,84 @@ {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Haskoin.Network.Bloom-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE DeriveGeneric #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Bloom filters are used to reduce data transfer when synchronizing thin cients.+-- When bloom filters are used a client will obtain filtered blocks that only+-- contain transactions that pass the bloom filter. Transactions announced via inv+-- messages also pass the filter. module Haskoin.Network.Bloom-    ( -- * Bloom Filters-      BloomFlags(..)-    , BloomFilter(..)-    , FilterLoad(..)-    , FilterAdd(..)-    , bloomCreate-    , bloomInsert-    , bloomContains-    , isBloomValid-    , isBloomEmpty-    , isBloomFull-    , acceptsFilters-    , bloomRelevantUpdate-    ) where+  ( -- * Bloom Filters+    BloomFlags (..),+    BloomFilter (..),+    FilterLoad (..),+    FilterAdd (..),+    maxBloomSize,+    maxHashFuncs,+    maxFilterAddSize,+    bloomCreate,+    bloomInsert,+    bloomContains,+    isBloomValid,+    isBloomEmpty,+    isBloomFull,+    acceptsFilters,+    bloomRelevantUpdate,+    scriptPushData,+  )+where -import           Control.DeepSeq-import           Control.Monad              (forM_, replicateM)-import           Data.Bits-import           Data.ByteString            (ByteString)-import qualified Data.ByteString            as BS-import qualified Data.Foldable              as F-import           Data.Hash.Murmur           (murmur3)-import           Data.List                  (foldl')-import qualified Data.Sequence              as S-import           Data.Serialize             (Serialize, encode, get, put)-import           Data.Serialize.Get         (getByteString, getWord32le,-                                             getWord8)-import           Data.Serialize.Put         (putByteString, putWord32le,-                                             putWord8)-import           Data.Word-import           GHC.Generics               (Generic)-import           Haskoin.Network.Common-import           Haskoin.Script.Standard-import           Haskoin.Transaction.Common+import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (guard, when)+import Crypto.Secp256k1 (Ctx)+import Data.Binary (Binary (..))+import Data.Bits+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Foldable qualified as F+import Data.Hash.Murmur (murmur3)+import Data.List (foldl')+import Data.Maybe (fromMaybe)+import Data.Sequence qualified as S+import Data.Serialize (Serialize (..))+import Data.Serialize qualified as Ser+import Data.Word+import GHC.Generics (Generic)+import Haskoin.Network.Common+import Haskoin.Script.Common+import Haskoin.Transaction.Common --- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%+-- | Largest filter a peer may load, in bytes. 20,000 items with fp rate+-- < 0.1% or 10,000 items and <0.0001%. maxBloomSize :: Int maxBloomSize = 36000 +-- | Largest number of hash functions a filter may use. maxHashFuncs :: Word32 maxHashFuncs = 50 +-- | Largest data element a 'FilterAdd' message may carry, in bytes. It is+-- the largest element a script can push (@MAX_SCRIPT_ELEMENT_SIZE@).+maxFilterAddSize :: Int+maxFilterAddSize = 520+ ln2Squared :: Double ln2Squared = 0.4804530139182014246671025263266649717305529515945455 @@ -68,25 +91,36 @@ -- | The bloom flags are used to tell the remote peer how to auto-update -- the provided bloom filter. data BloomFlags-    = BloomUpdateNone -- ^ never update-    | BloomUpdateAll -- ^ auto-update on all outputs-    | BloomUpdateP2PubKeyOnly-    -- ^ auto-update on pay-to-pubkey or pay-to-multisig (default)-    deriving (Eq, Show, Read, Generic, NFData)+  = -- | 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 Serialize BloomFlags where-    get = go =<< getWord8-      where-        go 0 = return BloomUpdateNone-        go 1 = return BloomUpdateAll-        go 2 = return BloomUpdateP2PubKeyOnly-        go _ = fail "BloomFlags get: Invalid bloom flag"+-- | Only the two low bits of the flags byte count, as with the+-- @BLOOM_UPDATE_MASK@ of Bitcoin Core. The value 3 means no update.+instance Serial BloomFlags where+  deserialize = go . (.&. 3) =<< getWord8+    where+      go 1 = return BloomUpdateAll+      go 2 = return BloomUpdateP2PubKeyOnly+      go _ = return BloomUpdateNone -    put 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++instance Serialize BloomFlags where+  get = deserialize+  put = serialize+ -- | A bloom filter is a probabilistic data structure that SPV clients send to -- other peers to filter the set of transactions received from them. Bloom -- filters can have false positives but not false negatives. Some transactions@@ -94,171 +128,327 @@ -- controlling the false positive rate, SPV nodes can trade off bandwidth -- versus privacy. data BloomFilter = BloomFilter-    { bloomData      :: !(S.Seq Word8)-    -- ^ bloom filter data-    , bloomHashFuncs :: !Word32-    -- ^ number of hash functions for this filter-    , bloomTweak     :: !Word32-    -- ^ hash function random nonce-    , bloomFlags     :: !BloomFlags-    -- ^ bloom filter auto-update flags-    }-    deriving (Eq, Show, Read, Generic, NFData)+  { -- | 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 Serialize BloomFilter where+-- | A filter with more than 'maxBloomSize' bytes or more than 'maxHashFuncs'+-- hash functions fails the parse, as Bitcoin Core refuses such a+-- @filterload@ message.+instance Serial BloomFilter where+  deserialize = do+    VarInt len <- deserialize+    when (len > fromIntegral maxBloomSize) $+      fail ("Bloom filter of " ++ show len ++ " bytes is too large")+    bytes <- getByteString (fromIntegral len)+    functions <- getWord32le+    when (functions > maxHashFuncs) $+      fail ("Bloom filter with " ++ show functions ++ " hash functions has too many")+    tweak <- getWord32le+    flags <- deserialize+    return BloomFilter {array = S.fromList (BS.unpack bytes), ..} -    get = BloomFilter <$> (S.fromList <$> (readDat =<< get))-                      <*> getWord32le <*> getWord32le-                      <*> get-      where-        readDat (VarInt len) = replicateM (fromIntegral len) getWord8+  serialize BloomFilter {..} = do+    putVarInt $ S.length array+    mapM_ putWord8 (F.toList array)+    putWord32le functions+    putWord32le tweak+    serialize flags -    put (BloomFilter dat hashFuncs tweak flags) = do-        putVarInt $ S.length dat-        forM_ (F.toList dat) putWord8-        putWord32le hashFuncs-        putWord32le tweak-        put flags+instance Binary BloomFilter where+  put = serialize+  get = deserialize +instance Serialize BloomFilter where+  put = serialize+  get = deserialize+ -- | Set a new bloom filter on the peer connection.-newtype FilterLoad = FilterLoad { 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++instance Binary FilterLoad where+  put = serialize+  get = deserialize+ instance Serialize FilterLoad where-    get = FilterLoad <$> get-    put (FilterLoad f) = put f+  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)+newtype FilterAdd = FilterAdd {get :: ByteString}+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData) -instance Serialize FilterAdd where-    get = do-        (VarInt len) <- get-        dat <- getByteString $ fromIntegral len-        return $ FilterAdd dat+-- | An element longer than 'maxFilterAddSize' fails the parse, as Bitcoin+-- Core refuses such a @filteradd@ message.+instance Serial FilterAdd where+  deserialize = do+    dat <- getVarIntBytes+    when (BS.length dat > maxFilterAddSize) $+      fail ("filteradd element of " ++ show (BS.length dat) ++ " bytes is too long")+    return $ FilterAdd dat -    put (FilterAdd bs) = do-        putVarInt $ BS.length bs-        putByteString bs+  serialize (FilterAdd bs) = putVarIntBytes bs +instance Binary FilterAdd where+  put = serialize+  get = deserialize +instance Serialize FilterAdd where+  put = serialize+  get = deserialize+ -- | Build a bloom filter that will provide the given false positive rate when--- the given number of elements have been inserted.-bloomCreate :: Int          -- ^ number of elements-            -> Double       -- ^ false positive rate-            -> Word32       -- ^ random nonce (tweak) for the hash function-            -> BloomFlags   -- ^ bloom filter flags-            -> BloomFilter  -- ^ bloom filter+-- the given number of elements have been inserted. The size is capped at+-- 'maxBloomSize' and the number of hash functions at 'maxHashFuncs'. A filter+-- built for many more elements than fit in 'maxBloomSize' gets zero hash+-- functions and then matches everything, as in Bitcoin Core.+bloomCreate ::+  -- | number of elements+  Int ->+  -- | false positive rate+  Double ->+  -- | random nonce (tweak) for the hash function+  Word32 ->+  -- | bloom filter flags+  BloomFlags ->+  -- | bloom filter+  BloomFilter bloomCreate numElem fpRate =-    BloomFilter (S.replicate bloomSize 0) numHashF+  BloomFilter (S.replicate bloomSize 0) numHashF   where     -- Bloom filter size in bytes     bloomSize = truncate $ min a b / 8     -- Suggested size in bits-    a         = -1 / ln2Squared * fromIntegral numElem * log fpRate+    a = -1 / ln2Squared * fromIntegral numElem * log fpRate     -- Maximum size in bits-    b         = fromIntegral $ maxBloomSize * 8-    numHashF  = truncate $ min c (fromIntegral maxHashFuncs)-    -- Suggested number of hash functions-    c         = fromIntegral bloomSize * 8 / fromIntegral numElem * ln2+    b = fromIntegral $ maxBloomSize * 8+    numHashF+      | numElem == 0 = 0+      | otherwise = truncate $ min c (fromIntegral maxHashFuncs)+    -- Suggested number of hash functions. The bits per element are divided+    -- as integers first, as in the constructor of CBloomFilter in Bitcoin+    -- Core, so that both pick the same number.+    c = fromIntegral ((bloomSize * 8) `div` 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 +-- | Bit positions that the hash functions of a filter map some data to. There+-- are as many as the filter has hash functions. The filter must not be empty.+bloomBits :: BloomFilter -> ByteString -> [Word32]+bloomBits b bs =+  map (\i -> bloomHash b i bs) (take (fromIntegral b.functions) [0 ..])+ -- | Insert arbitrary data into a bloom filter. Returns the new bloom filter--- containing the new data.-bloomInsert :: BloomFilter    -- ^ Original bloom filter-            -> ByteString     -- ^ New data to insert-            -> BloomFilter    -- ^ Bloom filter containing the new data-bloomInsert bfilter bs-    | isBloomFull bfilter = bfilter-    | otherwise = bfilter { bloomData = newData }+-- containing the new data. An empty filter stays empty.+bloomInsert ::+  -- | Original bloom filter+  BloomFilter ->+  -- | New data to insert+  ByteString ->+  -- | Bloom filter containing the new data+  BloomFilter+bloomInsert b bs+  | S.null b.array = b+  | otherwise = b {array = foldl' setBit' b.array (bloomBits b bs)}   where-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    upd s i = S.adjust (.|. bitMask !! fromIntegral (7 .&. i))-                       (fromIntegral $ i `shiftR` 3) s-    newData = foldl upd (bloomData bfilter) idxs+    setBit' s i =+      S.adjust+        (.|. bitMask !! fromIntegral (7 .&. i))+        (fromIntegral $ i `shiftR` 3)+        s  -- | Tests if some arbitrary data matches the filter. This can be either because--- the data was inserted into the filter or because it is a false positive.-bloomContains :: BloomFilter    -- ^ Bloom filter-              -> ByteString-              -- ^ Data that will be checked against the given bloom filter-              -> Bool-              -- ^ Returns True if the data matches the filter-bloomContains bfilter bs-    | isBloomFull bfilter  = True-    | isBloomEmpty bfilter = False-    | otherwise            = all isSet idxs+-- the data was inserted into the filter or because it is a false positive. An+-- empty filter, and a filter with no hash functions, match everything.+bloomContains ::+  -- | Bloom filter+  BloomFilter ->+  -- | Data that will be checked against the given bloom filter+  ByteString ->+  -- | Returns True if the data matches the filter+  Bool+bloomContains b bs+  | S.null b.array = True+  | otherwise = all isSet (bloomBits b bs)   where-    s       = bloomData bfilter-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    isSet i = S.index s (fromIntegral $ i `shiftR` 3)-          .&. (bitMask !! fromIntegral (7 .&. i)) /= 0+    isSet i =+      S.index b.array (fromIntegral $ i `shiftR` 3)+        .&. (bitMask !! fromIntegral (7 .&. i))+        /= 0 --- | Checks if any of the outputs of a tx is in the current bloom filter.--- If it is, add the txid and vout as an outpoint (i.e. so that--- a future tx that spends the output won't be missed).-bloomRelevantUpdate :: BloomFilter-                    -- ^ Bloom filter-                    -> Tx-                    -- ^ Tx that may (or may not) have relevant outputs-                    -> Maybe BloomFilter-                    -- ^ Returns an updated bloom filter adding relevant output-bloomRelevantUpdate bfilter tx-    | isBloomFull bfilter || isBloomEmpty bfilter = Nothing-    | bloomFlags bfilter == BloomUpdateNone = Nothing-    | not (null matchOuts) = Just $ foldl' addRelevant bfilter matchOuts-    | otherwise = Nothing-        -- TxHash if we end up inserting an outpoint+-- | Data elements pushed by a script, in order, without the empty ones. The+-- scan stops at the first byte sequence that is not a valid operation, as+-- @GetOp@ does in Bitcoin Core, and keeps the elements found before it.+scriptPushData :: ByteString -> [ByteString]+scriptPushData = either (const []) id . runGetS pushes   where-    h = txHash tx-        -- Decode the scriptOutpus and add vOuts in case we make them outpoints-    decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx-    err = error "Error Decoding output script"-    idxOutputScripts = either (const err) (zip [0 ..]) decodedOutputScripts-        -- Check if any txOuts were contained in the bloom filter-    matchFilter =-        filter (\(_, op) -> bloomContains bfilter $ encodeScriptOut op)-    matchOuts = matchFilter idxOutputScripts-    addRelevant :: BloomFilter -> (Word32, ScriptOutput) -> BloomFilter-    addRelevant bf (id', scriptOut) =-        case (bloomFlags bfilter, scriptType)-            -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig-              of-            (_, True) -> bloomInsert bf outpoint-            (BloomUpdateAll, _) -> bloomInsert bf outpoint-            _ -> error "Error Updating Bloom Filter with relevant outpoint"-      where-        outpoint = encode $ OutPoint {outPointHash = h, outPointIndex = id'}-        scriptType = (\s -> isPayPK s || isPayMulSig s) scriptOut-        -- Encodes a scriptOutput so it can be checked agains the Bloom Filter-    encodeScriptOut :: ScriptOutput -> ByteString-    encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig-    encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash-    encodeScriptOut (DataCarrier getOutputDat) = encode getOutputDat-    encodeScriptOut outputHash = (encode . getOutputHash) outputHash+    pushes :: Ser.Get [ByteString]+    pushes = do+      done <- isEmpty+      if done+        then return []+        else do+          op <- (Just <$> deserialize) <|> return Nothing+          case op of+            Nothing -> return []+            Just (OP_PUSHDATA d _)+              | not (BS.null d) -> (d :) <$> pushes+            Just _ -> pushes +-- | Tells whether a transaction is relevant to a filter, and updates the+-- filter, as @CBloomFilter::IsRelevantAndUpdate@ does in Bitcoin Core and as+-- BIP 37 specifies. A transaction is relevant when the filter contains:+--+-- 1. its hash, or+-- 2. a data element pushed by one of its output scripts, or+-- 3. an outpoint that one of its inputs spends, or+-- 4. a data element pushed by one of its input scripts.+--+-- The result is 'Nothing' for a transaction that is not relevant, and+-- otherwise the filter. For each output that matches, its outpoint is+-- inserted according to the flags: always with 'BloomUpdateAll', only for+-- pay-to-pubkey and bare multisig outputs with 'BloomUpdateP2PubKeyOnly', and+-- never with 'BloomUpdateNone'. This lets a later transaction that spends the+-- output match through rule 3. As in Bitcoin Core, the inputs are looked at+-- only when neither the hash nor an output matched.+--+-- Pay-to-pubkey and multisig are told by the shape of the script, as+-- @Solver@ does in Bitcoin Core: a key is any push of 33 bytes that starts+-- with 02 or 03, or of 65 bytes that starts with 04, 06 or 07. It does not+-- have to be a point of the curve. Non-standard scripts are scanned like any+-- other. An empty filter matches every transaction and gets no outpoints.+--+-- The context is not used. It is kept so that callers do not have to change.+bloomRelevantUpdate ::+  Ctx ->+  -- | Bloom filter+  BloomFilter ->+  -- | transaction to test+  Tx ->+  -- | the filter, updated, when the transaction is relevant+  Maybe BloomFilter+bloomRelevantUpdate _ b tx+  | S.null b.array = Just b+  | hashFound || outputFound = Just updated+  | any inputMatches tx.inputs = Just b+  | otherwise = Nothing+  where+    hash = txHash tx+    hashFound = bloomContains b (runPutS (serialize hash))+    (outputFound, updated) = foldl' step (False, b) (zip [0 ..] tx.outputs)+    step :: (Bool, BloomFilter) -> (Word32, TxOut) -> (Bool, BloomFilter)+    step (f, cur) (i, out)+      | any (bloomContains cur) (scriptPushData out.script) =+          (True, addOutpoint cur i out)+      | otherwise = (f, cur)+    addOutpoint :: BloomFilter -> Word32 -> TxOut -> BloomFilter+    addOutpoint cur i out =+      case b.flags of+        BloomUpdateAll -> bloomInsert cur (outpoint i)+        BloomUpdateP2PubKeyOnly+          | paysToKeys out.script -> bloomInsert cur (outpoint i)+        _ -> cur+    outpoint i = runPutS $ serialize $ OutPoint {hash = hash, index = i}+    inputMatches :: TxIn -> Bool+    inputMatches input =+      bloomContains b (runPutS (serialize input.outpoint))+        || any (bloomContains b) (scriptPushData input.script)++-- | Is the script pay-to-pubkey or bare multisig, as @Solver@ classifies them+-- in Bitcoin Core (@MatchPayToPubkey@ and @MatchMultisig@)? Keys are tested+-- by size and first byte only.+paysToKeys :: ByteString -> Bool+paysToKeys script = payToPubkey || multisig+  where+    opCheckSig = 0xac+    payToPubkey =+      or+        [ BS.length script == size + 2+            && BS.head script == fromIntegral size+            && BS.last script == opCheckSig+            && validKeySize (BS.take size (BS.drop 1 script))+        | size <- [65, 33]+        ]+    multisig =+      case runGetS deserialize script :: Either String Script of+        Right (Script (m : rest)) ->+          case span isKey rest of+            (keys, [n, OP_CHECKMULTISIG]) -> fromMaybe False $ do+              required <- scriptNumber m+              guard (required >= 1 && required <= maxKeys)+              total <- scriptNumber n+              guard (total >= required && total <= maxKeys)+              return (length keys == total)+            _ -> False+        _ -> False+    -- MAX_PUBKEYS_PER_MULTISIG+    maxKeys = 20+    isKey (OP_PUSHDATA bs _) = validKeySize bs+    isKey _ = False++-- | @CPubKey::ValidSize@ of Bitcoin Core: 33 bytes after 02 or 03, 65 bytes+-- after 04, 06 or 07.+validKeySize :: ByteString -> Bool+validKeySize key =+  case BS.uncons key of+    Just (prefix, _)+      | prefix == 2 || prefix == 3 -> BS.length key == 33+      | prefix == 4 || prefix == 6 || prefix == 7 -> BS.length key == 65+    _ -> False++-- | The number that a script operation stands for in a multisig template,+-- as @GetScriptNumber@ reads it in Bitcoin Core: 'OP_1' to 'OP_16', or a+-- minimal push of one byte for 17 and above.+scriptNumber :: ScriptOp -> Maybe Int+scriptNumber op =+  case op of+    OP_PUSHDATA bs OPCODE+      | BS.length bs == 1 && BS.head bs > 16 && BS.head bs < 0x80 ->+          Just (fromIntegral (BS.head bs))+    OP_PUSHDATA {} -> Nothing+    OP_0 -> Nothing+    OP_1NEGATE -> Nothing+    _ -> do+      bs <- pushOpData op+      guard (BS.length bs == 1)+      return (fromIntegral (BS.head bs))+ -- | 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 :: BloomFilter -- ^ Bloom filter to test-             -> Bool        -- ^ True if the given filter is valid-isBloomValid bfilter =-    S.length (bloomData bfilter) <= maxBloomSize &&-    bloomHashFuncs bfilter <= maxHashFuncs+isBloomValid ::+  -- | Bloom filter to test+  BloomFilter ->+  -- | True if the given filter is valid+  Bool+isBloomValid BloomFilter {..} =+  S.length array <= maxBloomSize && functions <= maxHashFuncs  -- | Does the peer with these version services accept bloom filters? acceptsFilters :: Word64 -> Bool-acceptsFilters srv = srv .&. (1 `shiftL` 2) /= 0+acceptsFilters srv = srv .&. nodeBloom /= 0
src/Haskoin/Network/Common.hs view
@@ -1,611 +1,1252 @@-{-# 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.Bits               (shiftL)-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as B-import           Data.ByteString.Char8   as C (replicate)-import           Data.Serialize          as S-import           Data.String-import           Data.String.Conversions (cs)-import           Data.Word               (Word32, Word64)-import           GHC.Generics            (Generic)-import           Haskoin.Crypto.Hash-import           Network.Socket          (SockAddr (..))-import           Text.Read               as R---- | Network address with a timestamp.-type NetworkAddressTime = (Word32, NetworkAddress)---- | Provides information about known nodes in the bitcoin network. An 'Addr'--- type is sent inside a 'Message' as a response to a 'GetAddr' message.-newtype Addr =-    Addr { -- List of addresses of other nodes on the network with timestamps.-           addrList :: [NetworkAddressTime]-         }-    deriving (Eq, Show, Generic, NFData)--instance Serialize Addr where--    get = Addr <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action             = liftM2 (,) getWord32le S.get--    put (Addr xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a,b) -> putWord32le a >> put b---- | Data type describing signed messages that can be sent between bitcoin--- nodes to display important notifications to end users about the health of--- the network.-data Alert =-    Alert {-          -- | Alert payload.-            alertPayload   :: !VarString-          -- | ECDSA signature of the payload-          , alertSignature :: !VarString-          } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Alert where-    get = Alert <$> S.get <*> S.get-    put (Alert p s) = put p >> put s---- | The 'GetData' type is used to retrieve information on a specific object--- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'--- request is a list of 'InvVector' which represent all the hashes of objects--- that a node wants. The response to a 'GetBlock' message will be either a--- 'Block' or a 'Tx' message depending on the type of the object referenced by--- the hash. Usually, 'GetData' messages are sent after a node receives an 'Inv'--- message that contains unknown object hashes.-newtype GetData =-    GetData { -- | list of object hashes-              getDataList :: [InvVector]-            } deriving (Eq, Show, Generic, NFData)--instance Serialize GetData where--    get = GetData <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (GetData xs) = do-        putVarInt $ length xs-        forM_ xs put---- | 'Inv' messages are used by nodes to advertise their knowledge of new--- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent--- unsolicited or in response to a 'GetBlocks' message.-newtype Inv =-    Inv {-        -- | inventory-          invList :: [InvVector]-        } deriving (Eq, Show, Generic, NFData)--instance Serialize Inv where--    get = Inv <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (Inv xs) = do-        putVarInt $ length xs-        forM_ xs put---- | Data type identifying the type of an inventory vector. SegWit types are--- only used in 'GetData' messages, not 'Inv'.-data InvType-    = InvError -- ^ error-    | InvTx -- ^ transaction-    | InvBlock -- ^ block-    | InvMerkleBlock -- ^ filtered block-    | InvWitnessTx -- ^ segwit transaction-    | InvWitnessBlock -- ^ segwit block-    | InvWitnessMerkleBlock -- ^ segwit filtere block-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize InvType where-    get = go =<< getWord32le-      where-        go x =-            case x of-                0 -> return InvError-                1 -> return InvTx-                2 -> return InvBlock-                3 -> return InvMerkleBlock-                _-                    | x == 1 `shiftL` 30 + 1 -> return InvWitnessTx-                    | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock-                    | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock-                    | otherwise -> fail "bitcoinGet InvType: Invalid Type"-    put x =-        putWord32le $-        case x of-            InvError              -> 0-            InvTx                 -> 1-            InvBlock              -> 2-            InvMerkleBlock        -> 3-            InvWitnessTx          -> 1 `shiftL` 30 + 1-            InvWitnessBlock       -> 1 `shiftL` 30 + 2-            InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3---- | Invectory vectors represent hashes identifying objects such as a 'Block' or--- a 'Tx'. They notify other peers about new data or data they have otherwise--- requested.-data InvVector =-    InvVector {-                -- | type of object-                invType :: !InvType-                -- | 256-bit hash of object-              , invHash :: !Hash256-              } deriving (Eq, Show, Generic, NFData)--instance Serialize InvVector where-    get = InvVector <$> S.get <*> S.get-    put (InvVector t h) = put t >> put h--newtype HostAddress =-    HostAddress ByteString-    deriving (Eq, Show, Ord, Generic, NFData)--instance Serialize HostAddress where-    put (HostAddress bs) = putByteString bs-    get = HostAddress <$> getByteString 18---- | Data type describing a bitcoin network address. Addresses are stored in--- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6--- addresses: <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>.-data NetworkAddress =-    NetworkAddress { -- | bitmask of services available for this address-                     naServices :: !Word64-                     -- | address and port information-                   , naAddress  :: !HostAddress-                   } deriving (Eq, Show, Generic, NFData)--hostToSockAddr :: HostAddress -> SockAddr-hostToSockAddr (HostAddress bs) =-    case runGet getSockAddr bs of-        Left e  -> error e-        Right x -> x--sockToHostAddress :: SockAddr -> HostAddress-sockToHostAddress = HostAddress . runPut . putSockAddr--putSockAddr :: SockAddr -> Put-putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do-    putWord32be a-    putWord32be b-    putWord32be c-    putWord32be d-    putWord16be (fromIntegral p)--putSockAddr (SockAddrInet p a) = do-    putWord32be 0x00000000-    putWord32be 0x00000000-    putWord32be 0x0000ffff-    putWord32host a-    putWord16be (fromIntegral p)--putSockAddr _ = error "Invalid address type"--getSockAddr :: Get SockAddr-getSockAddr = do-    a <- getWord32be-    b <- getWord32be-    c <- getWord32be-    if a == 0x00000000 && b == 0x00000000 && c == 0x0000ffff-        then do-            d <- getWord32host-            p <- getWord16be-            return $ SockAddrInet (fromIntegral p) d-        else do-            d <- getWord32be-            p <- getWord16be-            return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0--instance Serialize NetworkAddress where-    get = NetworkAddress <$> getWord64le <*> S.get-    put (NetworkAddress s a) = putWord64le s >> put a---- | A 'NotFound' message is returned as a response to a 'GetData' message--- whe one of the requested objects could not be retrieved. This could happen,--- for example, if a tranasaction was requested and was not available in the--- memory pool of the receiving node.-newtype NotFound =-    NotFound { -- | Inventory vectors related to this request-               notFoundList :: [InvVector]-             } deriving (Eq, Show, Generic, NFData)--instance Serialize NotFound where--    get = NotFound <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (NotFound xs) = do-        putVarInt $ length xs-        forM_ xs put---- | A 'Ping' message is sent to bitcoin peers to check if a connection is still--- open.-newtype Ping =-    Ping { -- | A random nonce used to identify the recipient of the ping-           -- request once a Pong response is received.-           pingNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)---- | A Pong message is sent as a response to a ping message.-newtype Pong =-    Pong {-           -- | nonce from corresponding 'Ping'-           pongNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Ping where-    get = Ping <$> getWord64le-    put (Ping n) = putWord64le n--instance Serialize Pong where-    get = Pong <$> getWord64le-    put (Pong n) = putWord64le n---- | The 'Reject' message is sent when messages are rejected by a peer.-data Reject =-    Reject {-             -- | type of message rejected-             rejectMessage :: !MessageCommand-             -- | rejection code-           , rejectCode    :: !RejectCode-             -- | text reason for rejection-           , rejectReason  :: !VarString-             -- | extra data such as block or tx hash-           , rejectData    :: !ByteString-           } deriving (Eq, Show, Read, Generic, NFData)---- | Rejection code associated to the 'Reject' message.-data RejectCode-    = RejectMalformed-    | RejectInvalid-    | RejectObsolete-    | RejectDuplicate-    | RejectNonStandard-    | RejectDust-    | RejectInsufficientFee-    | RejectCheckpoint-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize RejectCode where--    get = getWord8 >>= \code -> case code of-        0x01 -> return RejectMalformed-        0x10 -> return RejectInvalid-        0x11 -> return RejectObsolete-        0x12 -> return RejectDuplicate-        0x40 -> return RejectNonStandard-        0x41 -> return RejectDust-        0x42 -> return RejectInsufficientFee-        0x43 -> return RejectCheckpoint-        _    -> fail $ unwords-            [ "Reject get: Invalid code"-            , show code-            ]--    put code = putWord8 $ case code of-        RejectMalformed       -> 0x01-        RejectInvalid         -> 0x10-        RejectObsolete        -> 0x11-        RejectDuplicate       -> 0x12-        RejectNonStandard     -> 0x40-        RejectDust            -> 0x41-        RejectInsufficientFee -> 0x42-        RejectCheckpoint      -> 0x43---- | Convenience function to build a 'Reject' message.-reject :: MessageCommand -> RejectCode -> ByteString -> Reject-reject cmd code reason =-    Reject cmd code (VarString reason) B.empty--instance Serialize Reject where-    get =-        S.get >>= \(VarString bs) ->-            Reject (stringToCommand bs) <$> S.get <*> S.get <*> maybeData-      where-        maybeData =-            isEmpty >>= \done ->-                if done-                    then return B.empty-                    else getByteString 32-    put (Reject cmd code reason dat) = do-        put $ VarString $ commandToString cmd-        put code-        put reason-        unless (B.null dat) $ putByteString dat---- | Data type representing a variable-length integer. The 'VarInt' type--- usually precedes an array or a string that can vary in length.-newtype VarInt = VarInt { getVarInt :: Word64 }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarInt where--    get = VarInt <$> ( getWord8 >>= go )-      where-        go 0xff = getWord64le-        go 0xfe = fromIntegral <$> getWord32le-        go 0xfd = fromIntegral <$> getWord16le-        go x    = fromIntegral <$> return x--    put (VarInt x)-        | x < 0xfd =-            putWord8 $ fromIntegral x-        | x <= 0xffff = do-            putWord8 0xfd-            putWord16le $ fromIntegral x-        | x <= 0xffffffff = do-            putWord8 0xfe-            putWord32le $ fromIntegral x-        | otherwise = do-            putWord8 0xff-            putWord64le x--putVarInt :: Integral a => a -> Put-putVarInt = put . VarInt . fromIntegral---- | Data type for serialization of variable-length strings.-newtype VarString = VarString { getVarString :: ByteString }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarString where--    get = VarString <$> (readBS =<< S.get)-      where-        readBS (VarInt len) = getByteString (fromIntegral len)--    put (VarString bs) = do-        putVarInt $ B.length bs-        putByteString bs---- | When a bitcoin node creates an outgoing connection to another node,--- the first message it will send is a 'Version' message. The other node--- will similarly respond with it's own 'Version' message.-data Version =-    Version {-              -- | protocol version-              version     :: !Word32-              -- | features supported by this connection-            , services    :: !Word64-              -- | unix timestamp-            , timestamp   :: !Word64-              -- | network address of remote node-            , addrRecv    :: !NetworkAddress-              -- | network address of sending node-            , addrSend    :: !NetworkAddress-              -- | random nonce to detect connection to self-            , verNonce    :: !Word64-              -- | user agent string-            , userAgent   :: !VarString-              -- | height of the last block in sending node-            , startHeight :: !Word32-              -- | relay transactions flag (BIP-37)-            , relay       :: !Bool-            } deriving (Eq, Show, Generic, NFData)--instance Serialize Version where--    get = Version <$> getWord32le-                  <*> getWord64le-                  <*> getWord64le-                  <*> S.get-                  <*> S.get-                  <*> getWord64le-                  <*> S.get-                  <*> getWord32le-                  <*> (go =<< isEmpty)-      where-        go True  = return True-        go False = getBool--    put (Version v s t ar as n ua sh r) = do-        putWord32le v-        putWord64le s-        putWord64le t-        put         ar-        put         as-        putWord64le n-        put         ua-        putWord32le sh-        putBool     r---- | 0x00 is 'False', anything else is 'True'.-getBool :: Get Bool-getBool = go =<< getWord8-  where-    go 0 = return False-    go _ = return True--putBool :: Bool -> Put-putBool True  = putWord8 1-putBool False = putWord8 0---- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify--- the type of message present in the payload. This allows the message--- de-serialization code to know how to decode a particular message payload.--- Every valid 'Message' constructor has a corresponding 'MessageCommand'--- constructor.-data MessageCommand-    = MCVersion-    | MCVerAck-    | MCAddr-    | MCInv-    | MCGetData-    | MCNotFound-    | MCGetBlocks-    | MCGetHeaders-    | MCTx-    | MCBlock-    | MCMerkleBlock-    | MCHeaders-    | MCGetAddr-    | MCFilterLoad-    | MCFilterAdd-    | MCFilterClear-    | MCPing-    | MCPong-    | MCAlert-    | MCMempool-    | MCReject-    | MCSendHeaders-    | MCOther ByteString-    deriving (Eq, Generic, NFData)--instance Show MessageCommand where-    showsPrec _ = shows . commandToString--instance Read MessageCommand where-    readPrec = do-        String str <- lexP-        return (stringToCommand (cs str))--instance Serialize MessageCommand where-    get = go <$> getByteString 12-      where-        go bs =-            let str = unpackCommand bs-             in stringToCommand str-    put mc = putByteString $ packCommand $ commandToString mc--instance IsString MessageCommand where-    fromString str = stringToCommand (cs str)---- | Read a 'MessageCommand' from its string representation.-stringToCommand :: ByteString -> MessageCommand-stringToCommand str = case str of-    "version"     -> MCVersion-    "verack"      -> MCVerAck-    "addr"        -> MCAddr-    "inv"         -> MCInv-    "getdata"     -> MCGetData-    "notfound"    -> MCNotFound-    "getblocks"   -> MCGetBlocks-    "getheaders"  -> MCGetHeaders-    "tx"          -> MCTx-    "block"       -> MCBlock-    "merkleblock" -> MCMerkleBlock-    "headers"     -> MCHeaders-    "getaddr"     -> MCGetAddr-    "filterload"  -> MCFilterLoad-    "filteradd"   -> MCFilterAdd-    "filterclear" -> MCFilterClear-    "ping"        -> MCPing-    "pong"        -> MCPong-    "alert"       -> MCAlert-    "mempool"     -> MCMempool-    "reject"      -> MCReject-    "sendheaders" -> MCSendHeaders-    _             -> MCOther str---- | Convert a 'MessageCommand' to its string representation.-commandToString :: MessageCommand -> ByteString-commandToString mc = case mc of-    MCVersion     -> "version"-    MCVerAck      -> "verack"-    MCAddr        -> "addr"-    MCInv         -> "inv"-    MCGetData     -> "getdata"-    MCNotFound    -> "notfound"-    MCGetBlocks   -> "getblocks"-    MCGetHeaders  -> "getheaders"-    MCTx          -> "tx"-    MCBlock       -> "block"-    MCMerkleBlock -> "merkleblock"-    MCHeaders     -> "headers"-    MCGetAddr     -> "getaddr"-    MCFilterLoad  -> "filterload"-    MCFilterAdd   -> "filteradd"-    MCFilterClear -> "filterclear"-    MCPing        -> "ping"-    MCPong        -> "pong"-    MCAlert       -> "alert"-    MCMempool     -> "mempool"-    MCReject      -> "reject"-    MCSendHeaders -> "sendheaders"-    MCOther c     -> c---- | Pack a string 'MessageCommand' so that it is exactly 12-bytes long.-packCommand :: ByteString -> ByteString-packCommand s = B.take 12 $-    s `mappend` C.replicate 12 '\NUL'---- | Undo packing done by 'packCommand'.-unpackCommand :: ByteString -> ByteString-unpackCommand = B.takeWhile (/= 0)---- | Node offers no services.-nodeNone :: Word64-nodeNone = 0---- | Services indicate node is a full node that can serve full blocks.-nodeNetwork :: Word64-nodeNetwork = 1---- | Services indicate node allows to request 'UTXO' set.-nodeGetUTXO :: Word64-nodeGetUTXO = 1 `shiftL` 1---- | Services indicate node accepts bloom filters.-nodeBloom :: Word64-nodeBloom = 1 `shiftL` 2---- | Services indicate SegWit-capable node.-nodeWitness :: Word64-nodeWitness = 1 `shiftL` 3---- | Services indicate Xtreme Thinblocks compatibility.-nodeXThin :: Word64-nodeXThin = 1 `shiftL` 4+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# 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,+    AddrV2 (..),+    NetworkAddressV2 (..),+    AddressNetwork (..),+    addressNetworkId,+    addressNetworkLength,+    maxAddrV2Size,+    toNetworkAddressV2,+    fromNetworkAddressV2,+    Alert (..),+    FeeFilter (..),+    GetData (..),+    Inv (..),+    InvVector (..),+    InvType (..),+    HostAddress,+    hostToSockAddr,+    sockToHostAddress,+    NetworkAddress (..),+    NotFound (..),+    Ping (..),+    Pong (..),+    Reject (..),+    RejectCode (..),+    SendTxRcncl (..),+    VarInt (..),+    VarString (..),+    Version (..),+    maxUserAgentLength,+    MessageCommand (..),+    reject,+    nodeNone,+    nodeNetwork,+    nodeGetUTXO,+    nodeBloom,+    nodeWitness,+    nodeXThin,+    nodeCompactFilters,+    nodeNetworkLimited,+    nodeP2PV2,+    commandToString,+    stringToCommand,+    putVarInt,+    getVarIntList,+    getVarIntListMax,+    putVarIntList,+    getVarIntBytes,+    putVarIntBytes,+    maxListSize,+    maxInvSize,+    maxAddrSize,+    maxHeadersSize,+    maxLocatorSize,+    maxFilterHashes,+  )+where++import Control.DeepSeq+import Control.Monad (forM_, liftM2, unless, when)+import Data.Binary (Binary (..))+import Data.Bits (shiftL, shiftR, (.&.), (.|.))+import Data.Int (Int32, Int64)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..))+import Data.String+import Data.String.Conversions (cs)+import Data.Word (Word16, Word32, Word64, Word8)+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)++-- | A message with more than 'maxAddrSize' entries fails the parse.+instance Serial Addr where+  deserialize = Addr <$> getVarIntListMax "addr" maxAddrSize action+    where+      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++-- | Network that an 'addrv2' address belongs to (BIP 155).+data AddressNetwork+  = -- | IPv4, 4 address bytes+    AddrIPv4+  | -- | IPv6, 16 address bytes+    AddrIPv6+  | -- | Tor v2, 10 address bytes. No longer in use.+    AddrTorV2+  | -- | Tor v3, 32 address bytes+    AddrTorV3+  | -- | I2P, 32 address bytes+    AddrI2P+  | -- | CJDNS, 16 address bytes+    AddrCJDNS+  | -- | Yggdrasil, 16 address bytes+    AddrYggdrasil+  | -- | a network identifier this library does not know+    AddrUnknown Word8+  deriving (Eq, Show, Read, Generic, NFData)++-- | Network identifier byte of BIP 155.+addressNetworkId :: AddressNetwork -> Word8+addressNetworkId AddrIPv4 = 1+addressNetworkId AddrIPv6 = 2+addressNetworkId AddrTorV2 = 3+addressNetworkId AddrTorV3 = 4+addressNetworkId AddrI2P = 5+addressNetworkId AddrCJDNS = 6+addressNetworkId AddrYggdrasil = 7+addressNetworkId (AddrUnknown w) = w++-- | Network of a BIP 155 identifier byte.+addressNetwork :: Word8 -> AddressNetwork+addressNetwork 1 = AddrIPv4+addressNetwork 2 = AddrIPv6+addressNetwork 3 = AddrTorV2+addressNetwork 4 = AddrTorV3+addressNetwork 5 = AddrI2P+addressNetwork 6 = AddrCJDNS+addressNetwork 7 = AddrYggdrasil+addressNetwork w = AddrUnknown w++-- | Number of address bytes that BIP 155 requires for a network, if known.+addressNetworkLength :: AddressNetwork -> Maybe Int+addressNetworkLength AddrIPv4 = Just 4+addressNetworkLength AddrIPv6 = Just 16+addressNetworkLength AddrTorV2 = Just 10+addressNetworkLength AddrTorV3 = Just 32+addressNetworkLength AddrI2P = Just 32+addressNetworkLength AddrCJDNS = Just 16+addressNetworkLength AddrYggdrasil = Just 16+addressNetworkLength (AddrUnknown _) = Nothing++-- | Longest address that an 'addrv2' entry may carry, in bytes (BIP 155).+maxAddrV2Size :: Word64+maxAddrV2Size = 512++-- | An entry of an 'addrv2' message (BIP 155).+data NetworkAddressV2 = NetworkAddressV2+  { -- | time this node was last seen+    time :: !Word32,+    -- | bitmask of services available for this address+    services :: !Word64,+    -- | network the address belongs to+    network :: !AddressNetwork,+    -- | address bytes, as many as the network requires+    address :: !ByteString,+    -- | port, or 0 when the network has none+    port :: !Word16+  }+  deriving (Eq, Show, Read, Generic, NFData)++-- | An entry fails to parse when its address is longer than 'maxAddrV2Size'+-- or does not have the length its network requires. Entries of unknown+-- networks are kept with their bytes. So are Tor v2 entries, whatever their+-- length: BIP 155 says that clients "MUST ignore them on receive", and+-- Bitcoin Core skips them without failing the message. A caller must not+-- relay a Tor v2 entry.+instance Serial NetworkAddressV2 where+  deserialize = do+    time <- getWord32le+    VarInt services <- deserialize+    network <- addressNetwork <$> getWord8+    VarInt len <- deserialize+    when (len > maxAddrV2Size) $+      fail ("addrv2 address too long: " ++ show len)+    address <- getByteString (fromIntegral len)+    let required+          | network == AddrTorV2 = Nothing+          | otherwise = addressNetworkLength network+    forM_ required $ \expected ->+      unless (B.length address == expected) $+        fail $+          unwords+            [ "addrv2 address of",+              show (B.length address),+              "bytes for",+              show network,+              "(should be",+              show expected ++ ")"+            ]+    port <- getWord16be+    return NetworkAddressV2 {time, services, network, address, port}++  serialize a = do+    putWord32le a.time+    putVarInt a.services+    putWord8 (addressNetworkId a.network)+    putVarIntBytes a.address+    putWord16be a.port++instance Binary NetworkAddressV2 where+  get = deserialize+  put = serialize++instance Serialize NetworkAddressV2 where+  get = deserialize+  put = serialize++-- | The 'addrv2' message (BIP 155). It replaces 'Addr' once both peers have+-- sent @sendaddrv2@, and can carry Tor, I2P and CJDNS addresses.+newtype AddrV2 = AddrV2+  { list :: [NetworkAddressV2]+  }+  deriving (Eq, Show, Generic)+  deriving newtype (NFData)++-- | A message with more than 'maxAddrSize' entries fails the parse, as BIP+-- 155 asks ("Clients SHOULD reject messages with more addresses").+instance Serial AddrV2 where+  deserialize = AddrV2 <$> getVarIntListMax "addrv2" maxAddrSize deserialize+  serialize (AddrV2 xs) = putVarIntList serialize xs++instance Binary AddrV2 where+  get = deserialize+  put = serialize++instance Serialize AddrV2 where+  get = deserialize+  put = serialize++-- | Prefix of an IPv4-mapped IPv6 address, @::ffff:0:0/96@.+ipv4Prefix :: ByteString+ipv4Prefix = B.replicate 10 0 <> B.pack [0xff, 0xff]++-- | Prefix of a Tor v2 address wrapped in IPv6 by OnionCat,+-- @fd87:d87e:eb43::/48@.+onionCatPrefix :: ByteString+onionCatPrefix = B.pack [0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43]++-- | The 'addrv2' form of an 'addr' entry. An IPv4-mapped address becomes an+-- 'AddrIPv4' entry with four address bytes, and an OnionCat address an+-- 'AddrTorV2' entry with ten, since BIP 155 forbids sending either under the+-- IPv6 network. Any other address is IPv6. Tor v2 is no longer in use, and+-- BIP 155 says such entries must not be relayed.+toNetworkAddressV2 :: NetworkAddressTime -> NetworkAddressV2+toNetworkAddressV2 (t, NetworkAddress s (HostAddress bs)) =+  NetworkAddressV2 {time = t, services = s, network, address, port}+  where+    ip = B.take 16 bs+    port =+      (fromIntegral (B.index bs 16) `shiftL` 8)+        .|. fromIntegral (B.index bs 17)+    (network, address)+      | B.take 12 ip == ipv4Prefix = (AddrIPv4, B.drop 12 ip)+      | B.take 6 ip == onionCatPrefix = (AddrTorV2, B.drop 6 ip)+      | otherwise = (AddrIPv6, ip)++-- | The 'addr' form of an 'addrv2' entry. Only IPv4 and IPv6 entries have+-- one. Tor, I2P and CJDNS addresses do not fit the 'addr' message. An IPv6+-- entry that holds an IPv4-mapped or an OnionCat address has none either:+-- BIP 155 says to ignore those on receive, and Bitcoin Core reads them as+-- invalid addresses, because the same peer would otherwise be known under+-- two networks. An entry whose address does not have the length of its+-- network has no 'addr' form.+fromNetworkAddressV2 :: NetworkAddressV2 -> Maybe NetworkAddressTime+fromNetworkAddressV2 a =+  case a.network of+    AddrIPv4+      | B.length a.address == 4 -> Just (entry (ipv4Prefix <> a.address))+    AddrIPv6+      | B.length a.address == 16+          && B.take 12 a.address /= ipv4Prefix+          && B.take 6 a.address /= onionCatPrefix ->+          Just (entry a.address)+    _ -> Nothing+  where+    entry ip = (a.time, NetworkAddress a.services (HostAddress (ip <> portBytes)))+    portBytes = B.pack [fromIntegral (a.port `shiftR` 8), fromIntegral a.port]++-- | 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 'feefilter' message (BIP 133). The peer asks not to be sent+-- transactions that pay less than this fee rate.+newtype FeeFilter = FeeFilter+  { -- | minimum fee rate in satoshi per 1000 bytes+    feeRate :: Word64+  }+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial FeeFilter where+  deserialize = FeeFilter <$> getWord64le+  serialize (FeeFilter r) = putWord64le r++instance Binary FeeFilter where+  put = serialize+  get = deserialize++instance Serialize FeeFilter 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)++-- | A message with more than 'maxInvSize' entries fails the parse.+instance Serial GetData where+  deserialize = GetData <$> getVarIntListMax "getdata" maxInvSize 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)++-- | A message with more than 'maxInvSize' entries fails the parse.+instance Serial Inv where+  deserialize = Inv <$> getVarIntListMax "inv" maxInvSize 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+  | -- | compact block (BIP 152)+    InvCompactBlock+  | -- | transaction identified by its witness transaction id (BIP 339)+    InvWTx+  | -- | 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+          4 -> return InvCompactBlock+          5 -> return InvWTx+          _+            | 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+        InvCompactBlock -> 4+        InvWTx -> 5+        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++-- | Sixteen address bytes followed by a big-endian port, the 18-byte address+-- of the 'addr' and 'version' messages.+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)++-- | The socket address of a 'HostAddress': IPv4 for an IPv4-mapped address,+-- IPv6 otherwise.+hostToSockAddr :: HostAddress -> SockAddr+hostToSockAddr (HostAddress bs) =+  case runGetS getSockAddr bs of+    Left e -> error e+    Right x -> x++-- | The 'HostAddress' of an IPv4 or IPv6 socket address. Any other kind of+-- socket address, such as a Unix socket, is an error: check the address+-- first when it does not come from a TCP connection.+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 <$> getVarIntList 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,+    -- | hash of the rejected block or transaction, when the peer gives one+    extra :: !(Maybe Hash256)+  }+  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 without a hash.+reject :: MessageCommand -> RejectCode -> ByteString -> Reject+reject cmd code reason =+  Reject cmd code (VarString reason) Nothing++instance Serial Reject where+  deserialize =+    deserialize >>= \(VarString bs) ->+      Reject (stringToCommand bs)+        <$> deserialize+        <*> deserialize+        <*> maybeHash+    where+      maybeHash =+        isEmpty >>= \done ->+          if done+            then return Nothing+            else Just <$> deserialize+  serialize (Reject cmd code reason extra) = do+    serialize $ VarString $ commandToString cmd+    serialize code+    serialize reason+    forM_ extra serialize++instance Binary Reject where+  put = serialize+  get = deserialize++instance Serialize Reject where+  put = serialize+  get = deserialize++-- | The 'sendtxrcncl' message (BIP 330). It announces support for+-- transaction reconciliation.+data SendTxRcncl = SendTxRcncl+  { -- | reconciliation protocol version+    version :: !Word32,+    -- | salt for the short transaction identifiers+    salt :: !Word64+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial SendTxRcncl where+  deserialize = SendTxRcncl <$> getWord32le <*> getWord64le+  serialize (SendTxRcncl v s) = putWord32le v >> putWord64le s++instance Binary SendTxRcncl where+  put = serialize+  get = deserialize++instance Serialize SendTxRcncl 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)++-- | Only the shortest encoding of a value is accepted, as in the+-- @ReadCompactSize@ function of Bitcoin Core. A value below 253 must use one+-- byte, below 0x10000 three bytes, and below 0x100000000 five bytes.+instance Serial VarInt where+  deserialize = VarInt <$> (getWord8 >>= go)+    where+      go 0xff = do+        x <- getWord64le+        when (x < 0x100000000) $ nonCanonical 9 x+        return x+      go 0xfe = do+        x <- getWord32le+        when (x < 0x10000) $ nonCanonical 5 x+        return $ fromIntegral x+      go 0xfd = do+        x <- getWord16le+        when (x < 0xfd) $ nonCanonical 3 x+        return $ fromIntegral x+      go x = return $ fromIntegral x+      nonCanonical :: (MonadGet m, Show a) => Int -> a -> m ()+      nonCanonical n x =+        fail $+          unwords+            [ "VarInt: non-canonical",+              show n ++ "-byte encoding of",+              show 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++-- | Largest number of elements that a serialized list may announce, and+-- largest number of bytes that a serialized string may announce. Bitcoin Core+-- refuses larger counts when it reads a compact size (@MAX_SIZE@).+maxListSize :: Word64+maxListSize = 0x02000000++-- | Read a list that starts with its element count as a 'VarInt'. A count+-- above 'maxListSize' fails the parse. Without this check a huge count would+-- wrap around to a negative 'Int' and read as an empty list.+getVarIntList :: (MonadGet m) => m a -> m [a]+getVarIntList element = do+  VarInt count <- deserialize+  when (count > maxListSize) $+    fail ("List count too large: " ++ show count)+  getElements count element++-- | Read the given number of elements, in a loop that keeps nothing but the+-- elements read so far.+getElements :: (MonadGet m) => Word64 -> m a -> m [a]+getElements count element = go count []+  where+    go 0 acc = return (reverse acc)+    go n acc = do+      x <- element+      go (n - 1) (x : acc)++-- | Read a list like 'getVarIntList', and fail when its count is above the+-- given limit. The count is checked right after it is read, before any+-- element. The name is that of the message, for the error text.+getVarIntListMax :: (MonadGet m) => String -> Word64 -> m a -> m [a]+getVarIntListMax name limit element = do+  VarInt count <- deserialize+  when (count > limit) $+    fail (name ++ ": too many entries: " ++ show count ++ ", the limit is " ++ show limit)+  getElements count element++-- | Largest number of entries in an @inv@ or a @getdata@ message. Bitcoin+-- Core and Bitcoin Cash Node punish a peer that sends more (@MAX_INV_SZ@).+maxInvSize :: Word64+maxInvSize = 50000++-- | Largest number of entries in an @addr@ or an @addrv2@ message+-- (@MAX_ADDR_TO_SEND@, and BIP 155).+maxAddrSize :: Word64+maxAddrSize = 1000++-- | Largest number of headers in a @headers@ message+-- (@MAX_HEADERS_RESULTS@).+maxHeadersSize :: Word64+maxHeadersSize = 2000++-- | Largest number of hashes in the locator of a @getheaders@ or a+-- @getblocks@ message (@MAX_LOCATOR_SZ@).+maxLocatorSize :: Word64+maxLocatorSize = 101++-- | Largest number of filter hashes in a @cfheaders@ message (BIP 157:+-- "FilterHashesLength MUST NOT be greater than 2,000").+maxFilterHashes :: Word64+maxFilterHashes = 2000++-- | Write a list with its element count as a 'VarInt' in front.+putVarIntList :: (MonadPut m) => (a -> m ()) -> [a] -> m ()+putVarIntList element xs = do+  putVarInt (length xs)+  mapM_ element xs++-- | Read bytes that start with their count as a 'VarInt'. A count above+-- 'maxListSize' fails the parse. A count of zero gives the one shared empty+-- byte string, so that a long run of empty items, as a peer can put in a+-- witness, costs a list cell each and no more.+getVarIntBytes :: (MonadGet m) => m ByteString+getVarIntBytes = do+  VarInt count <- deserialize+  when (count > maxListSize) $+    fail ("Byte count too large: " ++ show count)+  if count == 0+    then return B.empty+    else getByteString (fromIntegral count)++-- | Write bytes with their count as a 'VarInt' in front.+putVarIntBytes :: (MonadPut m) => ByteString -> m ()+putVarIntBytes bs = do+  putVarInt (B.length bs)+  putByteString bs++-- | 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 <$> getVarIntBytes+  serialize (VarString bs) = putVarIntBytes bs++instance Binary VarString where+  put = serialize+  get = deserialize++instance Serialize VarString where+  put = serialize+  get = deserialize++-- | Longest user agent that a 'Version' message may carry, in bytes. Bitcoin+-- Core disconnects a peer that sends a longer one (@MAX_SUBVERSION_LENGTH@).+maxUserAgentLength :: Int+maxUserAgentLength = 256++-- | 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. Signed, as @int nVersion@ in Bitcoin Core, so a+    -- peer that sends ffffffff has version -1 and not a huge one.+    version :: !Int32,+    -- | features supported by this connection+    services :: !Word64,+    -- | unix timestamp, signed as @int64_t nTime@ in Bitcoin Core+    timestamp :: !Int64,+    -- | 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: up to 'maxUserAgentLength' bytes that the peer+    -- chose. They are not checked to be text. Bitcoin Core passes them+    -- through @SanitizeString@ before it logs or shows them, and a caller+    -- should do the same.+    userAgent :: !VarString,+    -- | height of the last block in sending node, signed as+    -- @int starting_height@ in Bitcoin Core+    startHeight :: !Int32,+    -- | relay transactions flag (BIP-37)+    relay :: !Bool+  }+  deriving (Eq, Show, Generic, NFData)++-- | A user agent longer than 'maxUserAgentLength' fails the parse. The relay+-- flag is 'True' when the message ends before it, as for peers that predate+-- BIP 37.+instance Serial Version where+  deserialize =+    Version+      <$> (fromIntegral <$> getWord32le)+      <*> getWord64le+      <*> (fromIntegral <$> getWord64le)+      <*> deserialize+      <*> deserialize+      <*> getWord64le+      <*> userAgent+      <*> (fromIntegral <$> getWord32le)+      <*> (go =<< isEmpty)+    where+      go True = return True+      go False = getBool+      userAgent = do+        ua@(VarString bs) <- deserialize+        when (B.length bs > maxUserAgentLength) $+          fail ("Version: user agent of " ++ show (B.length bs) ++ " bytes is too long")+        return ua++  serialize (Version v s t ar as n ua sh r) = do+    putWord32le (fromIntegral v)+    putWord64le s+    putWord64le (fromIntegral t)+    serialize ar+    serialize as+    putWord64le n+    serialize ua+    putWord32le (fromIntegral 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+  | MCAddrV2+  | MCSendAddrV2+  | MCInv+  | MCGetData+  | MCNotFound+  | MCGetBlocks+  | MCGetHeaders+  | MCTx+  | MCBlock+  | MCMerkleBlock+  | MCHeaders+  | MCGetAddr+  | MCFilterLoad+  | MCFilterAdd+  | MCFilterClear+  | MCPing+  | MCPong+  | MCAlert+  | MCMempool+  | MCReject+  | MCSendHeaders+  | MCFeeFilter+  | MCSendCmpct+  | MCCmpctBlock+  | MCGetBlockTxn+  | MCBlockTxn+  | MCGetCFilters+  | MCCFilter+  | MCGetCFHeaders+  | MCCFHeaders+  | MCGetCFCheckpt+  | MCCFCheckpt+  | MCWtxidRelay+  | MCSendTxRcncl+  | MCOther ByteString+  deriving (Eq, Generic, NFData)++instance Show MessageCommand where+  showsPrec _ = shows . commandToString++-- | Reads what 'Show' writes, for any bytes: the name as a byte string+-- literal. A 'Reject' message can name a command with bytes outside ASCII.+instance Read MessageCommand where+  readPrec = stringToCommand <$> readPrec++-- | A command is twelve bytes on the wire: its name in printable ASCII, then+-- NUL bytes. The parse fails for a byte outside printable ASCII in the name or+-- a byte other than NUL after it, as @IsMessageTypeValid@ does in Bitcoin+-- Core. Serializing a name longer than twelve bytes, or with a byte outside+-- printable ASCII, is an error.+instance Serial MessageCommand where+  deserialize = do+    bs <- getByteString 12+    either fail (return . stringToCommand) (unpackCommand bs)+  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+  "addrv2" -> MCAddrV2+  "sendaddrv2" -> MCSendAddrV2+  "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+  "feefilter" -> MCFeeFilter+  "sendcmpct" -> MCSendCmpct+  "cmpctblock" -> MCCmpctBlock+  "getblocktxn" -> MCGetBlockTxn+  "blocktxn" -> MCBlockTxn+  "getcfilters" -> MCGetCFilters+  "cfilter" -> MCCFilter+  "getcfheaders" -> MCGetCFHeaders+  "cfheaders" -> MCCFHeaders+  "getcfcheckpt" -> MCGetCFCheckpt+  "cfcheckpt" -> MCCFCheckpt+  "wtxidrelay" -> MCWtxidRelay+  "sendtxrcncl" -> MCSendTxRcncl+  _ -> MCOther str++-- | Convert a 'MessageCommand' to its string representation.+commandToString :: MessageCommand -> ByteString+commandToString mc = case mc of+  MCVersion -> "version"+  MCVerAck -> "verack"+  MCAddr -> "addr"+  MCAddrV2 -> "addrv2"+  MCSendAddrV2 -> "sendaddrv2"+  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"+  MCFeeFilter -> "feefilter"+  MCSendCmpct -> "sendcmpct"+  MCCmpctBlock -> "cmpctblock"+  MCGetBlockTxn -> "getblocktxn"+  MCBlockTxn -> "blocktxn"+  MCGetCFilters -> "getcfilters"+  MCCFilter -> "cfilter"+  MCGetCFHeaders -> "getcfheaders"+  MCCFHeaders -> "cfheaders"+  MCGetCFCheckpt -> "getcfcheckpt"+  MCCFCheckpt -> "cfcheckpt"+  MCWtxidRelay -> "wtxidrelay"+  MCSendTxRcncl -> "sendtxrcncl"+  MCOther c -> c++-- | Length of a command on the wire.+commandSize :: Int+commandSize = 12++-- | Is this byte printable ASCII? Command names are made of these.+printable :: Word8 -> Bool+printable w = w >= 0x20 && w <= 0x7e++-- | Pack a command name so that it is exactly 12 bytes long. A name that+-- does not fit, or has a byte outside printable ASCII, is an error. Bitcoin+-- Core asserts the same when it builds a header.+packCommand :: ByteString -> ByteString+packCommand s+  | B.length s > commandSize =+      error ("Message command longer than 12 bytes: " ++ show s)+  | not (B.all printable s) =+      error ("Message command with a byte outside printable ASCII: " ++ show s)+  | otherwise = s <> B.replicate (commandSize - B.length s) 0++-- | Undo packing done by 'packCommand'. Fails for a name with a byte outside+-- printable ASCII, or for a byte other than NUL after the name.+unpackCommand :: ByteString -> Either String ByteString+unpackCommand bs+  | not (B.all (== 0) padding) =+      Left ("Message command with bytes after its NUL terminator: " ++ show bs)+  | not (B.all printable name) =+      Left ("Message command with a byte outside printable ASCII: " ++ show bs)+  | otherwise = Right name+  where+    (name, padding) = B.break (== 0) bs++-- | Node offers no services.+nodeNone :: Word64+nodeNone = 0++-- | Services indicate node is a full node that can serve full blocks.+nodeNetwork :: Word64+nodeNetwork = 1++-- | Services indicate node allows to request 'UTXO' set.+nodeGetUTXO :: Word64+nodeGetUTXO = 1 `shiftL` 1++-- | Services indicate node accepts bloom filters.+nodeBloom :: Word64+nodeBloom = 1 `shiftL` 2++-- | Services indicate SegWit-capable node.+nodeWitness :: Word64+nodeWitness = 1 `shiftL` 3++-- | Services indicate Xtreme Thinblocks compatibility.+nodeXThin :: Word64+nodeXThin = 1 `shiftL` 4++-- | Services indicate node serves compact block filters (BIP 157).+nodeCompactFilters :: Word64+nodeCompactFilters = 1 `shiftL` 6++-- | Services indicate node serves only the last 288 blocks (BIP 159). Pruned+-- nodes advertise this instead of 'nodeNetwork'.+nodeNetworkLimited :: Word64+nodeNetworkLimited = 1 `shiftL` 10++-- | Services indicate node supports the version 2 encrypted transport+-- (BIP 324).+nodeP2PV2 :: Word64+nodeP2PV2 = 1 `shiftL` 11
+ src/Haskoin/Network/CompactBlock.hs view
@@ -0,0 +1,267 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Network.CompactBlock+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Messages of the compact block relay protocol (BIP 152): @sendcmpct@,+-- @cmpctblock@, @getblocktxn@ and @blocktxn@.+module Haskoin.Network.CompactBlock+  ( SendCompact (..),+    CompactBlock (..),+    PrefilledTx (..),+    GetBlockTxn (..),+    BlockTxn (..),+    maxShortId,+    getCompactBlock,+    getGetBlockTxn,+    getBlockTxn,+  )+where++import Control.DeepSeq+import Control.Monad (when)+import Data.Binary (Binary (..))+import Data.Bits (shiftL, shiftR, (.|.))+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..))+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin.Block.Common (BlockHash, BlockHeader)+import Haskoin.Network.Common+import Haskoin.Network.Data (Network (..), getNetworkTx)+import Haskoin.Transaction.Common (Tx)++-- | The @sendcmpct@ message. It tells the peer whether to announce new blocks+-- as compact blocks, and which version of the protocol to use. Version 1+-- identifies transactions by their txid and version 2 by their wtxid.+data SendCompact = SendCompact+  { -- | announce blocks with @cmpctblock@ instead of @inv@ or @headers@+    announce :: !Bool,+    -- | compact block protocol version+    version :: !Word64+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial SendCompact where+  deserialize = SendCompact <$> ((/= 0) <$> getWord8) <*> getWord64le+  serialize (SendCompact a v) = do+    putWord8 (if a then 1 else 0)+    putWord64le v++instance Binary SendCompact where+  put = serialize+  get = deserialize++instance Serialize SendCompact where+  put = serialize+  get = deserialize++-- | Largest short transaction identifier. Short identifiers are six bytes.+maxShortId :: Word64+maxShortId = 0xffffffffffff++getShortId :: (MonadGet m) => m Word64+getShortId = do+  low <- getWord32le+  high <- getWord16le+  return $ fromIntegral high `shiftL` 32 .|. fromIntegral low++putShortId :: (MonadPut m) => Word64 -> m ()+putShortId x+  | x > maxShortId = error ("Short transaction id does not fit six bytes: " ++ show x)+  | otherwise = do+      putWord32le (fromIntegral x)+      putWord16le (fromIntegral (x `shiftR` 32))++-- | A transaction sent along with a compact block, with its position in the+-- block.+data PrefilledTx = PrefilledTx+  { -- | position of the transaction in the block+    index :: !Word32,+    -- | the transaction+    tx :: !Tx+  }+  deriving (Eq, Show, Generic, NFData)++-- | The @cmpctblock@ message: a block header with short identifiers for most+-- of its transactions, and the full transactions the sender expects the peer+-- to lack. Positions in the block are absolute here; on the wire each one is+-- encoded as its distance from the previous one, as BIP 152 requires.+data CompactBlock = CompactBlock+  { -- | block header+    header :: !BlockHeader,+    -- | nonce the short identifiers were computed with+    nonce :: !Word64,+    -- | short identifiers of the transactions not sent in full+    shortIds :: ![Word64],+    -- | transactions sent in full, in order of their position+    prefilled :: ![PrefilledTx]+  }+  deriving (Eq, Show, Generic, NFData)++-- | This form knows no network, so it takes positions of up to 32 bits and+-- reads transactions in the extended format. Use 'getCompactBlock' for a+-- message from a peer.+instance Serial CompactBlock where+  deserialize = compactBlockWith maxBound deserialize++  serialize c = do+    serialize c.header+    putWord64le c.nonce+    putVarIntList putShortId c.shortIds+    putDifferentialList (.index) (serialize . (.tx)) c.prefilled++instance Binary CompactBlock where+  put = serialize+  get = deserialize++instance Serialize CompactBlock where+  put = serialize+  get = deserialize++-- | The @getblocktxn@ message. It asks for the transactions at the given+-- positions of a block. Positions are absolute here and differentially+-- encoded on the wire.+data GetBlockTxn = GetBlockTxn+  { -- | hash of the block+    hash :: !BlockHash,+    -- | positions of the wanted transactions, in increasing order+    indexes :: ![Word32]+  }+  deriving (Eq, Show, Generic, NFData)++-- | This form knows no network, so it takes positions of up to 32 bits. Use+-- 'getGetBlockTxn' for a message from a peer.+instance Serial GetBlockTxn where+  deserialize = GetBlockTxn <$> deserialize <*> getDifferentialList maxBound return+  serialize g = do+    serialize g.hash+    putDifferentialList id (const (return ())) g.indexes++instance Binary GetBlockTxn where+  put = serialize+  get = deserialize++instance Serialize GetBlockTxn where+  put = serialize+  get = deserialize++-- | The @blocktxn@ message, the answer to 'GetBlockTxn'.+data BlockTxn = BlockTxn+  { -- | hash of the block+    hash :: !BlockHash,+    -- | the requested transactions, in the order they were asked for+    transactions :: ![Tx]+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial BlockTxn where+  deserialize = BlockTxn <$> deserialize <*> getVarIntList deserialize+  serialize b = do+    serialize b.hash+    putVarIntList serialize b.transactions++instance Binary BlockTxn where+  put = serialize+  get = deserialize++instance Serialize BlockTxn where+  put = serialize+  get = deserialize++-- | Read a @cmpctblock@ message of a network. On Bitcoin a position above+-- 65535 fails the parse, and so does a block of more than 65535+-- transactions, as in Bitcoin Core ("indexes overflowed 16 bits"). On+-- Bitcoin Cash positions have 32 bits, as in Bitcoin Cash Node, and the+-- transactions are read in the old format.+getCompactBlock :: (MonadGet m) => Network -> m CompactBlock+getCompactBlock net = compactBlockWith net.maxCompactBlockIndex (getNetworkTx net)++compactBlockWith :: (MonadGet m) => Word32 -> m Tx -> m CompactBlock+compactBlockWith maxIndex getTx = do+  header <- deserialize+  nonce <- getWord64le+  shortIds <- getVarIntList getShortId+  prefilled <- getDifferentialList maxIndex (\i -> PrefilledTx i <$> getTx)+  -- The positions of a block of n transactions are 0 to n - 1, and Bitcoin+  -- Core refuses a count above the largest value of its position type.+  when (toInteger (length shortIds) + toInteger (length prefilled) > toInteger maxIndex) $+    fail ("Compact block: indexes overflowed " ++ show (indexBits maxIndex) ++ " bits")+  return (CompactBlock header nonce shortIds prefilled)++-- | Read a @getblocktxn@ message of a network. On Bitcoin a position above+-- 65535 fails the parse, as in Bitcoin Core ("differential value overflow").+getGetBlockTxn :: (MonadGet m) => Network -> m GetBlockTxn+getGetBlockTxn net =+  GetBlockTxn <$> deserialize <*> getDifferentialList net.maxCompactBlockIndex return++-- | Read a @blocktxn@ message of a network. On a network without SegWit the+-- transactions are read in the old format, see 'getNetworkTx'.+getBlockTxn :: (MonadGet m) => Network -> m BlockTxn+getBlockTxn net = BlockTxn <$> deserialize <*> getVarIntList (getNetworkTx net)++-- | Width of the position type that holds this largest position.+indexBits :: Word32 -> Int+indexBits maxIndex+  | maxIndex <= 0xffff = 16+  | otherwise = 32++-- | Read a list whose elements each start with a block position. The first+-- position is written as is, and every later one as its distance from the+-- previous position minus one. A position above the given largest one fails+-- the parse. Positions are strict numbers, so a long list costs no more than+-- its cells.+getDifferentialList :: (MonadGet m) => Word32 -> (Word32 -> m a) -> m [a]+getDifferentialList maxIndex element = do+  VarInt count <- deserialize+  when (count > maxListSize) $+    fail ("List count too large: " ++ show count)+  -- Positions increase strictly, so there are at most maxIndex + 1 of them.+  when (count > limit + 1) $ tooFar count+  go count Nothing []+  where+    limit = fromIntegral maxIndex :: Word64+    tooFar x =+      fail ("Block position does not fit " ++ show (indexBits maxIndex) ++ " bits: " ++ show x)+    go 0 _ acc = return (reverse acc)+    go n previous acc = do+      VarInt distance <- deserialize+      -- Checked first, so that the sum below cannot wrap around.+      when (distance > limit) $ tooFar distance+      let position = distance + maybe 0 (\p -> fromIntegral p + 1) previous+      when (position > limit) $ tooFar position+      let !i = fromIntegral position :: Word32+      x <- element i+      go (n - 1) (Just i) (x : acc)++-- | Write a list whose elements each carry a block position, in the encoding+-- that 'getDifferentialList' reads. Positions must be strictly increasing,+-- otherwise their distances cannot be encoded and this is an error.+putDifferentialList :: (MonadPut m) => (a -> Word32) -> (a -> m ()) -> [a] -> m ()+putDifferentialList position element xs = do+  putVarInt (length xs)+  go Nothing xs+  where+    go _ [] = return ()+    go previous (x : rest) = do+      let i = position x+      case previous of+        Nothing -> putVarInt i+        Just p+          | i > p -> putVarInt (i - p - 1)+          | otherwise ->+              error ("Block positions must be strictly increasing: " ++ show p ++ " then " ++ show i)+      element x+      go (Just i) rest
+ src/Haskoin/Network/Constants.hs view
@@ -0,0 +1,696 @@+{-# 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,+    btcTest4,+    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+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 1000000,+      maxMessageSize = 4000000,+      maxProtocolMessageSize = 4000000,+      maxCompactBlockIndex = 0xffff,+      maxSatoshi = 2100000000000000,+      userAgent =+        "/haskoin-btc:" <> versionString <> "/",+      defaultPort = 8333,+      minDiffBlocks = False,+      powNoRetarget = False,+      bip94 = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 227931,+      bip34Hash = Just "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8",+      bip65Height = 388381,+      bip66Height = 363725,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0x0145ec036acc5ba740052af1a0,+      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 =+        [ "dnsseed.bluematt.me", -- Matt Corallo+          "seed.bitcoin.jonasschnelli.ch", -- Jonas Schnelli+          "seed.btc.petertodd.net", -- Peter Todd+          "seed.bitcoin.sprovoost.nl", -- Sjors Provoost+          "dnsseed.emzy.de", -- Stephan Oeste+          "seed.bitcoin.wiz.biz", -- Jason Maurice+          "seed.mainnet.achownodes.xyz" -- Ava Chow+        ],+      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+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 1000000,+      maxMessageSize = 4000000,+      maxProtocolMessageSize = 4000000,+      maxCompactBlockIndex = 0xffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-btc-test:" <> versionString <> "/",+      defaultPort = 18333,+      minDiffBlocks = True,+      powNoRetarget = False,+      bip94 = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 21111,+      bip34Hash = Just "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8",+      bip65Height = 581885,+      bip66Height = 330776,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0x17f49f702147f10c0eb6,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 546,+            "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"+          )+        ],+      seeds =+        [ "testnet-seed.bitcoin.jonasschnelli.ch", -- Jonas Schnelli+          "seed.tbtc.petertodd.net", -- Peter Todd+          "testnet-seed.bluematt.me", -- Matt Corallo+          "seed.testnet.achownodes.xyz" -- Ava Chow+        ],+      bip44Coin = 1,+      sigHashForkId = Nothing,+      edaHeight = Nothing,+      daaHeight = Nothing,+      asertActivationTime = Nothing,+      asertHalfLife = 0,+      segWit = True,+      cashAddrPrefix = Nothing,+      bech32Prefix = Just "tb",+      replaceByFee = True,+      halvingInterval = 210000+    }++-- | Testnet4 for Bitcoin SegWit network (BIP94).+btcTest4 :: Network+btcTest4 =+  Network+    { name = "btctest4",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0x1c163f28,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "4e7b2b9128fe0291db0693af2ae418b767e657cd407e80cb1434221eaea7a07a"+          1714777860+          0x1d00ffff+          393743547,+      -- Hash 00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043+      genesisTx = testnet4GenesisTx,+      maxBlockSize = 1000000,+      maxMessageSize = 4000000,+      maxProtocolMessageSize = 4000000,+      maxCompactBlockIndex = 0xffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-btc-test4:" <> versionString <> "/",+      defaultPort = 48333,+      minDiffBlocks = True,+      powNoRetarget = False,+      bip94 = True,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 1,+      -- Bitcoin Core pins no block here: BIP34Hash is uint256{} on testnet4+      bip34Hash = Nothing,+      bip65Height = 1,+      bip66Height = 1,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0x0e346a558455ade8eca9,+      targetSpacing = 10 * 60,+      checkpoints = [],+      seeds =+        [ "seed.testnet4.bitcoin.sprovoost.nl", -- Sjors Provoost+          "seed.testnet4.wiz.biz" -- Jason Maurice+        ],+      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,+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 1000000,+      maxMessageSize = 4000000,+      maxProtocolMessageSize = 4000000,+      maxCompactBlockIndex = 0xffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-btc-regtest:" <> versionString <> "/",+      defaultPort = 18444,+      minDiffBlocks = True,+      powNoRetarget = True,+      bip94 = False,+      powLimit =+        0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 1,+      bip34Hash = Nothing,+      bip65Height = 1,+      bip66Height = 1,+      -- one day, as in Bitcoin Core (kernel/chainparams.cpp, CRegTestParams)+      targetTimespan = 24 * 60 * 60,+      minimumChainWork = 0,+      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+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 32000000,+      maxMessageSize = 64000000,+      maxProtocolMessageSize = 2097152,+      maxCompactBlockIndex = 0xffffffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch:" <> versionString <> "/",+      defaultPort = 8333,+      minDiffBlocks = False,+      powNoRetarget = False,+      bip94 = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 227931,+      bip34Hash = Just "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8",+      bip65Height = 388381,+      bip66Height = 363725,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0x030fd678912166474fe93f14,+      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"+          ),+          ( 951145,+            "000000000000000000f64dbed68370486f945097ba90b17e30d7bf5c43df7a60"+          ),+          ( 960710,+            "00000000000000000275ee55c04aa109347c9ee0b5bb4617837df3044f7e1be5"+          ),+          ( 970892,+            "000000000000000000e76270e57f4ebbd2b282db46a1695984935126c0a68cd8"+          )+        ],+      seeds =+        [ "seed.flowee.cash", -- Flowee+          "seed-bch.bitcoinforks.org", -- Bitcoin Forks+          "btccash-seeder.bitcoinunlimited.info", -- Bitcoin Unlimited+          "seed.bchd.cash", -- BCHD+          "seed.bch.loping.net", -- Loping.net+          "bchseed.c3-soft.com", -- C3 Soft+          "bch.bitjson.com" -- Jason Dreyzehner+        ],+      bip44Coin = 145,+      sigHashForkId = Just 0,+      edaHeight = Just 478559,+      daaHeight = Just 504032,+      asertActivationTime = Just 1605441600,+      asertHalfLife = 2 * 24 * 60 * 60,+      segWit = False,+      cashAddrPrefix = Just "bitcoincash",+      bech32Prefix = Nothing,+      replaceByFee = False,+      halvingInterval = 210000+    }++-- | Testnet4 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 000000001dd410c49a788668ce26751718cc797474d3152a5fc073dd44fd9f7b+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 2000000,+      maxMessageSize = 4000000,+      maxProtocolMessageSize = 2097152,+      maxCompactBlockIndex = 0xffffffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-test4:" <> versionString <> "/",+      defaultPort = 28333,+      minDiffBlocks = True,+      powNoRetarget = False,+      bip94 = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 2,+      bip34Hash = Just "00000000b0c65b1e03baace7d5c093db0d6aac224df01484985ffd5e86a1a20c",+      bip65Height = 3,+      bip66Height = 4,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0xbd5a1b5bda45b5da,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 5000,+            "000000009f092d074574a216faec682040a853c4f079c33dfd2c3ef1fd8108c4"+          ),+          -- Axion activation++          ( 16845,+            "00000000fb325b8f34fe80c96a5f708a08699a68bbab82dba4474d86bd743077"+          ),+          ( 38000,+            "000000000015197537e59f339e3b1bbf81a66f691bd3d7aa08560fc7bf5113fb"+          ),+          ( 54700,+            "00000000009af4379d87f17d0f172ee4769b48839a5a3a3e81d69da4322518b8"+          ),+          ( 305848,+            "00000000601bf68f9927154cf55557b1c08245b4d415658d1cbcd1eb4502dabc"+          ),+          ( 313651,+            "000000005c58d20403191a778c51206eca7552629c1e390313e786d2c388d0fb"+          ),+          ( 325709,+            "00000000000097a6ca206aa88b45a2158eea1a9ab8a49aed8f4b193d558e74b4"+          )+        ],+      seeds =+        [ "testnet4-seed-bch.toom.im", -- Jonathan Toomim+          "seed.tbch4.loping.net", -- Loping.net+          "testnet4-seed.flowee.cash", -- Flowee+          "testnet4.bitjson.com" -- Jason Dreyzehner+        ],+      bip44Coin = 1,+      sigHashForkId = Just 0,+      edaHeight = Just 7,+      daaHeight = Just 3001,+      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+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 32000000,+      maxMessageSize = 64000000,+      maxProtocolMessageSize = 2097152,+      maxCompactBlockIndex = 0xffffffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-test:" <> versionString <> "/",+      defaultPort = 18333,+      minDiffBlocks = True,+      powNoRetarget = False,+      bip94 = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 21111,+      bip34Hash = Just "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8",+      bip65Height = 581885,+      bip66Height = 330776,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0x011fdbbe582349040762,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 546,+            "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"+          ),+          -- UAHF fork block.++          ( 1155876,+            "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5"+          ),+          -- Nov, 13. DAA activation block.++          ( 1188697,+            "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb"+          ),+          ( 1710483,+            "00000000078fe0c74ffa0c5221081c34774aa7d17a3c14c36f952f829463f120"+          ),+          ( 1718198,+            "00000000fb70d93bc600b9189f2bb13383b0e44d96a4547615b3c194680e6308"+          ),+          ( 1730264,+            "000000000001ce982fa29717a4ed233b0e5a7a8b9936a17fdeeccc5c1d0af795"+          )+        ],+      seeds =+        [ "testnet-seed.bchd.cash", -- BCHD+          "seed.tbch.loping.net", -- Loping.net+          "testnet-seed.bitcoinunlimited.info" -- Bitcoin Unlimited+        ],+      bip44Coin = 1,+      sigHashForkId = Just 0,+      edaHeight = Just 1155876,+      daaHeight = Just 1188698,+      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,+      genesisTx = mainnetGenesisTx,+      maxBlockSize = 32000000,+      maxMessageSize = 64000000,+      maxProtocolMessageSize = 2097152,+      maxCompactBlockIndex = 0xffffffff,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-regtest:" <> versionString <> "/",+      defaultPort = 18444,+      minDiffBlocks = True,+      powNoRetarget = True,+      bip94 = False,+      powLimit =+        0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Height = 100000000,+      bip34Hash = Nothing,+      bip65Height = 1351,+      bip66Height = 1251,+      targetTimespan = 14 * 24 * 60 * 60,+      minimumChainWork = 0,+      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, btcTest4, bchTest4, bchTest, btcRegTest, bchRegTest]
+ src/Haskoin/Network/Data.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.Network.Data+  ( Network (..),+    getNetworkTx,+  )+where++import Control.DeepSeq+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.Bytes.Get (MonadGet)+import Data.Bytes.Serial (deserialize)+import Data.Serialize (Serialize (..))+import Data.Text (Text)+import Data.Word (Word32, Word64, Word8)+import GHC.Generics (Generic)+import Haskoin.Block.Common+import Haskoin.Transaction.Common (Tx, parseLegacyTx)++-- | 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,+    -- | coinbase transaction of the genesis block+    genesisTx :: !Tx,+    -- | maximum block size in bytes+    maxBlockSize :: !Int,+    -- | largest P2P message of any kind, in bytes. A message that carries+    -- block content (@block@, @cmpctblock@ or @blocktxn@) may be this large.+    -- Bitcoin Core refuses any message over 4,000,000 bytes+    -- (@MAX_PROTOCOL_MESSAGE_LENGTH@). Bitcoin Cash Node refuses those three+    -- messages over twice the block size. Use+    -- 'Haskoin.Network.Message.maxPayloadSize' for the limit of one command.+    maxMessageSize :: !Word32,+    -- | largest P2P message that carries no block content, in bytes. This is+    -- @MAX_PROTOCOL_MESSAGE_LENGTH@ of the reference node: 4,000,000 on+    -- Bitcoin, where it is the limit of every message, and 2 MiB on Bitcoin+    -- Cash, where @CMessageHeader::IsOversized@ applies it to every message+    -- but @block@, @cmpctblock@ and @blocktxn@.+    maxProtocolMessageSize :: !Word32,+    -- | largest transaction position that a compact block message may name.+    -- BIP 152 and Bitcoin Core use 16 bits (65535). Bitcoin Cash Node uses 32.+    maxCompactBlockIndex :: !Word32,+    -- | 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,+    -- | enforce BIP94 block storm and time warp rules (testnet4)+    bip94 :: !Bool,+    -- | proof-of-work target higest possible value+    powLimit :: !Integer,+    -- | height at which BIP34 activates+    bip34Height :: !BlockHeight,+    -- | hash of the block at 'bip34Height', when the chain pins one. Bitcoin+    -- Core pins none on regtest.+    bip34Hash :: !(Maybe 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)],+    -- | work that the best chain is known to have, @nMinimumChainWork@ of the+    -- reference node. Once the best block has this much work,+    -- 'Haskoin.Block.Headers.connectBlocks' refuses a new header with less,+    -- which can only be a fork from far below the tip. Zero on regtest.+    minimumChainWork :: !Integer,+    -- | BIP44 derivation path root+    bip44Coin :: !Word32,+    -- | peer-to-peer network seeds+    seeds :: ![String],+    -- | fork id for replay protection+    sigHashForkId :: !(Maybe Word32),+    -- | height of the first block that uses the EDA+    -- (one above @uahfHeight@ in Bitcoin Cash Node)+    edaHeight :: !(Maybe Word32),+    -- | height of the first block that uses the DAA+    -- (one above @daaHeight@ in Bitcoin Cash Node)+    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)++-- | Read a transaction as the reference node of the network reads one that+-- a peer sends. On a network with SegWit this is the extended format of BIP+-- 144, with its marker, flag and witness. On a network without SegWit it is+-- the old format only: Bitcoin Cash Node has no witness format, so it reads+-- the marker byte as an input count of zero. Reading the old format there+-- also means that a peer cannot make a Bitcoin Cash node build witness+-- stacks.+getNetworkTx :: (MonadGet m) => Network -> m Tx+getNetworkTx net+  | net.segWit = deserialize+  | otherwise = parseLegacyTx+
src/Haskoin/Network/Message.hs view
@@ -1,69 +1,87 @@ {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Haskoin.Network.Message-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-} -Peer-to-peer network message serialization.--}+-- |+-- 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+  ( -- * Network Message+    Message (..),+    MessageHeader (..),+    msgType,+    putMessage,+    getMessage,+    maxPayloadSize,+  )+where -import           Control.DeepSeq-import           Control.Monad              (unless)-import           Data.ByteString            (ByteString)-import qualified Data.ByteString            as BS-import           Data.Serialize             (Serialize, encode, get, put)-import           Data.Serialize.Get         (Get, getByteString, getWord32be,-                                             getWord32le, isolate, lookAhead)-import           Data.Serialize.Put         (Putter, putByteString, putWord32be,-                                             putWord32le)-import           Data.Word                  (Word32)-import           GHC.Generics               (Generic)-import           Haskoin.Block.Common-import           Haskoin.Block.Merkle-import           Haskoin.Constants-import           Haskoin.Crypto.Hash-import           Haskoin.Network.Bloom-import           Haskoin.Network.Common-import           Haskoin.Transaction.Common+import Control.DeepSeq+import Control.Monad (unless, when)+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..))+import Data.Word (Word32)+import GHC.Generics (Generic)+import Haskoin.Block.Common+import Haskoin.Block.Merkle+import Haskoin.Crypto.Hash+import Haskoin.Network.BlockFilter+import Haskoin.Network.Bloom+import Haskoin.Network.Common+import Haskoin.Network.CompactBlock+import Haskoin.Network.Data+import Haskoin.Transaction.Common  -- | Data type representing the header of a 'Message'. All messages sent between -- nodes contain a message header. data MessageHeader = MessageHeader-    { -- | magic bytes identify network-      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 Serialize MessageHeader where+instance Serial MessageHeader where+  deserialize =+    MessageHeader+      <$> getWord32be+      <*> deserialize+      <*> getWord32le+      <*> deserialize -    get = MessageHeader <$> getWord32be-                        <*> get-                        <*> getWord32le-                        <*> get+  serialize (MessageHeader m c l chk) = do+    putWord32be m+    serialize c+    putWord32le l+    serialize chk -    put (MessageHeader m c l chk) = do-        putWord32be m-        put         c-        putWord32le l-        put         chk+instance Binary MessageHeader where+  put = serialize+  get = deserialize +instance Serialize MessageHeader where+  put = serialize+  get = deserialize+ -- | The 'Message' type is used to identify all the valid messages that can be -- sent between bitcoin peers. Only values of type 'Message' will be accepted -- by other bitcoin peers as bitcoin protocol messages need to be correctly@@ -71,131 +89,220 @@ -- 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+  | MAddrV2 !AddrV2+  | MSendAddrV2+  | 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+  | MFeeFilter !FeeFilter+  | MSendCompact !SendCompact+  | MCompactBlock !CompactBlock+  | MGetBlockTxn !GetBlockTxn+  | MBlockTxn !BlockTxn+  | MGetCFilters !GetCFilters+  | MCFilter !CFilter+  | MGetCFHeaders !GetCFHeaders+  | MCFHeaders !CFHeaders+  | MGetCFCheckpt !GetCFCheckpt+  | MCFCheckpt !CFCheckpt+  | MWtxidRelay+  | MSendTxRcncl !SendTxRcncl+  | -- | a message of a command this library does not know, with its payload+    MOther !ByteString !ByteString+  deriving (Eq, Show, Generic, NFData)  -- | Get 'MessageCommand' assocated with a message. msgType :: Message -> MessageCommand-msgType (MVersion _)     = MCVersion-msgType MVerAck          = MCVerAck-msgType (MAddr _)        = MCAddr-msgType (MInv _)         = MCInv-msgType (MGetData _)     = MCGetData-msgType (MNotFound _)    = MCNotFound-msgType (MGetBlocks _)   = MCGetBlocks-msgType (MGetHeaders _)  = MCGetHeaders-msgType (MTx _)          = MCTx-msgType (MBlock _)       = MCBlock+msgType (MVersion _) = MCVersion+msgType MVerAck = MCVerAck+msgType (MAddr _) = MCAddr+msgType (MAddrV2 _) = MCAddrV2+msgType MSendAddrV2 = MCSendAddrV2+msgType (MInv _) = MCInv+msgType (MGetData _) = MCGetData+msgType (MNotFound _) = MCNotFound+msgType (MGetBlocks _) = MCGetBlocks+msgType (MGetHeaders _) = MCGetHeaders+msgType (MTx _) = MCTx+msgType (MBlock _) = MCBlock msgType (MMerkleBlock _) = MCMerkleBlock-msgType (MHeaders _)     = MCHeaders+msgType (MHeaders _) = MCHeaders+msgType MGetAddr = MCGetAddr+msgType (MFilterLoad _) = MCFilterLoad+msgType (MFilterAdd _) = MCFilterAdd+msgType MFilterClear = MCFilterClear+msgType (MPing _) = MCPing+msgType (MPong _) = MCPong+msgType (MAlert _) = MCAlert+msgType MMempool = MCMempool+msgType (MReject _) = MCReject+msgType MSendHeaders = MCSendHeaders+msgType (MFeeFilter _) = MCFeeFilter+msgType (MSendCompact _) = MCSendCmpct+msgType (MCompactBlock _) = MCCmpctBlock+msgType (MGetBlockTxn _) = MCGetBlockTxn+msgType (MBlockTxn _) = MCBlockTxn+msgType (MGetCFilters _) = MCGetCFilters+msgType (MCFilter _) = MCCFilter+msgType (MGetCFHeaders _) = MCGetCFHeaders+msgType (MCFHeaders _) = MCCFHeaders+msgType (MGetCFCheckpt _) = MCGetCFCheckpt+msgType (MCFCheckpt _) = MCCFCheckpt+msgType MWtxidRelay = MCWtxidRelay+msgType (MSendTxRcncl _) = MCSendTxRcncl+msgType (MOther c _) = MCOther c -msgType (MFilterLoad _)  = MCFilterLoad-msgType (MFilterAdd _)   = MCFilterAdd-msgType MFilterClear     = MCFilterClear-msgType (MPing _)        = MCPing-msgType (MPong _)        = MCPong-msgType (MAlert _)       = MCAlert-msgType MMempool         = MCMempool-msgType (MReject _)      = MCReject-msgType MSendHeaders     = MCSendHeaders-msgType MGetAddr         = MCGetAddr-msgType (MOther c _)     = MCOther c+-- | Largest payload that a message of this command may have on a network.+-- On Bitcoin every message is limited to 4,000,000 bytes, as in Bitcoin Core.+-- On Bitcoin Cash a message is limited to 2 MiB, unless it carries block+-- content: @block@, @cmpctblock@ and @blocktxn@ may be twice the block size.+-- This is @CMessageHeader::IsOversized@ in Bitcoin Cash Node. A node should+-- check the length of a message header against this limit before it buffers+-- the payload.+maxPayloadSize :: Network -> MessageCommand -> Word32+maxPayloadSize net cmd+  | carriesBlock = net.maxMessageSize+  | otherwise = min net.maxMessageSize net.maxProtocolMessageSize+  where+    carriesBlock = cmd `elem` [MCBlock, MCCmpctBlock, MCBlockTxn] --- | Deserializer for network messages.-getMessage :: Network -> Get Message+-- | Read one message of a network. The checks come in this order: the magic+-- bytes, the command name, the length against 'maxPayloadSize' of the+-- command, then the payload is read and its checksum compared. The payload+-- parser sees the declared bytes only. Any failure fails the parse. Bitcoin+-- Core drops a message with a wrong checksum or an invalid command name and+-- keeps the connection, which a caller can do by reading the header itself.+--+-- Transactions, in @tx@, @block@, @cmpctblock@ and @blocktxn@ messages, are+-- read with 'getNetworkTx', and compact block positions are limited as the+-- network requires, see 'getCompactBlock' and 'getGetBlockTxn'.+getMessage :: (MonadGet m) => Network -> m Message getMessage net = do-    (MessageHeader mgc cmd len chk) <- get-    bs <- lookAhead $ getByteString $ fromIntegral len-    unless-        (mgc == getNetworkMagic net)-        (fail $ "get: Invalid network magic bytes: " ++ show mgc)-    unless-        (checkSum32 bs == chk)-        (fail $ "get: Invalid message checksum: " ++ show chk)-    if len > 0-        then isolate (fromIntegral len) $-             case cmd of-                 MCVersion     -> MVersion <$> get-                 MCAddr        -> MAddr <$> get-                 MCInv         -> MInv <$> get-                 MCGetData     -> MGetData <$> get-                 MCNotFound    -> MNotFound <$> get-                 MCGetBlocks   -> MGetBlocks <$> get-                 MCGetHeaders  -> MGetHeaders <$> get-                 MCTx          -> MTx <$> get-                 MCBlock       -> MBlock <$> get-                 MCMerkleBlock -> MMerkleBlock <$> get-                 MCHeaders     -> MHeaders <$> get-                 MCFilterLoad  -> MFilterLoad <$> get-                 MCFilterAdd   -> MFilterAdd <$> get-                 MCPing        -> MPing <$> get-                 MCPong        -> MPong <$> get-                 MCAlert       -> MAlert <$> get-                 MCReject      -> MReject <$> get-                 MCOther c     -> MOther c <$> getByteString (fromIntegral len)-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " should not carry a payload"-        else case cmd of-                 MCGetAddr     -> return MGetAddr-                 MCVerAck      -> return MVerAck-                 MCFilterClear -> return MFilterClear-                 MCMempool     -> return MMempool-                 MCSendHeaders -> return MSendHeaders-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " is expected to carry a payload"+  -- The magic is checked before the rest of the header is looked at.+  mgc <- lookAhead getWord32be+  unless+    (mgc == net.magic)+    (fail $ "get: Invalid network magic bytes: " ++ show mgc)+  (MessageHeader _ cmd len chk) <- deserialize+  let limit = maxPayloadSize net cmd+  when (len > limit) $+    fail $+      "get: Message of "+        ++ show len+        ++ " bytes is larger than the limit of "+        ++ show limit+  payload <- getByteString (fromIntegral len)+  unless+    (checkSum32 payload == chk)+    (fail $ "get: Invalid message checksum: " ++ show chk)+  either fail return (runGetS (getPayload net cmd len) payload) +getPayload :: (MonadGet m) => Network -> MessageCommand -> Word32 -> m Message+getPayload net cmd len =+  case cmd of+    MCVersion -> MVersion <$> deserialize+    MCVerAck -> return MVerAck+    MCAddr -> MAddr <$> deserialize+    MCAddrV2 -> MAddrV2 <$> deserialize+    MCSendAddrV2 -> return MSendAddrV2+    MCInv -> MInv <$> deserialize+    MCGetData -> MGetData <$> deserialize+    MCNotFound -> MNotFound <$> deserialize+    MCGetBlocks -> MGetBlocks <$> deserialize+    MCGetHeaders -> MGetHeaders <$> deserialize+    MCTx -> MTx <$> getNetworkTx net+    MCBlock -> MBlock <$> (Block <$> deserialize <*> getVarIntList (getNetworkTx net))+    MCMerkleBlock -> MMerkleBlock <$> deserialize+    MCHeaders -> MHeaders <$> deserialize+    MCGetAddr -> return MGetAddr+    MCFilterLoad -> MFilterLoad <$> deserialize+    MCFilterAdd -> MFilterAdd <$> deserialize+    MCFilterClear -> return MFilterClear+    MCPing -> MPing <$> deserialize+    MCPong -> MPong <$> deserialize+    MCAlert -> MAlert <$> deserialize+    MCMempool -> return MMempool+    MCReject -> MReject <$> deserialize+    MCSendHeaders -> return MSendHeaders+    MCFeeFilter -> MFeeFilter <$> deserialize+    MCSendCmpct -> MSendCompact <$> deserialize+    MCCmpctBlock -> MCompactBlock <$> getCompactBlock net+    MCGetBlockTxn -> MGetBlockTxn <$> getGetBlockTxn net+    MCBlockTxn -> MBlockTxn <$> getBlockTxn net+    MCGetCFilters -> MGetCFilters <$> deserialize+    MCCFilter -> MCFilter <$> deserialize+    MCGetCFHeaders -> MGetCFHeaders <$> deserialize+    MCCFHeaders -> MCFHeaders <$> deserialize+    MCGetCFCheckpt -> MGetCFCheckpt <$> deserialize+    MCCFCheckpt -> MCFCheckpt <$> deserialize+    MCWtxidRelay -> return MWtxidRelay+    MCSendTxRcncl -> MSendTxRcncl <$> deserialize+    MCOther c -> MOther c <$> getByteString (fromIntegral len)+ -- | Serializer for network messages.-putMessage :: Network -> Putter Message+putMessage :: (MonadPut m) => Network -> Message -> m () putMessage net msg = do-    let (cmd, payload) =-            case msg of-                MVersion m     -> (MCVersion, encode m)-                MVerAck        -> (MCVerAck, BS.empty)-                MAddr m        -> (MCAddr, encode m)-                MInv m         -> (MCInv, encode m)-                MGetData m     -> (MCGetData, encode m)-                MNotFound m    -> (MCNotFound, encode m)-                MGetBlocks m   -> (MCGetBlocks, encode m)-                MGetHeaders m  -> (MCGetHeaders, encode m)-                MTx m          -> (MCTx, encode m)-                MBlock m       -> (MCBlock, encode m)-                MMerkleBlock m -> (MCMerkleBlock, encode m)-                MHeaders m     -> (MCHeaders, encode m)-                MGetAddr       -> (MCGetAddr, BS.empty)-                MFilterLoad m  -> (MCFilterLoad, encode m)-                MFilterAdd m   -> (MCFilterAdd, encode m)-                MFilterClear   -> (MCFilterClear, BS.empty)-                MPing m        -> (MCPing, encode m)-                MPong m        -> (MCPong, encode m)-                MAlert m       -> (MCAlert, encode m)-                MMempool       -> (MCMempool, BS.empty)-                MReject m      -> (MCReject, encode m)-                MSendHeaders   -> (MCSendHeaders, BS.empty)-                MOther c p     -> (MCOther c, p)-        chk = checkSum32 payload-        len = fromIntegral $ BS.length payload-        header = MessageHeader (getNetworkMagic net) cmd len chk-    put header-    putByteString payload+  let (cmd, payload) =+        case msg of+          MVersion m -> (MCVersion, runPutS $ serialize m)+          MVerAck -> (MCVerAck, B.empty)+          MAddr m -> (MCAddr, runPutS $ serialize m)+          MAddrV2 m -> (MCAddrV2, runPutS $ serialize m)+          MSendAddrV2 -> (MCSendAddrV2, B.empty)+          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)+          MFeeFilter m -> (MCFeeFilter, runPutS $ serialize m)+          MSendCompact m -> (MCSendCmpct, runPutS $ serialize m)+          MCompactBlock m -> (MCCmpctBlock, runPutS $ serialize m)+          MGetBlockTxn m -> (MCGetBlockTxn, runPutS $ serialize m)+          MBlockTxn m -> (MCBlockTxn, runPutS $ serialize m)+          MGetCFilters m -> (MCGetCFilters, runPutS $ serialize m)+          MCFilter m -> (MCCFilter, runPutS $ serialize m)+          MGetCFHeaders m -> (MCGetCFHeaders, runPutS $ serialize m)+          MCFHeaders m -> (MCCFHeaders, runPutS $ serialize m)+          MGetCFCheckpt m -> (MCGetCFCheckpt, runPutS $ serialize m)+          MCFCheckpt m -> (MCCFCheckpt, runPutS $ serialize m)+          MWtxidRelay -> (MCWtxidRelay, B.empty)+          MSendTxRcncl m -> (MCSendTxRcncl, runPutS $ serialize m)+          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,21 +1,21 @@-{-|-Module      : Haskoin.Script-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module provides functions for parsing and evaluating bitcoin-transaction scripts. Data types are provided for building and-deconstructing all of the standard input and output script types.--}+-- |+-- Module      : Haskoin.Script+-- 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+  ( module Common,+    module Standard,+    module SigHash,+  )+where -import           Haskoin.Script.Common   as Common-import           Haskoin.Script.SigHash  as SigHash-import           Haskoin.Script.Standard as Standard+import Haskoin.Script.Common as Common+import Haskoin.Script.SigHash as SigHash+import Haskoin.Script.Standard as Standard
src/Haskoin/Script/Common.hs view
@@ -1,578 +1,650 @@-{-# 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.ByteString (ByteString)-import qualified Data.ByteString as B-import           Data.Hashable-import           Data.Serialize  as S-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 Serialize Script where-    get =-        Script <$> getScriptOps-      where-        getScriptOps = do-            empty <- isEmpty-            if empty-                then return []-                else (:) <$> get <*> getScriptOps--    put (Script ops) = forM_ ops put---- | Data type representing the type of an OP_PUSHDATA opcode.-data PushDataType-    =-      -- | next opcode bytes is data to be pushed-      OPCODE-      -- | next byte contains number of bytes of data to be pushed-    | OPDATA1-      -- | next two bytes contains number of bytes to be pushed-    | OPDATA2-      -- | next four bytes contains the number of bytes to be pushed-    | OPDATA4-    deriving (Show, Read, Eq, Generic, Hashable, NFData)---- | Data type representing an operator allowed inside a 'Script'.-data ScriptOp-      -- Pushing Data-    = OP_PUSHDATA !ByteString-                  !PushDataType-    | OP_0-    | OP_1NEGATE-    | OP_RESERVED-    | OP_1-    | OP_2-    | OP_3-    | OP_4-    | OP_5-    | OP_6-    | OP_7-    | OP_8-    | OP_9-    | OP_10-    | OP_11-    | OP_12-    | OP_13-    | OP_14-    | OP_15-    | OP_16-      -- Flow control-    | OP_NOP-    | OP_VER -- reserved-    | OP_IF-    | OP_NOTIF-    | OP_VERIF -- resreved-    | OP_VERNOTIF -- reserved-    | OP_ELSE-    | OP_ENDIF-    | OP_VERIFY-    | OP_RETURN-      -- Stack operations-    | OP_TOALTSTACK-    | OP_FROMALTSTACK-    | OP_IFDUP-    | OP_DEPTH-    | OP_DROP-    | OP_DUP-    | OP_NIP-    | OP_OVER-    | OP_PICK-    | OP_ROLL-    | OP_ROT-    | OP_SWAP-    | OP_TUCK-    | OP_2DROP-    | OP_2DUP-    | OP_3DUP-    | OP_2OVER-    | OP_2ROT-    | OP_2SWAP-      -- Splice-    | OP_CAT-    | OP_SUBSTR-    | OP_LEFT-    | OP_RIGHT-    | OP_SIZE-      -- Bitwise logic-    | OP_INVERT-    | OP_AND-    | OP_OR-    | OP_XOR-    | OP_EQUAL-    | OP_EQUALVERIFY-    | OP_RESERVED1-    | OP_RESERVED2-      -- Arithmetic-    | OP_1ADD-    | OP_1SUB-    | OP_2MUL-    | OP_2DIV-    | OP_NEGATE-    | OP_ABS-    | OP_NOT-    | OP_0NOTEQUAL-    | OP_ADD-    | OP_SUB-    | OP_MUL-    | OP_DIV-    | OP_MOD-    | OP_LSHIFT-    | OP_RSHIFT-    | OP_BOOLAND-    | OP_BOOLOR-    | OP_NUMEQUAL-    | OP_NUMEQUALVERIFY-    | OP_NUMNOTEQUAL-    | OP_LESSTHAN-    | OP_GREATERTHAN-    | OP_LESSTHANOREQUAL-    | OP_GREATERTHANOREQUAL-    | OP_MIN-    | OP_MAX-    | OP_WITHIN-      -- Crypto-    | OP_RIPEMD160-    | OP_SHA1-    | OP_SHA256-    | OP_HASH160-    | OP_HASH256-    | OP_CODESEPARATOR-    | OP_CHECKSIG-    | OP_CHECKSIGVERIFY-    | OP_CHECKMULTISIG-    | OP_CHECKMULTISIGVERIFY-      -- Expansion-    | OP_NOP1-    | OP_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 Serialize ScriptOp where-    get = go =<< (fromIntegral <$> getWord8)-      where-        go op-            | op == 0x00 = return OP_0-            | op <= 0x4b = do-                payload <- getByteString (fromIntegral op)-                return $ OP_PUSHDATA payload OPCODE-            | op == 0x4c = do-                len  <- getWord8-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA1-            | op == 0x4d = do-                len  <- getWord16le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA2-            | op == 0x4e = do-                len  <- getWord32le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA4--            | op == 0x4f = return OP_1NEGATE-            | op == 0x50 = return OP_RESERVED-            | op == 0x51 = return OP_1-            | op == 0x52 = return OP_2-            | op == 0x53 = return OP_3-            | op == 0x54 = return OP_4-            | op == 0x55 = return OP_5-            | op == 0x56 = return OP_6-            | op == 0x57 = return OP_7-            | op == 0x58 = return OP_8-            | op == 0x59 = return OP_9-            | op == 0x5a = return OP_10-            | op == 0x5b = return OP_11-            | op == 0x5c = return OP_12-            | op == 0x5d = return OP_13-            | op == 0x5e = return OP_14-            | op == 0x5f = return OP_15-            | op == 0x60 = return OP_16--            -- Flow control-            | op == 0x61 = return OP_NOP-            | op == 0x62 = return OP_VER        -- reserved-            | op == 0x63 = return OP_IF-            | op == 0x64 = return OP_NOTIF-            | op == 0x65 = return OP_VERIF      -- reserved-            | op == 0x66 = return OP_VERNOTIF   -- reserved-            | op == 0x67 = return OP_ELSE-            | op == 0x68 = return OP_ENDIF-            | op == 0x69 = return OP_VERIFY-            | op == 0x6a = return OP_RETURN--            -- Stack-            | op == 0x6b = return OP_TOALTSTACK-            | op == 0x6c = return OP_FROMALTSTACK-            | op == 0x6d = return OP_2DROP-            | op == 0x6e = return OP_2DUP-            | op == 0x6f = return OP_3DUP-            | op == 0x70 = return OP_2OVER-            | op == 0x71 = return OP_2ROT-            | op == 0x72 = return OP_2SWAP-            | op == 0x73 = return OP_IFDUP-            | op == 0x74 = return OP_DEPTH-            | op == 0x75 = return OP_DROP-            | op == 0x76 = return OP_DUP-            | op == 0x77 = return OP_NIP-            | op == 0x78 = return OP_OVER-            | op == 0x79 = return OP_PICK-            | op == 0x7a = return OP_ROLL-            | op == 0x7b = return OP_ROT-            | op == 0x7c = return OP_SWAP-            | op == 0x7d = return OP_TUCK--            -- Splice-            | op == 0x7e = return OP_CAT-            | op == 0x7f = return OP_SUBSTR-            | op == 0x80 = return OP_LEFT-            | op == 0x81 = return OP_RIGHT-            | op == 0x82 = return OP_SIZE--            -- Bitwise logic-            | op == 0x83 = return OP_INVERT-            | op == 0x84 = return OP_AND-            | op == 0x85 = return OP_OR-            | op == 0x86 = return OP_XOR-            | op == 0x87 = return OP_EQUAL-            | op == 0x88 = return OP_EQUALVERIFY-            | op == 0x89 = return OP_RESERVED1-            | op == 0x8a = return OP_RESERVED2--            -- Arithmetic-            | op == 0x8b = return OP_1ADD-            | op == 0x8c = return OP_1SUB-            | op == 0x8d = return OP_2MUL-            | op == 0x8e = return OP_2DIV-            | op == 0x8f = return OP_NEGATE-            | op == 0x90 = return OP_ABS-            | op == 0x91 = return OP_NOT-            | op == 0x92 = return OP_0NOTEQUAL-            | op == 0x93 = return OP_ADD-            | op == 0x94 = return OP_SUB-            | op == 0x95 = return OP_MUL-            | op == 0x96 = return OP_DIV-            | op == 0x97 = return OP_MOD-            | op == 0x98 = return OP_LSHIFT-            | op == 0x99 = return OP_RSHIFT-            | op == 0x9a = return OP_BOOLAND-            | op == 0x9b = return OP_BOOLOR-            | op == 0x9c = return OP_NUMEQUAL-            | op == 0x9d = return OP_NUMEQUALVERIFY-            | op == 0x9e = return OP_NUMNOTEQUAL-            | op == 0x9f = return OP_LESSTHAN-            | op == 0xa0 = return OP_GREATERTHAN-            | op == 0xa1 = return OP_LESSTHANOREQUAL-            | op == 0xa2 = return OP_GREATERTHANOREQUAL-            | op == 0xa3 = return OP_MIN-            | op == 0xa4 = return OP_MAX-            | op == 0xa5 = return OP_WITHIN--            -- Crypto-            | op == 0xa6 = return OP_RIPEMD160-            | op == 0xa7 = return OP_SHA1-            | op == 0xa8 = return OP_SHA256-            | op == 0xa9 = return OP_HASH160-            | op == 0xaa = return OP_HASH256-            | op == 0xab = return OP_CODESEPARATOR-            | op == 0xac = return OP_CHECKSIG-            | op == 0xad = return OP_CHECKSIGVERIFY-            | op == 0xae = return OP_CHECKMULTISIG-            | op == 0xaf = return OP_CHECKMULTISIGVERIFY--            -- More NOPs-            | op == 0xb0 = return OP_NOP1-            | op == 0xb1 = return OP_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--    put op = case op of--        (OP_PUSHDATA payload optype)-> do-            let len = B.length payload-            case optype of-                OPCODE -> do-                    unless (len <= 0x4b) $-                        error "OP_PUSHDATA OPCODE: Payload size too big"-                    putWord8 $ fromIntegral len-                OPDATA1 -> do-                    unless (len <= 0xff) $-                        error "OP_PUSHDATA OPDATA1: Payload size too big"-                    putWord8 0x4c-                    putWord8 $ fromIntegral len-                OPDATA2 -> do-                    unless (len <= 0xffff) $-                        error "OP_PUSHDATA OPDATA2: Payload size too big"-                    putWord8 0x4d-                    putWord16le $ fromIntegral len-                OPDATA4 -> do-                    unless (len <= 0x7fffffff) $-                        error "OP_PUSHDATA OPDATA4: Payload size too big"-                    putWord8 0x4e-                    putWord32le $ fromIntegral len-            putByteString payload--        -- Constants-        OP_0                 -> putWord8 0x00-        OP_1NEGATE           -> putWord8 0x4f-        OP_RESERVED          -> putWord8 0x50-        OP_1                 -> putWord8 0x51-        OP_2                 -> putWord8 0x52-        OP_3                 -> putWord8 0x53-        OP_4                 -> putWord8 0x54-        OP_5                 -> putWord8 0x55-        OP_6                 -> putWord8 0x56-        OP_7                 -> putWord8 0x57-        OP_8                 -> putWord8 0x58-        OP_9                 -> putWord8 0x59-        OP_10                -> putWord8 0x5a-        OP_11                -> putWord8 0x5b-        OP_12                -> putWord8 0x5c-        OP_13                -> putWord8 0x5d-        OP_14                -> putWord8 0x5e-        OP_15                -> putWord8 0x5f-        OP_16                -> putWord8 0x60--        -- Crypto Constants-        OP_PUBKEY            -> putWord8 0xfe-        OP_PUBKEYHASH        -> putWord8 0xfd--        -- Invalid Opcodes-        (OP_INVALIDOPCODE x) -> putWord8 x--        -- Flow Control-        OP_NOP               -> putWord8 0x61-        OP_VER               -> putWord8 0x62-        OP_IF                -> putWord8 0x63-        OP_NOTIF             -> putWord8 0x64-        OP_VERIF             -> putWord8 0x65-        OP_VERNOTIF          -> putWord8 0x66-        OP_ELSE              -> putWord8 0x67-        OP_ENDIF             -> putWord8 0x68-        OP_VERIFY            -> putWord8 0x69-        OP_RETURN            -> putWord8 0x6a--        -- Stack Operations-        OP_TOALTSTACK        -> putWord8 0x6b-        OP_FROMALTSTACK      -> putWord8 0x6c-        OP_2DROP             -> putWord8 0x6d-        OP_2DUP              -> putWord8 0x6e-        OP_3DUP              -> putWord8 0x6f-        OP_2OVER             -> putWord8 0x70-        OP_2ROT              -> putWord8 0x71-        OP_2SWAP             -> putWord8 0x72-        OP_IFDUP             -> putWord8 0x73-        OP_DEPTH             -> putWord8 0x74-        OP_DROP              -> putWord8 0x75-        OP_DUP               -> putWord8 0x76-        OP_NIP               -> putWord8 0x77-        OP_OVER              -> putWord8 0x78-        OP_PICK              -> putWord8 0x79-        OP_ROLL              -> putWord8 0x7a-        OP_ROT               -> putWord8 0x7b-        OP_SWAP              -> putWord8 0x7c-        OP_TUCK              -> putWord8 0x7d--        -- Splice-        OP_CAT               -> putWord8 0x7e-        OP_SUBSTR            -> putWord8 0x7f-        OP_LEFT              -> putWord8 0x80-        OP_RIGHT             -> putWord8 0x81-        OP_SIZE              -> putWord8 0x82--        -- Bitwise Logic-        OP_INVERT            -> putWord8 0x83-        OP_AND               -> putWord8 0x84-        OP_OR                -> putWord8 0x85-        OP_XOR               -> putWord8 0x86-        OP_EQUAL             -> putWord8 0x87-        OP_EQUALVERIFY       -> putWord8 0x88-        OP_RESERVED1         -> putWord8 0x89-        OP_RESERVED2         -> putWord8 0x8a--        -- Arithmetic-        OP_1ADD              -> putWord8 0x8b-        OP_1SUB              -> putWord8 0x8c-        OP_2MUL              -> putWord8 0x8d-        OP_2DIV              -> putWord8 0x8e-        OP_NEGATE            -> putWord8 0x8f-        OP_ABS               -> putWord8 0x90-        OP_NOT               -> putWord8 0x91-        OP_0NOTEQUAL         -> putWord8 0x92-        OP_ADD               -> putWord8 0x93-        OP_SUB               -> putWord8 0x94-        OP_MUL               -> putWord8 0x95-        OP_DIV               -> putWord8 0x96-        OP_MOD               -> putWord8 0x97-        OP_LSHIFT            -> putWord8 0x98-        OP_RSHIFT            -> putWord8 0x99-        OP_BOOLAND           -> putWord8 0x9a-        OP_BOOLOR            -> putWord8 0x9b-        OP_NUMEQUAL          -> putWord8 0x9c-        OP_NUMEQUALVERIFY    -> putWord8 0x9d-        OP_NUMNOTEQUAL       -> putWord8 0x9e-        OP_LESSTHAN          -> putWord8 0x9f-        OP_GREATERTHAN       -> putWord8 0xa0-        OP_LESSTHANOREQUAL   -> putWord8 0xa1-        OP_GREATERTHANOREQUAL-> putWord8 0xa2-        OP_MIN               -> putWord8 0xa3-        OP_MAX               -> putWord8 0xa4-        OP_WITHIN            -> putWord8 0xa5--        -- Crypto-        OP_RIPEMD160           -> putWord8 0xa6-        OP_SHA1                -> putWord8 0xa7-        OP_SHA256              -> putWord8 0xa8-        OP_HASH160             -> putWord8 0xa9-        OP_HASH256             -> putWord8 0xaa-        OP_CODESEPARATOR       -> putWord8 0xab-        OP_CHECKSIG            -> putWord8 0xac-        OP_CHECKSIGVERIFY      -> putWord8 0xad-        OP_CHECKMULTISIG       -> putWord8 0xae-        OP_CHECKMULTISIGVERIFY -> putWord8 0xaf--        -- More NOPs-        OP_NOP1                -> putWord8 0xb0-        OP_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----- | Check whether opcode is only data.-isPushOp :: ScriptOp -> Bool-isPushOp op = case op of-    OP_PUSHDATA _ _ -> True-    OP_0            -> True-    OP_1NEGATE      -> True-    OP_1            -> True-    OP_2            -> True-    OP_3            -> True-    OP_4            -> True-    OP_5            -> True-    OP_6            -> True-    OP_7            -> True-    OP_8            -> True-    OP_9            -> True-    OP_10           -> True-    OP_11           -> True-    OP_12           -> True-    OP_13           -> True-    OP_14           -> True-    OP_15           -> True-    OP_16           -> True-    _               -> False---- | Optimally encode data using one of the 4 types of data pushing opcodes.-opPushData :: ByteString -> ScriptOp-opPushData bs-    | len <= 0x4b       = OP_PUSHDATA bs OPCODE-    | len <= 0xff       = OP_PUSHDATA bs OPDATA1-    | len <= 0xffff     = OP_PUSHDATA bs OPDATA2-    | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4-    | otherwise         = error "opPushData: payload size too big"-  where-    len = B.length bs---- | Transforms integers @[1 .. 16]@ to 'ScriptOp' @[OP_1 .. OP_16]@.-intToScriptOp :: Int -> ScriptOp-intToScriptOp i-    | i `elem` [1 .. 16] = op-    | otherwise = err-  where-    op = either (const err) id . S.decode . B.singleton . fromIntegral $ i + 0x50-    err = error $ "intToScriptOp: Invalid integer " ++ show i---- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions--- fails for other values of 'ScriptOp'-scriptOpToInt :: ScriptOp -> Either String Int-scriptOpToInt s-    | res `elem` [1..16] = return res-    | otherwise          = Left $ "scriptOpToInt: invalid opcode " ++ show s-  where-    res = fromIntegral (B.head $ S.encode s) - 0x50+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# 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 (..),+    pattern OP_CHECKSIGADD,+    Script (..),+    PushDataType (..),+    isPushOp,+    pushOpData,+    isMinimalPush,+    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.Hashable+import Data.Maybe (isJust)+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'.+--+-- An 'OP_PUSHDATA' must carry a payload that fits its 'PushDataType': at most+-- 75 bytes for 'OPCODE', 255 for 'OPDATA1', 65535 for 'OPDATA2'. Serializing+-- a push that breaks this invariant fails with an 'error' call, because no+-- byte sequence can represent it. Build pushes with 'opPushData' to get the+-- minimal encoding.+--+-- 'OP_INVALIDOPCODE' holds a byte that has no opcode of its own. The parser+-- only builds it for such a byte. Built by hand with the byte of a defined+-- opcode, such as 0xac, it is a second value for that opcode: it is not equal+-- to 'OP_CHECKSIG', it serializes to the same byte, and it reads back as+-- 'OP_CHECKSIG'. Code that looks for an opcode, such as the removal of+-- 'OP_CODESEPARATOR' in 'Haskoin.Script.SigHash.txSigHash', does not see it.+-- Do not build one for a byte that has a constructor.+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. Opcode 0xba is 'OP_CHECKSIGADD' in+    -- Tapscript (BIP 342), see the pattern synonym of that name.+    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)++-- | Opcode 0xba as Tapscript (BIP 342) names it. It is the same opcode as+-- 'OP_CHECKDATASIG' of Bitcoin Cash, so a value built with either name is+-- equal to the other and serializes to the same byte. 'Show' prints the+-- Bitcoin Cash name.+pattern OP_CHECKSIGADD :: ScriptOp+pattern OP_CHECKSIGADD = OP_CHECKDATASIG++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 <= 0xffffffff) $+            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: 'OP_PUSHDATA', 'OP_0', 'OP_1NEGATE' or+-- one of 'OP_1' to 'OP_16'.+isPushOp :: ScriptOp -> Bool+isPushOp = isJust . pushOpData++-- | The data that a push opcode puts on the stack. 'OP_0' pushes the empty+-- string, 'OP_1' to 'OP_16' push one byte with that value, 'OP_1NEGATE'+-- pushes the byte 0x81, and 'OP_PUSHDATA' pushes its payload. Other opcodes+-- push nothing and give 'Nothing'.+pushOpData :: ScriptOp -> Maybe ByteString+pushOpData op = case op of+  OP_PUSHDATA bs _ -> Just bs+  OP_0 -> Just B.empty+  OP_1NEGATE -> Just (B.singleton 0x81)+  _ -> B.singleton . fromIntegral <$> either (const Nothing) Just (scriptOpToInt op)++-- | Is the opcode a push in its shortest encoding? This is the rule of+-- @CheckMinimalPush@ in Bitcoin Core, which the @MINIMALDATA@ policy flag+-- enforces: the empty string is 'OP_0', one byte from 1 to 16 is the matching+-- 'OP_1' to 'OP_16', the byte 0x81 is 'OP_1NEGATE', and other data uses the+-- smallest 'PushDataType' that holds it. An opcode that pushes nothing is not+-- a minimal push.+isMinimalPush :: ScriptOp -> Bool+isMinimalPush op = maybe False (\bs -> opPushData bs == op) (pushOpData op)++-- | Encode data as the push opcode of its minimal encoding, see+-- 'isMinimalPush'.+opPushData :: ByteString -> ScriptOp+opPushData bs+  | len == 0 = OP_0+  | len == 1, Right op <- intToScriptOp (fromIntegral (B.head bs)) = op+  | bs == B.singleton 0x81 = OP_1NEGATE+  | 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]@. Other+-- integers give a 'Left'.+intToScriptOp :: Int -> Either String ScriptOp+intToScriptOp i = case i of+  1 -> Right OP_1+  2 -> Right OP_2+  3 -> Right OP_3+  4 -> Right OP_4+  5 -> Right OP_5+  6 -> Right OP_6+  7 -> Right OP_7+  8 -> Right OP_8+  9 -> Right OP_9+  10 -> Right OP_10+  11 -> Right OP_11+  12 -> Right OP_12+  13 -> Right OP_13+  14 -> Right OP_14+  15 -> Right OP_15+  16 -> Right OP_16+  _ -> Left $ "intToScriptOp: invalid integer " ++ show i++-- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. Other+-- opcodes give a 'Left'.+scriptOpToInt :: ScriptOp -> Either String Int+scriptOpToInt op = case op of+  OP_1 -> Right 1+  OP_2 -> Right 2+  OP_3 -> Right 3+  OP_4 -> Right 4+  OP_5 -> Right 5+  OP_6 -> Right 6+  OP_7 -> Right 7+  OP_8 -> Right 8+  OP_9 -> Right 9+  OP_10 -> Right 10+  OP_11 -> Right 11+  OP_12 -> Right 12+  OP_13 -> Right 13+  OP_14 -> Right 14+  OP_15 -> Right 15+  OP_16 -> Right 16+  _ -> Left $ "scriptOpToInt: invalid opcode " ++ show op
src/Haskoin/Script/SigHash.hs view
@@ -1,91 +1,142 @@-{-# LANGUAGE DeriveGeneric              #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings          #-}-{-|-Module      : Haskoin.Script.SigHash-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -Transaction signatures and related functions.--}+-- |+-- 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-    , hasForkIdFlag-    , setAnyoneCanPayFlag-    , setForkIdFlag-    , isSigHashAll-    , isSigHashNone-    , isSigHashSingle-    , isSigHashUnknown-    , sigHashAddForkId-    , sigHashGetForkId-    , sigHashAddNetworkId-    , txSigHash-    , txSigHashForkId-    , TxSignature(..)-    , encodeTxSig-    , decodeTxSig-    ) where+  ( -- * Script Signatures+    SigHash (..),+    SigHashFlag (..),+    sigHashAll,+    sigHashNone,+    sigHashSingle,+    sigHashDefault,+    anyoneCanPay,+    hasForkIdFlag,+    setAnyoneCanPay,+    setForkIdFlag,+    isSigHashAll,+    isSigHashNone,+    isSigHashSingle,+    isSigHashUnknown,+    sigHashAddForkId,+    sigHashGetForkId,+    sigHashAddNetworkId,+    txSigHash,+    txSigHashForkId,+    txSigHashCashToken,+    splitTokenPrefix,+    TxSignature (..),+    decodeTxSig,+    encodeTxSig,+  )+where -import           Control.DeepSeq-import           Control.Monad-import qualified Data.Aeson                 as J-import           Data.Bits-import qualified Data.ByteString            as BS-import           Data.Hashable-import           Data.Maybe-import           Data.Scientific-import           Data.Serialize-import           Data.Word-import           GHC.Generics               (Generic)-import           Haskoin.Constants-import           Haskoin.Crypto.Hash-import           Haskoin.Network.Common-import           Haskoin.Crypto-import           Haskoin.Script.Common-import           Haskoin.Transaction.Common-import           Haskoin.Util+import Control.DeepSeq+import Control.Monad+import Crypto.Secp256k1+import Data.Aeson+import Data.Bits+import Data.Bool (bool)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable+import Data.Maybe+import Data.Scientific+import Data.Word+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Signature+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Transaction.Common+import Haskoin.Util  -- | Constant representing a SIGHASH flag that controls what is being signed. data SigHashFlag-    = SIGHASH_ALL-      -- ^ sign all outputs-    | SIGHASH_NONE-      -- ^ sign no outputs-    | SIGHASH_SINGLE-      -- ^ sign the output index corresponding to the input-    | SIGHASH_FORKID-      -- ^ replay protection for Bitcoin Cash transactions-    | SIGHASH_ANYONECANPAY-      -- ^ new inputs can be added-    deriving (Eq, Ord, Show, Read, Generic)+  = -- | 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, Bounded)  instance NFData SigHashFlag  instance Hashable SigHashFlag +-- | 'fromEnum' gives the value of the flag in a hash type byte, and 'toEnum'+-- reads it back. The values are not consecutive, so the functions that list+-- flags walk the constructors in their order instead of counting: @[minBound+-- ..]@ is the five flags. 'toEnum' of a number that is no flag, 'succ' of the+-- last flag and 'pred' of the first raise an error, as they do for any+-- enumeration. 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"+  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"+  succ flag+    | flag == maxBound = error "succ: SIGHASH_ANYONECANPAY is the last sighash flag"+    | otherwise = sigHashFlags !! (flagPosition flag + 1)+  pred flag+    | flag == minBound = error "pred: SIGHASH_ALL is the first sighash flag"+    | otherwise = sigHashFlags !! (flagPosition flag - 1)+  enumFrom flag = enumFromTo flag maxBound+  enumFromTo from to = map (sigHashFlags !!) [flagPosition from .. flagPosition to]+  enumFromThen from next+    | next >= from = enumFromThenTo from next maxBound+    | otherwise = enumFromThenTo from next minBound+  enumFromThenTo from next to =+    map (sigHashFlags !!) [flagPosition from, flagPosition next .. flagPosition to] +-- | The flags in the order of their constructors.+sigHashFlags :: [SigHashFlag]+sigHashFlags =+  [ SIGHASH_ALL,+    SIGHASH_NONE,+    SIGHASH_SINGLE,+    SIGHASH_FORKID,+    SIGHASH_ANYONECANPAY+  ]++-- | The position of a flag in 'sigHashFlags'.+flagPosition :: SigHashFlag -> Int+flagPosition flag = length (takeWhile (/= flag) sigHashFlags)+ -- | 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@@ -97,31 +148,26 @@ -- 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-             )+newtype SigHash+  = 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_DEFAULT of BIP 341: the hash type 0, which a Taproot key path+-- signature leaves out and which signs like 'sigHashAll'. It is only valid+-- in Taproot signatures.+sigHashDefault :: SigHash+sigHashDefault = 0+ -- | SIGHASH_NONE as a byte. sigHashNone :: SigHash sigHashNone = fromIntegral $ fromEnum SIGHASH_NONE@@ -147,16 +193,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@@ -170,10 +216,17 @@ isSigHashSingle :: SigHash -> Bool isSigHashSingle = (== sigHashSingle) . (.&. 0x1f) --- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'.+-- | Is the hash type byte one that no node accepts in a signature? This is+-- the test of @IsDefinedHashtypeSignature@ in Bitcoin Core, which the+-- @STRICTENC@ flag enforces: after masking the 'SIGHASH_ANYONECANPAY' and+-- 'SIGHASH_FORKID' bits, the byte must be 'SIGHASH_ALL', 'SIGHASH_NONE' or+-- 'SIGHASH_SINGLE'. Whether the 'SIGHASH_FORKID' bit itself is allowed+-- depends on the network, see 'decodeTxSig'. Only the low byte is looked at,+-- since a fork id may sit in the upper bytes. isSigHashUnknown :: SigHash -> Bool-isSigHashUnknown =-    (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)+isSigHashUnknown sh = base < sigHashAll || base > sigHashSingle+  where+    base = (sh .&. 0xff) .&. complement (sigHashAnyoneCanPay .|. sigHashForkId)  -- | Add a fork id to a 'SigHash'. sigHashAddForkId :: SigHash -> Word32 -> SigHash@@ -182,138 +235,365 @@ -- | 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 sigHashGetForkId (SigHash n) = fromIntegral $ n `shiftR` 8 --- | Computes the hash that will be used for signing a transaction.-txSigHash :: Network-          -> Tx      -- ^ transaction to sign-          -> Script  -- ^ script from output being spent-          -> Word64  -- ^ value of output being spent-          -> Int     -- ^ index of input being signed-          -> SigHash -- ^ what to sign-          -> Hash256 -- ^ hash to be signed+-- | Computes the hash that will be used for signing a transaction input that+-- is not a SegWit input.+--+-- The script is the @scriptCode@ of the input: the output script being spent,+-- or the redeem script for a pay-to-script-hash output. If that script+-- contains 'OP_CODESEPARATOR', the caller must pass the part after the last+-- 'OP_CODESEPARATOR' executed before the signature check, as consensus+-- defines it. Any 'OP_CODESEPARATOR' left in that part is removed here, as+-- @SerializeScriptCode@ does in Bitcoin Core.+--+-- Bitcoin Core makes a second change to the script before it hashes a legacy+-- input: @FindAndDelete@ removes every push of the signature being checked.+-- This function does not do that. The standard output scripts never hold a+-- signature, so nothing is lost for them. A caller that verifies another+-- legacy script must remove the pushes of the signature from the script+-- first.+--+-- The hash type is written as four bytes, as Bitcoin Core writes the+-- @nHashType@ it is given. A signature only carries the low byte, so a+-- 'SigHash' above 0xff gives a digest that no signature can match.+--+-- On a network with a fork id, such as Bitcoin Cash, every signature must use+-- the fork id digest of 'txSigHashForkId', so the 'SigHash' must carry the+-- 'SIGHASH_FORKID' flag and the digest is computed by that function. A+-- 'SigHash' without the flag fails, instead of producing a digest no node+-- accepts.+--+-- Fails when the input index is not an index of the 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+  Either String 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 $-                runPut $ do-                    put newTx-                    putWord32le $ fromIntegral sh+  | i < 0 || i >= length tx.inputs =+      Left "txSigHash: input index out of range"+  | isJust net.sigHashForkId && not (hasForkIdFlag sh) =+      Left "txSigHash: SIGHASH_FORKID is required on this network"+  | 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+      return . 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.+-- | Build transaction inputs for computing sighashes. With+-- 'SIGHASH_ANYONECANPAY' only the signed input remains. With 'SIGHASH_NONE'+-- or 'SIGHASH_SINGLE' the sequence numbers of the other inputs are zero. Any+-- other base type, including the undefined ones, behaves as 'SIGHASH_ALL',+-- as in Bitcoin Core. buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn] buildInputs txins out i sh-    | hasAnyoneCanPayFlag sh =-        [ (txins !! i) { scriptInput = encode out } ]-    | isSigHashAll sh || isSigHashUnknown sh = single-    | otherwise = zipWith noSeq single [0 ..]+  | anyoneCanPay sh = [serialOut (txins !! i)]+  | isSigHashNone sh || isSigHashSingle sh = zipWith noSeq single [0 ..]+  | otherwise = single   where-    emptyIn = map (\ti -> ti { scriptInput = BS.empty }) txins-    single =-        updateIndex i emptyIn $ \ti -> ti { scriptInput = encode out }-    noSeq ti j =-        if i == j-        then ti-        else ti { txInSequence = 0 }+    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.+-- | Build transaction outputs for computing sighashes. 'SIGHASH_NONE' signs+-- no output. 'SIGHASH_SINGLE' signs the output at the input index, with the+-- outputs before it blanked, and gives 'Nothing' when there is no such+-- output. Any other base type signs every output. 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]+  | isSigHashNone sh = return []+  | not (isSigHashSingle sh) = return txos+  | 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.-txSigHashForkId-    :: Network-    -> Tx      -- ^ transaction to sign-    -> Script  -- ^ script from output being spent-    -> Word64  -- ^ value of output being spent-    -> Int     -- ^ index of input being signed-    -> SigHash -- ^ what to sign-    -> Hash256 -- ^ hash to be signed-txSigHashForkId net tx out v i sh =-    doubleSHA256 . runPut $ do-        putWord32le $ txVersion tx-        put hashPrevouts-        put hashSequence-        put $ prevOutput $ txIn tx !! i-        putScript out-        putWord64le v-        putWord32le $ txInSequence $ txIn tx !! i-        put hashOutputs-        putWord32le $ txLockTime tx-        putWord32le $ fromIntegral $ sigHashAddNetworkId net sh+-- | Compute the digest of BIP 143, used for SegWit version 0 inputs on+-- Bitcoin and, with the fork id of the network, for every input on Bitcoin+-- Cash.+--+-- The script is the @scriptCode@ of BIP 143: the pay-to-public-key-hash+-- script of the key for a pay-to-witness-public-key-hash input, or the+-- witness script for a pay-to-witness-script-hash input. If it contains+-- 'OP_CODESEPARATOR', the caller must pass the part after the last one+-- executed before the signature check. Unlike 'txSigHash', nothing is removed+-- from that part, as BIP 143 specifies.+--+-- The last field of the digest is the hash type as four bytes. On a network+-- without a fork id it is the 'SigHash' as given, which is what+-- @SignatureHash@ of Bitcoin Core writes. A signature only carries the low+-- byte, so a 'SigHash' above 0xff gives a digest that no signature can+-- match. On a network with a fork id it is the low byte with the fork id of+-- the network in the three bytes above it.+--+-- What is not supported on Bitcoin Cash, where Bitcoin Cash Node is the+-- reference:+--+-- * @SIGHASH_UTXOS@ (0x20), which adds a hash of all the coins spent to the+--   digest. 'isSigHashUnknown' treats the bit as undefined, so 'decodeTxSig'+--   refuses such a signature.+--+-- * Coins that hold tokens, in this function. Their digest also covers the+--   token data, and their output script starts with the prefix byte 0xef,+--   which 'Haskoin.Script.Standard.decodeOutput' does not read. Their+--   digest is 'txSigHashCashToken', with the prefix that 'splitTokenPrefix'+--   takes off the script.+--+-- * Schnorr signatures as a 'TxSignature'. They are verified by+--   'Haskoin.Crypto.Signature.verifyCashSchnorr', and by+--   'Haskoin.Transaction.Builder.judgeInputs' in an input.+--+-- Fails when the input index is not an index of the transaction.+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+  Either String Hash256+txSigHashForkId net tx = txSigHashCashToken net tx B.empty++-- | 'txSigHashForkId' for a coin of Bitcoin Cash that holds tokens. The+-- digest of such a coin also covers its token prefix, the bytes that+-- 'splitTokenPrefix' takes off the front of its output script, which go in+-- right before the script. With an empty prefix this is 'txSigHashForkId'.+--+-- The outputs of the transaction need nothing done: on the wire the+-- prefix of an output is part of its script field, and so it is in the+-- 'TxOut' that is hashed.+txSigHashCashToken ::+  Network ->+  -- | transaction to sign+  Tx ->+  -- | token prefix of the output being spent, or empty+  ByteString ->+  -- | script from output being spent, without the prefix+  Script ->+  -- | value of output being spent+  Word64 ->+  -- | index of input being signed+  Int ->+  -- | what to sign+  SigHash ->+  -- | hash to be signed+  Either String Hash256+txSigHashCashToken net tx prefix out v i sh+  | i < 0 || i >= length tx.inputs =+      Left "txSigHashForkId: input index out of range"+  | otherwise = Right . doubleSHA256 . runPutS $ do+    putWord32le tx.version+    serialize hashPrevouts+    serialize hashSequence+    serialize (tx.inputs !! i).outpoint+    putByteString prefix+    putScript out+    putWord64le v+    putWord32le (tx.inputs !! i).sequence+    serialize hashOutputs+    putWord32le tx.locktime+    putWord32le $ fromIntegral hashType   where+    hashType = case net.sigHashForkId of+      Just forkId -> sigHashAddForkId sh forkId+      Nothing -> sh     hashPrevouts-        | not $ hasAnyoneCanPayFlag sh =-            doubleSHA256 $ runPut $ mapM_ (put . 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 $ runPut $ 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 $ runPut $ mapM_ put $ txOut tx-        | isSigHashSingle sh && i < length (txOut tx) =-            doubleSHA256 $ encode $ 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 = encode s-        putVarInt $ BS.length encodedScript-        putByteString encodedScript+      let encodedScript = runPutS $ serialize s+      putVarInt $ B.length encodedScript+      putByteString encodedScript     zeros :: Hash256     zeros = "0000000000000000000000000000000000000000000000000000000000000000" +-- | Split the script field of an output of Bitcoin Cash into its token+-- prefix and the output script after it. An output without tokens has an+-- empty prefix. 'Nothing' for a prefix that is not well formed.+--+-- Since May 2023 an output may hold tokens (CashTokens). They are written+-- at the front of its script field: the byte @0xef@, the 32 bytes of the+-- token category, a byte of flags, and after it a commitment with its+-- length and an amount, each when its flag is set. What follows is the+-- output script that locks the coin.+splitTokenPrefix :: ByteString -> Maybe (ByteString, ByteString)+splitTokenPrefix bytes+  | B.null bytes || B.head bytes /= 0xef = Just (B.empty, bytes)+  | B.length bytes < 34 = Nothing+  | flags .&. 0x80 /= 0 = Nothing+  -- tokens of some kind, a commitment only with a non-fungible one, and+  -- a capability of none, mutable or minting, only with one too+  | not hasNft && not hasAmount = Nothing+  | not hasNft && (hasCommitment || capability /= 0) = Nothing+  | capability > 2 = Nothing+  | otherwise = do+      afterCommitment <-+        if hasCommitment+          then do+            (n, rest) <- compact afterFlags+            if n == 0 || fromIntegral (B.length rest) < n then Nothing else Just (B.drop (fromIntegral n) rest)+          else Just afterFlags+      afterAmount <-+        if hasAmount+          then do+            (n, rest) <- compact afterCommitment+            if n == 0 || n > 0x7fffffffffffffff then Nothing else Just rest+          else Just afterCommitment+      Just (B.splitAt (B.length bytes - B.length afterAmount) bytes)+  where+    flags = B.index bytes 33+    afterFlags = B.drop 34 bytes+    hasCommitment = flags .&. 0x40 /= 0+    hasNft = flags .&. 0x20 /= 0+    hasAmount = flags .&. 0x10 /= 0+    capability = flags .&. 0x0f+    -- a number in the compact form of the protocol, in its shortest+    -- encoding only+    compact :: ByteString -> Maybe (Word64, ByteString)+    compact b = case B.uncons b of+      Nothing -> Nothing+      Just (w, rest)+        | w < 0xfd -> Just (fromIntegral w, rest)+        | w == 0xfd -> sized 2 0xfd rest+        | w == 0xfe -> sized 4 0x10000 rest+        | otherwise -> sized 8 0x100000000 rest+    sized k least rest+      | B.length rest < k = Nothing+      | n < least = Nothing+      | otherwise = Just (n, B.drop k rest)+      where+        n = B.foldr (\w acc -> acc `shiftL` 8 .|. fromIntegral w) 0 (B.take k rest)+ -- | 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'.+--+-- This is the form of a signature that nodes relay. The binary and JSON+-- forms, and 'decodeTxSig', apply the three signature rules of the standard+-- script flags of Bitcoin Core, and one rule of the network:+--+-- * @DERSIG@: strict DER, as BIP 66 defines it, with r and s between 1 and+--   the curve order minus 1.+--+-- * @LOW_S@: s is at most half the curve order.+--+-- * @STRICTENC@: the hash type byte is defined ('isSigHashUnknown').+--+-- * The 'SIGHASH_FORKID' flag is set exactly on the networks that have a+--   fork id.+--+-- Nothing may follow the hash type byte.+--+-- Only the first rule is a consensus rule, and only since BIP 66. A block+-- can therefore hold signatures that do not decode: one with a high S, one+-- with an undefined hash type byte, and, before BIP 66 activated, one in a+-- looser DER form. An input with such a signature fails+-- 'Haskoin.Script.Standard.decodeInput', and has no+-- 'Haskoin.Address.inputAddress'. This type is not a parser for the whole+-- chain history. 'Haskoin.Crypto.Signature.decodeStrictSig' reads a+-- signature under BIP 66 alone.+--+-- On Bitcoin Cash, a signature of 64 bytes followed by the hash type byte is+-- a Schnorr signature, which Bitcoin Cash Node accepts in @OP_CHECKSIG@.+-- This type only holds ECDSA signatures, so those do not decode either. 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)) =-    runPut $ putSig sig >> putWord8 (fromIntegral n)+  marshalGet (net, ctx) =+    bool decode empty =<< isEmpty+    where+      empty = return TxSignatureEmpty+      decode = do+        sig <- marshalGet ctx+        unless (isCanonicalHalfOrder ctx sig) $+          fail "Non-canonical signature: S value is unnecessarily high"+        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"+        when (isJust net.sigHashForkId && not (hasForkIdFlag sh)) $+          fail "Non-canonical signature: SIGHASH_FORKID is required on this network"+        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++-- | Serialize a signature with its hash type byte. 'TxSignatureEmpty' is the+-- empty string.+encodeTxSig :: Network -> Ctx -> TxSignature -> ByteString+encodeTxSig net ctx = runPutS . marshalPut (net, ctx)++-- | Parse a signature with its hash type byte, under the rules listed at+-- 'TxSignature': strict DER, a low S, a defined hash type, and the fork id+-- flag that the network asks for. The empty string is 'TxSignatureEmpty'. A+-- signature that is valid by consensus alone does not decode.+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,407 +1,645 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Standard scripts like pay-to-public-key, pay-to-public-key-hash,+-- pay-to-script-hash, pay-to-multisig and corresponding SegWit variants. module Haskoin.Script.Standard-    ( -- * Standard Script Outputs-      ScriptOutput(..)-    , RedeemScript-    , isPayPK-    , isPayPKHash-    , isPayMulSig-    , isPayScriptHash-    , isPayWitnessPKHash-    , isPayWitnessScriptHash-    , isDataCarrier-    , encodeOutput-    , encodeOutputBS-    , decodeOutput-    , decodeOutputBS-    , toP2SH-    , toP2WSH-    , sortMulSig+  ( -- * Standard Script Outputs+    ScriptOutput (..),+    RedeemScript,+    isPayPK,+    isPayPKHash,+    isPayMulSig,+    isPayScriptHash,+    isPayWitness,+    isPayWitnessPKHash,+    isPayWitnessScriptHash,+    isPayScript32Hash,+    isDataCarrier,+    maxMultisigKeys,+    maxRedeemScriptSize,+    maxWitnessScriptSize,+    encodeOutput,+    decodeOutput,+    toP2SH,+    toP2WSH,+    p2shScriptHash,+    p2wshScriptHash,+    sortMulSig,+     -- * Standard Script Inputs-    , ScriptInput(..)-    , SimpleInput(..)-    , encodeInput-    , encodeInputBS-    , decodeInput-    , decodeInputBS-    , isSpendPK-    , isSpendPKHash-    , isSpendMulSig-    , isScriptHashInput-    ) where+    ScriptInput (..),+    SimpleInput (..),+    encodeInput,+    decodeInput,+    isSpendPK,+    isSpendPKHash,+    isSpendMulSig,+    isScriptHashInput,+    isNestedWitnessInput,+  )+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           Data.ByteString        (ByteString)-import qualified Data.ByteString        as BS-import           Data.Function          (on)-import           Data.Hashable-import           Data.List              (sortBy)-import           Data.Maybe             (fromJust, isJust)-import           Data.Serialize         as S-import           Data.Word              (Word8)-import           GHC.Generics           (Generic)-import           Haskoin.Constants-import           Haskoin.Crypto-import           Haskoin.Keys.Common-import           Haskoin.Script.Common-import           Haskoin.Script.SigHash-import           Haskoin.Util+import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (guard, unless)+import Crypto.Secp256k1+import Data.Aeson (ToJSON (..), Value (..), withText)+import Data.Aeson.Encoding (Encoding, text)+import Data.Aeson.Types (Parser)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Function (on)+import Data.Hashable+import Data.List (sortBy)+import Data.Word (Word8)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Util  -- | Data type describing standard transaction output scripts. Output scripts -- provide the conditions that must be fulfilled for someone to spend the funds -- in a transaction output. data ScriptOutput-      -- | pay to public key-    = PayPK { 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 of 1 to 'maxMultisigKeys' keys+    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}+  | -- | witness program of version 1 to 16, from 2 to 40 bytes long+    PayWitness+      { version :: !Word8,+        bytes :: !ByteString+      }+  | -- | pay to a script hash (256-bit)+    PayScript32Hash {hash256 :: !Hash256}+  | -- | provably unspendable data carrier: @OP_RETURN@ followed by the data+    -- of each push, which may be no push at all+    DataCarrier {pushes :: ![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-isPayPK _         = False+isPayPK _ = False  -- | Is script a pay-to-pub-key-hash output? isPayPKHash :: ScriptOutput -> Bool isPayPKHash (PayPKHash _) = True-isPayPKHash _             = False+isPayPKHash _ = False  -- | Is script a pay-to-multi-sig output? isPayMulSig :: ScriptOutput -> Bool isPayMulSig (PayMulSig _ _) = True-isPayMulSig _               = False+isPayMulSig _ = False  -- | Is script a pay-to-script-hash output? isPayScriptHash :: ScriptOutput -> Bool isPayScriptHash (PayScriptHash _) = True-isPayScriptHash _                 = False+isPayScriptHash _ = False  -- | Is script a pay-to-witness-pub-key-hash output? isPayWitnessPKHash :: ScriptOutput -> Bool isPayWitnessPKHash (PayWitnessPKHash _) = True-isPayWitnessPKHash _                    = False+isPayWitnessPKHash _ = False  -- | Is script a pay-to-witness-script-hash output? isPayWitnessScriptHash :: ScriptOutput -> Bool isPayWitnessScriptHash (PayWitnessScriptHash _) = True-isPayWitnessScriptHash _                        = False+isPayWitnessScriptHash _ = False  -- | Is script paying to a different type of witness address? isPayWitness :: ScriptOutput -> Bool isPayWitness (PayWitness _ _) = True-isPayWitness _                = False+isPayWitness _ = False +-- | Is script paying to 256-bit script hash?+isPayScript32Hash :: ScriptOutput -> Bool+isPayScript32Hash (PayScript32Hash _) = True+isPayScript32Hash _ = False+ -- | Is script a data carrier output? isDataCarrier :: ScriptOutput -> Bool isDataCarrier (DataCarrier _) = True-isDataCarrier _               = False+isDataCarrier _ = False +-- | The most keys a multisig output can have: @MAX_PUBKEYS_PER_MULTISIG@ of+-- Bitcoin Core.+--+-- Twenty keys fit in a bare multisig output and in a pay-to-witness-script-hash+-- output. They do not fit in a pay-to-script-hash output, whose redeem script+-- is limited to 'maxRedeemScriptSize' bytes. That is 15 compressed keys+-- (3 + 15 × 34 = 513 bytes) or 7 uncompressed keys (3 + 7 × 66 = 465 bytes).+maxMultisigKeys :: Int+maxMultisigKeys = 20++-- | The largest redeem script a pay-to-script-hash output can have, in bytes.+-- The redeem script is one push in the input script, and consensus limits a+-- push to 520 bytes: @MAX_SCRIPT_ELEMENT_SIZE@ of Bitcoin Core. Coins sent to+-- the hash of a larger script can never be spent.+maxRedeemScriptSize :: Int+maxRedeemScriptSize = 520++-- | The largest witness script a pay-to-witness-script-hash output can have,+-- in bytes: @MAX_SCRIPT_SIZE@ of Bitcoin Core, 10,000. The interpreter fails+-- on a larger script, so coins sent to its hash can never be spent.+--+-- This is the consensus limit. The relay policy of Bitcoin Core is stricter:+-- it only relays a spend whose witness script is at most 3,600 bytes+-- (@MAX_STANDARD_P2WSH_SCRIPT_SIZE@). This library does not enforce that.+maxWitnessScriptSize :: Int+maxWitnessScriptSize = 10000+ -- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the -- script is not recognized as any of the standard output types.-decodeOutput :: Script -> Either String ScriptOutput-decodeOutput s = case scriptOps s of-    -- Pay to PubKey-    [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs-    -- Pay to PubKey Hash-    [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->-        PayPKHash <$> S.decode bs-    -- Pay to Script Hash-    [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->-        PayScriptHash <$> S.decode  bs-    -- Pay to Witness-    [OP_0, OP_PUSHDATA bs OPCODE]-      | BS.length bs == 20 -> PayWitnessPKHash     <$> S.decode bs-      | BS.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs-    -- 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+--+-- The templates are matched on their exact bytes, as Bitcoin Core matches+-- them: the hash or key of a pay-to-public-key, pay-to-public-key-hash,+-- pay-to-script-hash or witness output must be a direct push ('OPCODE'). A+-- script that writes it with @OP_PUSHDATA1@ is not that output type. By+-- consensus it is not a pay-to-script-hash or witness output at all, and+-- anyone can spend it, so it must not be shown as the address it looks like.+-- The counts of a multisig output are @OP_1@ to @OP_16@, or a one-byte push+-- for 17 to 'maxMultisigKeys', and a key must fill its push exactly.+--+-- A 'ScriptOutput' describes what a script does, not its bytes. Two kinds of+-- script decode to a value that 'encodeOutput' writes back as other bytes:+-- a multisig whose keys are not direct pushes, and a data carrier whose data+-- is not pushed in the shortest way. Bitcoin Core gives those the same type+-- (@multisig@ and @nulldata@). So never hash or sign the result of+-- @encodeOutput@ in place of a script that came from somewhere else. Hash+-- and sign the bytes of that script. 'decodeInput' refuses a redeem script+-- that would not come back as the same bytes.+--+-- Three kinds of script that the @Solver@ of Bitcoin Core classifies are not+-- decoded here, because a 'PublicKey' holds a point of the curve:+--+-- * A pay-to-public-key output, or a multisig output, with a key of the+--   hybrid form (first byte 0x06 or 0x07). The @Solver@ only checks the size+--   and the first byte of a key. Such keys are valid by consensus and refused+--   by the relay policy.+--+-- * The same outputs with a key of the right size that is not a point of the+--   curve. Protocols that store data in bare multisig outputs make those.+--+-- * @OP_RETURN@ followed by @OP_RESERVED@, which the push-only test of+--   Bitcoin Core counts as a push.+--+-- On Bitcoin Cash, an output that holds tokens starts with the token prefix+-- byte 0xef and is not decoded. See 'Haskoin.Script.SigHash.txSigHashForkId'+-- for what is not supported on that network.+decodeOutput :: Ctx -> Script -> Either String ScriptOutput+decodeOutput ctx s = case s.ops of+  -- Pay to PubKey+  [OP_PUSHDATA bs OPCODE, OP_CHECKSIG] ->+    PayPK <$> decodePubKey ctx bs+  -- Pay to PubKey Hash+  [OP_DUP, OP_HASH160, OP_PUSHDATA bs OPCODE, OP_EQUALVERIFY, OP_CHECKSIG]+    | B.length bs == 20 ->+        PayPKHash <$> runGetS deserialize bs+    | otherwise ->+        Left "decodeOutput: public key hash must be 20 bytes long"+  -- Pay to Script Hash+  [OP_HASH160, OP_PUSHDATA bs OPCODE, OP_EQUAL]+    | B.length bs == 20 ->+        PayScriptHash <$> runGetS deserialize bs+    | otherwise ->+        Left "decodeOutput: script hash must be 20 bytes long"+  -- Pay to 256-bit Script Hash+  [OP_HASH256, OP_PUSHDATA bs OPCODE, OP_EQUAL]+    | B.length bs == 32 ->+        PayScript32Hash <$> runGetS deserialize bs+    | otherwise ->+        Left "decodeOutput: script hash must be 32 bytes long"+  -- Witness programs+  [ver, OP_PUSHDATA bs OPCODE]+    | Just 0 <- opWitnessVersion ver -> case B.length bs of+        20 -> PayWitnessPKHash <$> runGetS deserialize bs+        32 -> PayWitnessScriptHash <$> runGetS deserialize bs+        _ -> Left "decodeOutput: version 0 segwit must be 20 or 32 bytes long"+    | Just wv <- opWitnessVersion ver,+      B.length bs >= minWitnessProgramLength,+      B.length bs <= maxWitnessProgramLength ->+        Right $ PayWitness wv bs+  -- Provably unspendable data carrier output+  OP_RETURN : rest+    | Just ds <- mapM pushOpData rest -> Right $ DataCarrier ds+  -- Pay to MultiSig Keys+  _ -> case matchPayMulSig ctx s of+    Right x -> return x+    Left _ -> Left "decodeOutput: Non-standard output" +-- | Shortest and longest witness program, in bytes, as BIP 141 defines them.+minWitnessProgramLength, maxWitnessProgramLength :: Int+minWitnessProgramLength = 2+maxWitnessProgramLength = 40++-- | The opcode that starts a witness program of a version. witnessVersionOp :: Word8 -> Maybe ScriptOp-witnessVersionOp 0  = Just OP_0-witnessVersionOp 1  = Just OP_1-witnessVersionOp 2  = Just OP_2-witnessVersionOp 3  = Just OP_3-witnessVersionOp 4  = Just OP_4-witnessVersionOp 5  = Just OP_5-witnessVersionOp 6  = Just OP_6-witnessVersionOp 7  = Just OP_7-witnessVersionOp 8  = Just OP_8-witnessVersionOp 9  = Just OP_9-witnessVersionOp 10 = Just OP_10-witnessVersionOp 11 = Just OP_11-witnessVersionOp 12 = Just OP_12-witnessVersionOp 13 = Just OP_13-witnessVersionOp 14 = Just OP_14-witnessVersionOp 15 = Just OP_15-witnessVersionOp 16 = Just OP_16+witnessVersionOp 0 = Just OP_0+witnessVersionOp v = eitherToMaybe $ intToScriptOp $ fromIntegral v +-- | The witness version that an opcode starts. opWitnessVersion :: ScriptOp -> Maybe Word8-opWitnessVersion OP_0  = Just 0-opWitnessVersion OP_1  = Just 1-opWitnessVersion OP_2  = Just 2-opWitnessVersion OP_3  = Just 3-opWitnessVersion OP_4  = Just 4-opWitnessVersion OP_5  = Just 5-opWitnessVersion OP_6  = Just 6-opWitnessVersion OP_7  = Just 7-opWitnessVersion OP_8  = Just 8-opWitnessVersion OP_9  = Just 9-opWitnessVersion OP_10 = Just 10-opWitnessVersion OP_11 = Just 11-opWitnessVersion OP_12 = Just 12-opWitnessVersion OP_13 = Just 13-opWitnessVersion OP_14 = Just 14-opWitnessVersion OP_15 = Just 15-opWitnessVersion OP_16 = Just 16-opWitnessVersion _ = Nothing+opWitnessVersion OP_0 = Just 0+opWitnessVersion op = fromIntegral <$> eitherToMaybe (scriptOpToInt op) +-- | Computes a 'Script' from a standard 'ScriptOutput'. Fails for a value+-- that no script can express: a 'PayMulSig' whose required count is not+-- between 1 and its number of keys, or with more than 'maxMultisigKeys' keys,+-- and a 'PayWitness' whose version is not 1 to 16 or whose program is not 2+-- to 40 bytes long. A version 0 program is a 'PayWitnessPKHash' or a+-- 'PayWitnessScriptHash'.+encodeOutput :: Ctx -> ScriptOutput -> Either String Script+encodeOutput ctx s = Script <$> case s of+  -- Pay to PubKey+  (PayPK k) -> Right [opPushData $ marshal ctx k, OP_CHECKSIG]+  -- Pay to PubKey Hash Address+  (PayPKHash h) ->+    Right+      [ OP_DUP,+        OP_HASH160,+        opPushData $ runPutS $ serialize h,+        OP_EQUALVERIFY,+        OP_CHECKSIG+      ]+  -- Pay to MultiSig Keys+  (PayMulSig ps r)+    | r < 1 ->+        Left "encodeOutput: multisig must require at least one signature"+    | r > n ->+        Left "encodeOutput: multisig requires more signatures than it has keys"+    | n > maxMultisigKeys ->+        Left "encodeOutput: multisig has more than 20 keys"+    | otherwise ->+        let keys = map (opPushData . marshal ctx) ps+         in Right $ multisigCountOp r : keys ++ [multisigCountOp n, OP_CHECKMULTISIG]+    where+      n = length ps+  -- Pay to Script Hash Address+  (PayScriptHash h) ->+    Right [OP_HASH160, opPushData $ runPutS $ serialize h, OP_EQUAL]+  -- Pay to 256-bit Script Hash Address+  (PayScript32Hash h) ->+    Right [OP_HASH256, opPushData $ runPutS $ serialize h, OP_EQUAL]+  -- Pay to Witness PubKey Hash Address+  (PayWitnessPKHash h) ->+    Right [OP_0, opPushData $ runPutS $ serialize h]+  (PayWitnessScriptHash h) ->+    Right [OP_0, opPushData $ runPutS $ serialize h]+  (PayWitness v h)+    | v == 0 ->+        Left "encodeOutput: a version 0 witness output is PayWitnessPKHash or PayWitnessScriptHash"+    | B.length h < minWitnessProgramLength || B.length h > maxWitnessProgramLength ->+        Left "encodeOutput: witness program must be 2 to 40 bytes long"+    | Just c <- witnessVersionOp v ->+        Right [c, opPushData h]+    | otherwise ->+        Left "encodeOutput: witness version must be 16 or less"+  -- Provably unspendable output+  (DataCarrier ds) -> Right $ OP_RETURN : map opPushData ds --- | Similar to 'decodeOutput' but decodes from a 'ByteString'.-decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< S.decode+-- | The 'Script' of a 'ScriptOutput' is the binary form. Writing a value that+-- 'encodeOutput' rejects fails with an 'error' call, since 'marshalPut'+-- cannot report a failure.+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 = either error serialize . encodeOutput ctx --- | Computes a 'Script' from a standard 'ScriptOutput'.-encodeOutput :: ScriptOutput -> Script-encodeOutput s = Script $ case s of-    -- Pay to PubKey-    (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]-    -- Pay to PubKey Hash Address-    (PayPKHash h) ->-        [ OP_DUP-        , OP_HASH160-        , opPushData $ S.encode h-        , OP_EQUALVERIFY, OP_CHECKSIG-        ]-    -- Pay to MultiSig Keys-    (PayMulSig ps r)-      | r <= length ps ->-        let opM = intToScriptOp r-            opN = intToScriptOp $ length ps-            keys = map (opPushData . S.encode) ps-            in opM : keys ++ [opN, OP_CHECKMULTISIG]-      | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"-    -- Pay to Script Hash Address-    (PayScriptHash h) ->-        [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]-    -- Pay to Witness PubKey Hash Address-    (PayWitnessPKHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    (PayWitnessScriptHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    (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]+-- | The pay-to-script-hash output of a redeem script. Fails for a script+-- longer than 'maxRedeemScriptSize' bytes, which could never be spent.+toP2SH :: Script -> Either String ScriptOutput+toP2SH = fmap PayScriptHash . p2shScriptHash --- | Similar to 'encodeOutput' but encodes to a ByteString-encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = S.encode . encodeOutput+-- | The pay-to-witness-script-hash output of a witness script. Fails for a+-- script longer than 'maxWitnessScriptSize' bytes, which could never be+-- spent.+toP2WSH :: Script -> Either String ScriptOutput+toP2WSH = fmap PayWitnessScriptHash . p2wshScriptHash --- | Encode script as pay-to-script-hash script-toP2SH :: Script -> ScriptOutput-toP2SH = PayScriptHash . addressHash . S.encode+-- | The hash that a pay-to-script-hash output commits to: HASH160 of the+-- serialized redeem script. Fails for a script longer than+-- 'maxRedeemScriptSize' bytes.+p2shScriptHash :: Script -> Either String Hash160+p2shScriptHash script+  | size > maxRedeemScriptSize =+      Left $+        "The redeem script is "+          <> show size+          <> " bytes long, and a pay-to-script-hash redeem script can have at most "+          <> show maxRedeemScriptSize+  | otherwise = Right (addressHash bytes)+  where+    bytes = runPutS (serialize script)+    size = B.length bytes --- | Encode script as a pay-to-witness-script-hash script-toP2WSH :: Script -> ScriptOutput-toP2WSH = PayWitnessScriptHash . sha256 . S.encode+-- | The hash that a pay-to-witness-script-hash output commits to: SHA256 of+-- the serialized witness script. Fails for a script longer than+-- 'maxWitnessScriptSize' bytes.+p2wshScriptHash :: Script -> Either String Hash256+p2wshScriptHash script+  | size > maxWitnessScriptSize =+      Left $+        "The witness script is "+          <> show size+          <> " bytes long, and a pay-to-witness-script-hash witness script can have at most "+          <> show maxWitnessScriptSize+  | otherwise = Right (sha256 bytes)+  where+    bytes = runPutS (serialize script)+    size = B.length bytes --- | 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"+-- | Match @[OP_M, PubKey1, ..., PubKeyN, OP_N, OP_CHECKMULTISIG]@ with M and N+-- from 1 to 'maxMultisigKeys' and M at most N.+matchPayMulSig :: Ctx -> Script -> Either String ScriptOutput+matchPayMulSig ctx (Script ops) = case splitAt (length ops - 2) ops of+  (m : xs, [n, OP_CHECKMULTISIG]) -> do+    intM <- maybeToEither "matchPayMulSig: invalid M parameter" $ opMultisigCount m+    intN <- maybeToEither "matchPayMulSig: invalid N parameter" $ opMultisigCount n+    unless (intM <= intN && length xs == intN) $+      Left "matchPayMulSig: Invalid M or N parameters"+    keys <- mapM key xs+    return $ PayMulSig keys intM+  _ -> Left "matchPayMulSig: script did not match output template"   where-    go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)-    go []                    = return []-    go  _                    = Left "matchPayMulSig: invalid multisig opcode"+    key (OP_PUSHDATA bs _) = decodePubKey ctx bs+    key _ = Left "matchPayMulSig: invalid multisig opcode" +-- | The count that a multisig opcode stands for: @OP_1@ to @OP_16@, or a+-- one-byte push of 17 to 'maxMultisigKeys'. Bitcoin Core reads the count as a+-- minimal script number, so a push of a byte from 1 to 16 is not a count.+opMultisigCount :: ScriptOp -> Maybe Int+opMultisigCount op = case op of+  OP_PUSHDATA bs OPCODE+    | B.length bs == 1,+      let n = fromIntegral (B.head bs),+      n > 16 && n <= maxMultisigKeys ->+        Just n+  _ -> eitherToMaybe $ scriptOpToInt op++-- | The opcode of a multisig count from 1 to 'maxMultisigKeys'.+multisigCountOp :: Int -> ScriptOp+multisigCountOp n =+  either (const $ OP_PUSHDATA (B.singleton $ fromIntegral n) OPCODE) id $+    intToScriptOp n+ -- | Sort the public keys of a multisig output in ascending order by comparing--- their compressed serialized representations. Refer to BIP-67.-sortMulSig :: ScriptOutput -> ScriptOutput-sortMulSig out = case out of-    PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r-    _ -> error "Can only call orderMulSig on PayMulSig scripts"+-- their serialized representations. Refer to BIP-67, which defines the order+-- for compressed keys. Any other output is returned unchanged.+sortMulSig :: Ctx -> ScriptOutput -> ScriptOutput+sortMulSig ctx out = case out of+  PayMulSig keys r ->+    PayMulSig+      (sortBy (compare `on` marshal ctx) keys)+      r+  _ -> out  -- | 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-    { getInputSig :: !TxSignature-    -- ^ transaction signature-    }-    | SpendPKHash-    { getInputSig :: !TxSignature-    -- ^ embedded signature-    , getInputKey :: !PubKeyI-    -- ^ public key-    }-    | SpendMulSig-    { getInputMulSigKeys :: ![TxSignature]-    -- ^ list of signatures-    }-    deriving (Eq, Show, Generic, NFData)+data SimpleInput+  = SpendPK+      { -- | transaction signature+        signature :: !TxSignature+      }+  | SpendPKHash+      { -- | embedded signature+        signature :: !TxSignature,+        -- | public key+        key :: !PublicKey+      }+  | SpendMulSig+      { -- | list of signatures+        signatures :: ![TxSignature]+      }+  deriving (Eq, Show, Read, Generic, NFData)  -- | Returns true if the input script is spending from a pay-to-public-key -- output. isSpendPK :: ScriptInput -> Bool isSpendPK (RegularInput (SpendPK _)) = True-isSpendPK _                          = False+isSpendPK _ = False  -- | Returns true if the input script is spending from a pay-to-public-key-hash -- output. isSpendPKHash :: ScriptInput -> Bool isSpendPKHash (RegularInput (SpendPKHash _ _)) = True-isSpendPKHash _                                = False+isSpendPKHash _ = False  -- | Returns true if the input script is spending a multisig output. isSpendMulSig :: ScriptInput -> Bool isSpendMulSig (RegularInput (SpendMulSig _)) = True-isSpendMulSig _                              = False+isSpendMulSig _ = False  -- | Returns true if the input script is spending a pay-to-script-hash output. isScriptHashInput :: ScriptInput -> Bool isScriptHashInput (ScriptHashInput _ _) = True-isScriptHashInput _                     = False+isScriptHashInput _ = False +-- | Returns true if the input script is spending a pay-to-script-hash output+-- that wraps a SegWit program.+isNestedWitnessInput :: ScriptInput -> Bool+isNestedWitnessInput (NestedWitnessInput _) = True+isNestedWitnessInput _ = False+ -- | A redeem script is the output script serialized into the spending input -- script. It must be included in inputs that spend pay-to-script-hash outputs. type RedeemScript = ScriptOutput  -- | Standard input script high-level representation.-data ScriptInput = RegularInput-    { getRegularInput :: !SimpleInput-    -- ^ get wrapped simple input-    }-    | ScriptHashInput-    { getScriptHashInput  :: !SimpleInput-    -- ^ get simple input associated with redeem script-    , getScriptHashRedeem :: !RedeemScript-    -- ^ redeem script-    }-    deriving (Eq, Show, Generic, NFData)+data ScriptInput+  = RegularInput+      { -- | get wrapped simple input+        get :: !SimpleInput+      }+  | ScriptHashInput+      { -- | get simple input associated with redeem script+        get :: !SimpleInput,+        -- | redeem script+        redeem :: !RedeemScript+      }+  | -- | input of a pay-to-script-hash output that wraps a SegWit program+    -- (P2SH-P2WPKH or P2SH-P2WSH): the script only pushes the witness output+    -- script, and the signatures are in the witness+    NestedWitnessInput+      { -- | the wrapped 'PayWitnessPKHash' or 'PayWitnessScriptHash' output+        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+-- | The signature that a script element stands for: an empty push is+-- 'TxSignatureEmpty', any other push must decode as a signature.+sigOp :: Network -> Ctx -> ScriptOp -> Maybe TxSignature+sigOp net ctx op = do+  bs <- pushOpData op+  if B.null bs+    then return TxSignatureEmpty+    else eitherToMaybe $ decodeTxSig net ctx bs++-- | Heuristic to decode an input script into one of the standard types, when+-- the output it spends is not known. A single empty push is read as a+-- 'SpendPK' with an empty signature, although it is also the form of a+-- 'SpendMulSig' without signatures.+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+    matchPK _ = Nothing     matchPKHash [op, OP_PUSHDATA pub _] =-        SpendPKHash <$> f op <*> eitherToMaybe (decode pub)+      SpendPKHash <$> f op <*> eitherToMaybe (decodePubKey ctx pub)     matchPKHash _ = Nothing-    matchMulSig (x:xs) = do-        guard $ x == OP_0-        SpendMulSig <$> mapM f xs+    matchMulSig (OP_0 : xs) = SpendMulSig <$> mapM f xs     matchMulSig _ = Nothing-    f OP_0                    = return TxSignatureEmpty-    f (OP_PUSHDATA "" OPCODE) = f OP_0-    f (OP_PUSHDATA bs _)      = eitherToMaybe $ decodeTxSig net bs-    f _                       = Nothing+    f = sigOp net ctx     errMsg = "decodeInput: Could not decode script input" +-- | Decode the input of a pay-to-script-hash output as the redeem script+-- requires: one signature for 'PayPK', a signature and a key for+-- 'PayPKHash', and @OP_0@ followed by the signatures, possibly none, for+-- 'PayMulSig'. Other redeem scripts have no standard input.+decodeSimpleInputFor ::+  Network -> Ctx -> RedeemScript -> [ScriptOp] -> Maybe SimpleInput+decodeSimpleInputFor net ctx rdm ops = case (rdm, ops) of+  (PayPK _, [op]) -> SpendPK <$> f op+  (PayPKHash _, [op, OP_PUSHDATA pub _]) ->+    SpendPKHash <$> f op <*> eitherToMaybe (decodePubKey ctx pub)+  (PayMulSig _ _, OP_0 : xs) -> SpendMulSig <$> mapM f xs+  _ -> Nothing+  where+    f = sigOp net ctx+ -- | 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+--+-- A script whose last element is a standard output script is the input of a+-- pay-to-script-hash output. The elements before it must be the input that+-- the redeem script requires, see 'ScriptHashInput'. When the redeem script+-- is a SegWit program and nothing comes before it, the input is a+-- 'NestedWitnessInput'.+--+-- What this function reads is the standard form that nodes relay, not every+-- input that a block may hold:+--+-- * A signature must be a 'TxSignature': strict DER, a low S value and a+--   defined hash type. An input with a signature that is valid by consensus+--   alone does not decode. See 'TxSignature'.+--+-- * The redeem script must be in the form that 'encodeOutput' writes, byte+--   for byte. Bitcoin Core's @Solver@ also calls a script a multisig when a+--   key is pushed with @OP_PUSHDATA1@, but a 'ScriptOutput' cannot hold the+--   form of a push, so such a script would come back out as other bytes with+--   another hash. It is refused here, so that 'Haskoin.Address.inputAddress'+--   is always the hash of the bytes the input carries, and so that encoding+--   a decoded input never rewrites its redeem script. Spending such a script+--   breaks the @MINIMALDATA@ rule of the relay policy, so no standard+--   transaction has one.+--+-- * The witness program of a 'NestedWitnessInput' must be a direct push and+--   the only element of the script. BIP 141 makes any other form invalid by+--   consensus: "the scriptSig must be exactly a push of the BIP16+--   redeemScript".+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 pushType]) -> do+          rdm <- eitherToMaybe $ unmarshal ctx bs+          guard (canonical rdm bs)+          case (is, rdm) of+            ([], PayWitnessPKHash _) -> nested pushType rdm+            ([], PayWitnessScriptHash _) -> nested pushType rdm+            _ -> do+              inp <- decodeSimpleInputFor net ctx rdm is+              return $ ScriptHashInput inp rdm+        _ -> Nothing+    -- The redeem script, written back, gives the bytes that were pushed.+    canonical rdm bs =+      fmap (runPutS . serialize) (encodeOutput ctx rdm) == Right bs+    -- A witness program is 22 or 34 bytes, so its only valid push is the+    -- direct one.+    nested pushType rdm = do+      guard (pushType == OPCODE)+      return (NestedWitnessInput rdm)     errMsg = "decodeInput: Could not decode script input" --- | Like 'decodeInput' but decodes directly from a serialized script--- 'ByteString'.-decodeInputBS :: Network -> ByteString -> Either String ScriptInput-decodeInputBS net = decodeInput net <=< decode+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 = encode . 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]+  NestedWitnessInput o ->+    Script [opPushData $ marshal ctx o]  -- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'.-encodeSimpleInput :: SimpleInput -> Script-encodeSimpleInput s =-    Script $+encodeSimpleInput :: Network -> Ctx -> SimpleInput -> Script+encodeSimpleInput net ctx s =+  Script $     case s of-        SpendPK ts       -> [f ts]-        SpendPKHash ts p -> [f ts, opPushData $ encode p]-        SpendMulSig xs   -> OP_0 : map f xs+      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,27 @@-{-|-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+-- 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 Partial-    ) where+  ( module Common,+    module Builder,+    module Segwit,+    module Taproot,+    module Psbt,+    module Genesis,+  )+where -import           Haskoin.Transaction.Builder as Builder-import           Haskoin.Transaction.Common  as Common-import           Haskoin.Transaction.Partial as Partial-import           Haskoin.Transaction.Segwit  as Segwit+import Haskoin.Transaction.Builder as Builder+import Haskoin.Transaction.Common as Common+import Haskoin.Transaction.Genesis as Genesis+import Haskoin.Transaction.Psbt as Psbt+import Haskoin.Transaction.Segwit as Segwit+import Haskoin.Transaction.Taproot as Taproot
src/Haskoin/Transaction/Builder.hs view
@@ -1,480 +1,1197 @@-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Haskoin.Transaction.Builder-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Code to simplify transaction creation, signing, fee calculation and coin-selection.--}-module Haskoin.Transaction.Builder-    ( -- * Transaction Builder-      buildAddrTx-    , buildTx-    , buildInput-    , SigInput(..)-    , signTx-    , signNestedWitnessTx-    , makeSignature-    , signInput-    , signNestedInput-    , verifyStdTx-    , mergeTxs-    , sigKeys-    , mergeTxInput-    , findSigInput-    , verifyStdInput-      -- * Coin Selection-    , Coin(..)-    , chooseCoins-    , chooseCoinsSink-    , chooseMSCoins-    , chooseMSCoinsSink-    , countMulSig-    , greedyAddSink-    , guessTxFee-    , guessMSTxFee-    , guessTxSize-    , guessMSSize-    ) where--import           Control.Applicative              ((<|>))-import           Control.Arrow                    (first)-import           Control.Monad                    (foldM, unless)-import           Control.Monad.Identity           (runIdentity)-import           Crypto.Secp256k1-import qualified Data.ByteString                  as B-import           Data.Conduit                     (ConduitT, Void, await,-                                                   runConduit, (.|))-import           Data.Conduit.List                (sourceList)-import           Data.Either                      (fromRight)-import           Data.List                        (nub)-import           Data.Maybe                       (catMaybes, fromJust, isJust)-import           Data.Serialize                   (decode, encode)-import           Data.String.Conversions          (cs)-import           Data.Text                        (Text)-import           Data.Word                        (Word64)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Crypto.Hash              (Hash256, addressHash)-import           Haskoin.Crypto.Signature-import           Haskoin.Keys.Common-import           Haskoin.Network.Common-import           Haskoin.Script-import           Haskoin.Transaction.Builder.Sign (SigInput (..), buildInput,-                                                   makeSignature, sigKeys)-import qualified Haskoin.Transaction.Builder.Sign as S-import           Haskoin.Transaction.Common-import           Haskoin.Transaction.Segwit       (decodeWitnessInput, isSegwit,-                                                   viewWitnessProgram)-import           Haskoin.Util---- | Any type can be used as a Coin if it can provide a value in Satoshi.--- The value is used in coin selection algorithms.-class Coin c where-    coinValue :: c -> Word64---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account.-chooseCoins :: Coin c-            => Word64 -- ^ value to send-            -> Word64 -- ^ fee per byte-            -> Int    -- ^ number of outputs (including change)-            -> Bool   -- ^ try to find better solutions-            -> [c]    -- ^ list of ordered coins to choose from-            -> Either String ([c], Word64)-            -- ^ coin selection and change-chooseCoins target fee nOut continue coins =-    runIdentity . runConduit $-    sourceList coins .| chooseCoinsSink target fee nOut continue---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account. This version uses a Sink--- for conduit-based coin selection.-chooseCoinsSink :: (Monad m, Coin c)-                => Word64 -- ^ value to send-                -> Word64 -- ^ fee per byte-                -> Int    -- ^ number of outputs (including change)-                -> Bool   -- ^ try to find better solution-                -> ConduitT c Void m (Either String ([c], Word64))-                -- ^ coin selection and change-chooseCoinsSink target fee nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessTxFee fee nOut) continue-    | otherwise = return $ Left "chooseCoins: Target must be > 0"-  where-    err = "chooseCoins: No solution found"---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address.-chooseMSCoins :: Coin c-              => Word64     -- ^ value to send-              -> Word64     -- ^ fee per byte-              -> (Int, Int) -- ^ m of n multisig-              -> Int        -- ^ number of outputs (including change)-              -> Bool       -- ^ try to find better solution-              -> [c]-              -> Either String ([c], Word64)-              -- ^ coin selection change amount-chooseMSCoins target fee ms nOut continue coins =-    runIdentity . runConduit $-        sourceList coins .| chooseMSCoinsSink target fee ms nOut continue---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address. This--- version uses a Sink if you need conduit-based coin selection.-chooseMSCoinsSink :: (Monad m, Coin c)-                  => Word64     -- ^ value to send-                  -> Word64     -- ^ fee per byte-                  -> (Int, Int) -- ^ m of n multisig-                  -> Int        -- ^ number of outputs (including change)-                  -> Bool       -- ^ try to find better solution-                  -> ConduitT c Void m (Either String ([c], Word64))-                  -- ^ coin selection and change-chooseMSCoinsSink target fee ms nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessMSTxFee fee ms nOut) continue-    | otherwise = return $ Left "chooseMSCoins: Target must be > 0"-  where-    err = "chooseMSCoins: No solution found"---- | Select coins greedily by starting from an empty solution. If the 'continue'--- flag is set, the algorithm will try to find a better solution in the stream--- after a solution is found. If the next solution found is not strictly better--- than the previously found solution, the algorithm stops and returns the--- previous solution. If the continue flag is not set, the algorithm will return--- the first solution it finds in the stream.-greedyAddSink :: (Monad m, Coin c)-              => Word64          -- ^ value to send-              -> (Int -> Word64) -- ^ coin count to fee function-              -> Bool            -- ^ try to find better solutions-              -> ConduitT c Void m (Maybe ([c], Word64))-              -- ^ coin selection and change-greedyAddSink target guessFee continue =-    go [] 0 [] 0-  where-    -- The goal is the value we must reach (including the fee) for a certain-    -- amount of selected coins.-    goal c = target + guessFee c-    go acc aTot ps pTot = await >>= \case-        -- A coin is available in the stream-        Just coin -> do-            let val = coinValue coin-            -- We have reached the goal using this coin-            if val + aTot >= goal (length acc + 1)-                -- If we want to continue searching for better solutions-                then if continue-                    -- This solution is the first one or-                    -- This solution is better than the previous one-                    then if pTot == 0 || val + aTot < pTot-                        -- Continue searching for better solutions in the stream-                        then go [] 0 (coin:acc) (val + aTot)-                        -- Otherwise, we stop here and return the previous-                        -- solution-                        else return $ Just (ps, pTot - goal (length ps))-                    -- Otherwise, return this solution-                    else return $-                        Just (coin : acc, val + aTot - goal (length acc + 1))-                -- We have not yet reached the goal. Add the coin to the-                -- accumulator-                else go (coin:acc) (val + aTot) ps pTot-        -- We reached the end of the stream-        Nothing ->-            return $ if null ps-                -- If no solution was found, return Nothing-                then Nothing-                -- If we have a solution, return it-                else Just (ps, pTot - goal (length ps))---- | Estimate tranasction fee to pay based on transaction size estimation.-guessTxFee :: Word64 -> Int -> Int -> Word64-guessTxFee byteFee nOut nIn =-    byteFee * fromIntegral (guessTxSize nIn [] nOut 0)---- | Same as 'guessTxFee' but for multisig transactions.-guessMSTxFee :: Word64 -> (Int, Int) -> Int -> Int -> Word64-guessMSTxFee byteFee ms nOut nIn =-    byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0)---- | Computes an upper bound on the size of a transaction based on some known--- properties of the transaction.-guessTxSize :: Int         -- ^ number of regular transaction inputs-            -> [(Int,Int)] -- ^ multisig m of n for each input-            -> Int         -- ^ number of P2PKH outputs-            -> Int         -- ^ number of P2SH outputs-            -> Int         -- ^ upper bound on transaction size-guessTxSize pki msi pkout msout =-    8 + inpLen + inp + outLen + out-  where-    inpLen = B.length $ encode $ VarInt $ fromIntegral $ length msi + pki-    outLen = B.length $ encode $ VarInt $ fromIntegral $ pkout + msout-    inp = pki * 148 + sum (map guessMSSize msi)-             -- (20: hash160) + (5: opcodes) +-             -- (1: script len) + (8: Word64)-    out =-        pkout * 34 +-             -- (20: hash160) + (3: opcodes) +-             -- (1: script len) + (8: Word64)-        msout * 32---- | Size of a multisig P2SH input.-guessMSSize :: (Int,Int) -> Int-guessMSSize (m, n)-    -- OutPoint (36) + Sequence (4) + Script- = 40 + fromIntegral (B.length $ encode $ VarInt $ fromIntegral scp) + scp-    -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG-  where-    rdm =-        fromIntegral $-        B.length $ encode $ opPushData $ B.replicate (n * 34 + 3) 0-    -- Redeem + m*sig + OP_0-    scp = rdm + m * 73 + 1--{- Build a new Tx -}---- | Build a transaction by providing a list of outpoints as inputs--- and a list of recipient addresses and amounts as outputs.-buildAddrTx :: Network -> [OutPoint] -> [(Text, Word64)] -> Either String Tx-buildAddrTx net ops rcps =-    buildTx ops <$> mapM f rcps-  where-    f (aTxt, v) =-        maybeToEither ("buildAddrTx: Invalid address " <> cs aTxt) $ do-            a <- textToAddr net aTxt-            let o = addressToOutput a-            return (o, v)---- | Build a transaction by providing a list of outpoints as inputs--- and a list of 'ScriptOutput' and amounts as outputs.-buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Tx-buildTx 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------ > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)--- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]-signTx :: Network-       -> Tx               -- ^ transaction to sign-       -> [SigInput]       -- ^ signing parameters-       -> [SecKey]         -- ^ private keys to sign with-       -> Either String Tx -- ^ signed transaction-signTx net tx si = S.signTx net tx $ notNested <$> si-  where notNested s = (s, False)---- | This function differs from 'signTx' by assuming all segwit inputs are--- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.-signNestedWitnessTx :: Network-                    -> Tx               -- ^ transaction to sign-                    -> [SigInput]       -- ^ signing parameters-                    -> [SecKey]         -- ^ private keys to sign with-                    -> Either String Tx -- ^ signed transaction-signNestedWitnessTx net tx si = S.signTx net tx $ nested <$> si-  where-    -- NOTE: the nesting flag is ignored for non-segwit inputs-    nested s = (s, True)----- | Sign a single input in a transaction deterministically (RFC-6979).-signInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signInput net tx i si = S.signInput net tx i (si, False)---- | Like 'signInput' but treat segwit inputs as nested-signNestedInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signNestedInput net tx i si = S.signInput net tx i (si, True)---- | Order the 'SigInput' with respect to the transaction inputs. This allows--- the user to provide the 'SigInput' in any order. Users can also provide only--- a partial set of 'SigInput' entries.-findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]-findSigInput = S.findInputIndex sigInputOP--{- Merge multisig transactions -}---- | Merge partially-signed multisig transactions.  This function does not--- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with--- segwit inputs.-mergeTxs ::-       Network -> [Tx] -> [(ScriptOutput, Word64, OutPoint)] -> Either String Tx-mergeTxs net txs os-    | null txs = Left "Transaction list is empty"-    | length (nub emptyTxs) /= 1 = Left "Transactions do not match"-    | length txs == 1 = return $ head txs-    | otherwise = foldM (mergeTxInput net txs) (head emptyTxs) outs-  where-    zipOp = zip (matchTemplate os (txIn $ head txs) f) [0 ..]-    outs =-        map (first $ (\(o, v, _) -> (o, v)) . fromJust) $-        filter (isJust . fst) zipOp-    f (_, _, o) txin = o == prevOutput txin-    emptyTxs = map (\tx -> foldl clearInput tx outs) txs-    ins is i = updateIndex i is (\ti -> ti {scriptInput = B.empty})-    clearInput tx (_, i) =-        Tx (txVersion tx) (ins (txIn tx) i) (txOut tx) [] (txLockTime tx)---- | Merge input from partially-signed multisig transactions.  This function--- does not support segwit and P2SH-segwit inputs.-mergeTxInput ::-       Network-    -> [Tx]-    -> Tx-    -> ((ScriptOutput, Word64), Int)-    -> Either String Tx-mergeTxInput net txs tx ((so, val), i) = 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)-  where-    go allSigs out rdmM =-        case out of-            PayMulSig msPubs r ->-                let sigs =-                        take r $-                        catMaybes $ matchTemplate allSigs msPubs $ f out-                 in return $ RegularInput $ SpendMulSig sigs-            PayScriptHash _ ->-                case rdmM of-                    Just rdm -> do-                        si <- go allSigs rdm Nothing-                        return $ ScriptHashInput (getRegularInput si) rdm-                    _ -> Left "Invalid output script type"-            _ -> Left "Invalid output script type"-    extractSigs si =-        case decodeInputBS net si of-            Right (RegularInput (SpendMulSig sigs)) -> Right (sigs, Nothing)-            Right (ScriptHashInput (SpendMulSig sigs) rdm) ->-                Right (sigs, Just rdm)-            _ -> Left "Invalid script input type"-    f out (TxSignature x sh) p =-        verifyHashSig-            (txSigHash net tx (encodeOutput out) val i sh)-            x-            (pubKeyPoint p)-    f _ TxSignatureEmpty _ = False--{- Tx verification -}---- | Verify if a transaction is valid and all of its inputs are standard.-verifyStdTx :: Network -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool-verifyStdTx net tx xs =-    not (null (txIn tx)) && all go (zip (matchTemplate xs (txIn tx) f) [0 ..])-  where-    f (_, _, o) txin = o == prevOutput txin-    go (Just (so, val, _), i) = verifyStdInput net tx i so val-    go _                      = False---- | Verify if a transaction input is valid and standard.-verifyStdInput :: Network -> Tx -> Int -> ScriptOutput -> Word64 -> Bool-verifyStdInput net tx i so0 val-    | isSegwit so0 =-      fromRight False $ (inp == mempty &&) . verifySegwitInput so0 <$> wp so0-    | otherwise =-        fromRight False $-            (verifyLegacyInput so0 <$> decodeInputBS net inp) <|>-            (nestedScriptOutput >>= \so -> verifyNestedInput so0 so <$> wp so)-  where-    inp = scriptInput $ txIn tx !! i-    theTxSigHash so = S.makeSigHash net tx i so val--    ws :: WitnessStack-    ws | length (txWitness tx) > i = txWitness tx !! i-       | otherwise = []--    wp :: ScriptOutput -> Either String (Maybe ScriptOutput, SimpleInput)-    wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws--    nestedScriptOutput :: Either String ScriptOutput-    nestedScriptOutput = scriptOps <$> decode inp >>= \case-        [OP_PUSHDATA bs _] -> decodeOutputBS 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-      where out = encodeOutput so--    verifySegwitInput ::-        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 (encode pub) == kh &&-            verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm'@(PayMulSig pubs r), SpendMulSig sigs) ->-            payToWitnessScriptAddress rdm' == p2wshAddr h &&-            countMulSig' (\sh -> theTxSigHash so sh $ Just rdm') (pubKeyPoint <$> pubs) sigs == r-        _ -> False--    verifyNestedInput ::-        ScriptOutput -> ScriptOutput -> (Maybe RedeemScript, SimpleInput) -> Bool-    verifyNestedInput so so' x = case so of-        PayScriptHash h -> payToScriptAddress 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-  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+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Builder+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Code to simplify transaction creation, signing, fee calculation and coin+-- selection.+module Haskoin.Transaction.Builder+  ( -- * Transaction Builder+    buildAddrTx,+    buildTx,+    buildInput,+    SigInput (..),+    signTx,+    signNestedWitnessTx,+    makeSignature,+    signInput,+    signNestedInput,+    signTaprootInput,+    verifyStdTx,+    mergeTxs,+    sigKeys,+    mergeTxInput,+    findSigInput,+    verifyStdInput,+    InputVerdict (..),+    judgeInputs,++    -- * Coin Selection+    Coin (..),+    chooseCoins,+    chooseCoinsSink,+    countMulSig,+    greedyAddSink,+    maxStandardTxWeight,++    -- * Size and fee estimation+    InputKind (..),+    OutputKind (..),+    guessTxWeight,+    guessTxSize,+    guessTxFee,+    inputWeight,+    outputSize,+    dustThreshold,+  )+where++import Control.Monad (foldM, unless, when)+import Control.Monad.Identity (runIdentity)+import Crypto.Secp256k1+import Data.Bits (popCount, testBit)+import Data.ByteString qualified as B+import Data.Bytes.Get (runGetS)+import Data.Bytes.Serial (deserialize)+import Data.Conduit (ConduitT, Void, await, runConduit, (.|))+import Data.Conduit.List (sourceList)+import Data.Either (fromRight, rights)+import Data.List (nub)+import Data.Map.Strict qualified as Map+import Data.Maybe (catMaybes, isNothing)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word64)+import Haskoin.Address+import Haskoin.Crypto.Hash (Hash256, addressHash)+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Signature+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Builder.Sign (SigInput, buildInput, makeSignature, sigKeys, signTaprootInput)+import Haskoin.Transaction.Builder.Sign qualified as Sign+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit+import Haskoin.Transaction.Taproot+import Haskoin.Util++-- | Any type can be used as a Coin if it can provide a value in Satoshi and+-- say how it is spent. The value and the kind of input are used in coin+-- selection algorithms, to reach the amount and to estimate the fee.+class Coin c where+  coinValue :: c -> Word64+  coinInputKind :: c -> InputKind++-- | The weight above which nodes do not relay a transaction: the+-- @MAX_STANDARD_TX_WEIGHT@ of Bitcoin Core (@policy/policy.h@). A Bitcoin+-- Cash transaction has no witness, so its weight is four times its size,+-- and the limit is the 100,000 bytes of @MAX_STANDARD_TX_SIZE@ in Bitcoin+-- Cash Node.+maxStandardTxWeight :: Int+maxStandardTxWeight = 400000++-- | Coin selection algorithm. This function returns the selected coins+-- together with the amount of change to send back to yourself, taking the+-- fee into account. The fee is estimated from the kind of input each coin+-- is spent with and the kinds of the outputs, as 'guessTxFee' estimates it.+--+-- The coins are taken in the order of the list until they cover the target+-- and the fee. The selection fails when the coins do not suffice, when a+-- kind of input or output is not valid, and when the transaction would+-- weigh more than 'maxStandardTxWeight', which nodes do not relay.+--+-- The change can be any amount, including one below 'dustThreshold'. Nodes+-- do not relay a transaction with an output below that threshold, so leave+-- such a change to the fee instead of making an output for it.+chooseCoins ::+  (Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per virtual byte+  Word64 ->+  -- | outputs of the transaction, including the change+  [OutputKind] ->+  -- | 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 outputs continue coins =+  runIdentity . runConduit $+    sourceList coins .| chooseCoinsSink target fee outputs continue++-- | Coin selection algorithm. 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. See 'chooseCoins'.+chooseCoinsSink ::+  (Monad m, Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per virtual byte+  Word64 ->+  -- | outputs of the transaction, including the change+  [OutputKind] ->+  -- | try to find better solution+  Bool ->+  -- | coin selection and change+  ConduitT c Void m (Either String ([c], Word64))+chooseCoinsSink target fee outputs continue+  | target == 0 = return $ Left "chooseCoins: Target must be > 0"+  | Left e <- mapM_ checkOutputKind outputs = return $ Left ("chooseCoins: " <> e)+  | otherwise = go emptySelection Nothing+  where+    outputBytes = varIntSize (length outputs) + sum (map outputSize outputs)+    -- What the coins of a selection must reach: the target and the fee of+    -- the transaction they make. Amounts are added as unbounded integers,+    -- so that no sum wraps around.+    goal sel = toInteger target + toInteger fee * toInteger ((selectionWeight outputBytes sel + 3) `div` 4)+    go sel previous =+      await >>= \case+        Nothing -> return $ maybe (Left "chooseCoins: No solution found") solution previous+        Just coin -> case addCoin coin sel of+          Left e -> return $ Left ("chooseCoins: " <> e)+          Right sel'+            -- A heavier transaction is not relayed. Coins only add weight,+            -- so nothing further in the stream can complete this selection.+            | selectionWeight outputBytes sel' > maxStandardTxWeight ->+                return $ maybe (Left tooHeavy) solution previous+            | sel'.total < goal sel' -> go sel' previous+            | not continue -> return (solution sel')+            -- This solution is the first one, or it takes less than the+            -- previous one: look for a better one in the rest of the stream+            | maybe True (\p -> sel'.total < p.total) previous -> go emptySelection (Just sel')+            | otherwise -> return $ maybe (Left "chooseCoins: No solution found") solution previous+    solution sel+      | change > toInteger (maxBound :: Word64) = Left "chooseCoins: The change does not fit in 64 bits"+      | otherwise = Right (sel.coins, fromInteger change)+      where+        change = sel.total - goal sel+    tooHeavy =+      "chooseCoins: The selection exceeds the standard transaction weight of " <> show maxStandardTxWeight++-- | The coins selected so far, with the sums that give the weight of the+-- transaction they make. The sums are kept as coins are added, so that+-- adding a coin does not walk over the coins before it.+data Selection c = Selection+  { -- | the coins, the last one added first+    coins :: [c],+    count :: !Int,+    -- | the sum of the values+    total :: !Integer,+    -- | the bytes of the inputs outside the witness+    baseBytes :: !Int,+    -- | the bytes of the witness stacks, an input without a witness+    -- counting the one byte of its empty stack+    stackBytes :: !Int,+    -- | whether an input has a witness+    hasStack :: !Bool+  }++emptySelection :: Selection c+emptySelection = Selection [] 0 0 0 0 False++addCoin :: (Coin c) => c -> Selection c -> Either String (Selection c)+addCoin coin sel = do+  let kind = coinInputKind coin+  checkInputKind kind+  let (base, stack) = inputWeight kind+  return+    Selection+      { coins = coin : sel.coins,+        count = sel.count + 1,+        total = sel.total + toInteger (coinValue coin),+        baseBytes = sel.baseBytes + base,+        stackBytes = sel.stackBytes + max 1 stack,+        hasStack = sel.hasStack || stack > 0+      }++-- | The weight of the transaction that spends the coins of a selection to+-- outputs of the size given, counted as 'guessTxWeight' counts it+selectionWeight :: Int -> Selection c -> Int+selectionWeight outputBytes sel = 4 * base + witness+  where+    base = 8 + varIntSize sel.count + sel.baseBytes + outputBytes+    witness+      | sel.hasStack = 2 + sel.stackBytes+      | otherwise = 0++-- | 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.+--+-- The fee function is called on the coins taken so far each time a coin is+-- added. Amounts are added as unbounded integers, so that no sum wraps+-- around. The result is 'Nothing' when no selection covers the target and+-- its fee, or when the change does not fit in 64 bits. 'chooseCoinsSink'+-- does not use this function: it also bounds the weight of the selection.+greedyAddSink ::+  (Monad m, Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee of a transaction that spends the coins given+  ([c] -> Word64) ->+  -- | try to find better solutions+  Bool ->+  -- | coin selection and change+  ConduitT c Void m (Maybe ([c], Word64))+greedyAddSink target guessFee continue =+  go [] 0 Nothing+  where+    -- The goal is the value we must reach (including the fee) for a certain+    -- selection of coins.+    goal coins = toInteger target + toInteger (guessFee coins)+    go acc aTot previous =+      await >>= \case+        -- A coin is available in the stream+        Just coin -> do+          let tot = aTot + toInteger (coinValue coin)+          if tot < goal (coin : acc)+            then -- We have not yet reached the goal. Add the coin to the+            -- accumulator+              go (coin : acc) tot previous+            else+              if not continue+                then return $ solution (coin : acc, tot)+                else -- This solution is the first one, or it is better than+                -- the previous one: continue searching for better solutions+                -- in the stream. Otherwise, stop here and return the+                -- previous solution.+                  if maybe True ((tot <) . snd) previous+                    then go [] 0 (Just (coin : acc, tot))+                    else return $ solution =<< previous+        -- We reached the end of the stream+        Nothing -> return $ solution =<< previous+    solution (coins, tot)+      | change > toInteger (maxBound :: Word64) = Nothing+      | otherwise = Just (coins, fromInteger change)+      where+        change = tot - goal coins++{- Size and fee estimation -}++-- | The kind of input a coin is spent with, which decides the bytes it+-- takes once signed. Keys are compressed, as every SegWit kind requires,+-- unless the kind says otherwise. A multisig kind is @m@ of @n@, with @m@+-- from 1 to @n@. A SegWit multisig has up to 'maxMultisigKeys' keys. A+-- pay-to-script-hash multisig has up to 15: its redeem script is at most+-- 'maxRedeemScriptSize' bytes, and 15 compressed keys make 513.+--+-- 'guessTxFee' and 'chooseCoins' refuse a kind outside these ranges.+-- 'inputWeight', 'guessTxWeight' and 'guessTxSize' do not check: on such a+-- kind their result means nothing.+--+-- Some inputs the library signs have no kind: bare multisig,+-- pay-to-script-hash or pay-to-witness-script-hash of a single key,+-- multisig with an uncompressed key, and Taproot spent by a script path.+data InputKind+  = -- | pay-to-public-key. Its input is the signature alone, so the+    -- compression of the key does not matter.+    InputPK+  | -- | pay-to-public-key-hash+    InputPKHash+  | -- | pay-to-public-key-hash with an uncompressed, 65-byte key+    InputUncompressedPKHash+  | -- | pay-to-script-hash multisig, @m@ of @n@+    InputMulSig Int Int+  | -- | pay-to-witness-public-key-hash+    InputWitnessPKHash+  | -- | pay-to-witness-script-hash multisig, @m@ of @n@+    InputWitnessMulSig Int Int+  | -- | pay-to-witness-public-key-hash nested in pay-to-script-hash+    InputNestedWitnessPKHash+  | -- | pay-to-witness-script-hash multisig nested in pay-to-script-hash+    InputNestedWitnessMulSig Int Int+  | -- | Taproot, spent by its key path+    InputTaproot+  deriving (Eq, Show, Read)++-- | The kind of an output, which decides its size. An output of any other+-- script is 'OutputScript' with the size of the script, so every output+-- has a kind.+data OutputKind+  = -- | pay-to-public-key-hash+    OutputPKHash+  | -- | pay-to-script-hash+    OutputScriptHash+  | -- | pay-to-script-hash of a 256-bit hash, the P2SH32 of Bitcoin Cash+    OutputScript32Hash+  | -- | pay-to-witness-public-key-hash+    OutputWitnessPKHash+  | -- | pay-to-witness-script-hash+    OutputWitnessScriptHash+  | -- | Taproot+    OutputTaproot+  | -- | any other script, of that many bytes: bare multisig, a data+    -- carrier, or a witness program of another version+    OutputScript Int+  deriving (Eq, Show, Read)++-- | Whether an input kind is one that can exist, see 'InputKind'+checkInputKind :: InputKind -> Either String ()+checkInputKind kind = case kind of+  InputMulSig m n -> multisig m n 15+  InputWitnessMulSig m n -> multisig m n maxMultisigKeys+  InputNestedWitnessMulSig m n -> multisig m n maxMultisigKeys+  _ -> Right ()+  where+    multisig m n most =+      unless (1 <= m && m <= n && n <= most) $+        Left $+          "The input kind " <> show kind <> " is not valid: m of n needs 1 <= m <= n <= " <> show most++-- | Whether an output kind is one that can exist: a script is from 0 to+-- 10,000 bytes long+checkOutputKind :: OutputKind -> Either String ()+checkOutputKind kind = case kind of+  OutputScript n+    | n < 0 || n > maxWitnessScriptSize ->+        Left $ "The output kind " <> show kind <> " is not valid: a script has from 0 to 10000 bytes"+  _ -> Right ()++-- | An upper bound on the weight, in weight units of BIP 141, of a signed+-- transaction with the inputs and outputs given. A signature is counted as+-- 72 bytes with its hash type, the most a low-S DER signature takes, and a+-- Taproot signature as 65 bytes.+guessTxWeight :: [InputKind] -> [OutputKind] -> Int+guessTxWeight inputs outputs = 4 * base + witness+  where+    base =+      8 -- version and lock time+        + varIntSize (length inputs)+        + sum (map (fst . inputWeight) inputs)+        + varIntSize (length outputs)+        + sum (map outputSize outputs)+    witnessBytes = map (snd . inputWeight) inputs+    -- A transaction with a witness has the marker and the flag, and a+    -- witness stack for every input. The stack of an input without a+    -- witness is one byte, its count of zero.+    witness+      | any (> 0) witnessBytes = 2 + sum (map (max 1) witnessBytes)+      | otherwise = 0++-- | An upper bound on the virtual size, in virtual bytes of BIP 141, of a+-- signed transaction with the inputs and outputs given: its weight divided+-- by four, rounded up. Nodes charge fees by virtual byte.+--+-- Bitcoin Core charges for signature operations as well: its size for fee+-- purposes is the larger of this size and 5 virtual bytes per signature+-- operation (@GetVirtualTransactionSize@ with @DEFAULT_BYTES_PER_SIGOP@ of+-- 20, in @policy/policy.cpp@). That only exceeds the size for transactions+-- dense in signature operations, such as one that pays to many bare+-- multisig outputs. This estimate, like 'txVirtualSize', leaves it out.+guessTxSize :: [InputKind] -> [OutputKind] -> Int+guessTxSize inputs outputs = (guessTxWeight inputs outputs + 3) `div` 4++-- | Estimate the fee to pay, from a fee rate per virtual byte and the+-- inputs and outputs of the transaction. Fails for a kind of input or+-- output that cannot exist, see 'InputKind', and for a fee that does not+-- fit in 64 bits.+guessTxFee :: Word64 -> [InputKind] -> [OutputKind] -> Either String Word64+guessTxFee feeRate inputs outputs = do+  mapM_ (prefixed . checkInputKind) inputs+  mapM_ (prefixed . checkOutputKind) outputs+  -- Multiplied as unbounded integers, so that the fee cannot wrap around+  let fee = toInteger feeRate * toInteger (guessTxSize inputs outputs)+  when (fee > toInteger (maxBound :: Word64)) $+    Left "guessTxFee: The fee does not fit in 64 bits"+  return (fromInteger fee)+  where+    prefixed = either (Left . ("guessTxFee: " <>)) Right++-- | The amount below which nodes do not relay an output of the kind, at+-- the default dust relay fee of Bitcoin Core, 3,000 satoshi per 1,000+-- virtual bytes: three satoshi for each byte of the output and of the+-- smallest input that spends it. This is @GetDustThreshold@ of Bitcoin+-- Core (@policy/policy.cpp@), which counts 148 bytes for the input of an+-- output without a witness and 67 for the input of a witness program. It+-- gives 546 satoshi for pay-to-public-key-hash, 294 for+-- pay-to-witness-public-key-hash, and 330 for pay-to-witness-script-hash+-- and Taproot. Bitcoin Cash Node gives the same amounts for the outputs+-- it has.+--+-- An 'OutputScript' is counted as an output without a witness. For a+-- witness program of another version this is more than Bitcoin Core asks,+-- which is the safe side. A data carrier output is never dust, whatever+-- this function says of its size.+dustThreshold :: OutputKind -> Word64+dustThreshold kind = 3 * fromIntegral (outputSize kind + spendSize)+  where+    spendSize = case kind of+      OutputWitnessPKHash -> witnessSpend+      OutputWitnessScriptHash -> witnessSpend+      OutputTaproot -> witnessSpend+      _ -> 32 + 4 + 1 + 107 + 4+    -- The script of the input counts a quarter in a witness+    witnessSpend = 32 + 4 + 1 + (107 `div` 4) + 4++-- | The bytes an input of the kind takes: in the transaction without its+-- witness, and in the witness. A kind without a witness counts no witness+-- bytes. Its empty stack takes one byte when another input has a witness,+-- which 'guessTxWeight' counts.+inputWeight :: InputKind -> (Int, Int)+inputWeight = \case+  InputPK -> (prefix + withSize sigPush, 0)+  InputPKHash -> (prefix + withSize (sigPush + keyPush), 0)+  InputUncompressedPKHash -> (prefix + withSize (sigPush + uncompressedKeyPush), 0)+  InputMulSig m n -> (prefix + withSize (1 + m * sigPush + push (multisig m n)), 0)+  InputWitnessPKHash -> (prefix + 1, pkhWitness)+  InputWitnessMulSig m n -> (prefix + 1, multisigWitness m n)+  InputNestedWitnessPKHash -> (prefix + withSize (push 22), pkhWitness)+  InputNestedWitnessMulSig m n -> (prefix + withSize (push 34), multisigWitness m n)+  InputTaproot -> (prefix + 1, 1 + withSize 65)+  where+    -- the outpoint and the sequence+    prefix = 36 + 4+    -- a signature with its hash type, pushed: 1 + 72+    sigPush = push 72+    -- a compressed key, pushed+    keyPush = push 33+    -- an uncompressed key, pushed+    uncompressedKeyPush = push 65+    -- bytes after their count+    withSize n = varIntSize n + n+    -- OP_m, the compressed keys pushed, OP_n and OP_CHECKMULTISIG+    multisig m n = count m + 34 * n + count n + 1+    -- a count of a multisig script: OP_1 to OP_16, or a one-byte push of+    -- one byte from 17 to 'maxMultisigKeys'+    count c+      | c <= 16 = 1+      | otherwise = 2+    -- the number of items, the signature and the key+    pkhWitness = 1 + withSize 72 + withSize 33+    -- the number of items, the empty item, the signatures and the script+    multisigWitness m n = 1 + 1 + m * withSize 72 + withSize (multisig m n)++-- | The bytes an output of the kind takes: its amount, and its script with+-- its size+outputSize :: OutputKind -> Int+outputSize = \case+  OutputPKHash -> 8 + withSize 25+  OutputScriptHash -> 8 + withSize 23+  OutputScript32Hash -> 8 + withSize 35+  OutputWitnessPKHash -> 8 + withSize 22+  OutputWitnessScriptHash -> 8 + withSize 34+  OutputTaproot -> 8 + withSize 34+  OutputScript n -> 8 + withSize n+  where+    withSize n = varIntSize n + n++-- | The bytes of a push of that many bytes+push :: Int -> Int+push n+  | n <= 75 = 1 + n+  | n <= 0xff = 2 + n+  | n <= 0xffff = 3 + n+  | otherwise = 5 + n++-- | The bytes of a count as a 'VarInt'+varIntSize :: Int -> Int+varIntSize n+  | n < 0xfd = 1+  | n <= 0xffff = 3+  | n <= 0xffffffff = 5+  | otherwise = 9++{- 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. The+-- transaction has version 1, no lock time, and final sequence numbers, so+-- it does not signal replacement (BIP 125).+--+-- Fails for an address that is not one of the network, and for an amount+-- above the 'maxSatoshi' of the network, as @createrawtransaction@ of+-- Bitcoin Core refuses an amount out of range. Like that call, it does not+-- check what needs the whole transaction: amounts that add up to more+-- than 'maxSatoshi', and an outpoint listed twice. Nodes refuse a+-- transaction with either, and so does 'verifyStdTx' for the second.+--+-- Nothing is added for change: what the outputs do not spend is the fee.+buildAddrTx :: Network -> Ctx -> [OutPoint] -> [(Text, Word64)] -> Either String Tx+buildAddrTx net ctx ops rcps = do+  when (any ((> net.maxSatoshi) . snd) rcps) $+    Left $ "buildAddrTx: An amount is above the maximum of " <> show net.maxSatoshi <> " satoshi"+  buildTx ctx ops <$> mapM f rcps+  where+    f (aTxt, v) =+      maybeToEither ("buildAddrTx: Invalid address " <> cs aTxt) $ do+        a <- textToAddr net aTxt+        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. It has no network+-- to check against, so it builds what it is given, whatever the amounts.+-- 'buildAddrTx' refuses an amount above the maximum of its network.+buildTx :: Ctx -> [OutPoint] -> [(ScriptOutput, Word64)] -> Tx+buildTx ctx ops rcpts =+  Tx 1 (toIn <$> ops) (toOut <$> rcpts) [] 0+  where+    toIn op = TxIn op B.empty maxBound+    toOut (o, v) = TxOut v $ marshal ctx o++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed deterministically as defined+-- in RFC-6979.+--+-- A 'SigInput' whose script is 'PayWitnessPKHash' or 'PayWitnessScriptHash'+-- is a native SegWit input: the signature goes in the witness and the input+-- script stays empty. For a SegWit program nested in pay-to-script-hash, use+-- 'signNestedWitnessTx'.+--+-- A 'SigInput' whose script is a Taproot output is signed by its key path,+-- with a key whose x-only public key is the output key, or whose tweak+-- without a script tree is the output key, as a wallet of BIP 86 spends. The+-- BIP 341 digest commits to the output every input spends, so every input+-- of the transaction must have a 'SigInput' then.+--+-- An input is signed with the keys of the list that its coin asks for. An+-- input whose coin asks for none of them is left as it is, so that several+-- parties can sign in turn. The result is then 'Right' with that input+-- unsigned: use 'verifyStdTx' to tell whether the transaction is complete.+--+-- The signer refuses, with the reason, what would make a transaction that+-- nodes do not accept: a hash type that is not defined, a redeem or a+-- witness script that does not hash to its coin or is too long, a SegWit+-- or Taproot coin on a network without SegWit, and a SegWit version 0 coin+-- whose key is uncompressed. See 'makeSignature' and 'sigKeys'. It signs+-- any defined hash type that a 'SigInput' asks for, 'sigHashNone' and+-- 'sigHashSingle' included, so check a 'SigInput' that comes from someone+-- else.+--+-- Example: P2WPKH+--+-- > sigIn = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing+-- > signedTx = signTx btc ctx unsignedTx [sigIn] [key]+--+-- Example: P2SH-P2WSH multisig+--+-- > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)+-- > signedTx = signNestedWitnessTx btc ctx unsignedTx [sigIn] [k1,k3]+signTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters+  [SigInput] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signTx net ctx tx si = Sign.signTx net ctx tx $ notNested <$> si+  where+    notNested s = (s, False)++-- | This function differs from 'signTx' by assuming all segwit inputs are+-- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.+signNestedWitnessTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters+  [SigInput] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signNestedWitnessTx net ctx tx si = Sign.signTx net ctx tx $ nested <$> si+  where+    -- NOTE: the nesting flag is ignored for non-segwit inputs+    nested s = (s, True)++-- | Sign a single input in a transaction deterministically (RFC-6979). A+-- Taproot input is signed with 'signTaprootInput' instead, since its+-- digest needs the output every input spends.+signInput ::+  Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> Either String Tx+signInput net ctx tx i si =+  Sign.signInput net ctx tx i (si, False)++-- | Like 'signInput' but treat segwit inputs as nested+signNestedInput ::+  Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> Either String Tx+signNestedInput net ctx tx i si =+  Sign.signInput net ctx tx i (si, True)++-- | Order the 'SigInput' with respect to the transaction inputs. This allows+-- the user to provide the 'SigInput' in any order. Users can also provide only+-- a partial set of 'SigInput' entries.+findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]+findSigInput = Sign.findInputIndex (.outpoint)++{- Merge multisig transactions -}++-- | Merge partially-signed multisig transactions.  This function does not+-- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with+-- segwit inputs. An input that no transaction has signed stays unsigned.+-- The witness is not merged: the transactions must have the same one, and+-- the result keeps it, so that a SegWit input signed before the multisig+-- signers took their copies stays signed.+mergeTxs ::+  Network ->+  Ctx ->+  [Tx] ->+  [(ScriptOutput, Word64, OutPoint)] ->+  Either String Tx+mergeTxs net ctx txs os = case txs of+  [] -> Left "Transaction list is empty"+  [tx] -> return tx+  tx : _+    | length (nub emptyTxs) /= 1 -> Left "Transactions do not match"+    | otherwise -> foldM (mergeTxInput net ctx txs) (clearAll tx) (outs tx)+  where+    zipOp tx = zip (matchTemplate os tx.inputs f) [0 ..]+    outs tx =+      [ ((o, v), i)+      | (Just (o, v, _), i) <- zipOp tx+      ]+    f (_, _, o) txin = o == txin.outpoint+    emptyTxs = map clearAll txs+    clearAll tx = foldl clearInput tx (outs tx)+    ins is i = updateIndex i is (\TxIn {..} -> TxIn {script = B.empty, ..})+    clearInput tx (_, i) =+      Tx tx.version (ins tx.inputs i) tx.outputs tx.witness tx.locktime++-- | Merge input from partially-signed multisig transactions.  This function+-- does not support segwit and P2SH-segwit inputs. When no transaction has a+-- script for the input, the input is left as it is. Fails when one of the+-- transactions has no input at the index. The witness of the transaction+-- is kept.+mergeTxInput ::+  Network ->+  Ctx ->+  [Tx] ->+  Tx ->+  ((ScriptOutput, Word64), Int) ->+  Either String Tx+mergeTxInput net ctx txs tx ((so, val), i) = do+  when (i < 0 || any ((<= i) . length . (.inputs)) (tx : txs)) $+    Left "mergeTxInput: input index out of range"+  -- Ignore transactions with empty inputs+  let ins = map ((.script) . (!! i) . (.inputs)) txs+  sigRes <- mapM extractSigs $ filter (not . B.null) ins+  case sigRes of+    [] -> return tx+    (_, rdm) : _ -> do+      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.witness tx.locktime+  where+    go allSigs out rdmM =+      case out of+        PayMulSig msPubs r ->+          let sigs =+                take r $+                  catMaybes $+                    matchTemplate allSigs msPubs $+                      f out+           in return $ RegularInput $ SpendMulSig sigs+        PayScriptHash _ ->+          case rdmM of+            Just rdm -> do+              si <- go allSigs rdm Nothing+              return $ ScriptHashInput si.get rdm+            _ -> Left "Invalid output script type"+        _ -> Left "Invalid output script type"+    extractSigs si =+      case unmarshal (net, ctx) si of+        Right (RegularInput (SpendMulSig sigs)) ->+          Right (sigs, Nothing)+        Right (ScriptHashInput (SpendMulSig sigs) rdm) ->+          Right (sigs, Just rdm)+        _ -> Left "Invalid script input type"+    f out (TxSignature x sh) p =+      either (const False) (\h -> verifyHashSig ctx h x p.point) $+        encodeOutput ctx out >>= \s -> txSigHash net tx s val i sh+    f _ TxSignatureEmpty _ = False++{- Tx verification -}++-- | Verify that every input of the transaction spends one of the coins+-- given with a standard input that verifies ('verifyStdInput'), that no+-- two inputs spend the same coin, and that the outputs do not spend more+-- than the coins hold. Other rules of @CheckTransaction@ in Bitcoin Core,+-- such as the limits on sizes and amounts, are not checked, and neither+-- are lock times.+--+-- This is not a script interpreter. It answers 'True' only for the+-- standard kinds of input listed at 'verifyStdInput', and 'False' for any+-- other script, valid or not. For those kinds it follows consensus: it+-- does not answer 'True' for an input that no block can contain.+verifyStdTx ::+  Network -> Ctx -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool+verifyStdTx net ctx tx coins =+  not (null tx.inputs)+    && Map.size (Map.fromList [(i.outpoint, ()) | i <- tx.inputs]) == length tx.inputs+    && all (verifyInput net ctx tx spent) [0 .. length tx.inputs - 1]+    && maybe False (\outs -> toSpend <= sum (map (toInteger . snd) outs)) (sequence spent.coins)+  where+    -- The coins are looked up once for the whole transaction+    spent = spentCoins ctx tx coins+    toSpend = sum (map (toInteger . (.value)) tx.outputs)++-- | The coin of each input of a transaction, and the outputs that the+-- Taproot digest commits to. 'verifyStdTx' computes this once, so that the+-- time it takes does not grow with the square of the number of inputs.+data SpentCoins = SpentCoins+  { -- | the coin of each input, in order, when it is among those given+    coins :: [Maybe (ScriptOutput, Word64)],+    -- | the output every input spends, when every coin is given+    outputs :: Maybe [TxOut]+  }++spentCoins :: Ctx -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> SpentCoins+spentCoins ctx tx given =+  SpentCoins+    { coins = found,+      outputs = map (\(so, val) -> TxOut val (marshal ctx so)) <$> sequence found+    }+  where+    -- The first coin given for an outpoint is its coin+    byOutPoint = Map.fromListWith (\_ first -> first) [(o, (so, val)) | (so, val, o) <- given]+    found = map (\i -> Map.lookup i.outpoint byOutPoint) tx.inputs++-- | Verify that an input spends one of the coins given with a standard+-- input that verifies: a signature of the right key and digest for+-- pay-to-public-key, pay-to-public-key-hash, multisig and pay-to-script-hash+-- inputs, their SegWit forms, native or nested in pay-to-script-hash, and+-- Taproot inputs spent by the key path or by a script path whose leaf is a+-- single key check or a multisig. The multisig of Taproot is @multi_a@ of+-- the output descriptors: a key and @OP_CHECKSIG@, then a key and+-- @OP_CHECKSIGADD@ for each other key, the number of signatures asked+-- for, and @OP_NUMEQUAL@; its witness has an item for every key, that of+-- the last key first, empty for a key that did not sign. An input that+-- is not a witness program must have no witness, as @VerifyScript@+-- requires in Bitcoin Core. Other script path spends need script+-- execution and are not verified: they are refused.+--+-- The answer is never 'True' for an input that no block can contain:+--+-- * A multisig input has exactly as many signatures as its script+--   requires, each of a key of the script, in the order of the keys, and+--   no other item. @OP_CHECKMULTISIG@ takes that many signatures from the+--   top of the stack, and the item below them, its dummy element, must be+--   empty (BIP 147). So an item more, after the signatures or among them,+--   makes the operation fail.+-- * The input script of a SegWit program nested in pay-to-script-hash is+--   the push of the program and nothing else (BIP 141).+-- * A redeem script is at most 'maxRedeemScriptSize' bytes, and a witness+--   script at most 'maxWitnessScriptSize'.+-- * A network without SegWit has no witness inputs.+--+-- The answer can be 'False' for an input that a block may hold but that+-- nodes do not relay:+--+-- * A signature is read with 'decodeTxSig', which applies the relay rules:+--   low S and a defined hash type.+-- * A legacy multisig input with an empty item before its signatures+--   leaves that item on the stack. Consensus allows it outside a witness,+--   and the @CLEANSTACK@ rule of the relay policy does not.+--+-- An uncompressed key in a SegWit input is accepted, since a block may+-- hold it, although nodes do not relay it.+verifyStdInput :: Network -> Ctx -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Int -> Bool+verifyStdInput net ctx tx coins = verifyInput net ctx tx (spentCoins ctx tx coins)++-- | 'verifyStdInput' on coins that are looked up already+verifyInput :: Network -> Ctx -> Tx -> SpentCoins -> Int -> Bool+verifyInput net ctx tx spent i+  | i < 0 || i >= length tx.inputs = False+  | otherwise = case spent.coins !! i of+      Nothing -> False+      Just (so0, val)+        | isSegwit so0 ->+            net.segWit && fromRight False ((B.null inp &&) . verifySegwitInput so0 val <$> wp so0)+        | isTaproot so0 ->+            net.segWit && B.null inp && verifyTaprootInput so0+        | otherwise ->+            or $+              rights+                [ (\si -> null ws && verifyLegacyInput so0 val si) <$> unmarshal (net, ctx) inp,+                  nestedScriptOutput >>= \so -> verifyNestedInput so0 so val <$> wp so+                ]+  where+    inp = (tx.inputs !! i).script+    theTxSigHash so val = Sign.makeSigHash net ctx tx i so val++    -- A signature verifies only when its digest could be computed.+    checkSig :: Either String Hash256 -> Sig -> PubKey -> Bool+    checkSig h sig pub = either (const False) (\m -> verifyHashSig ctx m sig pub) h++    ws :: WitnessStack+    ws+      | length tx.witness > i = tx.witness !! i+      | otherwise = []++    wp :: ScriptOutput -> Either String (Maybe ScriptOutput, SimpleInput)+    wp so = decodeWitnessInput net ctx =<< viewWitnessProgram net ctx so ws++    -- The witness program of an input script that is its push and nothing+    -- else, on a network that has SegWit+    nestedScriptOutput :: Either String ScriptOutput+    nestedScriptOutput = case unmarshal (net, ctx) inp of+      Right (NestedWitnessInput so)+        | net.segWit && inp == marshal (net, ctx) (NestedWitnessInput so) -> Right so+      _ -> Left "nestedScriptOutput: not a nested output"++    -- OP_CHECKMULTISIG takes the signatures the script requires from the+    -- top of the stack, and each must verify against a later key than the+    -- one before. An empty item among them is no signature. An item more+    -- pushes a signature into the place of the dummy element, which must+    -- be empty. Only an empty item before the signatures does not fail the+    -- operation: it stays on the stack, which the relay rules refuse.+    multisigVerifies :: (SigHash -> Either String Hash256) -> [PubKey] -> Int -> [TxSignature] -> Bool+    multisigVerifies hashfun pubs r sigs =+      -- With exactly r items, r of them verify only when none is empty+      length sigs == r && countMulSig' ctx hashfun pubs sigs == r++    verifyLegacyInput :: ScriptOutput -> Word64 -> ScriptInput -> Bool+    verifyLegacyInput so val si = case (so, si) of+      (PayPK pub, RegularInput (SpendPK (TxSignature sig sh))) ->+        checkSig (theTxSigHash so val sh Nothing) sig pub.point+      (PayPKHash h, RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->+        pubKeyAddr ctx pub == p2pkhAddr h+          && checkSig (theTxSigHash so val sh Nothing) sig pub.point+      (PayMulSig pubs r, RegularInput (SpendMulSig sigs)) ->+        either (const False) (\out -> multisigVerifies (txSigHash net tx out val i) ((.point) <$> pubs) r sigs) $+          encodeOutput ctx so+      (PayScriptHash h, ScriptHashInput si' rdm) ->+        payToScriptAddress ctx rdm == Right (p2shAddr h) && verifyLegacyInput rdm val (RegularInput si')+      _ -> False++    verifySegwitInput ::+      ScriptOutput -> Word64 -> (Maybe ScriptOutput, SimpleInput) -> Bool+    verifySegwitInput so val (rdm, si) = case (so, rdm, si) of+      ( PayWitnessPKHash h,+        Nothing,+        SpendPKHash (TxSignature sig sh) pub+        ) ->+          -- Compare the key hash only. An uncompressed key is non-standard+          -- here but still valid by consensus, so verification accepts it.+          let keytest = pubKeyAddr ctx pub == p2pkhAddr h+              sighash = theTxSigHash so val sh Nothing+              pkpoint = pub.point+              verify = checkSig sighash sig pkpoint+           in keytest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayPK pub),+        SpendPK (TxSignature sig sh)+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == Right (p2wshAddr h)+              sighash = theTxSigHash so val sh $ Just rdm'+              pkpoint = pub.point+              verify = checkSig sighash sig pkpoint+           in keytest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayPKHash kh),+        SpendPKHash (TxSignature sig sh) pub+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == Right (p2wshAddr h)+              addrtest = addressHash (marshal ctx pub) == kh+              pkpoint = pub.point+              sighash = theTxSigHash so val sh $ Just rdm'+              verify = checkSig sighash sig pkpoint+           in keytest && addrtest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayMulSig pubs r),+        SpendMulSig sigs+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == Right (p2wshAddr h)+              pkpoints = (.point) <$> pubs+              hashfun sh = theTxSigHash so val sh $ Just rdm'+              verify = multisigVerifies hashfun pkpoints r sigs+           in keytest && verify+      _ -> False++    verifyNestedInput ::+      ScriptOutput -> ScriptOutput -> Word64 -> (Maybe RedeemScript, SimpleInput) -> Bool+    verifyNestedInput so so' val x = case so of+      PayScriptHash h -> payToScriptAddress ctx so' == Right (p2shAddr h) && verifySegwitInput so' val x+      _ -> False++    verifyTaprootInput :: ScriptOutput -> Bool+    verifyTaprootInput so = fromRight False $ do+      outKey <- case so of+        PayWitness 1 program -> maybe (Left "not an output key") Right (importXOnlyPubKey ctx program)+        _ -> Left "not a Taproot output"+      -- The outputs every input spends, in order, which the Taproot digest+      -- commits to+      spentOutputs <- maybe (Left "a spent output is missing") Right spent.outputs+      let schnorr sigBytes annex leaf = do+            TaprootSignature sig sh <- decodeTaprootSig sigBytes+            h <- taprootSigHash tx spentOutputs i sh annex leaf+            return (sig, h)+      case viewTaprootWitness ctx ws of+        Just (KeyPathSpend sigBytes annex) -> do+          (sig, h) <- schnorr sigBytes annex Nothing+          return (verifySchnorr ctx outKey sig (hashToMsg h))+        Just (ScriptPathSpend spd) -> case (spd.script.ops, spd.stack) of+          ([OP_PUSHDATA keyBytes OPCODE, OP_CHECKSIG], [sigBytes])+            | spd.leafVersion == tapscriptLeafVersion -> do+                leafKey <- maybe (Left "not an x-only key") Right (importXOnlyPubKey ctx keyBytes)+                (sig, h) <- schnorr sigBytes spd.annex (Just (leafHash spd.leafVersion spd.script, 0xffffffff))+                return (verifyScriptPathData ctx outKey spd && verifySchnorr ctx leafKey sig (hashToMsg h))+          -- The multisig of Taproot, @multi_a@ of the output descriptors:+          -- a key and OP_CHECKSIG, then a key and OP_CHECKSIGADD for each+          -- other key, the number of signatures asked for, and+          -- OP_NUMEQUAL. The witness has an item for every key, that of+          -- the last key first: a signature, or nothing for a key that+          -- did not sign. A signature that does not verify fails the+          -- script, it does not count for nothing (BIP 342).+          (ops, items)+            | spd.leafVersion == tapscriptLeafVersion,+              Just (keys, asked) <- multiA ops,+              length items == length keys -> do+                let leaf = Just (leafHash spd.leafVersion spd.script, 0xffffffff)+                    signs keyBytes sigBytes+                      | B.null sigBytes = Right False+                      | otherwise = do+                          key <- maybe (Left "not an x-only key") Right (importXOnlyPubKey ctx keyBytes)+                          (sig, h) <- schnorr sigBytes spd.annex leaf+                          if verifySchnorr ctx key sig (hashToMsg h)+                            then Right True+                            else Left "a signature that does not verify"+                signed <- sequence (zipWith signs keys (reverse items))+                return (verifyScriptPathData ctx outKey spd && length (filter id signed) == asked)+          _ -> Left "script path spends other than key checks are not verified"+        Nothing -> Left "not a Taproot witness"++-- | What can be said of the signature of an input without running its+-- script.+data InputVerdict+  = -- | the input is one of the standard kinds and its signatures verify+    -- ('verifyStdInput')+    InputVerified+  | -- | the input spends a coin locked to one key, with the signature and+    -- key that such a coin takes, and they do not open it: no node takes+    -- the transaction+    InputForged+  | -- | neither: a script that is not run here, a coin that was not+    -- given, a hash type other than all, an encoding that is not read+    InputUndecided+  deriving (Eq, Show)++-- | Judge each input of a transaction, given the output it spends, in+-- order, as it is in the transaction that made it: 'Nothing' for one that+-- is not known.+--+-- 'verifyStdTx' answers 'False' both for a forged signature and for a+-- script it does not read. To refuse a transaction before sending it,+-- those have to be told apart: a wrong 'InputForged' turns away a good+-- transaction, while 'InputUndecided' only leaves the question to a+-- node. So 'InputForged' is said of little, and only where there is+-- nothing to run:+--+-- * a coin locked to a key hash, to a key, to a witness key hash, to one+--   nested in a script hash, or to a Taproot key, spent by the key and+--   not by a script;+-- * with exactly the items such an input has, and a hash type of all;+-- * whose key is not the one the coin names, or whose signature does not+--   verify over the digest.+--+-- On Bitcoin Cash it reads what 'verifyStdInput' does not: a Schnorr+-- signature ('verifyCashSchnorr'), of one key or in a multisig, bare or in+-- a script hash, and a coin that holds tokens, whose digest covers them+-- ('txSigHashCashToken'). Inputs of those kinds are 'InputVerified' here+-- and not by 'verifyStdInput'. A multisig is never 'InputForged': one+-- that does not verify is 'InputUndecided'.+judgeInputs :: Network -> Ctx -> Tx -> [Maybe TxOut] -> [InputVerdict]+judgeInputs net ctx tx given = zipWith judge [0 ..] decoded+  where+    prevs = take (length tx.inputs) (given <> repeat Nothing)+    forked = net.sigHashForkId /= Nothing+    split bytes+      | forked = splitTokenPrefix bytes+      | otherwise = Just (B.empty, bytes)+    decoded = map (>>= decode) prevs+    decode o = do+      (prefix, script) <- split o.script+      so <- eitherToMaybe (unmarshal ctx script)+      return (prefix, so, o.value)+    spent =+      SpentCoins+        { coins = [d >>= \(prefix, so, v) -> if B.null prefix then Just (so, v) else Nothing | d <- decoded],+          outputs = sequence prevs+        }+    judge :: Int -> Maybe (B.ByteString, ScriptOutput, Word64) -> InputVerdict+    judge _ Nothing = InputUndecided+    judge i (Just (prefix, so, val))+      | B.null prefix && verifyInput net ctx tx spent i = InputVerified+      | otherwise = case so of+          PayPKHash h+            | null ws, Just [sig, pub] <- pushes -> keyed (addressHash pub == h) sig pub legacy+          PayPK pub+            | null ws, Just [sig] <- pushes -> signed sig pub.point legacy+          PayWitnessPKHash h+            | net.segWit, B.null inp, [sig, pub] <- ws -> keyed (addressHash pub == h) sig pub (segwit so)+          PayScriptHash h+            | net.segWit,+              Just [program] <- pushes,+              addressHash program == h,+              Right nested@(PayWitnessPKHash kh) <- unmarshal ctx program,+              [sig, pub] <- ws ->+                keyed (addressHash pub == kh) sig pub (segwit nested)+          PayWitness 1 program+            | net.segWit, B.length program == 32, B.null inp, [sig] <- ws -> taproot program sig+          PayMulSig keys asked+            | forked, null ws, Just (bits : sigs) <- pushes -> cashMultisig so keys asked bits sigs+          PayScriptHash h+            | forked,+              null ws,+              Just items@(_ : _ : _) <- pushes,+              addressHash (last items) == h,+              Right redeem@(PayMulSig keys asked) <- unmarshal ctx (last items),+              bits : sigs <- init items ->+                cashMultisig redeem keys asked bits sigs+          _ -> InputUndecided+      where+        inp = (tx.inputs !! i).script+        ws = if length tx.witness > i then tx.witness !! i else []+        -- the items of an input script that only pushes data+        pushes = case runGetS deserialize inp of+          Right (Script ops) -> mapM pushed ops+          Left _ -> Nothing+        -- a small number is pushed by an opcode of its own+        pushed = \case+          OP_PUSHDATA d _ -> Just d+          op -> B.singleton . fromIntegral <$> eitherToMaybe (scriptOpToInt op)+        legacy sh = do+          script <- encodeOutput ctx so+          if forked then txSigHashCashToken net tx prefix script val i sh else txSigHash net tx script val i sh+        segwit out sh = Sign.makeSigHash net ctx tx i out val sh Nothing+        -- a coin that names the hash of its key: another key does not+        -- open it, whatever it signed+        keyed named sig pub digest+          | not named = InputForged+          | otherwise = maybe InputUndecided (\k -> signed sig k digest) (importPubKey ctx pub)+        -- Forged is said of the hash type "all" only. With another hash+        -- type a signature that verifies is verified, and one that does+        -- not is left alone.+        signed sig key digest+          | B.null sig || not (readable (B.last sig)) = InputUndecided+          | otherwise = case digest (fromIntegral (B.last sig)) of+              Left _ -> InputUndecided+              Right m+                -- on Bitcoin Cash a signature of 64 bytes is a Schnorr one+                | forked && B.length body == 64 -> sure (verifyCashSchnorr ctx key body m)+                | otherwise -> maybe InputUndecided (\s -> sure (verifyHashSig ctx m s key)) (decodeStrictSig ctx body)+          where+            body = B.init sig+            sure ok+              | ok = InputVerified+              | B.last sig == (if forked then 0x41 else 0x01) = InputForged+              | otherwise = InputUndecided+        -- The hash types whose digest is computed here. On Bitcoin Cash:+        -- all, none or single, with the fork id, alone or with "anyone+        -- can pay"; not the one that covers every coin spent.+        readable ht+          | forked = ht `elem` [0x41, 0x42, 0x43, 0xc1, 0xc2, 0xc3]+          | otherwise = ht == 0x01+        -- A multisig of Bitcoin Cash signed with Schnorr signatures. In+        -- place of the empty item that a multisig takes first there is a+        -- field of bits, one for each key in the order of the script,+        -- the first key lowest, set for the keys that signed. It has+        -- exactly the bytes that hold a bit for every key, no bit past+        -- the keys, and as many bits set as signatures are asked for.+        -- The signatures follow in the order of their keys, and every+        -- one must be a Schnorr signature that verifies. This is+        -- OP_CHECKMULTISIG of Bitcoin Cash Node, since November 2019.+        cashMultisig script keys asked bits sigs+          | B.null bits = InputUndecided+          | B.length bits /= (length keys + 7) `div` 8 = InputUndecided+          | field >= 2 ^ length keys || popCount field /= asked || length sigs /= asked = InputUndecided+          | all good (zip signers sigs) = InputVerified+          | otherwise = InputUndecided+          where+            field = B.foldr (\w acc -> acc * 256 + toInteger w) 0 bits :: Integer+            signers = [k | (n, k) <- zip [0 ..] keys, testBit field n]+            good (key, sig) =+              B.length sig == 65+                && readable (B.last sig)+                && either+                  (const False)+                  (verifyCashSchnorr ctx key.point (B.init sig))+                  (encodeOutput ctx script >>= \code -> cashDigest code (fromIntegral (B.last sig)))+            cashDigest code = txSigHashCashToken net tx prefix code val i+        taproot program sig = fromRight InputUndecided $ do+          key <- maybe (Left "not a key") Right (importXOnlyPubKey ctx program)+          outs <- maybe (Left "a spent output is missing") Right spent.outputs+          TaprootSignature s sh <- decodeTaprootSig sig+          unless (sh == sigHashDefault || sh == sigHashAll) $ Left "another hash type"+          m <- taprootSigHash tx outs i sh Nothing Nothing+          return (verdict (verifySchnorr ctx key s (hashToMsg m)))+        verdict ok = if ok then InputVerified else InputForged++-- | The keys of a Taproot multisig script and the number of signatures it+-- asks for, if the script is one: see 'verifyStdInput'.+multiA :: [ScriptOp] -> Maybe ([B.ByteString], Int)+multiA (OP_PUSHDATA first OPCODE : OP_CHECKSIG : rest) = go [first] rest+  where+    go keys (OP_PUSHDATA key OPCODE : OP_CHECKSIGADD : more) = go (key : keys) more+    go keys [number, OP_NUMEQUAL]+      | Right asked <- scriptOpToInt number,+        asked <= length keys,+        all ((== 32) . B.length) keys =+          Just (reverse keys, asked)+    go _ _ = Nothing+multiA _ = Nothing++-- | Whether an output script is a Taproot output: a witness program of+-- version 1 that is 32 bytes long+isTaproot :: ScriptOutput -> Bool+isTaproot = \case+  PayWitness 1 program -> B.length program == 32+  _ -> False++-- | Count the signatures of a multisig input that verify against the keys+-- of its script, taken in the order of the keys. An empty item counts for+-- nothing and passes over one key.+--+-- The count is not a verdict. An input is valid when it has exactly the+-- signatures its script requires and all of them count, which+-- 'verifyStdInput' checks. An input with an item more, or with an empty+-- item, can count enough signatures and still fail @OP_CHECKMULTISIG@.+countMulSig ::+  Network ->+  Ctx ->+  Tx ->+  Script ->+  Word64 ->+  Int ->+  [PubKey] ->+  [TxSignature] ->+  Int+countMulSig net ctx tx out val i =+  countMulSig' ctx h+  where+    h = txSigHash net tx out val i++countMulSig' :: Ctx -> (SigHash -> Either String 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')+  | either (const False) (\m -> verifyHashSig ctx m sig pub) (h sh) =+      1 + countMulSig' ctx h pubs sigs'+  | otherwise = countMulSig' ctx h pubs sigs
src/Haskoin/Transaction/Builder/Sign.hs view
@@ -1,271 +1,535 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE LambdaCase        #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Types and logic for signing transactions. module Haskoin.Transaction.Builder.Sign-    ( SigInput (..)-    , makeSignature-    , makeSigHash-    , signTx-    , findInputIndex-    , signInput-    , buildInput-    , sigKeys-    ) where+  ( SigInput (..),+    makeSignature,+    makeSigHash,+    signTx,+    findInputIndex,+    signInput,+    signTaprootInput,+    buildInput,+    sigKeys,+  )+where -import           Control.DeepSeq            (NFData)-import           Control.Monad              (foldM, when)-import           Data.Aeson                 (FromJSON, ToJSON (..), object,-                                             pairs, parseJSON, withObject, (.:),-                                             (.:?), (.=))-import           Data.Either                (rights)-import           Data.Hashable              (Hashable)-import           Data.List                  (find, nub)-import           Data.Maybe                 (catMaybes, fromMaybe, mapMaybe,-                                             maybeToList)-import qualified Data.Serialize             as S-import           Data.Word                  (Word64)-import           GHC.Generics               (Generic)-import           Haskoin.Address            (getAddrHash160, pubKeyAddr)-import           Haskoin.Constants          (Network)-import           Haskoin.Crypto             (Hash256, SecKey)-import           Haskoin.Crypto.Signature   (signHash, verifyHashSig)-import           Haskoin.Keys.Common        (PubKeyI (..), SecKeyI (..),-                                             derivePubKeyI, wrapSecKey)-import           Haskoin.Script-import           Haskoin.Transaction.Common-import           Haskoin.Transaction.Segwit-import           Haskoin.Util               (matchTemplate, updateIndex)+import Control.DeepSeq+import Control.Monad+import Crypto.Secp256k1+import Data.Aeson+import Data.Aeson.Encoding+import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either+import Data.Hashable+import Data.List+import Data.Maybe+import Data.Word+import GHC.Generics+import Haskoin.Address+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Signature+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit+import Haskoin.Transaction.Taproot+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.+-- script is required. A Taproot output ('PayWitness' version 1) is signed+-- by its key path, with 'sigHashDefault' or one of the hash types+-- 'isTaprootSigHash' accepts.+--+-- A 'SigInput' is an instruction to the signer, and the signer follows it.+-- When it comes from someone else, for example as JSON, check it first:+--+-- * The hash type. Any type a node accepts is signed, including+--   'sigHashNone' and 'sigHashSingle', which leave outputs unsigned.+-- * The value of a legacy coin. The legacy digest does not commit to it, so+--   a wrong value still gives a valid signature, and the fee is then not+--   what the value suggests. Only the previous transaction settles it.+--   The digests of SegWit, Taproot and Bitcoin Cash commit to the value. data SigInput = SigInput-    { sigInputScript :: !ScriptOutput -- ^ output script to spend-    -- ^ output script value-    , sigInputValue  :: !Word64 -- ^ output script value-    -- ^ outpoint to spend-    , sigInputOP     :: !OutPoint -- ^ outpoint to spend-    -- ^ signature type-    , sigInputSH     :: !SigHash -- ^ signature type-    -- ^ redeem script-    , sigInputRedeem :: !(Maybe RedeemScript) -- ^ redeem script-    }-    deriving (Eq, Show, Read, Generic, Hashable, NFData)+  { -- | output script to spend+    script :: !ScriptOutput,+    -- | output script value+    value :: !Word64,+    -- | outpoint to spend+    outpoint :: !OutPoint,+    -- | signature type+    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+        ] +  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-       -> Tx                 -- ^ transaction to sign-       -> [(SigInput, Bool)] -- ^ signing parameters, with nesting flag-       -> [SecKey]           -- ^ private keys to sign with-       -> Either String Tx   -- ^ signed transaction-signTx net otx sigis allKeys-    | null ti   = Left "signTx: Transaction has no inputs"-    | otherwise = foldM go otx $ findInputIndex (sigInputOP . fst) sigis ti+--+-- An input is signed with the keys of the list that its coin asks for. An+-- input whose coin asks for none of them is left as it is, so that several+-- parties can sign in turn. The result is then 'Right' with that input+-- unsigned: use 'Haskoin.Transaction.Builder.verifyStdTx' to tell whether+-- the transaction is complete.+--+-- A SegWit version 0 coin is signed with a compressed key only, and fails+-- when its key is uncompressed. Nodes do not relay such a spend+-- (@SCRIPT_VERIFY_WITNESS_PUBKEYTYPE@ in Bitcoin Core), although a block+-- may hold it. 'signInput' signs with the key it is given, in the form it+-- is given.+signTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters, with nesting flag+  [(SigInput, Bool)] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signTx net ctx otx sigis allKeys+  | null ti = Left "signTx: Transaction has no inputs"+  | otherwise = foldM go otx $ findInputIndex ((.outpoint) . fst) sigis ti   where-    ti = txIn otx-    go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do-        keys <- sigKeys so rdmM allKeys-        foldM (\t k -> signInput net t i sigi k) tx keys+    ti = otx.inputs+    -- The public keys of the secret keys are derived once for the whole+    -- transaction, not once per input+    index = keyIndex ctx allKeys+    go tx (sigi@(SigInput so _ _ sh rdmM, _), i) = do+      keys <- indexedSigKeys index so rdmM+      if isTaprootOutput so+        then do+          unless net.segWit $ Left "signTx: this network has no Taproot"+          spent <- spentOutputs+          foldM (\t k -> signTaprootInput ctx t spent i sh k.key) tx keys+        else foldM (\t k -> signInput net ctx t i sigi k) tx keys+    -- The outputs every input spends, which a Taproot signature commits+    -- to: a nested SegWit program is spent as its P2SH wrapper+    spentOutputs =+      forM ti $ \txin ->+        case find ((== txin.outpoint) . (.outpoint) . fst) sigis of+          Just (s, nested) -> (\so -> TxOut s.value (marshal ctx so)) <$> coinScript s nested+          Nothing -> Left "signTx: a Taproot signature needs the output every input spends"+    coinScript s nested+      | nested && isSegwit s.script = toP2SH =<< encodeOutput ctx s.script+      | otherwise = Right s.script +-- | Whether the output is a Taproot output: a witness program of version 1+-- that is 32 bytes long+isTaprootOutput :: ScriptOutput -> Bool+isTaprootOutput = \case+  PayWitness 1 program -> B.length program == 32+  _ -> False++-- | Sign a Taproot input by its key path, as BIP 341 defines it: the+-- Schnorr signature of the digest of 'taprootSigHash' goes in the witness+-- of the input, with its hash type byte unless the type is+-- 'sigHashDefault'. The secret key is the one of the output key, which+-- 'taprootOutputSecKey' derives from the internal key. The outputs spent+-- are those of every input, in order. Fails when the index is out of+-- range, when the spent outputs do not match the inputs, when the hash+-- type is not one 'isTaprootSigHash' accepts, when the output that the+-- input spends is not a Taproot output, or when the secret key is not the+-- key of that output: a signature of another key would make a transaction+-- that no node accepts.+signTaprootInput ::+  Ctx ->+  Tx ->+  -- | outputs spent, one per input+  [TxOut] ->+  -- | input index+  Int ->+  SigHash ->+  -- | the secret key of the output key+  SecKey ->+  Either String Tx+signTaprootInput ctx tx spent i sh secret = do+  -- The digest checks the index and the number of spent outputs+  h <- taprootSigHash tx spent i sh Nothing Nothing+  outputKey <- case B.unpack (spent !! i).script of+    0x51 : 0x20 : program | length program == 32 -> Right (B.pack program)+    _ -> Left "signTaprootInput: the output spent is not a Taproot output"+  unless (exportXOnlyPubKey ctx (derivePubKey ctx secret) == outputKey) $+    Left "signTaprootInput: the key is not the key of the output"+  signature <- taprootSignature (signSchnorr ctx secret (hashToMsg h)) sh+  let stacks+        | null tx.witness = replicate (length tx.inputs) []+        | otherwise = tx.witness+  unless (length stacks == length tx.inputs) $+    Left "signTaprootInput: Invalid number of witness stacks"+  return tx {witness = updateIndex i stacks (const [encodeTaprootSig signature])}+ -- | Sign a single input in a transaction deterministically (RFC-6979).  The -- nesting flag only affects the behavior of segwit inputs.+--+-- The key must be one the coin asks a signature of: the key of a+-- pay-to-public-key coin, the key whose hash a coin pays to, or a key of a+-- multisig. Any other key fails, since its signature would make an input+-- that no node accepts. The key is used in the form given, compressed or+-- not. That is the way to spend a SegWit coin of an uncompressed key on+-- purpose, which 'signTx' refuses. signInput ::-       Network-    -> Tx-    -> Int-    -> (SigInput, Bool) -- ^ boolean flag: nest input-    -> 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+  when (isTaprootOutput so) $+    Left "signInput: a Taproot input is signed with signTaprootInput"+  let pub = derivePublicKey ctx key+  unless (keyOfCoin ctx so rdmM pub) $+    Left "signInput: the key is not a key of the coin"+  sig <- makeSignature net ctx tx i sigIn key+  si <- buildInput net ctx tx i so val rdmM sig pub+  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 = S.encode . opPushData $ encodeOutputBS so'}-    txis = txIn tx+    f si TxIn {..} = TxIn {script = marshal (net, ctx) si, ..}+    g so' = f (NestedWitnessInput so')     nextTxIn so' si-        | isSegwit so' && nest = updateIndex i txis (g so')-        | isSegwit so'         = txIn tx-        | otherwise            = updateIndex i txis (f si)+      | isSegwit so' && nest = updateIndex i tx.inputs (g so')+      | isSegwit so' = tx.inputs+      | otherwise = updateIndex i tx.inputs (f si) +-- | Whether the public key is one that the coin asks a signature of+keyOfCoin :: Ctx -> ScriptOutput -> Maybe RedeemScript -> PublicKey -> Bool+keyOfCoin ctx so rdmM pub = case (so, rdmM) of+  (PayPK p, Nothing) -> p == pub+  (PayPKHash h, Nothing) -> keyHash == h+  (PayMulSig ps _, Nothing) -> pub `elem` ps+  (PayScriptHash _, Just rdm) -> keyOfCoin ctx rdm Nothing pub+  (PayWitnessPKHash h, _) -> keyHash == h+  (PayWitnessScriptHash _, Just rdm) -> keyOfCoin ctx rdm Nothing pub+  _ -> False+  where+    keyHash = addressHash (marshal ctx pub)+ -- | Add the witness data of the transaction given segwit parameters for an input. -- -- @since 0.11.0.0-updatedWitnessData :: Tx -> Int -> ScriptOutput -> ScriptInput -> Either String WitnessData-updatedWitnessData tx i so si-    | isSegwit so = updateWitness . toWitnessStack =<< calcWitnessProgram so si-    | otherwise   = return $ txWitness tx+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 ::-       (a -> OutPoint) -- ^ extract an outpoint-    -> [a]             -- ^ input list-    -> [TxIn]          -- ^ reference list of inputs-    -> [(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-    g (Just s, i)  = Just (s,i)+    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'.+-- the 'ScriptOutput'. For a Taproot output, the key returned is the secret+-- key of the output key: a key whose x-only public key is the output key,+-- or the tweak of a key without a script tree, as BIP 86 spends.+--+-- A legacy coin is matched with each key in its compressed and in its+-- uncompressed form. A SegWit version 0 coin, native or nested, takes a+-- compressed key only: when the key it asks for is uncompressed, the+-- result is an error, because nodes do not relay such a spend+-- (@SCRIPT_VERIFY_WITNESS_PUBKEYTYPE@ in Bitcoin Core). sigKeys ::-       ScriptOutput-    -> Maybe RedeemScript-    -> [SecKey]-    -> Either String [SecKeyI]-sigKeys so rdmM keys =-    case (so, rdmM) of-        (PayPK p, Nothing) ->-            return . map fst . maybeToList $ find ((== p) . snd) zipKeys-        (PayPKHash h, Nothing) -> return $ keyByHash h-        (PayMulSig ps r, Nothing) ->-            return $ map fst $ take r $ filter ((`elem` ps) . snd) zipKeys-        (PayScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        (PayWitnessPKHash h, _) -> return $ keyByHash h-        (PayWitnessScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        _ -> Left "sigKeys: Could not decode output script"-  where-    zipKeys =-        [ (prv, pub)+  Ctx ->+  ScriptOutput ->+  Maybe RedeemScript ->+  [SecKey] ->+  Either String [PrivateKey]+sigKeys ctx so rdmM keys = indexedSigKeys (keyIndex ctx keys) so rdmM++-- | The secret keys of a signer with what is derived from them, so that it+-- is derived once for a whole transaction+data KeyIndex = KeyIndex+  { -- | each key in its compressed and then in its uncompressed form, with+    -- its public key and the hash of that public key+    forms :: [(PrivateKey, PublicKey, Hash160)],+    -- | for each key, the Taproot output keys it spends, as x-only bytes,+    -- with the secret key of each: the key itself, then its tweak without+    -- a script tree+    outputKeys :: [[(B.ByteString, SecKey)]]+  }++keyIndex :: Ctx -> [SecKey] -> KeyIndex+keyIndex ctx keys =+  KeyIndex+    { forms =+        [ (PrivateKey k compressed, pub, addressHash (marshal ctx pub))+        | k <- keys,+          let point = derivePubKey ctx k,+          compressed <- [True, False],+          let pub = PublicKey point compressed+        ],+      outputKeys =+        [ (xOnly k, k) : [(xOnly tweaked, tweaked) | Just tweaked <- [taprootOutputSecKey ctx k Nothing]]         | k <- keys-        , t <- [True, False]-        , let prv = wrapSecKey t k-        , let pub = derivePubKeyI prv         ]-    keyByHash h = fmap fst . maybeToList . findKey h $ zipKeys-    findKey h   = find $ (== h) . getAddrHash160 . pubKeyAddr . snd+    }+  where+    xOnly = exportXOnlyPubKey ctx . derivePubKey ctx +-- | 'sigKeys' on keys that are indexed already+indexedSigKeys :: KeyIndex -> ScriptOutput -> Maybe RedeemScript -> Either String [PrivateKey]+indexedSigKeys index = go False+  where+    -- The flag tells whether the keys sign inside a SegWit version 0 witness+    go witness so rdmM = case (so, rdmM) of+      (PayPK p, Nothing) ->+        compressedIn witness . take 1 $ [prv | (prv, pub, _) <- index.forms, pub == p]+      (PayPKHash h, Nothing) ->+        compressedIn witness . take 1 $ [prv | (prv, _, keyHash) <- index.forms, keyHash == h]+      (PayMulSig ps r, Nothing) ->+        compressedIn witness . take r $ [prv | (prv, pub, _) <- index.forms, pub `elem` ps]+      (PayScriptHash _, Just rdm) -> go witness rdm Nothing+      (PayWitnessPKHash h, _) -> go True (PayPKHash h) Nothing+      (PayWitnessScriptHash _, Just rdm) -> go True rdm Nothing+      (PayWitness 1 program, Nothing)+        | B.length program == 32 ->+            return [PrivateKey k True | Just k <- map (lookup program) index.outputKeys]+      _ -> Left "sigKeys: Could not decode output script"+    compressedIn witness found+      | witness && not (all (.compress) found) =+          Left "sigKeys: a SegWit input is signed with a compressed key only, and this coin pays to an uncompressed key"+      | otherwise = Right found+ -- | Construct an input for a transaction given a signature, public key and data -- about the previous output. buildInput ::-       Network-    -> Tx                 -- ^ transaction where input will be added-    -> Int                -- ^ input index where signature will go-    -> ScriptOutput       -- ^ output script being spent-    -> Word64             -- ^ amount of previous output-    -> Maybe RedeemScript -- ^ redeem script if pay-to-script-hash-    -> TxSignature-    -> PubKeyI-    -> Either String ScriptInput-buildInput net tx i so val rdmM sig pub = do-    when (i >= length (txIn tx)) $ Left "buildInput: Invalid input index"-    case (so, rdmM) of-        (PayScriptHash _, Just rdm)        -> buildScriptHashInput rdm-        (PayWitnessScriptHash _, Just rdm) -> buildScriptHashInput rdm-        (PayWitnessPKHash _, Nothing)      -> return . RegularInput $ SpendPKHash sig pub-        (_, Nothing)                       -> buildRegularInput so-        _ -> Left "buildInput: Invalid output/redeem script combination"+  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 < 0 || i >= length tx.inputs) $ Left "buildInput: Invalid input index"+  when (isTaprootOutput so) $ Left "buildInput: a Taproot input has no input script"+  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)+      either (const False) (\m -> verifyHashSig ctx m x p.point) $+        makeSigHash net ctx tx i so val sh rdmM     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+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 = []+      | length tx.witness <= i = []+      | 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+-- | Produce a structured representation of a deterministic (RFC-6979)+-- signature over an input. On a network with a fork id, such as Bitcoin+-- Cash, the 'SIGHASH_FORKID' flag is added to the 'SigHash' of the+-- 'SigInput', since every signature there must carry it. Fails when the+-- input index is out of range or the output script cannot be encoded.+--+-- The signer refuses what a node would refuse, with the reason:+--+-- * A hash type that does not fit in the one byte a signature carries, one+--   that is not 'sigHashAll', 'sigHashNone' or 'sigHashSingle' with or+--   without 'setAnyoneCanPay' (the test of @IsDefinedHashtypeSignature@ in+--   Bitcoin Core), and the fork id flag on a network without a fork id.+--   On Bitcoin Cash this refuses @SIGHASH_UTXOS@ too, whose digest the+--   library does not compute.+-- * A pay-to-script-hash coin without its redeem script, with a redeem+--   script over 'maxRedeemScriptSize' bytes, or with one that does not+--   hash to the coin. The same for a pay-to-witness-script-hash coin and+--   its witness script, with 'maxWitnessScriptSize'.+-- * A SegWit coin on a network without SegWit.+--+-- A legacy input signed with 'SIGHASH_SINGLE' at an index that has no+-- output is refused. The digest of that case is the constant 1, which+-- commits to nothing, so the signature would spend any other coin of the+-- key wherever the same case comes up. Bitcoin Core computes that digest+-- in @SignatureHash@, and so does 'txSigHash', since a node must verify+-- such signatures; a signer has no reason to make one. The BIP 143 digest+-- of a SegWit input and the fork id digest of Bitcoin Cash commit to the+-- coin in that case, so those are signed.+--+-- The key is not checked against the coin here: 'signInput' does that.+makeSignature :: Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> Either String TxSignature+makeSignature net ctx tx i (SigInput so val _ sh rdmM) key = do+  when (isSegwit so && not net.segWit) $+    Left "makeSignature: this network has no SegWit"+  when (sh > 0xff) $+    Left $ "makeSignature: the hash type " <> shown <> " does not fit in the one byte of a signature"+  when (isNothing net.sigHashForkId && hasForkIdFlag sh) $+    Left $ "makeSignature: the hash type " <> shown <> " has the fork id flag, which this network does not use"+  when (isSigHashUnknown sh') $+    Left $ "makeSignature: the hash type " <> shown <> " is not one a node accepts"+  committedScript+  m <- makeSigHash net ctx tx i so val sh' rdmM+  when (signsConstantDigest && i >= length tx.outputs) $+    Left "makeSignature: SIGHASH_SINGLE without an output at the input index"+  return $ TxSignature (signHash ctx key.key m) sh'   where-    m = makeSigHash net tx i so val sh rdmM+    shown = show (toInteger sh)+    sh'+      | isJust net.sigHashForkId = setForkIdFlag sh+      | otherwise = sh+    signsConstantDigest =+      isSigHashSingle sh' && not (isSegwit so) && isNothing net.sigHashForkId+    -- A coin that commits to a script is signed only with that script+    committedScript = case (so, rdmM) of+      (PayScriptHash h, Just rdm) -> do+        committed <- p2shScriptHash =<< encodeOutput ctx rdm+        unless (committed == h) $ Left "makeSignature: the redeem script does not hash to the coin"+      (PayScriptHash _, Nothing) ->+        Left "makeSignature: a pay-to-script-hash coin needs its redeem script"+      (PayWitnessScriptHash h, Just rdm) -> do+        committed <- p2wshScriptHash =<< encodeOutput ctx rdm+        unless (committed == h) $ Left "makeSignature: the witness script does not hash to the coin"+      (PayWitnessScriptHash _, Nothing) ->+        Left "makeSignature: a pay-to-witness-script-hash coin needs its witness script"+      _ -> Right ()  -- | 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 ->+  Either String Hash256+makeSigHash net ctx tx i so val sh rdmM = do+  when (isTaprootOutput so) $+    Left "makeSigHash: the digest of a Taproot input is taprootSigHash"+  script <- encodeOutput ctx so'+  h net tx script val i sh   where     so' = case so of-        PayWitnessPKHash h' -> PayPKHash h'-        _                   -> fromMaybe so rdmM-    h | isSegwit so = txSigHashForkId-      | otherwise   = txSigHash-+      PayWitnessPKHash h' -> PayPKHash h'+      _ -> fromMaybe so rdmM+    h+      | isSegwit so = txSigHashForkId+      | otherwise = txSigHash
src/Haskoin/Transaction/Common.hs view
@@ -1,344 +1,492 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Code related to transactions parsing and serialization. module Haskoin.Transaction.Common-    ( -- * Transactions-      Tx(..)-    , TxIn(..)-    , TxOut(..)-    , OutPoint(..)-    , TxHash(..)-    , WitnessData-    , WitnessStack-    , WitnessStackItem-    , txHash-    , hexToTxHash-    , txHashToHex-    , nosigTxHash-    , nullOutPoint-    ) where+  ( -- * Transactions+    Tx (..),+    TxIn (..),+    TxOut (..),+    OutPoint (..),+    TxHash (..),+    WitnessData,+    WitnessStack,+    WitnessStackItem,+    txHash,+    txWitnessHash,+    txWeight,+    txVirtualSize,+    hexToTxHash,+    txHashToHex,+    nosigTxHash,+    hasWitness,+    decodeTx,+    parseLegacyTx,+    nullOutPoint,+  )+where -import           Control.Applicative     ((<|>))-import           Control.DeepSeq-import           Control.Monad           (forM_, guard, liftM2, mzero,-                                          replicateM, (<=<))-import           Data.Aeson              as A-import           Data.Aeson.Encoding     (unsafeToEncoding)-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as B-import           Data.ByteString.Builder (char7)-import           Data.Hashable           (Hashable)-import           Data.Maybe              (fromMaybe)-import           Data.Serialize          as S-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import           Data.Word               (Word32, Word64)-import           GHC.Generics            (Generic)-import           Haskoin.Crypto.Hash-import           Haskoin.Network.Common-import           Haskoin.Util-import           Text.Read               as R+import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (forM_, guard, liftM2, mzero, replicateM, unless, when, (<=<))+import Data.Aeson as A+import Data.Aeson.Encoding qualified as E+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Builder (char7)+import Data.ByteString.Lazy qualified as BL+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable (Hashable)+import Data.Maybe (fromMaybe)+import Data.Serialize (Serialize (..))+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word32, Word64, Word8)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Util.Helpers+import Text.Read as R --- | Transaction id: hash of transaction excluding witness data.-newtype TxHash = TxHash { getTxHash :: Hash256 }-    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)+-- | A hash that identifies a transaction: the transaction id of 'txHash',+-- which leaves the witness out, or the witness transaction id of+-- 'txWitnessHash', which includes it. Shown in hex with the bytes reversed,+-- as block explorers and Bitcoin Core show it.+newtype TxHash = TxHash {get :: Hash256}+  deriving (Eq, Ord, Generic)+  deriving newtype (Hashable, NFData) +instance Serial TxHash where+  serialize (TxHash h) = serialize h+  deserialize = TxHash <$> deserialize++instance Serialize TxHash where+  put = serialize+  get = deserialize++instance Binary TxHash where+  put = serialize+  get = deserialize+ instance Show TxHash where-    showsPrec _ = shows . txHashToHex+  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 (B.reverse (S.encode h)) <> char7 '"'+  toJSON = A.String . txHashToHex+  toEncoding = hexEncoding . BL.reverse . runPutL . serialize --- | Transaction hash excluding signatures.+-- | Hash of the transaction with every input script and the whole witness+-- removed, so that it is the same before and after signing, whether the+-- signatures go in the input scripts or in the witness. nosigTxHash :: Tx -> TxHash-nosigTxHash tx =-    TxHash $ doubleSHA256 $ S.encode 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, witness = [], ..}+    clr TxIn {..} = TxIn {script = B.empty, ..}  -- | Convert transaction hash to hex form, reversing bytes. txHashToHex :: TxHash -> Text-txHashToHex (TxHash h) = encodeHex (B.reverse (S.encode h))+txHashToHex (TxHash h) =+  encodeHex . B.reverse . runPutS $ serialize h --- | Convert transaction hash from hex, reversing bytes.+-- | Convert transaction hash from hex, reversing bytes. The hex must be+-- exactly 32 bytes long, as @uint256::FromHex@ requires in Bitcoin Core. hexToTxHash :: Text -> Maybe TxHash hexToTxHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- either (const Nothing) Just (S.decode bs)-    return $ TxHash h+  bs <- B.reverse <$> decodeHex hex+  guard (B.length bs == 32)+  h <- either (const Nothing) Just (runGetS deserialize bs)+  return $ TxHash h  -- | Witness stack for SegWit transactions. type WitnessData = [WitnessStack]+ -- | Witness stack for SegWit transactions. type WitnessStack = [WitnessStackItem]+ -- | Witness stack item for SegWit transactions. type WitnessStackItem = ByteString  -- | Data type representing a transaction. data Tx = Tx-    { -- | transaction data format version-      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: one stack per input, or empty+    -- when no input has a witness. A transaction whose stacks are all empty+    -- has no witness: it is serialized without the witness marker, as+    -- Bitcoin Core serializes it, and reads back with an empty list here.+    witness :: !WitnessData,+    -- | earliest mining height or time+    locktime :: !Word32+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData) --- | Compute transaction hash.+-- | Compute transaction hash: the transaction id, which leaves the witness+-- out. txHash :: Tx -> TxHash-txHash tx = TxHash (doubleSHA256 (S.encode tx {txWitness = []}))+txHash tx = TxHash . doubleSHA256 . runPutS $ serialize tx {witness = []} +-- | The witness transaction id of BIP 141: the hash of the transaction with+-- its witness. It is the same as 'txHash' when the transaction has no+-- witness, as @GetWitnessHash@ gives it in Bitcoin Core.+txWitnessHash :: Tx -> TxHash+txWitnessHash = TxHash . doubleSHA256 . runPutS . serialize++-- | The weight of the transaction in weight units, as BIP 141 defines it:+-- three times the size without the witness, plus the size with it.+txWeight :: Tx -> Int+txWeight tx =+  3 * B.length (runPutS (serialize tx {witness = []})) + B.length (runPutS (serialize tx))++-- | The virtual size of the transaction in virtual bytes, as BIP 141+-- defines it: the weight divided by four, rounded up. Fees are charged by+-- virtual byte.+--+-- Bitcoin Core charges a transaction that is dense in signature operations+-- for 5 virtual bytes per operation when that is more than this size+-- (@GetVirtualTransactionSize@ in @policy/policy.cpp@). This function does+-- not count signature operations.+txVirtualSize :: Tx -> Int+txVirtualSize tx = (txWeight tx + 3) `div` 4++-- | Whether any input of the transaction has a witness, as @HasWitness@+-- tells in Bitcoin Core. A transaction whose stacks are all empty has+-- none. Only the stacks of the inputs count: the witness list has one+-- stack per input, and a stack beyond the inputs belongs to no input and+-- is not written.+hasWitness :: Tx -> Bool+hasWitness tx = any (not . null) (take (length tx.inputs) tx.witness)++-- | Read a transaction from bytes that hold the transaction and nothing+-- else, as @DecodeHexTx@ requires in Bitcoin Core. 'deserialize' reads a+-- transaction from the front of a longer input, as a block needs it to,+-- and so it does not notice bytes that follow. Use this function for a+-- transaction that arrives on its own.+decodeTx :: ByteString -> Either String Tx+decodeTx = runGetS $ do+  tx <- deserialize+  done <- isEmpty+  unless done $ fail "Trailing bytes after transaction"+  return tx++-- | A transaction from its hex. The hex must hold the transaction and+-- nothing else, as @DecodeHexTx@ requires in Bitcoin Core. instance IsString Tx where-    fromString =-        fromMaybe e . (eitherToMaybe . S.decode <=< decodeHex) . cs-      where-        e = error "Could not read transaction from hex string"+  fromString =+    fromMaybe e . (eitherToMaybe . decodeTx <=< decodeHex) . cs+    where+      e = error "Could not read transaction from hex string" +instance Serial Tx where+  deserialize = do+    flags <- lookAhead witnessFlags+    case flags of+      Nothing -> parseLegacyTx+      Just 0x00 -> parseLegacyTx+      Just 0x01 -> parseWitnessTx+      Just other -> fail $ "Unknown transaction optional data: flags " ++ show other+  serialize tx+    | hasWitness tx = putWitnessTx tx+    | otherwise = putLegacyTx tx++instance Binary Tx where+  put = serialize+  get = deserialize+ instance Serialize Tx where-    get = parseWitnessTx <|> parseLegacyTx-    put tx-        | null (txWitness tx) = putLegacyTx tx-        | otherwise = putWitnessTx tx+  put = serialize+  get = deserialize -putInOut :: Tx -> Put+putInOut :: (MonadPut m) => Tx -> m () putInOut tx = do-    putVarInt $ length (txIn tx)-    forM_ (txIn tx) put-    putVarInt $ length (txOut tx)-    forM_ (txOut tx) put+  putVarIntList serialize tx.inputs+  putVarIntList serialize tx.outputs  -- | Non-SegWit transaction serializer.-putLegacyTx :: Tx -> Put+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 :: Tx -> Put+-- | Witness transaction serializer. One stack is written per input: an+-- input without a stack gets an empty one, and stacks beyond the inputs+-- are not written.+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 $ take n (tx.witness ++ repeat [])+  putWord32le tx.locktime+  where+    n = length tx.inputs --- | Non-SegWit transaction deseralizer.-parseLegacyTx :: Get Tx+-- | The flags byte of the extended format of BIP 144, when the transaction+-- has one: the byte after an input count of zero, as @UnserializeTransaction@+-- reads it in Bitcoin Core. A transaction whose input count is not zero has+-- no flags. Only the flags 0x01, the witness, and 0x00, which Bitcoin Core+-- reads as a transaction with no inputs and no outputs, are known; any other+-- value fails the parse there with "Unknown transaction optional data".+witnessFlags :: (MonadGet m) => m (Maybe Word8)+witnessFlags = do+  _ <- getWord32le+  marker <- getWord8+  if marker == 0x00+    then Just <$> getWord8+    else return Nothing++-- | Non-SegWit transaction deserializer: the old format, without the+-- marker, flags and witness of BIP 144. Use it where the bytes are known+-- to be in that format, as the unsigned transaction of a PSBT is. There a+-- transaction with no inputs reads as such, whereas 'deserialize' reads+-- the byte after an empty input list as the flags of BIP 144, as Bitcoin+-- Core does when it allows a witness. Every count is bounded by+-- 'maxListSize', as @ReadCompactSize@ bounds it in Bitcoin Core.+parseLegacyTx :: (MonadGet m) => m Tx parseLegacyTx = do-    v <- getWord32le-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    l <- getWord32le-    return-        Tx-        {txVersion = v, txIn = is, txOut = os, txWitness = [], txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get+  version <- getWord32le+  inputs <- getVarIntList deserialize+  outputs <- getVarIntList deserialize+  locktime <- getWord32le+  return Tx {witness = [], ..} --- | Witness transaction deserializer.-parseWitnessTx :: Get Tx+-- | Witness transaction deserializer. A transaction with the witness marker+-- must have a witness: one whose stacks are all empty fails, as it does in+-- Bitcoin Core ("Superfluous witness record").+parseWitnessTx :: (MonadGet m) => m Tx parseWitnessTx = do-    v <- getWord32le-    m <- getWord8-    f <- getWord8-    guard $ m == 0x00-    guard $ f == 0x01-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    w <- parseWitnessData $ length is-    l <- getWord32le-    return-        Tx {txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get+  version <- getWord32le+  m <- getWord8+  f <- getWord8+  unless (m == 0x00 && f == 0x01) $ fail "Not a witness transaction"+  inputs <- getVarIntList deserialize+  outputs <- getVarIntList deserialize+  witness <- parseWitnessData $ length inputs+  unless (any (not . null) witness) $+    fail "Superfluous witness record"+  locktime <- getWord32le+  return Tx {..}  -- | Witness data deserializer. Requires count of inputs.-parseWitnessData :: Int -> Get WitnessData-parseWitnessData n = replicateM n parseWitnessStack-  where-    parseWitnessStack = do-        VarInt i <- S.get-        replicateM (fromIntegral i) parseWitnessStackItem-    parseWitnessStackItem = do-        VarInt i <- S.get-        getByteString $ fromIntegral i+parseWitnessData :: (MonadGet m) => Int -> m WitnessData+parseWitnessData n = replicateM n (getVarIntList getVarIntBytes)  -- | Witness data serializer.-putWitnessData :: WitnessData -> Put-putWitnessData = mapM_ putWitnessStack-  where-    putWitnessStack ws = do-        putVarInt $ length ws-        mapM_ putWitnessStackItem ws-    putWitnessStackItem bs = do-        putVarInt $ B.length bs-        putByteString bs+putWitnessData :: (MonadPut m) => WitnessData -> m ()+putWitnessData = mapM_ (putVarIntList putVarIntBytes) +-- | The witness of the JSON form has one stack per input, or none at all:+-- a stack without an input would not be part of the transaction. 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 -> do+    version <- o .: "version"+    inputs <- o .: "inputs"+    outputs <- o .: "outputs"+    witness <- mapM (mapM f) =<< o .: "witnessdata"+    locktime <- o .: "locktime"+    unless (null witness || length witness == length inputs) $+      fail "The witness data must have one stack per input, or none"+    return Tx {..}+    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)+data TxIn = TxIn+  { -- | output being spent+    outpoint :: !OutPoint,+    -- | signatures and redeem script+    script :: !ByteString,+    -- | lock-time using sequence numbers (BIP-68)+    sequence :: !Word32+  }+  deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData) -instance Serialize TxIn where-    get =-        TxIn <$> S.get <*> (readBS =<< S.get) <*> getWord32le-      where-        readBS (VarInt len) = getByteString $ fromIntegral len+instance Serial TxIn where+  deserialize =+    TxIn <$> deserialize <*> getVarIntBytes <*> getWord32le -    put (TxIn o s q) = do-        put o-        putVarInt $ B.length s-        putByteString s-        putWord32le q+  serialize (TxIn o s q) = do+    serialize o+    putVarIntBytes s+    putWord32le q +instance Binary TxIn where+  get = deserialize+  put = serialize++instance Serialize TxIn where+  get = deserialize+  put = serialize+ instance FromJSON TxIn where-    parseJSON =-        withObject "TxIn" $ \o ->-            TxIn <$> o .: "prevoutput"-                 <*> (maybe mzero return . decodeHex =<< o .: "inputscript")-                 <*> o .: "sequence"+  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)+data TxOut = TxOut+  { -- | value of output is satoshi+    value :: !Word64,+    -- | pubkey script+    script :: !ByteString+  }+  deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData) -instance Serialize TxOut where-    get = do-        val <- getWord64le-        (VarInt len) <- S.get-        TxOut val <$> getByteString (fromIntegral len)+instance Serial TxOut where+  deserialize = TxOut <$> getWord64le <*> getVarIntBytes -    put (TxOut o s) = do-        putWord64le o-        putVarInt $ B.length s-        putByteString s+  serialize (TxOut o s) = do+    putWord64le o+    putVarIntBytes s +instance Binary TxOut where+  put = serialize+  get = deserialize++instance Serialize TxOut where+  put = serialize+  get = deserialize+ instance FromJSON TxOut where-    parseJSON =-        withObject "TxOut" $ \o ->-            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++instance Binary OutPoint where+  put = serialize+  get = deserialize+ instance Serialize OutPoint where-    get = do-        (h,i) <- liftM2 (,) S.get getWord32le-        return $ OutPoint h i-    put (OutPoint h i) = put h >> putWord32le i+  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,37 +1,71 @@ {-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Coinbase transactions of the genesis blocks. module Haskoin.Transaction.Genesis-    ( genesisTx-    ) where+  ( mainnetGenesisTx,+    testnet4GenesisTx,+    genesisCoinbase,+  )+where -import           Data.String                (fromString)-import           Haskoin.Script.Standard-import           Haskoin.Transaction.Common-import           Haskoin.Util+import Data.ByteString (ByteString)+import Data.Maybe (fromMaybe)+import Data.Text (Text)+import Haskoin.Transaction.Common+import Haskoin.Util.Helpers (decodeHex) --- | Transaction from Genesis block.-genesisTx :: Tx-genesisTx =-    Tx 1 [txin] [txout] [] locktime+-- | Build a genesis coinbase the way Bitcoin Core's @CreateGenesisBlock@+-- does: version 1, one input that spends nothing, one output of 50 BTC and a+-- lock time of zero.+genesisCoinbase ::+  -- | input script: the bits, the number 4 and the message+  ByteString ->+  -- | output script+  ByteString ->+  Tx+genesisCoinbase inputScript outputScript =+  Tx 1 [TxIn (OutPoint nullHash maxBound) inputScript maxBound] [TxOut 5000000000 outputScript] [] 0   where-    txin = TxIn outpoint inputBS maxBound-    txout = TxOut 5000000000 (encodeOutputBS output)-    locktime = 0-    outpoint = OutPoint z maxBound-    Just inputBS = decodeHex $ fromString $-        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" ++-        "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" ++-        "757420666f722062616e6b73"-    output = PayPK $ fromString $-        "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" ++-        "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"-    z = "0000000000000000000000000000000000000000000000000000000000000000"+    nullHash = "0000000000000000000000000000000000000000000000000000000000000000"++-- | Coinbase of the Bitcoin genesis block. Every network except Bitcoin+-- testnet4 reuses it. Its hash is+-- @4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b@.+mainnetGenesisTx :: Tx+mainnetGenesisTx =+  genesisCoinbase+    ( hex+        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686\+        \16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f\+        \757420666f722062616e6b73"+    )+    ( hex+        "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61d\+        \eb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d\+        \5fac"+    )++-- | Coinbase of the Bitcoin testnet4 genesis block. The message is+-- @03/May/2024 000000000000000000001ebd58c244970b3aa9d783bb001011fbe8ea8e98e00e@+-- and the output pays to a public key of 33 zero bytes. Its hash is+-- @7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e@.+testnet4GenesisTx :: Tx+testnet4GenesisTx =+  genesisCoinbase+    ( hex+        "04ffff001d01044c4c30332f4d61792f32303234203030303030303030303030\+        \3030303030303030303165626435386332343439373062336161396437383362\+        \623030313031316662653865613865393865303065"+    )+    (hex "21000000000000000000000000000000000000000000000000000000000000000000ac")++hex :: Text -> ByteString+hex = fromMaybe (error "genesis script is not valid hex") . decodeHex
− src/Haskoin/Transaction/Partial.hs
@@ -1,501 +0,0 @@-{-# 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-    , mergeInput-    , mergeOutput-    , complete-    , finalTransaction-    , emptyPSBT-    , emptyInput-    , emptyOutput-    ) where--import           Control.Applicative        ((<|>))-import           Control.DeepSeq-import           Control.Monad              (guard, replicateM, void)-import           Data.ByteString            (ByteString)-import qualified Data.ByteString            as B-import           Data.Hashable              (Hashable)-import           Data.HashMap.Strict        (HashMap)-import qualified Data.HashMap.Strict        as HashMap-import           Data.List                  (foldl')-import           Data.Maybe                 (fromMaybe, isJust)-import           Data.Serialize             as S-import           GHC.Generics               (Generic)-import           GHC.Word                   (Word32, Word8)-import           Haskoin.Address            (Address (..), pubKeyAddr)-import           Haskoin.Keys               (Fingerprint, KeyIndex, PubKeyI)-import           Haskoin.Network            (VarInt (..), VarString (..),-                                             putVarInt)-import           Haskoin.Script             (Script (..), ScriptOp (..),-                                             ScriptOutput (..), SigHash,-                                             decodeOutput, decodeOutputBS,-                                             encodeOutputBS, isPayScriptHash,-                                             opPushData, toP2SH, toP2WSH)-import           Haskoin.Transaction.Common (Tx (..), TxOut, WitnessStack,-                                             outPointIndex, prevOutput,-                                             scriptInput, scriptOutput)-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--mergeInput :: Input -> Input -> Input-mergeInput a b = Input-    { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b-    , witnessUtxo = witUtx-    , sigHashType = sigHashType a <|> sigHashType b-    , partialSigs = partialSigs a <> partialSigs b-    , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b-    , inputUnknown = inputUnknown a <> inputUnknown b-    , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b-    , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b-    , finalScriptSig = finalScriptSig a <|> finalScriptSig b-    , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b-    }-  where-    witUtx = witnessUtxo a <|> witnessUtxo b--mergeOutput :: Output -> Output -> Output-mergeOutput a b = Output-    { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b-    , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b-    , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b-    , outputUnknown = outputUnknown a <> outputUnknown b-    }---- | Take partial signatures from all of the 'Input's and finalize the signature.-complete :: PartiallySignedTransaction -> PartiallySignedTransaction-complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (indexed $ inputs psbt) }-  where-    analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)-      where-        nonWitScript = getPrevOut i =<< nonWitnessUtxo input--    getPrevOut i tx =-       (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i-    xs !!? i = lookup i $ indexed xs--    outputScript = eitherToMaybe . decodeOutputBS . scriptOutput--    completeInput (Nothing, input)     = input-    completeInput (Just script, input) = completeSig input script--    indexed :: [a] -> [(Word32, a)]-    indexed = zip [0..]--completeSig :: Input -> ScriptOutput -> Input-completeSig input (PayPK k) =-    input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }-completeSig input (PayPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }-completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input-completeSig input (PayScriptHash h)-    | Just rdmScript <- inputRedeemScript input-    , PayScriptHash h == toP2SH rdmScript-    , Right decodedScript <- decodeOutput rdmScript-    , not (isPayScriptHash decodedScript)-    = completeSig input decodedScript-completeSig input (PayWitnessPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptWitness = Just [sig, S.encode k]-            , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-            }-completeSig input (PayWitnessScriptHash h)-    | Just witScript <- inputWitnessScript input-    , PayWitnessScriptHash h == toP2WSH witScript-    , Right decodedScript <- decodeOutput witScript-    = completeWitnessSig input decodedScript-completeSig input _ = input--completeWitnessSig :: Input -> ScriptOutput -> Input-completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input-    { finalScriptWitness = Just finalWit-    , finalScriptSig = finalSig-    }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-    finalWit = mempty : sigs <> [encodeOutputBS script]-completeWitnessSig input _ = input--collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]-collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys-  where-    lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)---- | Take a finalized 'PartiallySignedTransaction' and produce the signed final transaction.  You may need to call--- 'complete' on the 'PartiallySignedTransaction' before producing the final transaction.-finalTransaction :: PartiallySignedTransaction -> Tx-finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)-  where-    tx = unsignedTransaction psbt-    hasWitness = any (isJust . finalScriptWitness) (inputs psbt)-    setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }-    finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput-      where-        finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)-        finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)---- | Take an unsigned transaction and produce an empty 'PartiallySignedTransaction'-emptyPSBT :: Tx -> PartiallySignedTransaction-emptyPSBT tx = PartiallySignedTransaction-    { unsignedTransaction = tx-    , globalUnknown = mempty-    , inputs = replicate (length (txIn tx)) emptyInput-    , outputs = replicate (length (txOut tx)) emptyOutput-    }--emptyInput :: Input-emptyInput = Input-    Nothing Nothing HashMap.empty Nothing-    Nothing Nothing HashMap.empty-    Nothing Nothing (UnknownMap HashMap.empty)--emptyOutput :: Output-emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)--instance Serialize PartiallySignedTransaction where-    get = do-        magic <- getBytes 4-        guard $ magic == "psbt"-        headerSep <- getWord8-        guard $ headerSep == 0xff--        keySize <- getWord8-        guard $ keySize == 1-        globalUnsignedTxType <- getWord8-        guard $ globalUnsignedTxType == 0x00-        unsignedTransaction <- getSizedBytes-        guard $ all (B.null . scriptInput) (txIn unsignedTransaction)-        guard $ null (txWitness unsignedTransaction)--        globalUnknown <- get-        globalEnd <- getWord8-        guard $ globalEnd == 0x00--        inputs <- replicateM (length $ txIn unsignedTransaction) get-        outputs <- replicateM (length $ txOut unsignedTransaction) get--        return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }--    put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do-        putByteString "psbt"-        putWord8 0xff -- Header separator--        putWord8 0x01 -- Key size-        putWord8 0x00 -- Unsigned Transaction type-        putSizedBytes unsignedTransaction-        put globalUnknown-        putWord8 0x00 -- Global end--        mapM_ put inputs-        mapM_ put outputs--instance Serialize Key where-    get = do-        VarInt keySize <- get-        guard $ keySize > 0-        t <- getWord8-        k <- getBytes (fromIntegral keySize - 1)-        return (Key t k)--    put (Key t k) = do-        putVarInt $ 1 + B.length k-        putWord8 t-        putByteString k--instance Serialize UnknownMap where-    get = go HashMap.empty-      where-        getItem m = do-            k <- get-            VarString v <- get-            go $ HashMap.insert k v m-        go m = do-            isEnd <- lookAhead getWord8-            if isEnd == 0x00-                then return (UnknownMap m)-                else getItem m--    put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m--instance Serialize Input where-    get = getMap getInputItem setInputUnknown emptyInput-      where-        setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }--    put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType-                  , inputRedeemScript, inputWitnessScript, inputHDKeypaths-                  , finalScriptSig, finalScriptWitness, inputUnknown-                  } = do-        whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo-        whenJust (putKeyValue InWitnessUtxo) witnessUtxo-        putPartialSig partialSigs-        whenJust putSigHash sigHashType-        whenJust (putKeyValue InRedeemScript) inputRedeemScript-        whenJust (putKeyValue InWitnessScript) inputWitnessScript-        putHDPath InBIP32Derivation inputHDKeypaths-        whenJust (putKeyValue InFinalScriptSig) finalScriptSig-        whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness-        put inputUnknown-        putWord8 0x00-      where-        putPartialSig = putPubKeyMap InPartialSig . fmap VarString-        putSigHash sigHash = do-            putKey InSigHashType-            putWord8 0x04-            putWord32le (fromIntegral sigHash)--instance Serialize Output where-    get = getMap getOutputItem setOutputUnknown emptyOutput-      where-        setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }--    put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do-        whenJust (putKeyValue OutRedeemScript) outputRedeemScript-        whenJust (putKeyValue OutWitnessScript) outputWitnessScript-        putHDPath OutBIP32Derivation outputHDKeypaths-        put outputUnknown-        putWord8 0x00--putSizedBytes :: Serialize a => a -> Put-putSizedBytes a = putVarInt (B.length bs) >> putByteString bs-  where bs = encode a--getSizedBytes :: Serialize a => Get a-getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get--putKeyValue :: (Enum t, Serialize v) => t -> v -> Put-putKeyValue t v = putKey t >> putSizedBytes v--putKey :: Enum t => t -> Put-putKey t = putVarInt (1 :: Word8) >> putWord8 (enumWord8 t)--getMap :: (Bounded t, Enum t)-       => (Int -> a -> t -> Get a)-       -> ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a)-       -> a -> Get a-getMap getMapItem setUnknown = go-  where-    getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go-    getItem keySize m (Left t)  = do-        k <- getBytes (fromIntegral keySize - 1)-        VarString v <- get-        go $ setUnknown (HashMap.insert (Key t k) v) m-    go m = do-        keySize <- getVarInt <$> get-        if keySize == 0-            then return m-            else getItem keySize m =<< (word8Enum <$> getWord8)--data InputType-    = InNonWitnessUtxo-    | InWitnessUtxo-    | InPartialSig-    | InSigHashType-    | InRedeemScript-    | InWitnessScript-    | InBIP32Derivation-    | InFinalScriptSig-    | InFinalScriptWitness-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData InputType--data OutputType-    = OutRedeemScript-    | OutWitnessScript-    | OutBIP32Derivation-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData OutputType--getInputItem :: Int -> Input -> InputType -> Get Input-getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { nonWitnessUtxo = Just utxo }-getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { witnessUtxo = Just utxo }-getInputItem keySize input InPartialSig = do-    (k, v) <- getPartialSig-    return $ input { partialSigs = HashMap.insert k v (partialSigs input) }-  where-    getPartialSig = (,) <$> isolate keySize get <*> (getVarString <$> get)-getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do-    VarInt size <- get-    guard $ size == 0x04-    sigHash <- fromIntegral <$> getWord32le-    return $ input { sigHashType = Just sigHash }-getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do-    script <- getSizedBytes-    return $ input { inputRedeemScript = Just script }-getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do-    script <- getSizedBytes-    return $ input { inputWitnessScript = Just script }-getInputItem keySize input InBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }-getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do-    script <- getSizedBytes-    return $ input { finalScriptSig = Just script }-getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do-    scripts <- map getVarString <$> getVarIntList-    return $ input { finalScriptWitness = Just scripts }-  where-    getVarIntList = do-        VarInt n <- get-        replicateM (fromIntegral n) get-getInputItem keySize input inputType = fail $-    "Incorrect key size for input item or item already existed: " <>-    show (keySize, input, inputType)--getOutputItem :: Int -> Output -> OutputType -> Get Output-getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do-    script <- getSizedBytes-    return $ output { outputRedeemScript = Just script }-getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do-    script <- getSizedBytes-    return $ output { outputWitnessScript = Just script }-getOutputItem keySize output OutBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }-getOutputItem keySize output outputType = fail $-    "Incorrect key size for output item or item already existed: " <>-    show (keySize, output, outputType)--getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))-getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)--putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put-putHDPath t = putPubKeyMap t . fmap PSBTHDPath--newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }-    deriving (Show, Eq, Generic)--instance NFData PSBTHDPath--instance Serialize PSBTHDPath where-    get = do-        VarInt valueSize <- get-        guard $ valueSize `mod` 4 == 0-        let numIndices = (fromIntegral valueSize - 4) `div` 4-        PSBTHDPath <$>-            isolate-                (fromIntegral valueSize)-                ((,) <$> getWord32le <*> getKeyIndexList numIndices)-      where-        getKeyIndexList n = replicateM n getWord32le-    put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs-      where-        bs = runPut $ putWord32le fp >> mapM_ putWord32le kis--putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put-putPubKeyMap t = void . HashMap.traverseWithKey putItem-  where-    putItem k v = put (Key (enumWord8 t) (encode k)) >> put v--enumWord8 :: Enum a => a -> Word8-enumWord8 = fromIntegral . fromEnum--word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a-word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n-word8Enum n = Left n--whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()-whenJust = maybe (return ())
+ src/Haskoin/Transaction/Psbt.hs view
@@ -0,0 +1,2312 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Psbt+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Partially signed Bitcoin transactions, the format of+-- [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki),+-- in its version 0 and in version 2, the version of+-- [BIP 370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki).+--+-- A PSBT is five magic bytes, then maps of keys and values: one global map,+-- then one map per input and one per output of the transaction. A key is a+-- type and some data. The spec gives the keys and values of the fields it+-- defines, and leaves the type 0xFC to proprietary fields, under an+-- identifier of the software that writes them.+--+-- In version 0, the global map holds the unsigned transaction. In version+-- 2, the maps hold the transaction field by field: its version and a+-- fallback lock time in the global map, the output each input spends and+-- its sequence in the map of the input, the amount and script of each+-- output in its map, so that inputs and outputs can be added. The lock+-- time is the one the inputs require, or the fallback. Here a PSBT of+-- either version has its unsigned transaction, 'unsignedTx': the one of+-- the global map, or the one the fields describe.+--+-- The maps are kept as they are read, byte for byte, so that every field+-- passes through, known or not. A map is written with its keys in order, as+-- Bitcoin Core writes it. A PSBT that breaks the spec is refused when it is+-- read: a key out of shape, a key twice in a map, a value that does not+-- decode, a field of the other version, a field version 2 requires+-- missing, bytes after the last map. So is what Bitcoin Core refuses when+-- it reads a PSBT: a key type that is not a compact size integer in its+-- shortest form, a public key that is not a point of the curve, a partial+-- signature that is not in the encoding of BIP 66 with a defined sighash+-- byte, a previous transaction that is not the one its input spends, and+-- a script tree that is not whole.+--+-- The amounts a PSBT states are claims of whoever wrote it, unless the+-- input carries its previous transaction. 'inputCoins' tells which coins+-- are checked, 'psbtFee' gives the fee from checked coins only, and the+-- signers want the previous transaction of a SegWit version 0 coin+-- ('requirePreviousTx'). The derivation records of outputs are claims+-- too: to treat an output as change, derive its key and compare.+--+-- The fields the spec defines are read and written with the functions+-- below, with the Taproot fields of+-- [BIP 371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki).+-- The roles of the spec are here too: a creator ('psbtOf'), a+-- constructor ('addInput', 'addOutput'), a combiner ('combinePsbt'), a+-- signer ('signPsbt'), a finalizer ('finalizePsbt') and a transaction+-- extractor ('psbtTransaction'), which wants every input final. An updater+-- is the functions that insert fields, and 'setInputSequence'.+module Haskoin.Transaction.Psbt+  ( -- * A PSBT+    Psbt (..),+    PsbtVersion (..),+    Key (..),+    PsbtMap,+    psbtOf,+    psbtOfMaps,+    withVersion,+    psbtId,+    combinePsbt,+    psbtTransaction,+    psbtPartialTransaction,++    -- * The inputs and outputs of version 2+    addInput,+    addOutput,+    setInputSequence,+    Modifiable (..),+    txModifiable,+    insertTxModifiable,+    deleteTxModifiable,+    fallbackLockTime,+    requiredTimeLockTime,+    insertRequiredTimeLockTime,+    requiredHeightLockTime,+    insertRequiredHeightLockTime,++    -- * Bytes and text+    psbtMagic,+    isPsbt,+    encodePsbt,+    decodePsbt,+    encodePsbtBase64,+    decodePsbtBase64,++    -- * The global fields+    Derivation (..),+    xpubRecords,+    insertXpubRecord,++    -- * The fields of an input+    nonWitnessUtxo,+    insertNonWitnessUtxo,+    witnessUtxo,+    insertWitnessUtxo,+    partialSignatures,+    hasPartialSignatures,+    insertPartialSignature,+    deletePartialSignatures,+    sigHashType,+    insertSigHashType,+    deleteSigHashType,+    inputRedeemScript,+    insertInputRedeemScript,+    inputWitnessScript,+    insertInputWitnessScript,+    inputDerivations,+    insertInputDerivation,+    finalScriptSig,+    insertFinalScriptSig,+    finalScriptWitness,+    insertFinalScriptWitness,++    -- * The Taproot fields of an input+    tapKeySig,+    insertTapKeySig,+    tapScriptSigs,+    insertTapScriptSig,+    TapLeafScript (..),+    tapLeafScripts,+    insertTapLeafScript,+    TapDerivation (..),+    inputTapDerivations,+    insertInputTapDerivation,+    inputTapInternalKey,+    insertInputTapInternalKey,+    inputTapMerkleRoot,+    insertInputTapMerkleRoot,++    -- * The fields of an output+    outputRedeemScript,+    insertOutputRedeemScript,+    outputWitnessScript,+    insertOutputWitnessScript,+    outputDerivations,+    insertOutputDerivation,+    outputTapInternalKey,+    insertOutputTapInternalKey,+    outputTapTree,+    insertOutputTapTree,+    outputTapDerivations,+    insertOutputTapDerivation,++    -- * Proprietary fields+    Proprietary (..),+    proprietaryKey,+    proprietaryOf,+    lookupProprietary,+    insertProprietary,++    -- * The coins of a PSBT+    InputCoin (..),+    inputCoins,+    psbtFee,++    -- * Signing+    Signer (..),+    defaultSigHashes,+    keySigner,+    xPrvSigner,+    signPsbt,+    afterSignature,++    -- * Finalizing+    finalizePsbt,+  )+where++import Control.DeepSeq (NFData)+import Control.Monad (ap, forM, forM_, guard, replicateM, unless, when, zipWithM)+import Crypto.Hash (Digest, SHA256, digestFromByteString)+import Crypto.Secp256k1 (Ctx, PubKey, SecKey, derivePubKey, exportXOnlyPubKey, importXOnlyPubKey, signSchnorr, verifySchnorr)+import Data.Bifunctor (first)+import Data.Bits (complement, shiftL, testBit, (.&.), (.|.))+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Base64.Types (extractBase64)+import Data.ByteString.Base64 (decodeBase64Untyped, encodeBase64')+import Data.Char (toUpper)+import Data.List (find, nubBy, stripPrefix)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, listToMaybe, mapMaybe, maybeToList)+import Data.Serialize qualified as S+import Data.Word (Word32, Word64, Word8)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash (Hash160, addressHash)+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Crypto.Signature (hashToMsg, verifyHashSig)+import Haskoin.Network.Common (VarInt (..))+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Script.Standard+import Haskoin.Transaction.Builder.Sign (SigInput (..), makeSigHash, makeSignature)+import Haskoin.Transaction.Common hiding (decodeTx)+import Haskoin.Transaction.Segwit (isSegwit)+import Haskoin.Transaction.Taproot+import Haskoin.Util+import Numeric (showHex)+import Text.Read (readMaybe)++{- A PSBT -}++-- | A key of a map: its type, and the data after the type byte. Most+-- fields have none; a derivation or a signature has the public key it is+-- of. The type of a key is a compact size integer. Every type the spec+-- defines fits one byte. A type from 253 on takes three or five bytes on+-- the wire: its first byte, 0xfd or 0xfe, is the 'keyType' here, and the+-- other bytes of the type are the start of the 'keyData', where+-- 'decodePsbt' requires them to be the shortest form of the type.+data Key = Key+  { keyType :: !Word8,+    keyData :: !ByteString+  }+  deriving (Eq, Ord, Show, Read, Generic)+  deriving anyclass (NFData)++-- | A map of a PSBT. It is written with its keys in order, the type first,+-- as Bitcoin Core writes it.+type PsbtMap = Map Key ByteString++-- | The version of a PSBT: 0, the version of BIP 174, whose global map+-- holds the unsigned transaction, or 2, the version of BIP 370, whose maps+-- hold the transaction field by field, so that inputs and outputs can be+-- added. There is no version 1.+data PsbtVersion = Version0 | Version2+  deriving (Eq, Ord, Show, Read, Generic)+  deriving anyclass (NFData)++versionNumber :: PsbtVersion -> Word32+versionNumber Version0 = 0+versionNumber Version2 = 2++-- | A PSBT: its version, the unsigned transaction, the global map, and the+-- maps of the inputs and of the outputs, one each, in the order of the+-- transaction. In version 0, the global map is kept without the unsigned+-- transaction. In version 2, the maps keep the fields that describe the+-- transaction, and the unsigned transaction is the one they describe, with+-- the lock time they determine.+--+-- In version 2 the record so holds the transaction twice. 'signPsbt' signs+-- 'unsignedTx', and 'encodePsbt' writes the maps. A record update that+-- changes one and not the other gives a PSBT whose bytes describe another+-- transaction than the one that is signed. Change the transaction of such a+-- PSBT with 'addInput', 'addOutput' and 'psbtOfMaps', which keep the two in+-- step.+data Psbt = Psbt+  { version :: !PsbtVersion,+    unsignedTx :: !Tx,+    globals :: !PsbtMap,+    inputs :: ![PsbtMap],+    outputs :: ![PsbtMap]+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++-- | The PSBT of a transaction, with nothing else, as the creator of the+-- spec makes it, in version 0: the transaction without its input scripts+-- and witness, and an empty map per input and per output. 'withVersion'+-- makes it one of version 2.+psbtOf :: Tx -> Psbt+psbtOf tx =+  Psbt+    { version = Version0,+      unsignedTx = Tx tx.version [TxIn input.outpoint B.empty input.sequence | input <- tx.inputs] tx.outputs [] tx.locktime,+      globals = Map.empty,+      inputs = Map.empty <$ tx.inputs,+      outputs = Map.empty <$ tx.outputs+    }++-- | The PSBT whose maps these are, as they are in its bytes: the global+-- map names the version, and holds the unsigned transaction in version 0.+-- The maps are checked against the spec of the version, and in version 2+-- the transaction is read from their fields.+psbtOfMaps :: PsbtMap -> [PsbtMap] -> [PsbtMap] -> Either String Psbt+psbtOfMaps globals inputs outputs = do+  (version, inputCount, outputCount) <- countsOf globals+  unless (length inputs == inputCount) $+    Left ("The PSBT has " <> show (length inputs) <> " inputs, and its global map counts " <> show inputCount)+  unless (length outputs == outputCount) $+    Left ("The PSBT has " <> show (length outputs) <> " outputs, and its global map counts " <> show outputCount)+  forM_ (zip [0 :: Int ..] inputs) $ \(i, m) -> checkMap ("input " <> show i) version inputFields m+  forM_ (zip [0 :: Int ..] outputs) $ \(i, m) -> checkMap ("output " <> show i) version outputFields m+  tx <- case version of+    Version0 -> unsignedTxOf globals+    Version2 -> transactionOf globals inputs outputs+  forM_ (zip3 [0 :: Int ..] tx.inputs inputs) $ \(i, input, m) ->+    first (("Input " <> show i <> ": ") <>) (checkPreviousTx input m)+  return $ Psbt version tx (Map.delete unsignedTxKey globals) inputs outputs++-- | The previous transaction of an input, when its map has one, must be+-- the transaction the input spends from and have the output it spends, as+-- Bitcoin Core requires when it reads a PSBT ("Non-witness UTXO does not+-- match outpoint hash", psbt.h)+checkPreviousTx :: TxIn -> PsbtMap -> Either String ()+checkPreviousTx input m = do+  parentM <- nonWitnessUtxo m+  forM_ parentM (previousOutput input)++-- | The output an input spends, from its previous transaction: the+-- transaction must be the one the input names, and have that output+previousOutput :: TxIn -> Tx -> Either String TxOut+previousOutput input parent = do+  unless (txHash parent == input.outpoint.hash) $+    Left "the previous transaction is not the one the input spends"+  maybe (Left "the input spends an output its previous transaction does not have") Right $+    listToMaybe (drop (fromIntegral input.outpoint.index) parent.outputs)++-- | The PSBT in the version given, as BIP 370 converts one. To version 2:+-- the transaction goes in the fields of the maps, its lock time as the+-- fallback. To version 0: the fields of version 2 leave the maps, and the+-- transaction keeps the lock time they determined.+withVersion :: PsbtVersion -> Psbt -> Psbt+withVersion v psbt+  | v == psbt.version = psbt+  | v == Version2 =+      Psbt+        Version2+        tx+        (withTxFields tx (insertPlainField versionType (word32le 2) psbt.globals))+        (zipWith withInputFields tx.inputs psbt.inputs)+        (zipWith withOutputFields tx.outputs psbt.outputs)+  | otherwise =+      Psbt+        Version0+        tx+        (without globalV2Types (deletePlainField versionType psbt.globals))+        (without inputV2Types <$> psbt.inputs)+        (without outputV2Types <$> psbt.outputs)+  where+    tx = psbt.unsignedTx+    without :: [Word8] -> PsbtMap -> PsbtMap+    without types = Map.filterWithKey (\key _ -> key.keyType `notElem` types)++-- | What identifies a PSBT: the id of its unsigned transaction. In version+-- 2 the sequence of every input is taken as 0 for it, as BIP 370 says,+-- since an updater may change the sequences.+psbtId :: Psbt -> TxHash+psbtId psbt = txHash $ case psbt.version of+  Version0 -> tx+  Version2 -> Tx tx.version [TxIn input.outpoint input.script 0 | input <- tx.inputs] tx.outputs tx.witness tx.locktime+  where+    tx = psbt.unsignedTx++-- | The PSBT with the fields of both, as the combiner of the spec makes+-- it. Both must be of the same version and of the same transaction. A+-- field both have, with different values, keeps the value of the first.+-- The flags of version 2 are combined as Bitcoin Core combines them:+-- inputs and outputs stay modifiable only when both say so, and a+-- SIGHASH_SINGLE signature of either is kept.+combinePsbt :: Psbt -> Psbt -> Either String Psbt+combinePsbt a b = do+  unless (a.version == b.version) $ Left "The PSBTs are not of the same version"+  unless (psbtId a == psbtId b) $ Left "The PSBTs are not of the same transaction"+  let globals = combinedFlags (Map.union a.globals b.globals)+      inputs = zipWith Map.union a.inputs b.inputs+      outputs = zipWith Map.union a.outputs b.outputs+  case a.version of+    Version0 -> return $ Psbt Version0 a.unsignedTx globals inputs outputs+    Version2 -> (\tx -> Psbt Version2 tx globals inputs outputs) <$> transactionOf globals inputs outputs+  where+    combinedFlags globals = case (flagsOf a, flagsOf b) of+      (Nothing, Nothing) -> globals+      (x, y) ->+        let fx = fromMaybe noFlags x+            fy = fromMaybe noFlags y+         in insertTxModifiable+              (Modifiable (fx.inputsModifiable && fy.inputsModifiable) (fx.outputsModifiable && fy.outputsModifiable) (fx.hasSigHashSingle || fy.hasSigHashSingle) (fx.undefinedBits .&. fy.undefinedBits))+              globals+    flagsOf :: Psbt -> Maybe Modifiable+    flagsOf psbt = either (const Nothing) id (txModifiable psbt.globals)++-- | The transaction of a PSBT whose inputs are all final, with their final+-- scripts and witnesses, as the transaction extractor of the spec builds+-- it. BIP 174 has the extractor check "whether all inputs have complete+-- scriptSigs and scriptWitnesses" first, and Bitcoin Core gives no+-- transaction otherwise (@FinalizeAndExtractPSBT@), so an input that is+-- not final is refused here. 'psbtPartialTransaction' takes such a PSBT.+--+-- The final scripts are taken as they are: nothing runs them. Finalize+-- with 'finalizePsbt', which verifies the signatures it uses, or verify+-- the transaction before it is sent.+psbtTransaction :: Psbt -> Either String Tx+psbtTransaction psbt = do+  forM_ (zip [0 :: Int ..] psbt.inputs) $ \(i, m) ->+    unless (isFinal m) $ Left ("Input " <> show i <> " is not final")+  psbtPartialTransaction psbt++-- | The transaction of a PSBT with the final script and witness of every+-- input that has them. An input without them stays unsigned, so the+-- transaction may not be valid yet: this is for looking at a PSBT that is+-- still being signed, not for sending. 'psbtTransaction' is the extractor.+psbtPartialTransaction :: Psbt -> Either String Tx+psbtPartialTransaction psbt = do+  finals <- forM (zip psbt.unsignedTx.inputs psbt.inputs) $ \(input, m) -> do+    stack <- fromMaybe [] <$> finalScriptWitness m+    let scriptSig = fromMaybe B.empty (finalScriptSig m)+    return (TxIn input.outpoint scriptSig input.sequence, stack)+  let stacks = snd <$> finals+      witness = if all null stacks then [] else stacks+  return $ Tx psbt.unsignedTx.version (fst <$> finals) psbt.unsignedTx.outputs witness psbt.unsignedTx.locktime++{- The inputs and outputs of version 2 -}++-- | The PSBT with an input added at the end, as the constructor of BIP 370+-- adds one: the output it spends and its sequence from the input given,+-- and the fields of the map given, such as the lock time it requires. A+-- PSBT of version 0 takes none, nor one whose inputs are not modifiable.+-- A lock time the input requires must fit the other inputs, and must not+-- change the lock time of the transaction once an input is signed. Added+-- at the end, the input leaves every SIGHASH_SINGLE signature paired with+-- its output. The map is checked against the spec as a map read from+-- bytes is: a field out of shape, or a previous transaction that is not+-- the one the input spends, is refused.+addInput :: TxIn -> PsbtMap -> Psbt -> Either String Psbt+addInput input m psbt = do+  flags <- modifiableOf psbt+  unless flags.inputsModifiable $ Left "The inputs of the PSBT may not be modified"+  when (any ((== input.outpoint) . (.outpoint)) psbt.unsignedTx.inputs) $+    Left "An input of the PSBT spends that output already"+  let added = withInputFields input m+      inputs = psbt.inputs <> [added]+      globals = withCounts (length inputs) (length psbt.outputs) psbt.globals+      place = "input " <> show (length psbt.inputs)+  -- The same checks as when a PSBT is read, so that the bytes written are+  -- bytes 'decodePsbt' reads+  checkMap place Version2 inputFields added+  first ((capital place <> ": ") <>) (checkPreviousTx input added)+  tx <- transactionOf globals inputs psbt.outputs+  when (any hasSignatures psbt.inputs && tx.locktime /= psbt.unsignedTx.locktime) $+    Left "An input is signed already: the lock time may not change"+  return $ Psbt Version2 tx globals inputs psbt.outputs++-- | The PSBT with an output added at the end, as the constructor of BIP+-- 370 adds one: its amount and script from the output given, and the+-- fields of the map given. A PSBT of version 0 takes none, nor one whose+-- outputs are not modifiable.+addOutput :: TxOut -> PsbtMap -> Psbt -> Either String Psbt+addOutput output m psbt = do+  flags <- modifiableOf psbt+  unless flags.outputsModifiable $ Left "The outputs of the PSBT may not be modified"+  let added = withOutputFields output m+      outputs = psbt.outputs <> [added]+      globals = withCounts (length psbt.inputs) (length outputs) psbt.globals+  checkMap ("output " <> show (length psbt.outputs)) Version2 outputFields added+  tx <- transactionOf globals psbt.inputs outputs+  return $ Psbt Version2 tx globals psbt.inputs outputs++-- | The flags of a PSBT of version 2, none set when it has no field of+-- them. A PSBT of version 0 takes no input or output.+modifiableOf :: Psbt -> Either String Modifiable+modifiableOf psbt = do+  unless (psbt.version == Version2) $ Left "A PSBT of version 0 takes no input or output: its transaction is fixed"+  fromMaybe noFlags <$> txModifiable psbt.globals++-- | Whether an input has a signature: one that is not final, a Taproot+-- one, or its final script or witness+hasSignatures :: PsbtMap -> Bool+hasSignatures m = hasPartialSignatures m || isFinal m || any ((`elem` [tapKeySigType, tapScriptSigType]) . (.keyType)) (Map.keys m)++-- | The PSBT with the sequence of the input at the index set, as an+-- updater of BIP 370 may set one. The sequences of a PSBT of version 0+-- are in its unsigned transaction, which is fixed. An input that is+-- signed keeps its sequence, since every signature commits to it.+setInputSequence :: Int -> Word32 -> Psbt -> Either String Psbt+setInputSequence i sequence' psbt = do+  unless (psbt.version == Version2) $ Left "The sequences of a PSBT of version 0 are in its unsigned transaction, which is fixed"+  unless (i >= 0 && i < length psbt.inputs) $ Left ("The PSBT has no input " <> show i)+  when (hasSignatures (psbt.inputs !! i)) $ Left ("Input " <> show i <> " is signed already: its sequence may not change")+  let inputs = [if j == i then insertPlainField sequenceType (word32le sequence') m else m | (j, m) <- zip [0 ..] psbt.inputs]+  tx <- transactionOf psbt.globals inputs psbt.outputs+  return $ Psbt Version2 tx psbt.globals inputs psbt.outputs++-- | The global map with the fields of version 2 that describe the+-- transaction: its version, its lock time as the fallback, and the number+-- of its inputs and outputs+withTxFields :: Tx -> PsbtMap -> PsbtMap+withTxFields tx =+  insertPlainField txVersionType (word32le tx.version)+    . insertPlainField fallbackLockTimeType (word32le tx.locktime)+    . withCounts (length tx.inputs) (length tx.outputs)++withCounts :: Int -> Int -> PsbtMap -> PsbtMap+withCounts inputCount outputCount =+  insertPlainField inputCountType (compactSize (fromIntegral inputCount))+    . insertPlainField outputCountType (compactSize (fromIntegral outputCount))++-- | The map of an input with the fields of version 2 that describe it: the+-- id of the transaction it spends from, the index of the output, and its+-- sequence+withInputFields :: TxIn -> PsbtMap -> PsbtMap+withInputFields input =+  insertPlainField prevTxidType (S.encode input.outpoint.hash)+    . insertPlainField outputIndexType (word32le input.outpoint.index)+    . insertPlainField sequenceType (word32le input.sequence)++-- | The map of an output with the fields of version 2 that describe it:+-- its amount and its script+withOutputFields :: TxOut -> PsbtMap -> PsbtMap+withOutputFields output =+  insertPlainField amountType (word64le output.value)+    . insertPlainField outScriptType output.script++{- The types of the spec -}++unsignedTxType, xpubType, txVersionType, fallbackLockTimeType, inputCountType, outputCountType, txModifiableType, versionType :: Word8+unsignedTxType = 0x00+xpubType = 0x01+txVersionType = 0x02+fallbackLockTimeType = 0x03+inputCountType = 0x04+outputCountType = 0x05+txModifiableType = 0x06+versionType = 0xfb++nonWitnessUtxoType, witnessUtxoType, partialSigType, sigHashTypeType, inRedeemScriptType, inWitnessScriptType, inDerivationType, finalScriptSigType, finalScriptWitnessType, tapKeySigType, tapScriptSigType, tapLeafScriptType, inTapDerivationType, inTapInternalKeyType, tapMerkleRootType :: Word8+nonWitnessUtxoType = 0x00+witnessUtxoType = 0x01+partialSigType = 0x02+sigHashTypeType = 0x03+inRedeemScriptType = 0x04+inWitnessScriptType = 0x05+inDerivationType = 0x06+finalScriptSigType = 0x07+finalScriptWitnessType = 0x08+tapKeySigType = 0x13+tapScriptSigType = 0x14+tapLeafScriptType = 0x15+inTapDerivationType = 0x16+inTapInternalKeyType = 0x17+tapMerkleRootType = 0x18++prevTxidType, outputIndexType, sequenceType, requiredTimeType, requiredHeightType :: Word8+prevTxidType = 0x0e+outputIndexType = 0x0f+sequenceType = 0x10+requiredTimeType = 0x11+requiredHeightType = 0x12++outRedeemScriptType, outWitnessScriptType, outDerivationType, amountType, outScriptType, outTapInternalKeyType, outTapTreeType, outTapDerivationType :: Word8+outRedeemScriptType = 0x00+outWitnessScriptType = 0x01+outDerivationType = 0x02+amountType = 0x03+outScriptType = 0x04+outTapInternalKeyType = 0x05+outTapTreeType = 0x06+outTapDerivationType = 0x07++-- | The types of the fields of version 2, which version 0 excludes, by+-- map+globalV2Types, inputV2Types, outputV2Types :: [Word8]+globalV2Types = [txVersionType, fallbackLockTimeType, inputCountType, outputCountType, txModifiableType]+inputV2Types = [prevTxidType, outputIndexType, sequenceType, requiredTimeType, requiredHeightType]+outputV2Types = [amountType, outScriptType]++-- | The lock times from this value are times, below it heights+lockTimeThreshold :: Word32+lockTimeThreshold = 500000000++proprietaryType :: Word8+proprietaryType = 0xfc++-- | The key of the unsigned transaction in the global map+unsignedTxKey :: Key+unsignedTxKey = Key unsignedTxType B.empty++{- Bytes -}++-- | The five bytes every PSBT starts with: "psbt" and 0xff+psbtMagic :: ByteString+psbtMagic = B.pack [0x70, 0x73, 0x62, 0x74, 0xff]++-- | Whether the bytes are those of a PSBT, by their first five+isPsbt :: ByteString -> Bool+isPsbt = B.isPrefixOf psbtMagic++-- | The bytes of a PSBT: the magic, the global map, with the unsigned+-- transaction in version 0, then the maps of the inputs and of the outputs+encodePsbt :: Psbt -> ByteString+encodePsbt psbt =+  B.concat $ psbtMagic : encodeMap globals : (encodeMap <$> psbt.inputs) <> (encodeMap <$> psbt.outputs)+  where+    globals = case psbt.version of+      Version0 -> Map.insert unsignedTxKey (S.encode psbt.unsignedTx) psbt.globals+      Version2 -> psbt.globals++-- | A map: each key and value after its length, the keys in order, then a+-- zero byte+encodeMap :: PsbtMap -> ByteString+encodeMap m =+  B.concat [sized (B.cons key.keyType key.keyData) <> sized value | (key, value) <- Map.toAscList m]+    <> B.singleton 0++-- | Bytes after their length, as a compact size integer+sized :: ByteString -> ByteString+sized bytes = compactSize (fromIntegral (B.length bytes)) <> bytes++-- | A compact size integer, in its shortest form+compactSize :: Word64 -> ByteString+compactSize = S.encode . VarInt++-- | A 32-bit integer, little-endian+word32le :: Word32 -> ByteString+word32le = S.runPut . S.putWord32le++-- | A 64-bit integer, little-endian+word64le :: Word64 -> ByteString+word64le = S.runPut . S.putWord64le++-- | The PSBT the bytes hold, or why they hold none: not a PSBT, or one that+-- breaks the spec+decodePsbt :: ByteString -> Either String Psbt+decodePsbt = parseAll psbtParser "the last map"++-- | The text of a PSBT, in base64, as the spec gives it for text+encodePsbtBase64 :: Psbt -> ByteString+encodePsbtBase64 = extractBase64 . encodeBase64' . encodePsbt++-- | The PSBT of a base64 text, around which spaces and line breaks are+-- left aside+decodePsbtBase64 :: ByteString -> Either String Psbt+decodePsbtBase64 text = do+  bytes <- first (const "Not the base64 text of a PSBT") $ decodeBase64Untyped (B.filter (`notElem` spaces) text)+  decodePsbt bytes+  where+    spaces = [0x20, 0x09, 0x0a, 0x0d]++{- Reading bytes -}++-- | A reader of bytes, which stops at the first byte out of place with a+-- sentence saying so+newtype BytesParser a = BytesParser (ByteString -> Either String (a, ByteString))++instance Functor BytesParser where+  fmap f (BytesParser p) = BytesParser $ \bytes -> do+    (a, rest) <- p bytes+    return (f a, rest)++instance Applicative BytesParser where+  pure a = BytesParser $ \bytes -> Right (a, bytes)+  (<*>) = ap++instance Monad BytesParser where+  BytesParser p >>= f = BytesParser $ \bytes -> do+    (a, rest) <- p bytes+    let BytesParser q = f a+    q rest++refuse :: String -> BytesParser a+refuse reason = BytesParser $ \_ -> Left reason++-- | Read all the bytes with the parser: nothing may follow what it reads+parseAll :: BytesParser a -> String -> ByteString -> Either String a+parseAll (BytesParser p) what bytes = do+  (a, rest) <- p bytes+  unless (B.null rest) $ Left ("Bytes follow " <> what)+  return a++-- | The next bytes, which must be there. What they are part of names+-- them in the refusal.+bytesOf :: Int -> String -> BytesParser ByteString+bytesOf n what = BytesParser $ \bytes ->+  if B.length bytes < n+    then Left ("The PSBT ends inside " <> what)+    else Right (B.splitAt n bytes)++-- | All the bytes left+restOf :: BytesParser ByteString+restOf = BytesParser $ \bytes -> Right (bytes, B.empty)++-- | A compact size integer: one byte below 0xfd, or 0xfd, 0xfe or 0xff and+-- two, four or eight bytes, little-endian, in the shortest form, as+-- Bitcoin Core reads it+compactSizeOf :: String -> BytesParser Word64+compactSizeOf what = do+  first' <- B.head <$> bytesOf 1 what+  case first' of+    0xfd -> longer 2 0xfd+    0xfe -> longer 4 0x10000+    0xff -> longer 8 0x100000000+    _ -> return (fromIntegral first')+  where+    longer :: Int -> Word64 -> BytesParser Word64+    longer n least = do+      value <- B.foldr (\b acc -> acc `shiftL` 8 .|. fromIntegral b) 0 <$> bytesOf n what+      when (value < least) $ refuse ("A length in " <> what <> " is not in its shortest form")+      return value++-- | A length, which must fit in memory+lengthOf :: String -> BytesParser Int+lengthOf what = do+  n <- compactSizeOf what+  when (n > fromIntegral (maxBound :: Int)) $ refuse ("A length in " <> what <> " is beyond any PSBT")+  return (fromIntegral n)++-- | Bytes after their length+sizedBytes :: String -> BytesParser ByteString+sizedBytes what = lengthOf what >>= (`bytesOf` what)++-- | A map: keys and values up to a zero byte, each key once+mapOf :: String -> BytesParser PsbtMap+mapOf what = go Map.empty+  where+    go m = do+      keyLength <- lengthOf what+      if keyLength == 0+        then return m+        else do+          keyBytes <- bytesOf keyLength what+          either refuse return (checkKeyType what keyBytes)+          value <- sizedBytes what+          let key = Key (B.head keyBytes) (B.tail keyBytes)+          when (Map.member key m) $+            refuse (capital what <> " has the key " <> showKey key <> " twice")+          go (Map.insert key value m)++-- | The type of a key is a compact size integer at the start of the key.+-- Every type the spec defines is below 0xfd and so is the first byte. A+-- type from 0xfd on takes three, five or nine bytes: it must be whole, in+-- its shortest form, and at most 'maxCompactSize', as Bitcoin Core reads+-- it (@ReadCompactSize@ in serialize.h). Such a key is kept as its first+-- byte and, as its data, the bytes after it.+checkKeyType :: String -> ByteString -> Either String ()+checkKeyType what keyBytes = case B.unpack (B.take 1 keyBytes) of+  [0xfd] -> longer 2 0xfd+  [0xfe] -> longer 4 0x10000+  [0xff] -> longer 8 0x100000000+  _ -> Right ()+  where+    longer :: Int -> Word64 -> Either String ()+    longer n least = do+      let bytes = B.take n (B.drop 1 keyBytes)+      unless (B.length bytes == n) $+        Left (capital what <> " has a key that ends inside its type")+      let value = B.foldr (\b acc -> acc `shiftL` 8 .|. fromIntegral b) 0 bytes+      when (value < least) $+        Left (capital what <> " has a key whose type is not in its shortest form")+      when (value > maxCompactSize) $+        Left (capital what <> " has a key whose type is beyond any type")++-- | The largest compact size integer Bitcoin Core reads: its @MAX_SIZE@+maxCompactSize :: Word64+maxCompactSize = 0x02000000++psbtParser :: BytesParser Psbt+psbtParser = do+  magic <- bytesOf 5 "the magic bytes"+  unless (magic == psbtMagic) $ refuse "Not a PSBT: the bytes do not start with the magic bytes of BIP 174"+  globals <- mapOf "the global map"+  (_, inputCount, outputCount) <- either refuse return (countsOf globals)+  inputs <- forM [0 .. inputCount - 1] $ \i -> mapOf ("input " <> show i)+  outputs <- forM [0 .. outputCount - 1] $ \i -> mapOf ("output " <> show i)+  either refuse return (psbtOfMaps globals inputs outputs)++-- | The version a global map names, and the number of inputs and of+-- outputs: those of the unsigned transaction in version 0, those the map+-- counts in version 2. The map is checked against the spec of the version+-- first.+countsOf :: PsbtMap -> Either String (PsbtVersion, Int, Int)+countsOf globals = do+  version <- versionOf globals+  checkMap "the global map" version globalFields globals+  case version of+    Version0 -> do+      tx <- unsignedTxOf globals+      return (Version0, length tx.inputs, length tx.outputs)+    Version2 -> do+      _ <- requiredField "The global map" "transaction version" txVersionType decodeWord32le globals+      inputCount <- requiredField "The global map" "input count" inputCountType decodeCount globals+      outputCount <- requiredField "The global map" "output count" outputCountType decodeCount globals+      return (Version2, inputCount, outputCount)++-- | The version the global map names with PSBT_GLOBAL_VERSION: 0 when it+-- names none+versionOf :: PsbtMap -> Either String PsbtVersion+versionOf globals = case plainField versionType globals of+  Nothing -> Right Version0+  Just bytes -> do+    n <- first ("The global map: the value of the key 0xfb " <>) (decodeWord32le bytes)+    case n of+      0 -> Right Version0+      2 -> Right Version2+      _ -> Left ("The PSBT is of version " <> show n <> ": only versions 0 and 2 are supported")++-- | The unsigned transaction of the global map of version 0, which must be+-- there, in the serialization without witness, with no input script. It+-- is read with 'parseLegacyTx', as Bitcoin Core reads it without allowing+-- a witness, so that a transaction with no inputs, which the spec allows,+-- is not taken for the extended format of BIP 144.+unsignedTxOf :: PsbtMap -> Either String Tx+unsignedTxOf globals = do+  bytes <- maybe (Left "The global map has no unsigned transaction") Right (Map.lookup unsignedTxKey globals)+  tx <- case S.runGetState parseLegacyTx bytes 0 of+    Right (parsed, rest) | B.null rest -> Right parsed+    _ -> Left "The unsigned transaction does not decode"+  unless (null tx.witness && S.encode tx == bytes) $+    Left "The unsigned transaction is not in the serialization without witness"+  unless (all (B.null . (.script)) tx.inputs) $+    Left "The unsigned transaction has an input with a script"+  return tx++-- | The transaction the fields of version 2 describe: its version from+-- the global map, the lock time the inputs require or the fallback, an+-- input per input map from the id of the transaction it spends from, the+-- index of the output and the sequence, final when the map has none, and+-- an output per output map from its amount and script+transactionOf :: PsbtMap -> [PsbtMap] -> [PsbtMap] -> Either String Tx+transactionOf globals inputs outputs = do+  version <- requiredField "The global map" "transaction version" txVersionType decodeWord32le globals+  locktime <- lockTimeOf globals inputs+  txInputs <- forM (zip [0 :: Int ..] inputs) $ \(i, m) -> do+    let place = "Input " <> show i+    hash <- requiredField place "previous transaction id" prevTxidType decodeTxHash m+    index <- requiredField place "output index" outputIndexType decodeWord32le m+    sequence' <- fromMaybe 0xffffffff <$> optionalField "The sequence" sequenceType decodeWord32le m+    return (TxIn (OutPoint hash index) B.empty sequence')+  txOutputs <- forM (zip [0 :: Int ..] outputs) $ \(i, m) -> do+    let place = "Output " <> show i+    value <- requiredField place "amount" amountType decodeWord64le m+    script <- requiredField place "script" outScriptType Right m+    return (TxOut value script)+  return (Tx version txInputs txOutputs [] locktime)++-- | The lock time of a PSBT of version 2, as BIP 370 determines it. An+-- input may require a lock time as a height, as a time, or either, with+-- both. When no input requires one, it is the fallback lock time of the+-- global map, 0 without one. Otherwise it is the greatest of the kind every+-- input that requires one takes, a height when every one takes a height.+-- When some inputs take a height only and others a time only, no lock time+-- fits them.+lockTimeOf :: PsbtMap -> [PsbtMap] -> Either String Word32+lockTimeOf globals inputs = do+  fallback <- fromMaybe 0 <$> fallbackLockTime globals+  required <- forM inputs $ \m -> (,) <$> requiredTimeLockTime m <*> requiredHeightLockTime m+  let asking = [r | r@(time, height) <- required, isJust time || isJust height]+      heights = [height | (_, Just height) <- asking]+      times = [time | (Just time, _) <- asking]+  case () of+    _+      | null asking -> Right fallback+      | length heights == length asking -> Right (maximum heights)+      | length times == length asking -> Right (maximum times)+      | otherwise -> Left "No lock time fits every input: some require a height, others a time"++-- | A field a map must have, decoded. Its absence, or a value out of shape,+-- names the map and the field.+requiredField :: String -> String -> Word8 -> (ByteString -> Either String a) -> PsbtMap -> Either String a+requiredField what name t decode m = do+  bytes <- maybe (Left (what <> " has no " <> name)) Right (plainField t m)+  first ((what <> ": the " <> name <> " ") <>) (decode bytes)++-- | A field a map may have, decoded, named in a refusal+optionalField :: String -> Word8 -> (ByteString -> Either String a) -> PsbtMap -> Either String (Maybe a)+optionalField name t decode = traverse (first ((name <> " ") <>) . decode) . plainField t++-- | A value decoded from all the bytes given, or a refusal naming it+decodeFully :: (S.Serialize a) => String -> ByteString -> Either String a+decodeFully what bytes = case S.runGetState S.get bytes 0 of+  Right (a, rest) | B.null rest -> Right a+  _ -> Left (what <> " does not decode")++-- | A sentence that starts with the words given+capital :: String -> String+capital (c : rest) = toUpper c : rest+capital [] = []++-- | A key as a refusal names it: its type in hexadecimal, and its data.+-- Only the first 'maxShownKeyData' bytes of the data are written, then+-- its length, so that whoever writes a PSBT cannot make a refusal as+-- long as they like.+showKey :: Key -> String+showKey key =+  "0x" <> hexByte key.keyType <> (if B.null key.keyData then "" else " with the data " <> shown)+  where+    hexByte b = (if b < 16 then "0" else "") <> showHex b ""+    hexOf = concatMap hexByte . B.unpack+    shown+      | B.length key.keyData <= maxShownKeyData = hexOf key.keyData+      | otherwise = hexOf (B.take maxShownKeyData key.keyData) <> "… (" <> show (B.length key.keyData) <> " bytes)"++-- | How many bytes of the data of a key a refusal writes: enough for a+-- compressed public key, which is 33 bytes+maxShownKeyData :: Int+maxShownKeyData = 40++{- The shapes of the spec -}++-- | The data of a key of the spec: none, a public key, an extended public+-- key, an x-only public key, an x-only public key with a leaf hash, or a+-- control block+data KeyShape = NoData | PublicKeyData | XpubData | XOnlyKeyData | KeyAndLeafData | ControlBlockData++-- | A field the spec defines: the shape of its key, the versions of the+-- PSBT that may hold it, and a check of its value given the data of the+-- key+data Field = Field+  { shape :: KeyShape,+    versions :: [PsbtVersion],+    checkValue :: ByteString -> ByteString -> Either String ()+  }++-- | The fields the spec defines for a map, by type+type Fields = Map Word8 Field++-- | A field of both versions whose key carries no data+plain :: (ByteString -> Either String ()) -> Field+plain = Field NoData [Version0, Version2] . const++-- | A field of version 2 only whose key carries no data+plain2 :: (ByteString -> Either String ()) -> Field+plain2 = Field NoData [Version2] . const++-- | Check every key of a map against the spec: the keys of the fields it+-- defines must be of the version, must have their shape and their values+-- must decode, and a proprietary key must have its identifier and subtype.+-- Other keys are not the spec's to check.+checkMap :: String -> PsbtVersion -> Fields -> PsbtMap -> Either String ()+checkMap what version fields m = forM_ (Map.toList m) $ \(key, value) ->+  first ((capital what <> ": ") <>) $+    case Map.lookup key.keyType fields of+      Just field -> do+        unless (version `elem` field.versions) $+          Left ("the key " <> showKey key <> " is not of PSBT version " <> show (versionNumber version))+        checkShape field.shape key+        first (("the value of the key " <> showKey key <> " ") <>) $ field.checkValue key.keyData value+      Nothing+        | key.keyType == proprietaryType -> () <$ proprietaryOf key+        | otherwise -> return ()++checkShape :: KeyShape -> Key -> Either String ()+checkShape shape key = case shape of+  NoData -> unless (B.null key.keyData) $ Left ("the key " <> showKey key <> " carries data")+  PublicKeyData -> unless (isPublicKeyBytes key.keyData) $ Left ("the key " <> showKey key <> " is not a public key")+  XpubData -> unless (isXpubBytes key.keyData) $ Left ("the key " <> showKey key <> " is not an extended public key")+  XOnlyKeyData -> unless (B.length key.keyData == 32) $ Left ("the key " <> showKey key <> " is not an x-only public key")+  KeyAndLeafData -> unless (B.length key.keyData == 64) $ Left ("the key " <> showKey key <> " is not an x-only public key and a leaf hash")+  ControlBlockData -> unless (isControlBlockLength (B.length key.keyData)) $ Left ("the key " <> showKey key <> " is not a control block")++-- | Whether a control block may have the length: 33 bytes plus 32 per+-- node, with at most 'maxControlBlockNodes' nodes+isControlBlockLength :: Int -> Bool+isControlBlockLength len =+  len >= 33 && (len - 33) `mod` 32 == 0 && (len - 33) `div` 32 <= maxControlBlockNodes++-- | Whether the bytes are a public key, compressed or not: they have the+-- length and the first byte of one, and are a point of the curve, as+-- Bitcoin Core requires of the keys of a PSBT (@IsFullyValid@ in psbt.h).+-- The point is checked through the 'Read' instance of the binding, which+-- parses the hexadecimal form of a key without a context.+isPublicKeyBytes :: ByteString -> Bool+isPublicKeyBytes bytes = shaped && isJust (readMaybe (show (encodeHex bytes)) :: Maybe PubKey)+  where+    shaped = case B.unpack (B.take 1 bytes) of+      [0x02] -> B.length bytes == 33+      [0x03] -> B.length bytes == 33+      [0x04] -> B.length bytes == 65+      _ -> False++-- | Whether the bytes are an extended public key as BIP 32 serializes+-- one: 78 bytes that end with a compressed public key+isXpubBytes :: ByteString -> Bool+isXpubBytes bytes = B.length bytes == 78 && B.length key == 33 && isPublicKeyBytes key+  where+    key = B.drop 45 bytes++-- | Whether the bytes are a signature as a script holds one: in the+-- encoding BIP 66 requires, with its sighash byte last. This is+-- @IsValidSignatureEncoding@ of Bitcoin Core (script/interpreter.cpp),+-- line for line. It checks the shape of the bytes, not the range of the+-- two numbers.+isSignatureEncoding :: ByteString -> Bool+isSignatureEncoding sig =+  -- Minimum and maximum size+  size >= 9+    && size <= 73+    -- A signature is of type 0x30 (compound)+    && at 0 == 0x30+    -- The length covers the entire signature, without the sighash byte+    && fromIntegral (at 1) == size - 3+    -- The length of the S element is still inside the signature+    && 5 + lenR < size+    -- The lengths of the two elements add up to the signature+    && lenR + lenS + 7 == size+    -- R is an integer, not empty, not negative, with no needless zero byte+    && at 2 == 0x02+    && lenR /= 0+    && at 4 .&. 0x80 == 0+    && not (lenR > 1 && at 4 == 0x00 && at 5 .&. 0x80 == 0)+    -- S is an integer, not empty, not negative, with no needless zero byte+    && at (lenR + 4) == 0x02+    && lenS /= 0+    && at (lenR + 6) .&. 0x80 == 0+    && not (lenS > 1 && at (lenR + 6) == 0x00 && at (lenR + 7) .&. 0x80 == 0)+  where+    size = B.length sig+    at = B.index sig+    lenR = fromIntegral (at 3) :: Int+    lenS = fromIntegral (at (5 + lenR)) :: Int++-- The unsigned transaction is checked apart, by 'unsignedTxOf', and the+-- version by 'versionOf'+globalFields :: Fields+globalFields =+  Map.fromList+    [ (unsignedTxType, Field NoData [Version0] (const anyValue)),+      (xpubType, Field XpubData [Version0, Version2] xpubValue),+      (txVersionType, plain2 word32Value),+      (fallbackLockTimeType, plain2 word32Value),+      (inputCountType, plain2 countValue),+      (outputCountType, plain2 countValue),+      (txModifiableType, plain2 flagsValue),+      (versionType, plain word32Value)+    ]+  where+    -- The path of an extended key has one index per level of its depth,+    -- the fifth byte of the key+    xpubValue keyData value = do+      derivation <- decodeDerivation value+      unless (length (pathToList derivation.path) == fromIntegral (B.index keyData 4)) $+        Left "has a path that is not as deep as the key"+    countValue value = () <$ decodeCount value+    flagsValue value = unless (B.length value == 1) $ Left "is not a byte of flags"++inputFields :: Fields+inputFields =+  Map.fromList+    [ (nonWitnessUtxoType, plain txValue),+      (witnessUtxoType, plain txOutValue),+      (partialSigType, Field PublicKeyData [Version0, Version2] (const partialSigValue)),+      (sigHashTypeType, plain word32Value),+      (inRedeemScriptType, plain scriptValue),+      (inWitnessScriptType, plain scriptValue),+      (inDerivationType, Field PublicKeyData [Version0, Version2] (const derivationValue)),+      (finalScriptSigType, plain anyValue),+      (finalScriptWitnessType, plain witnessValue),+      (tapKeySigType, plain tapSigValue),+      (tapScriptSigType, Field KeyAndLeafData [Version0, Version2] (const tapSigValue)),+      (tapLeafScriptType, Field ControlBlockData [Version0, Version2] (const leafScriptValue)),+      (inTapDerivationType, Field XOnlyKeyData [Version0, Version2] (const tapDerivationValue)),+      (inTapInternalKeyType, plain xOnlyKeyValue),+      (tapMerkleRootType, plain hashValue),+      (prevTxidType, plain2 txHashValue),+      (outputIndexType, plain2 word32Value),+      (sequenceType, plain2 word32Value),+      (requiredTimeType, plain2 timeLockValue),+      (requiredHeightType, plain2 heightLockValue)+    ]+  where+    txValue value = () <$ (decodeTx value :: Either String Tx)+    -- Bitcoin Core checks a partial signature with the flags DERSIG and+    -- STRICTENC when it reads a PSBT: the encoding of BIP 66, and a sighash+    -- byte that is ALL, NONE or SINGLE, with ANYONECANPAY or not. The fork+    -- id flag is let through here, since the decoder knows no network; the+    -- signer and the finalizer check it.+    partialSigValue value =+      unless (isSignatureEncoding value && not (isSigHashUnknown (fromIntegral (B.last value)))) $+        Left "is not a signature in the encoding of BIP 66 with a defined sighash byte"+    txOutValue value = () <$ (decodeTxOut value :: Either String TxOut)+    witnessValue value = () <$ decodeWitness value+    txHashValue value = () <$ decodeTxHash value+    timeLockValue value = do+      time <- decodeWord32le value+      unless (time >= lockTimeThreshold) $ Left ("is a time lock below " <> show lockTimeThreshold)+    heightLockValue value = do+      height <- decodeWord32le value+      unless (height > 0 && height < lockTimeThreshold) $ Left ("is not a height between 1 and " <> show (lockTimeThreshold - 1))++outputFields :: Fields+outputFields =+  Map.fromList+    [ (outRedeemScriptType, plain scriptValue),+      (outWitnessScriptType, plain scriptValue),+      (outDerivationType, Field PublicKeyData [Version0, Version2] (const derivationValue)),+      (amountType, plain2 amountValue),+      (outScriptType, plain2 anyValue),+      (outTapInternalKeyType, plain xOnlyKeyValue),+      (outTapTreeType, plain tapTreeValue),+      (outTapDerivationType, Field XOnlyKeyData [Version0, Version2] (const tapDerivationValue))+    ]+  where+    amountValue value = () <$ decodeWord64le value++tapSigValue :: ByteString -> Either String ()+tapSigValue value = () <$ first ("is not a Taproot signature: " <>) (decodeTaprootSig value)++leafScriptValue :: ByteString -> Either String ()+leafScriptValue value = when (B.null value) $ Left "has no leaf version"++tapDerivationValue :: ByteString -> Either String ()+tapDerivationValue value = () <$ decodeTapDerivation value++xOnlyKeyValue :: ByteString -> Either String ()+xOnlyKeyValue value = unless (B.length value == 32) $ Left "is not an x-only public key"++hashValue :: ByteString -> Either String ()+hashValue value = unless (B.length value == 32) $ Left "is not a 32-byte hash"++-- | A script tree as Bitcoin Core accepts one when it reads a PSBT: not+-- empty, no leaf deeper than 128, every leaf version even, and the leaves+-- those of one whole tree in depth-first order+tapTreeValue :: ByteString -> Either String ()+tapTreeValue value = do+  leaves <- decodeTapTree value+  when (null leaves) $ Left "is an empty script tree"+  when (any ((> maxControlBlockNodes) . fromIntegral . (.depth)) leaves) $+    Left ("has a leaf deeper than " <> show maxControlBlockNodes)+  when (any (odd . (.version)) leaves) $ Left "has a leaf with an odd leaf version"+  when (isNothing (tapTreeFromLeaves leaves)) $ Left "is not a whole script tree"++word32Value :: ByteString -> Either String ()+word32Value value = () <$ decodeWord32le value++anyValue :: ByteString -> Either String ()+anyValue _ = Right ()++derivationValue :: ByteString -> Either String ()+derivationValue value = () <$ decodeDerivation value++scriptValue :: ByteString -> Either String ()+scriptValue value = () <$ decodeScript value++decodeTx :: ByteString -> Either String Tx+decodeTx = decodeFully "is not a transaction: it"++decodeTxOut :: ByteString -> Either String TxOut+decodeTxOut = decodeFully "is not an output: it"++decodeScript :: ByteString -> Either String Script+decodeScript = decodeFully "is not a script: it"++decodeWord32le :: ByteString -> Either String Word32+decodeWord32le bytes+  | B.length bytes == 4 = Right $ B.foldr (\b acc -> acc `shiftL` 8 .|. fromIntegral b) 0 bytes+  | otherwise = Left "is not a 32-bit integer"++decodeWord64le :: ByteString -> Either String Word64+decodeWord64le bytes+  | B.length bytes == 8 = Right $ B.foldr (\b acc -> acc `shiftL` 8 .|. fromIntegral b) 0 bytes+  | otherwise = Left "is not a 64-bit integer"++decodeTxHash :: ByteString -> Either String TxHash+decodeTxHash = decodeFully "is not a transaction id: it"++-- | A count, as a compact size integer in its shortest form+decodeCount :: ByteString -> Either String Int+decodeCount bytes = do+  n <- first (const "is not a count") (parseAll (compactSizeOf "a count") "a count" bytes)+  when (n > fromIntegral (maxBound :: Int)) $ Left "is a count beyond any PSBT"+  return (fromIntegral n)++-- | A witness stack: the number of items, then each item after its length+decodeWitness :: ByteString -> Either String WitnessStack+decodeWitness = parseAll stack "the final witness"+  where+    stack = do+      count <- lengthOf "the final witness"+      replicateM count (sizedBytes "the final witness")++encodeWitness :: WitnessStack -> ByteString+encodeWitness stack = compactSize (fromIntegral (length stack)) <> B.concat (sized <$> stack)++{- Derivations -}++-- | How a key derives: the fingerprint of the key the path starts from,+-- the master key's as the spec wants it, and the path from it. A key+-- whose origin is unknown is named by its own fingerprint and an empty+-- path, or by the fingerprint of an ancestor and the path from it.+data Derivation = Derivation+  { fingerprint :: !Fingerprint,+    path :: !DerivPath+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++encodeDerivation :: Derivation -> ByteString+encodeDerivation derivation =+  S.encode derivation.fingerprint <> B.concat (word32le <$> pathToList derivation.path)++-- | A derivation: four bytes of fingerprint, then four per index of the+-- path, little-endian+decodeDerivation :: ByteString -> Either String Derivation+decodeDerivation bytes = do+  unless (B.length bytes >= 4 && (B.length bytes - 4) `mod` 4 == 0) $+    Left "is not a fingerprint and a path"+  let (fingerprintBytes, pathBytes) = B.splitAt 4 bytes+  fingerprint <- decodeFully "is not a fingerprint: it" fingerprintBytes+  indices <- mapM decodeWord32le (chunksOf 4 pathBytes)+  return $ Derivation fingerprint (listToPath indices)++chunksOf :: Int -> ByteString -> [ByteString]+chunksOf n bytes+  | B.null bytes = []+  | otherwise = let (chunk, rest) = B.splitAt n bytes in chunk : chunksOf n rest++-- | The records of the type whose keys are public keys, in the order of+-- the map, with their values decoded+publicKeyRecords :: Word8 -> Ctx -> (ByteString -> Either String a) -> PsbtMap -> Either String [(PublicKey, a)]+publicKeyRecords t ctx decodeValue m =+  forM [(key, value) | (key, value) <- Map.toAscList m, key.keyType == t] $ \(key, value) -> do+    public <- first (const ("The key " <> showKey key <> " is not a public key")) (unmarshal ctx key.keyData)+    decoded <- first (("The value of the key " <> showKey key <> " ") <>) (decodeValue value)+    return (public, decoded)++derivationsOf :: Word8 -> Ctx -> PsbtMap -> Either String [(PublicKey, Derivation)]+derivationsOf t ctx = publicKeyRecords t ctx decodeDerivation++insertDerivation :: Word8 -> Ctx -> PublicKey -> Derivation -> PsbtMap -> PsbtMap+insertDerivation t ctx public derivation = Map.insert (Key t (marshal ctx public)) (encodeDerivation derivation)++-- | The derivations of the keys of an input: PSBT_IN_BIP32_DERIVATION+inputDerivations :: Ctx -> PsbtMap -> Either String [(PublicKey, Derivation)]+inputDerivations = derivationsOf inDerivationType++insertInputDerivation :: Ctx -> PublicKey -> Derivation -> PsbtMap -> PsbtMap+insertInputDerivation = insertDerivation inDerivationType++-- | The derivations of the keys of an output: PSBT_OUT_BIP32_DERIVATION.+-- A record is a claim of whoever wrote the PSBT. To treat an output as+-- change, derive the key and compare it with the script of the output.+outputDerivations :: Ctx -> PsbtMap -> Either String [(PublicKey, Derivation)]+outputDerivations = derivationsOf outDerivationType++insertOutputDerivation :: Ctx -> PublicKey -> Derivation -> PsbtMap -> PsbtMap+insertOutputDerivation = insertDerivation outDerivationType++-- | The extended public keys of the global map, with their derivations:+-- PSBT_GLOBAL_XPUB. Those of another network, by their version bytes, are+-- left aside.+xpubRecords :: Network -> Ctx -> PsbtMap -> Either String [(XPubKey, Derivation)]+xpubRecords net ctx m =+  fmap catMaybes . forM [(key, value) | (key, value) <- Map.toAscList m, key.keyType == xpubType] $ \(key, value) -> do+    derivation <- first (("The value of the key " <> showKey key <> " ") <>) (decodeDerivation value)+    return $ (\xpub -> (xpub, derivation)) <$> eitherToMaybe (unmarshal (net, ctx) key.keyData)++insertXpubRecord :: Network -> Ctx -> XPubKey -> Derivation -> PsbtMap -> PsbtMap+insertXpubRecord net ctx xpub derivation = Map.insert (Key xpubType (marshal (net, ctx) xpub)) (encodeDerivation derivation)++{- The fields of an input and of an output -}++-- | The field of a type whose key carries no data, if the map has it+plainField :: Word8 -> PsbtMap -> Maybe ByteString+plainField t = Map.lookup (Key t B.empty)++insertPlainField :: Word8 -> ByteString -> PsbtMap -> PsbtMap+insertPlainField t = Map.insert (Key t B.empty)++deletePlainField :: Word8 -> PsbtMap -> PsbtMap+deletePlainField t = Map.delete (Key t B.empty)++-- | The whole transaction the input spends from: PSBT_IN_NON_WITNESS_UTXO+nonWitnessUtxo :: PsbtMap -> Either String (Maybe Tx)+nonWitnessUtxo = traverse (first ("The previous transaction " <>) . decodeTx) . plainField nonWitnessUtxoType++insertNonWitnessUtxo :: Tx -> PsbtMap -> PsbtMap+insertNonWitnessUtxo = insertPlainField nonWitnessUtxoType . S.encode++-- | The output the input spends, alone: PSBT_IN_WITNESS_UTXO+witnessUtxo :: PsbtMap -> Either String (Maybe TxOut)+witnessUtxo = traverse (first ("The witness output " <>) . decodeTxOut) . plainField witnessUtxoType++insertWitnessUtxo :: TxOut -> PsbtMap -> PsbtMap+insertWitnessUtxo = insertPlainField witnessUtxoType . S.encode++-- | The signatures of the input that are not final, by the public key each+-- is of, in the order of the map: PSBT_IN_PARTIAL_SIG. A signature is as+-- it goes in a script, with its sighash byte last.+partialSignatures :: Ctx -> PsbtMap -> Either String [(PublicKey, ByteString)]+partialSignatures ctx = publicKeyRecords partialSigType ctx Right++-- | Whether the input carries a signature that is not final+hasPartialSignatures :: PsbtMap -> Bool+hasPartialSignatures = any ((== partialSigType) . (.keyType)) . Map.keys++insertPartialSignature :: Ctx -> PublicKey -> ByteString -> PsbtMap -> PsbtMap+insertPartialSignature ctx public = Map.insert (Key partialSigType (marshal ctx public))++-- | The map without its signatures that are not final, as a finalizer+-- leaves it+deletePartialSignatures :: PsbtMap -> PsbtMap+deletePartialSignatures = Map.filterWithKey (\key _ -> key.keyType /= partialSigType)++-- | The sighash the input is to be signed with: PSBT_IN_SIGHASH_TYPE+sigHashType :: PsbtMap -> Either String (Maybe SigHash)+sigHashType = traverse (fmap fromIntegral . first ("The sighash type " <>) . decodeWord32le) . plainField sigHashTypeType++insertSigHashType :: SigHash -> PsbtMap -> PsbtMap+insertSigHashType = insertPlainField sigHashTypeType . word32le . fromIntegral++-- | The map without the sighash the input was to be signed with, as a+-- finalizer leaves it+deleteSigHashType :: PsbtMap -> PsbtMap+deleteSigHashType = deletePlainField sigHashTypeType++-- | The script of a type whose key carries no data, if the map has it+scriptField :: Word8 -> String -> PsbtMap -> Either String (Maybe Script)+scriptField t what = traverse (first ((what <> " ") <>) . decodeScript) . plainField t++insertScriptField :: Word8 -> Script -> PsbtMap -> PsbtMap+insertScriptField t = insertPlainField t . S.encode++-- | The redeem script of the P2SH coin an input spends:+-- PSBT_IN_REDEEM_SCRIPT+inputRedeemScript :: PsbtMap -> Either String (Maybe Script)+inputRedeemScript = scriptField inRedeemScriptType "The redeem script"++insertInputRedeemScript :: Script -> PsbtMap -> PsbtMap+insertInputRedeemScript = insertScriptField inRedeemScriptType++-- | The witness script of the P2WSH coin an input spends:+-- PSBT_IN_WITNESS_SCRIPT+inputWitnessScript :: PsbtMap -> Either String (Maybe Script)+inputWitnessScript = scriptField inWitnessScriptType "The witness script"++insertInputWitnessScript :: Script -> PsbtMap -> PsbtMap+insertInputWitnessScript = insertScriptField inWitnessScriptType++-- | The input script of a signed input: PSBT_IN_FINAL_SCRIPTSIG+finalScriptSig :: PsbtMap -> Maybe ByteString+finalScriptSig = plainField finalScriptSigType++insertFinalScriptSig :: ByteString -> PsbtMap -> PsbtMap+insertFinalScriptSig = insertPlainField finalScriptSigType++-- | The witness of a signed input: PSBT_IN_FINAL_SCRIPTWITNESS+finalScriptWitness :: PsbtMap -> Either String (Maybe WitnessStack)+finalScriptWitness = traverse decodeWitness . plainField finalScriptWitnessType++insertFinalScriptWitness :: WitnessStack -> PsbtMap -> PsbtMap+insertFinalScriptWitness = insertPlainField finalScriptWitnessType . encodeWitness++-- | The redeem script of a P2SH output: PSBT_OUT_REDEEM_SCRIPT+outputRedeemScript :: PsbtMap -> Either String (Maybe Script)+outputRedeemScript = scriptField outRedeemScriptType "The redeem script"++insertOutputRedeemScript :: Script -> PsbtMap -> PsbtMap+insertOutputRedeemScript = insertScriptField outRedeemScriptType++-- | The witness script of a P2WSH output: PSBT_OUT_WITNESS_SCRIPT+outputWitnessScript :: PsbtMap -> Either String (Maybe Script)+outputWitnessScript = scriptField outWitnessScriptType "The witness script"++insertOutputWitnessScript :: Script -> PsbtMap -> PsbtMap+insertOutputWitnessScript = insertScriptField outWitnessScriptType++{- The Taproot fields of BIP 371 -}++-- | The key path signature of a Taproot input: PSBT_IN_TAP_KEY_SIG+tapKeySig :: PsbtMap -> Either String (Maybe TaprootSignature)+tapKeySig = optionalField "The key path signature" tapKeySigType decodeTaprootSig++insertTapKeySig :: TaprootSignature -> PsbtMap -> PsbtMap+insertTapKeySig = insertPlainField tapKeySigType . encodeTaprootSig++-- | The script path signatures of a Taproot input, each by the x-only key+-- and the leaf hash it is for: PSBT_IN_TAP_SCRIPT_SIG+tapScriptSigs :: Ctx -> PsbtMap -> Either String [((PubKey, Digest SHA256), TaprootSignature)]+tapScriptSigs ctx m =+  forM [(key, value) | (key, value) <- Map.toAscList m, key.keyType == tapScriptSigType] $ \(key, value) -> do+    let (keyBytes, leafBytes) = B.splitAt 32 key.keyData+    leafKey <- maybe (Left ("The key " <> showKey key <> " is not an x-only public key and a leaf hash")) Right $ importXOnlyPubKey ctx keyBytes+    leaf <- maybe (Left ("The key " <> showKey key <> " is not an x-only public key and a leaf hash")) Right $ digestOf leafBytes+    signature <- first (("The value of the key " <> showKey key <> " ") <>) (decodeTaprootSig value)+    return ((leafKey, leaf), signature)++insertTapScriptSig :: Ctx -> PubKey -> Digest SHA256 -> TaprootSignature -> PsbtMap -> PsbtMap+insertTapScriptSig ctx leafKey leaf signature =+  Map.insert (Key tapScriptSigType (exportXOnlyPubKey ctx leafKey <> BA.convert leaf)) (encodeTaprootSig signature)++-- | A leaf script a Taproot input may be spent with, and the control+-- block that commits it to the output key: PSBT_IN_TAP_LEAF_SCRIPT+data TapLeafScript = TapLeafScript+  { controlBlock :: !ByteString,+    script :: !Script,+    leafVersion :: !TapLeafVersion+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++-- | The leaf scripts of a Taproot input, with their control blocks:+-- PSBT_IN_TAP_LEAF_SCRIPT+tapLeafScripts :: Ctx -> PsbtMap -> Either String [TapLeafScript]+tapLeafScripts ctx m =+  forM [(key, value) | (key, value) <- Map.toAscList m, key.keyType == tapLeafScriptType] $ \(key, value) -> do+    _ <- first (const ("The key " <> showKey key <> " is not a control block")) (decodeControlBlock ctx key.keyData)+    when (B.null value) $ Left ("The value of the key " <> showKey key <> " has no leaf version")+    script <- first (("The value of the key " <> showKey key <> " ") <>) (decodeScript (B.init value))+    return (TapLeafScript key.keyData script (B.last value))++insertTapLeafScript :: TapLeafScript -> PsbtMap -> PsbtMap+insertTapLeafScript leaf =+  Map.insert (Key tapLeafScriptType leaf.controlBlock) (S.encode leaf.script <> B.singleton leaf.leafVersion)++-- | How an x-only key of a Taproot input or output derives, and the+-- leaves it appears in: the value of PSBT_IN_TAP_BIP32_DERIVATION and+-- PSBT_OUT_TAP_BIP32_DERIVATION. The internal key has no leaf.+data TapDerivation = TapDerivation+  { leafHashes :: ![Digest SHA256],+    derivation :: !Derivation+  }+  deriving (Eq, Show, Generic)++encodeTapDerivation :: TapDerivation -> ByteString+encodeTapDerivation d =+  compactSize (fromIntegral (length d.leafHashes)) <> B.concat (BA.convert <$> d.leafHashes) <> encodeDerivation d.derivation++-- | A Taproot derivation: the number of leaf hashes, the hashes, then the+-- fingerprint and the path+decodeTapDerivation :: ByteString -> Either String TapDerivation+decodeTapDerivation = parseAll record "a Taproot derivation"+  where+    record = do+      count <- lengthOf "a Taproot derivation"+      hashes <- replicateM count (bytesOf 32 "a Taproot derivation")+      leaves <- maybe (refuse "is not a Taproot derivation") return (mapM digestOf hashes)+      rest <- restOf+      derivation <- either refuse return (decodeDerivation rest)+      return (TapDerivation leaves derivation)++tapDerivationsOf :: Word8 -> Ctx -> PsbtMap -> Either String [(PubKey, TapDerivation)]+tapDerivationsOf t ctx m =+  forM [(key, value) | (key, value) <- Map.toAscList m, key.keyType == t] $ \(key, value) -> do+    public <- maybe (Left ("The key " <> showKey key <> " is not an x-only public key")) Right (importXOnlyPubKey ctx key.keyData)+    decoded <- first (("The value of the key " <> showKey key <> " ") <>) (decodeTapDerivation value)+    return (public, decoded)++insertTapDerivation :: Word8 -> Ctx -> PubKey -> TapDerivation -> PsbtMap -> PsbtMap+insertTapDerivation t ctx public d = Map.insert (Key t (exportXOnlyPubKey ctx public)) (encodeTapDerivation d)++-- | The derivations of the x-only keys of a Taproot input, each with the+-- leaves it appears in: PSBT_IN_TAP_BIP32_DERIVATION. The key is the+-- point of even y with that x coordinate.+inputTapDerivations :: Ctx -> PsbtMap -> Either String [(PubKey, TapDerivation)]+inputTapDerivations = tapDerivationsOf inTapDerivationType++insertInputTapDerivation :: Ctx -> PubKey -> TapDerivation -> PsbtMap -> PsbtMap+insertInputTapDerivation = insertTapDerivation inTapDerivationType++-- | The derivations of the x-only keys of a Taproot output:+-- PSBT_OUT_TAP_BIP32_DERIVATION. A record is a claim of whoever wrote the+-- PSBT, as for 'outputDerivations'.+outputTapDerivations :: Ctx -> PsbtMap -> Either String [(PubKey, TapDerivation)]+outputTapDerivations = tapDerivationsOf outTapDerivationType++insertOutputTapDerivation :: Ctx -> PubKey -> TapDerivation -> PsbtMap -> PsbtMap+insertOutputTapDerivation = insertTapDerivation outTapDerivationType++xOnlyKeyField :: Word8 -> String -> Ctx -> PsbtMap -> Either String (Maybe PubKey)+xOnlyKeyField t what ctx = optionalField what t (maybe (Left "is not an x-only public key") Right . importXOnlyPubKey ctx)++insertXOnlyKeyField :: Word8 -> Ctx -> PubKey -> PsbtMap -> PsbtMap+insertXOnlyKeyField t ctx = insertPlainField t . exportXOnlyPubKey ctx++-- | The internal key of the Taproot coin an input spends:+-- PSBT_IN_TAP_INTERNAL_KEY. The key is the point of even y with that x+-- coordinate.+inputTapInternalKey :: Ctx -> PsbtMap -> Either String (Maybe PubKey)+inputTapInternalKey = xOnlyKeyField inTapInternalKeyType "The internal key"++insertInputTapInternalKey :: Ctx -> PubKey -> PsbtMap -> PsbtMap+insertInputTapInternalKey = insertXOnlyKeyField inTapInternalKeyType++-- | The root of the script tree of the Taproot coin an input spends:+-- PSBT_IN_TAP_MERKLE_ROOT+inputTapMerkleRoot :: PsbtMap -> Either String (Maybe (Digest SHA256))+inputTapMerkleRoot = optionalField "The Merkle root" tapMerkleRootType (maybe (Left "is not a 32-byte hash") Right . digestOf)++insertInputTapMerkleRoot :: Digest SHA256 -> PsbtMap -> PsbtMap+insertInputTapMerkleRoot = insertPlainField tapMerkleRootType . BA.convert++-- | The internal key of a Taproot output: PSBT_OUT_TAP_INTERNAL_KEY+outputTapInternalKey :: Ctx -> PsbtMap -> Either String (Maybe PubKey)+outputTapInternalKey = xOnlyKeyField outTapInternalKeyType "The internal key"++insertOutputTapInternalKey :: Ctx -> PubKey -> PsbtMap -> PsbtMap+insertOutputTapInternalKey = insertXOnlyKeyField outTapInternalKeyType++-- | The script tree of a Taproot output, as its leaves depth first:+-- PSBT_OUT_TAP_TREE. 'tapTreeFromLeaves' builds the tree.+outputTapTree :: PsbtMap -> Either String (Maybe [TapLeaf])+outputTapTree = optionalField "The script tree" outTapTreeType decodeTapTree++insertOutputTapTree :: [TapLeaf] -> PsbtMap -> PsbtMap+insertOutputTapTree leaves = insertPlainField outTapTreeType (B.concat (encodeLeaf <$> leaves))+  where+    encodeLeaf :: TapLeaf -> ByteString+    encodeLeaf leaf = B.pack [leaf.depth, leaf.version] <> sized (S.encode leaf.script)++-- | A script tree: for each leaf, its depth, its leaf version and its+-- script after its length+decodeTapTree :: ByteString -> Either String [TapLeaf]+decodeTapTree = parseAll leaves "a script tree"+  where+    leaves = do+      rest <- restOf+      if B.null rest+        then return []+        else do+          BytesParser (\_ -> Right ((), rest)) -- put the bytes back+          depth <- B.head <$> bytesOf 1 "a script tree"+          version <- B.head <$> bytesOf 1 "a script tree"+          scriptBytes <- sizedBytes "a script tree"+          script <- either refuse return (decodeScript scriptBytes)+          (TapLeaf depth version script :) <$> leaves++-- | A 32-byte hash as a digest+digestOf :: ByteString -> Maybe (Digest SHA256)+digestOf = digestFromByteString++{- The fields of version 2 -}++-- | The flags of PSBT_GLOBAL_TX_MODIFIABLE: whether inputs may be added,+-- whether outputs may be added, and whether an input is signed with+-- SIGHASH_SINGLE, which pairs it with the output at its index. The other+-- bits of the byte are not defined; they are kept as they are.+data Modifiable = Modifiable+  { inputsModifiable :: !Bool,+    outputsModifiable :: !Bool,+    hasSigHashSingle :: !Bool,+    undefinedBits :: !Word8+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++-- | No flag set+noFlags :: Modifiable+noFlags = Modifiable False False False 0++-- | The flags of the global map, if it has the field: PSBT_GLOBAL_TX_MODIFIABLE+txModifiable :: PsbtMap -> Either String (Maybe Modifiable)+txModifiable = optionalField "The modifiable flags" txModifiableType decodeFlags+  where+    decodeFlags value = case B.unpack value of+      [byte] -> Right $ Modifiable (testBit byte 0) (testBit byte 1) (testBit byte 2) (byte .&. 0xf8)+      _ -> Left "are not one byte"++insertTxModifiable :: Modifiable -> PsbtMap -> PsbtMap+insertTxModifiable flags = insertPlainField txModifiableType (B.singleton byte)+  where+    byte = bit 0 flags.inputsModifiable .|. bit 1 flags.outputsModifiable .|. bit 2 flags.hasSigHashSingle .|. (flags.undefinedBits .&. 0xf8)+    bit :: Int -> Bool -> Word8+    bit n set = if set then 1 `shiftL` n else 0++-- | The global map without the flags, which a constructor may remove once+-- nothing is to be added+deleteTxModifiable :: PsbtMap -> PsbtMap+deleteTxModifiable = deletePlainField txModifiableType++-- | The lock time of the transaction when no input requires one:+-- PSBT_GLOBAL_FALLBACK_LOCKTIME+fallbackLockTime :: PsbtMap -> Either String (Maybe Word32)+fallbackLockTime = optionalField "The fallback lock time" fallbackLockTimeType decodeWord32le++-- | The least time, as a Unix timestamp, the input requires as the lock+-- time of the transaction: PSBT_IN_REQUIRED_TIME_LOCKTIME+requiredTimeLockTime :: PsbtMap -> Either String (Maybe Word32)+requiredTimeLockTime = optionalField "The required time lock time" requiredTimeType decodeWord32le++insertRequiredTimeLockTime :: Word32 -> PsbtMap -> PsbtMap+insertRequiredTimeLockTime = insertPlainField requiredTimeType . word32le++-- | The least block height the input requires as the lock time of the+-- transaction: PSBT_IN_REQUIRED_HEIGHT_LOCKTIME+requiredHeightLockTime :: PsbtMap -> Either String (Maybe Word32)+requiredHeightLockTime = optionalField "The required height lock time" requiredHeightType decodeWord32le++insertRequiredHeightLockTime :: Word32 -> PsbtMap -> PsbtMap+insertRequiredHeightLockTime = insertPlainField requiredHeightType . word32le++{- Proprietary fields -}++-- | A proprietary field, of type 0xFC: the identifier of the software it+-- belongs to, its subtype under that identifier, and the data after the+-- subtype, none for most+data Proprietary = Proprietary+  { identifier :: !ByteString,+    subtype :: !Word64,+    extra :: !ByteString+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++-- | The key of a proprietary field: the type 0xFC, the identifier after+-- its length, the subtype as a compact size integer, and the data+proprietaryKey :: Proprietary -> Key+proprietaryKey p =+  Key proprietaryType (sized p.identifier <> compactSize p.subtype <> p.extra)++-- | The proprietary field a key is, if it is one: nothing for a key of+-- another type, a refusal for a proprietary key without its identifier+-- and subtype+proprietaryOf :: Key -> Either String (Maybe Proprietary)+proprietaryOf key+  | key.keyType /= proprietaryType = Right Nothing+  | otherwise =+      first (const ("the proprietary key " <> showKey key <> " has no identifier and subtype")) $+        Just <$> parseAll (Proprietary <$> sizedBytes what <*> compactSizeOf what <*> restOf) what key.keyData+  where+    what = "a proprietary key"++-- | The value of the proprietary field of the identifier and subtype,+-- with no data after the subtype, if the map has it+lookupProprietary :: ByteString -> Word64 -> PsbtMap -> Maybe ByteString+lookupProprietary identifier subtype = Map.lookup (proprietaryKey (Proprietary identifier subtype B.empty))++insertProprietary :: ByteString -> Word64 -> ByteString -> PsbtMap -> PsbtMap+insertProprietary identifier subtype = Map.insert (proprietaryKey (Proprietary identifier subtype B.empty))++{- Signing -}++-- | Where a signer gets its private keys: by a derivation record naming a+-- public key, or by the hash of a public key when the input has no record+-- for it. A key that is not held is 'Nothing'. The signer also says which+-- sighash types it agrees to sign with, and whether it wants the previous+-- transaction of a SegWit version 0 coin.+data Signer = Signer+  { -- | the private key of the public key a derivation record names+    keyOfRecord :: PublicKey -> Derivation -> Maybe SecKey,+    -- | the private key whose public key, compressed or not, has the hash+    keyOfHash :: Hash160 -> Maybe PrivateKey,+    -- | the sighash types the signer signs with. An input that asks for+    -- another type is refused, as BIP 174 asks of a signer. The fork id+    -- flag does not count, since the network decides it.+    acceptedSigHashes :: [SigHash],+    -- | whether a SegWit version 0 coin (P2WPKH or P2WSH, nested in P2SH+    -- or not) is signed only when the input carries its previous+    -- transaction. A BIP 143 signature commits to the amount of its own+    -- coin and to no other. With the witness output alone, whoever writes+    -- the PSBT can lie about the amount of one coin of the signer in each+    -- of two signing rounds, keep the valid signature of each round, and+    -- make a transaction that pays the difference as fee+    -- (CVE-2020-14199). BIP 174 lets a signer "fail to sign a segwit+    -- input if a non-witness UTXO is not provided" for this reason. A+    -- Taproot signature commits to the amount of every coin and needs no+    -- such care.+    requirePreviousTx :: Bool+  }++-- | The sighash types a signer accepts unless told otherwise:+-- 'sigHashDefault', which a Taproot input uses when it asks for none, and+-- 'sigHashAll'. The other types leave part of the transaction out of the+-- signature, so a PSBT written by someone else may not ask for them on its+-- own; a signer that agrees to them lists them, for example+-- @(keySigner ctx key) {acceptedSigHashes = sigHashNone : defaultSigHashes}@.+defaultSigHashes :: [SigHash]+defaultSigHashes = [sigHashDefault, sigHashAll]++-- | Whether the signer accepts the sighash type: it is one of its types,+-- the fork id flag aside+acceptsSigHash :: Signer -> SigHash -> Bool+acceptsSigHash signer sighash =+  withoutForkId sighash `elem` map withoutForkId signer.acceptedSigHashes+  where+    withoutForkId = (.&. complement forkIdBit)+    forkIdBit = fromIntegral (fromEnum SIGHASH_FORKID)++-- | A signer that holds one private key, for the records of its public+-- key and for the hash of its public key in either form. It wants the+-- previous transaction of a SegWit version 0 coin ('requirePreviousTx').+-- A caller that has checked the amounts of its coins another way, for+-- example against its own records, turns that off with+-- @(keySigner ctx key) {requirePreviousTx = False}@.+keySigner :: Ctx -> SecKey -> Signer+keySigner ctx secret =+  Signer+    { keyOfRecord = \public _ -> do+        guard (public.point == derivePubKey ctx secret)+        return secret,+      keyOfHash = \h -> find ((== h) . addressHash . marshal ctx . derivePublicKey ctx) [PrivateKey secret True, PrivateKey secret False],+      acceptedSigHashes = defaultSigHashes,+      requirePreviousTx = True+    }++-- | A signer that derives from an extended private key. A record names a+-- key it derives by the fingerprint of the extended key and the path from+-- it, or, when the origin of the extended key is given, by the+-- fingerprint of that origin and the path from it, which starts with the+-- path of the extended key. A path the key cannot follow, such as one+-- deeper than the 255 levels of BIP 32 or one with a hardened step+-- below a key that cannot derive it, names no key. It has no key for a+-- hash, since it cannot know which path to derive. Like 'keySigner', it+-- wants the previous transaction of a SegWit version 0 coin.+xPrvSigner :: Ctx -> XPrvKey -> Maybe Derivation -> Signer+xPrvSigner ctx xprv originM =+  Signer+    { keyOfRecord = \public derivation -> do+        path <- relative derivation+        secret <- (.key) <$> eitherToMaybe (tryDerivePath ctx path xprv)+        guard (public.point == derivePubKey ctx secret)+        return secret,+      keyOfHash = const Nothing,+      acceptedSigHashes = defaultSigHashes,+      requirePreviousTx = True+    }+  where+    own = xPubFP ctx (deriveXPubKey ctx xprv)+    relative :: Derivation -> Maybe DerivPath+    relative derivation+      | derivation.fingerprint == own = Just derivation.path+      | Just origin <- originM, derivation.fingerprint == origin.fingerprint =+          listToPath <$> stripPrefix (pathToList origin.path) (pathToList derivation.path)+      | otherwise = Nothing++-- | Sign every input the signer has keys for, as the signer of the spec+-- does. An input is signed against the coin it spends, from its previous+-- transaction, whose id must be the one the input spends, or from its+-- witness output alone, which only a witness signature may use, as BIP+-- 174 requires.+--+-- The keys are those the script that is signed names: the script of the+-- coin, its redeem script or its witness script. The signer is asked for+-- the key of a derivation record only when that script names the key of+-- the record, and for the key of each hash the script names. A key that+-- the script does not name gets no signature, whatever the records say,+-- and a P2SH coin without its redeem script or a P2WSH coin without its+-- witness script gets none either, as BIP 174 has it: "If a Signer cannot+-- sign a transaction, it must not add a Partial Signature."+--+-- The sighash is the one the input asks for. When it asks for none it is+-- SIGHASH_ALL, with the fork id flag on a network that has one. A type no+-- node accepts, or one the signer does not accept ('acceptedSigHashes'),+-- is refused on any input that has its coin, as Bitcoin Core refuses a+-- PSBT with a sighash type the caller did not ask for.+--+-- An input the signer holds a key for is refused, with the reason, when:+--+-- * its redeem script does not hash to the coin, or its witness script to+--   the witness program;+-- * it is a legacy coin known from its witness output only;+-- * it is a SegWit version 0 coin known from its witness output only and+--   the signer wants the previous transaction ('requirePreviousTx');+-- * the key is uncompressed and the coin is a SegWit version 0 one, which+--   no node relays and Bitcoin Core does not sign;+-- * it is a legacy coin asked to sign SIGHASH_SINGLE with no output at its+--   index, as 'makeSignature' explains.+--+-- A Taproot coin is signed by its key path when the input has its internal+-- key, which with the Merkle root must give the output key, and by each of+-- its leaf scripts that is a single key check (@<key> OP_CHECKSIG@) and+-- that its control block commits to the output key, with 'sigHashDefault'+-- when the input asks for no sighash. The BIP 341 digest commits to the+-- output every input spends, so every input must have its coin then.+--+-- An input without its coin, with a final script already, with a coin,+-- redeem script or witness script that is no standard template, or for+-- which the signer holds no key, is left as it is.+signPsbt :: Network -> Ctx -> Signer -> Psbt -> Either String Psbt+signPsbt net ctx signer psbt = do+  coins <- spentOutputs psbt+  let allCoins = mapM (fmap fst) coins+  signed <- forM (zip3 [0 ..] (zip tx.inputs psbt.inputs) coins) $ \(i, (input, m), coinM) ->+    first (("Input " <> show i <> ": ") <>) $ case coinM of+      Just (coin, witnessOnly)+        | not (isFinal m),+          Right coinScript <- unmarshal ctx coin.script -> do+            sighashM <- sigHashType m+            if isTaprootCoin coinScript+              then signTaproot i m coinScript sighashM allCoins+              else signEcdsa i input m coin coinScript witnessOnly sighashM+      _ -> return (m, [])+  return $ foldr afterSignature (Psbt psbt.version tx psbt.globals (fst <$> signed) psbt.outputs) (concatMap snd signed)+  where+    tx = psbt.unsignedTx++    -- A signature per key the signer holds among those the script names,+    -- for a coin signed with ECDSA+    signEcdsa i input m coin coinScript witnessOnly sighashM = do+      let sighash = fromMaybe (defaultEcdsaSigHash net) sighashM+      checkEcdsaSigHash net sighash+      unless (acceptsSigHash signer sighash) $+        Left (notAcceptedSigHash sighash)+      scriptsM <- inputScripts ctx coinScript m+      case scriptsM of+        Nothing -> return (m, [])+        Just scripts -> do+          keys <- heldKeys m (maybe [] (scriptKeyHashes ctx) scripts.signedScript)+          if null keys+            then return (m, [])+            else do+              forM_ scripts.mismatch Left+              let witnessCoin = isSegwit scripts.program+              when (witnessOnly && not witnessCoin) $+                Left "the coin is given as a witness output only, which may not be signed with a non-witness signature"+              when (witnessOnly && witnessCoin && signer.requirePreviousTx) $+                Left "the coin is given as a witness output only, and the signer requires the previous transaction of a SegWit version 0 coin"+              when (witnessCoin && not (all (.compress) keys)) $+                Left "a SegWit version 0 coin may not be signed with an uncompressed key"+              let sigInput = signingInput input coin sighash scripts+              signatures <- forM keys $ \key -> do+                signature <- makeSignature net ctx tx i sigInput key+                return (derivePublicKey ctx key, encodeTxSig net ctx signature)+              return (foldr (\(public, sig) -> insertPartialSignature ctx public sig) m signatures, [sighash])++    -- The keys the signer holds among those whose hashes the script that+    -- is signed names. The signer is asked for the key of a derivation+    -- record only when the hash of the key of the record is one of them,+    -- so the work it does is bounded by the script, however many records+    -- the PSBT carries. A key the signer gives is kept only when it is the+    -- key that was asked for.+    heldKeys :: PsbtMap -> [Hash160] -> Either String [PrivateKey]+    heldKeys m named = do+      let namedRecords = [(key, value) | (key, value) <- Map.toAscList m, key.keyType == inDerivationType, addressHash key.keyData `elem` named]+      recordKeys <- forM namedRecords $ \(key, value) -> do+        public <- first (const ("The key " <> showKey key <> " is not a public key")) (unmarshal ctx key.keyData)+        derivation <- first (("The value of the key " <> showKey key <> " ") <>) (decodeDerivation value)+        return [PrivateKey secret public.compress | Just secret <- [signer.keyOfRecord public derivation], derivePubKey ctx secret == public.point]+      let hashKeys = [key | h <- named, Just key <- [signer.keyOfHash h], addressHash (marshal ctx (derivePublicKey ctx key)) == h]+      return $ nubBy (\a b -> derivePublicKey ctx a == derivePublicKey ctx b) (concat recordKeys <> hashKeys)++    -- A key path signature, and one per leaf that is a single key check,+    -- for a Taproot coin+    signTaproot i m coinScript sighashM allCoins = do+      let sighash = fromMaybe sigHashDefault sighashM+      unless (isTaprootSigHash sighash) $+        Left ("the sighash type " <> show (fromIntegral sighash :: Word32) <> " is not one a Taproot signature may use")+      unless (acceptsSigHash signer sighash) $+        Left (notAcceptedSigHash sighash)+      spent <- maybe (Left "a Taproot signature needs the output every input spends") Right allCoins+      program <- taprootProgram coinScript+      internalM <- inputTapInternalKey ctx m+      rootM <- inputTapMerkleRoot m+      records <- inputTapDerivations ctx m+      leaves <- tapLeafScripts ctx m+      -- The secret key of an x-only key, by a record of that key which+      -- the test accepts, or by the hash of the key. A key the signer+      -- gives is kept only when it is the key that was asked for.+      let heldXOnly accepts public =+            listToMaybe . filter (sameX public . derivePubKey ctx) . catMaybes $+              [signer.keyOfRecord (PublicKey candidate True) d.derivation | (recordKey, d) <- records, sameX recordKey public, accepts d, candidate <- bothParities public]+                <> [(.key) <$> signer.keyOfHash (addressHash (marshal ctx (PublicKey candidate True))) | candidate <- bothParities public]+      keySig <- case internalM of+        Nothing -> return []+        Just internal -> do+          let expected = taprootOutputKey ctx (TaprootOutput internal (MASTCommitment <$> rootM))+          unless (exportXOnlyPubKey ctx expected == program) $+            Left "the internal key and the Merkle root do not give the output key of the coin"+          -- Any record of the internal key will do: BIP 371 lists in a+          -- record the leaves the key is in, and the internal key may be+          -- in a leaf too+          case heldXOnly (const True) internal of+            Nothing -> return []+            Just secret -> do+              tweaked <- maybe (Left "the key cannot be tweaked") Right (taprootOutputSecKey ctx secret (MASTCommitment <$> rootM))+              h <- taprootSigHash tx spent i sighash Nothing Nothing+              signature <- taprootSignature (signSchnorr ctx tweaked (hashToMsg h)) sighash+              return [insertTapKeySig signature]+      scriptSigs <- fmap concat . forM leaves $ \leaf -> case singleKeyLeaf ctx leaf of+        Just leafKey+          -- Only a leaf of the coin is signed: its control block must+          -- commit it to the output key+          | Just outKey <- importXOnlyPubKey ctx program,+            commitsLeaf ctx outKey leaf,+            Just secret <- heldXOnly ((hashOfLeaf `elem`) . (.leafHashes)) leafKey -> do+              h <- taprootSigHash tx spent i sighash Nothing (Just (hashOfLeaf, 0xffffffff))+              signature <- taprootSignature (signSchnorr ctx secret (hashToMsg h)) sighash+              return [insertTapScriptSig ctx leafKey hashOfLeaf signature]+          where+            hashOfLeaf = leafHash leaf.leafVersion leaf.script+        _ -> return []+      let inserts = keySig <> scriptSigs+      return (foldr ($) m inserts, [sighash | not (null inserts)])++    sameX a b = exportXOnlyPubKey ctx a == exportXOnlyPubKey ctx b+    -- The two points with the x coordinate of the key+    bothParities public =+      mapMaybe (\prefix -> (.point) <$> eitherToMaybe (unmarshal ctx (B.cons prefix (exportXOnlyPubKey ctx public)) :: Either String PublicKey)) [0x02, 0x03]++-- | The sighash type of an ECDSA signature for an input that asks for+-- none: SIGHASH_ALL, with the fork id flag on a network that has one+defaultEcdsaSigHash :: Network -> SigHash+defaultEcdsaSigHash net+  | isJust net.sigHashForkId = setForkIdFlag sigHashAll+  | otherwise = sigHashAll++-- | The hashes of the keys a script names: the keys a coin of that script+-- is signed with+scriptKeyHashes :: Ctx -> ScriptOutput -> [Hash160]+scriptKeyHashes ctx = \case+  PayPK public -> [addressHash (marshal ctx public)]+  PayPKHash h -> [h]+  PayWitnessPKHash h -> [h]+  PayMulSig publics _ -> map (addressHash . marshal ctx) publics+  _ -> []++-- | The leaf key of a leaf script that is a single key check, @<key>+-- OP_CHECKSIG@ of leaf version 0xc0+singleKeyLeaf :: Ctx -> TapLeafScript -> Maybe PubKey+singleKeyLeaf ctx leaf = case leaf.script.ops of+  [OP_PUSHDATA keyBytes OPCODE, OP_CHECKSIG]+    | leaf.leafVersion == tapscriptLeafVersion -> importXOnlyPubKey ctx keyBytes+  _ -> Nothing++-- | Whether the control block of a leaf script commits the leaf to the+-- output key of the coin+commitsLeaf :: Ctx -> PubKey -> TapLeafScript -> Bool+commitsLeaf ctx outKey leaf = case decodeControlBlock ctx leaf.controlBlock of+  Right (v, internalKey, proof) ->+    verifyScriptPathData ctx outKey (ScriptPathData Nothing [] leaf.script (odd v) (v .&. 0xfe) internalKey proof Nothing)+  Left _ -> False++-- | The error for a sighash type the signer does not accept+notAcceptedSigHash :: SigHash -> String+notAcceptedSigHash sighash =+  "the sighash type " <> show (fromIntegral sighash :: Word32) <> " is not one the signer accepts"++-- | A sighash type an ECDSA signature may carry on the network: one of+-- ALL, NONE and SINGLE, with ANYONECANPAY or not, and the fork id flag+-- exactly on the networks with a fork id+checkEcdsaSigHash :: Network -> SigHash -> Either String ()+checkEcdsaSigHash net sighash = do+  when (isSigHashUnknown sighash) $+    Left ("the sighash type " <> show (fromIntegral sighash :: Word32) <> " is not one a node accepts")+  when (isJust net.sigHashForkId && not (hasForkIdFlag sighash)) $+    Left "the sighash type lacks the fork id flag the network requires"+  when (isNothing net.sigHashForkId && hasForkIdFlag sighash) $+    Left "the sighash type has a fork id flag the network does not use"++-- | Whether a coin is a Taproot output, and its program+isTaprootCoin :: ScriptOutput -> Bool+isTaprootCoin = either (const False) (const True) . taprootProgram++taprootProgram :: ScriptOutput -> Either String ByteString+taprootProgram = \case+  PayWitness 1 program | B.length program == 32 -> Right program+  _ -> Left "not a Taproot output"++-- | The PSBT after a signature with the sighash was added, as the signer+-- of BIP 370 leaves the flags of version 2: inputs may no longer be added+-- unless the sighash has ANYONECANPAY, outputs no longer unless it is+-- NONE, and the PSBT has a SIGHASH_SINGLE signature when it is SINGLE. A+-- PSBT of version 0 has no flags.+afterSignature :: SigHash -> Psbt -> Psbt+afterSignature sighash psbt = case psbt.version of+  Version0 -> psbt+  Version2+    | isJust beforeM || after /= noFlags -> Psbt Version2 psbt.unsignedTx (insertTxModifiable after psbt.globals) psbt.inputs psbt.outputs+    | otherwise -> psbt+  where+    beforeM = either (const Nothing) id (txModifiable psbt.globals)+    before = fromMaybe noFlags beforeM+    after =+      before+        { inputsModifiable = before.inputsModifiable && anyoneCanPay sighash,+          outputsModifiable = before.outputsModifiable && isSigHashNone sighash,+          hasSigHashSingle = before.hasSigHashSingle || isSigHashSingle sighash+        }++-- | Whether an input has its final script or witness+isFinal :: PsbtMap -> Bool+isFinal m = any (`Map.member` m) [Key finalScriptSigType B.empty, Key finalScriptWitnessType B.empty]++{- The coins of a PSBT -}++-- | The coin an input of a PSBT spends, as the PSBT gives it+data InputCoin = InputCoin+  { -- | the output the input spends: its amount and its script+    coin :: !TxOut,+    -- | 'True' when the coin is the output of the previous transaction of+    -- the input, whose id the input names, so that it cannot be made up.+    -- 'False' when the PSBT gives the output alone (PSBT_IN_WITNESS_UTXO):+    -- its amount is then a claim of whoever wrote the PSBT.+    fromPreviousTx :: !Bool+  }+  deriving (Eq, Show, Read, Generic)+  deriving anyclass (NFData)++-- | The coin every input of a PSBT spends, 'Nothing' for an input that+-- carries none. A previous transaction must be the one its input spends+-- and have the output it spends, and a witness output given with it must+-- be that output.+--+-- An amount with 'fromPreviousTx' is checked. Another is only checked by a+-- signature that commits to it: a SegWit version 0 signature commits to+-- the amount of its own coin, and a Taproot signature to the amount of+-- every coin of the transaction. Show a user amounts and a fee from+-- checked coins only, or from a PSBT in which the user signs a Taproot+-- input.+inputCoins :: Psbt -> Either String [Maybe InputCoin]+inputCoins psbt = map (fmap (\(output, witnessOnly) -> InputCoin output (not witnessOnly))) <$> spentOutputs psbt++-- | The fee of the transaction of a PSBT: what its coins hold above what+-- its outputs pay. Every input must carry its previous transaction, so+-- that no amount is a claim ('inputCoins' tells which are). A PSBT whose+-- outputs pay more than its coins hold has no fee.+psbtFee :: Psbt -> Either String Word64+psbtFee psbt = do+  coins <- inputCoins psbt+  amounts <- forM (zip [0 :: Int ..] coins) $ \(i, coinM) -> case coinM of+    Nothing -> Left ("Input " <> show i <> " has no coin")+    Just found+      | found.fromPreviousTx -> Right (toInteger found.coin.value)+      | otherwise -> Left ("Input " <> show i <> ": the coin is given as a witness output only, so its amount is not checked")+  let fee = sum amounts - sum (toInteger . (.value) <$> psbt.unsignedTx.outputs)+  when (fee < 0) $ Left "The outputs pay more than the coins hold"+  return (fromInteger fee)++-- | The output every input spends, from the fields of each input, and+-- whether it is known from the witness output alone+spentOutputs :: Psbt -> Either String [Maybe (TxOut, Bool)]+spentOutputs psbt =+  forM (zip3 [0 :: Int ..] psbt.unsignedTx.inputs psbt.inputs) $ \(i, input, m) ->+    first (("Input " <> show i <> ": ") <>) (spentOutput input m)++-- | The output an input spends, from the fields of the input: the output+-- of its previous transaction, which must be the transaction the input+-- spends from, or else its witness output alone. With both, the witness+-- output must be that output. The flag tells when the witness output+-- alone is known, which only a witness signature may rely on.+spentOutput :: TxIn -> PsbtMap -> Either String (Maybe (TxOut, Bool))+spentOutput input m = do+  witnessM <- witnessUtxo m+  parentM <- nonWitnessUtxo m+  case (parentM, witnessM) of+    (Just parent, _) -> do+      output <- previousOutput input parent+      forM_ witnessM $ \witnessCoin ->+        unless (witnessCoin == output) $+          Left "the witness output of the input is not the output of its previous transaction"+      return (Just (output, False))+    (Nothing, Just output) -> return (Just (output, True))+    (Nothing, Nothing) -> return Nothing++-- | What the scripts of an input say about how its coin is signed+data InputScripts = InputScripts+  { -- | the script the coin is signed against: the script of the coin, or+    -- the witness program its redeem script is when it is nested in P2SH+    program :: ScriptOutput,+    -- | the script that names the keys: the script of the coin, its+    -- redeem script or its witness script. 'Nothing' when the input does+    -- not have the redeem or witness script its coin needs.+    signedScript :: Maybe ScriptOutput,+    -- | why a redeem or witness script of the input does not fit its coin+    mismatch :: Maybe String+  }++-- | The scripts of an input, from the script of its coin and its redeem+-- and witness scripts. A redeem script must hash to the coin, and a+-- witness script to the witness program, as BIP 174 requires of a signer:+-- one that does not is reported in 'mismatch'. 'Nothing' when the script+-- that names the keys is no standard template: the library cannot sign or+-- finalize such an input, and leaves it to others.+inputScripts :: Ctx -> ScriptOutput -> PsbtMap -> Either String (Maybe InputScripts)+inputScripts ctx coinScript m = do+  redeemM <- inputRedeemScript m+  witnessM <- inputWitnessScript m+  let redeemOutM = decodeOutput ctx <$> redeemM+      witnessOutM = decodeOutput ctx <$> witnessM+      -- A SegWit coin, nested in P2SH or not, is signed against its witness+      -- program; any other against its own script+      program = case redeemOutM of+        Just (Right redeem) | not (isSegwit coinScript), isSegwit redeem -> redeem+        _ -> coinScript+      redeemMismatch = case redeemM of+        Just redeem+          | not (isPayScriptHash coinScript) -> Just "the input has a redeem script, but its coin is not pay-to-script-hash"+          | toP2SH redeem /= Right coinScript -> Just "the redeem script of the input does not hash to its coin"+        _ -> Nothing+      witnessMismatch = case witnessM of+        Just witness+          | not (isPayWitnessScriptHash program) -> Just "the input has a witness script, but its coin is not pay-to-witness-script-hash"+          | toP2WSH witness /= Right program -> Just "the witness script of the input does not hash to its witness program"+        _ -> Nothing+      -- The script that names the keys, as it decodes+      naming = case program of+        PayWitnessScriptHash _ -> witnessOutM+        PayScriptHash _ -> redeemOutM+        _ -> Just (Right program)+  return $ case naming of+    Just (Left _) -> Nothing+    _ ->+      Just+        InputScripts+          { program = program,+            signedScript = naming >>= eitherToMaybe,+            mismatch = listToMaybe (catMaybes [redeemMismatch, witnessMismatch])+          }++-- | What an input is signed against, as 'makeSignature' wants it: the+-- script of its coin or its witness program, with the redeem script of a+-- P2SH coin or the witness script of a P2WSH coin+signingInput :: TxIn -> TxOut -> SigHash -> InputScripts -> SigInput+signingInput input output sighash scripts =+  SigInput+    { script = scripts.program,+      value = output.value,+      outpoint = input.outpoint,+      sighash = sighash,+      redeem = case scripts.program of+        PayWitnessScriptHash _ -> scripts.signedScript+        PayScriptHash _ -> scripts.signedScript+        _ -> Nothing+    }++{- Finalizing -}++-- | Finalize every input whose signatures suffice for the coin it spends,+-- as the finalizer of the spec does: the final script and witness go in+-- the input, and its partial signatures, sighash type, redeem script,+-- witness script, derivation records and Taproot fields go, as Bitcoin+-- Core clears them. Its coin and the keys the spec does not define stay.+--+-- Only the signatures of the keys the script of the coin names are+-- looked at. Each one is verified first, against the digest of the coin,+-- which is computed once per sighash type. A signature that does not+-- verify, or is of an uncompressed key on a SegWit version 0 coin, is+-- refused, as @PSBTInputSignedAndVerified@ and the standard script flags+-- refuse it in Bitcoin Core. Every signature of the input must use the+-- sighash type the input names, and SIGHASH_ALL when it names none+-- (SIGHASH_DEFAULT for a Taproot coin, and with the fork id flag on a+-- network that has one), as Bitcoin Core requires.+--+-- The coins finalized are those of P2PK, P2PKH and multisig scripts, P2SH+-- coins with their redeem script, P2WPKH coins, P2WSH coins with their+-- witness script, nested in P2SH or not, and Taproot coins by their key+-- path signature or by a leaf script that is a single key check. Any+-- other input stays as it is: one without enough signatures, one whose+-- scripts are no standard template, and a legacy coin known from its+-- witness output only, which Bitcoin Core does not finalize either.+finalizePsbt :: Network -> Ctx -> Psbt -> Either String Psbt+finalizePsbt net ctx psbt = do+  coins <- spentOutputs psbt+  let allCoins = mapM (fmap fst) coins+  inputs <- forM (zip3 [0 ..] (zip tx.inputs psbt.inputs) coins) $ \(i, (input, m), coinM) ->+    first (("Input " <> show i <> ": ") <>) $ case coinM of+      Just (output :: TxOut, witnessOnly)+        | not (isFinal m),+          Right coinScript <- unmarshal ctx output.script -> do+            finals <-+              if isTaprootCoin coinScript+                then finalTaprootScripts ctx tx allCoins i m coinScript+                else finalScripts net ctx tx i input output witnessOnly coinScript m+            return $ case finals of+              Just (scriptSig, stack) ->+                foldr ($) (cleared m) $+                  [insertFinalScriptSig scriptSig | not (B.null scriptSig)]+                    <> [insertFinalScriptWitness stack | not (null stack)]+              Nothing -> m+      _ -> return m+  return $ Psbt psbt.version tx psbt.globals inputs psbt.outputs+  where+    tx = psbt.unsignedTx+    cleared = Map.filterWithKey (\key _ -> key.keyType `notElem` clearedTypes)+    clearedTypes =+      [partialSigType, sigHashTypeType, inRedeemScriptType, inWitnessScriptType, inDerivationType, tapKeySigType, tapScriptSigType, tapLeafScriptType, inTapDerivationType, inTapInternalKeyType, tapMerkleRootType]++-- | The final script and witness of an input signed with ECDSA, from the+-- partial signatures of the keys its script names, when they suffice.+-- Each of those signatures is verified first.+finalScripts :: Network -> Ctx -> Tx -> Int -> TxIn -> TxOut -> Bool -> ScriptOutput -> PsbtMap -> Either String (Maybe (ByteString, WitnessStack))+finalScripts net ctx tx i input output witnessOnly coinScript m = do+  sighashM <- sigHashType m+  scriptsM <- inputScripts ctx coinScript m+  case scriptsM of+    Nothing -> return Nothing+    Just scripts+      -- Only a witness signature may rely on a witness output+      | witnessOnly && not (isSegwit scripts.program) -> return Nothing+      | otherwise -> do+          forM_ scripts.mismatch Left+          let asked = fromMaybe (defaultEcdsaSigHash net) sighashM+              otherType keyBytes =+                Left $+                  "the signature of the key " <> quoted keyBytes <> case sighashM of+                    Just _ -> " does not use the sighash type of the input"+                    Nothing -> " does not use SIGHASH_ALL, and the input names no sighash type"+              sigInput = signingInput input output asked scripts+              named = maybe [] (namedSignatures ctx m) scripts.signedScript+          -- The signatures of the keys the script names: each is a+          -- signature, of the sighash type asked, by a key the coin may be+          -- spent with+          checked <- forM named $ \(public, bytes) -> do+            (sig, sh) <- case decodeTxSig net ctx bytes of+              Right (TxSignature sig sh) -> return (sig, sh)+              _ -> Left ("the signature of the key " <> quoted (marshal ctx public) <> " is not a valid signature")+            unless (sh == asked) $ otherType (marshal ctx public)+            when (isSegwit scripts.program && not public.compress) $+              Left ("the signature of the key " <> quoted (marshal ctx public) <> " is of an uncompressed key, which a SegWit version 0 coin may not be spent with")+            return (public, sig)+          -- Every other signature of the input uses that sighash type too:+          -- its last byte tells, and nothing else of it is looked at+          forM_ [(key, sig) | (key, sig) <- Map.toAscList m, key.keyType == partialSigType] $ \(key, sig) ->+            unless (not (B.null sig) && B.last sig == fromIntegral (asked .&. 0xff)) $ otherType key.keyData+          if null checked+            then return Nothing+            else do+              -- One digest serves every signature, since all use one type+              digest <- makeSigHash net ctx tx i sigInput.script output.value asked sigInput.redeem+              forM_ checked $ \(public, sig) ->+                unless (verifyHashSig ctx digest sig public.point) $+                  Left ("the signature of the key " <> quoted (marshal ctx public) <> " does not verify")+              return (assemble scripts named)+  where+    quoted = show . encodeHex+    -- The final script and witness, from the signatures of the keys the+    -- script names+    assemble :: InputScripts -> [(PublicKey, ByteString)] -> Maybe (ByteString, WitnessStack)+    assemble scripts named = case coinScript of+      PayScriptHash _ -> do+        redeem <- either (const Nothing) id (inputRedeemScript m)+        if isSegwit scripts.program+          then (scriptOf [S.encode redeem],) <$> witnessOf scripts named+          else (\found -> (scriptOf (found <> [S.encode redeem]), [])) <$> (items named =<< scripts.signedScript)+      _+        | isSegwit coinScript -> (B.empty,) <$> witnessOf scripts named+        | otherwise -> (\found -> (scriptOf found, [])) <$> (items named =<< scripts.signedScript)+    -- The witness of a SegWit coin: the items of its program, with the+    -- witness script last for P2WSH+    witnessOf :: InputScripts -> [(PublicKey, ByteString)] -> Maybe [ByteString]+    witnessOf scripts named = case scripts.program of+      PayWitnessScriptHash _ -> do+        witnessScript <- either (const Nothing) id (inputWitnessScript m)+        (<> [S.encode witnessScript]) <$> (items named =<< scripts.signedScript)+      _ -> items named =<< scripts.signedScript+    -- The items a script is spent with: the pushes of an input script,+    -- or the items of a witness+    items :: [(PublicKey, ByteString)] -> ScriptOutput -> Maybe [ByteString]+    items named = \case+      PayPK public -> (: []) <$> lookup public named+      PayPKHash h -> withKey <$> ofHash named h+      PayMulSig keys n -> (B.empty :) <$> enough named keys n+      PayWitnessPKHash h -> withKey <$> ofHash named h+      _ -> Nothing+    ofHash :: [(PublicKey, ByteString)] -> Hash160 -> Maybe (PublicKey, ByteString)+    ofHash named h = listToMaybe [(public, signature) | (public, signature) <- named, addressHash (marshal ctx public) == h]+    enough :: [(PublicKey, ByteString)] -> [PublicKey] -> Int -> Maybe [ByteString]+    enough named keys n = let found = mapMaybe (`lookup` named) keys in if length found >= n then Just (take n found) else Nothing+    withKey :: (PublicKey, ByteString) -> [ByteString]+    withKey (public, signature) = [signature, marshal ctx public]+    scriptOf = S.encode . Script . map opPushData++-- | The partial signatures of an input whose keys the script names, in+-- the order of the map. A key is named by a script of public keys when it+-- is one of them, and by a script of a key hash when it has that hash.+-- The others are not looked at, so their number costs nothing.+namedSignatures :: Ctx -> PsbtMap -> ScriptOutput -> [(PublicKey, ByteString)]+namedSignatures ctx m script =+  [ (public, sig)+  | (key, sig) <- Map.toAscList m,+    key.keyType == partialSigType,+    addressHash key.keyData `elem` named,+    Right public <- [unmarshal ctx key.keyData]+  ]+  where+    named = scriptKeyHashes ctx script++-- | The final witness of a Taproot input: its key path signature, or the+-- signature of the first leaf script that is a single key check with the+-- script and its control block. Every signature is verified first,+-- against the output key and the digest of its leaf. A signature must+-- use the sighash type the input names, and SIGHASH_DEFAULT when it names+-- none, as Bitcoin Core requires.+finalTaprootScripts :: Ctx -> Tx -> Maybe [TxOut] -> Int -> PsbtMap -> ScriptOutput -> Either String (Maybe (ByteString, WitnessStack))+finalTaprootScripts ctx tx allCoins i m coinScript = do+  program <- taprootProgram coinScript+  outKey <- maybe (Left "the coin is not a Taproot output") Right (importXOnlyPubKey ctx program)+  sighashM <- sigHashType m+  keySigM <- tapKeySig m+  scriptSigs <- tapScriptSigs ctx m+  leaves <- tapLeafScripts ctx m+  let sameType :: TaprootSignature -> Either String ()+      sameType signature = case sighashM of+        Just asked -> unless (signature.hash == asked) $ Left "a Taproot signature does not use the sighash type of the input"+        Nothing -> unless (signature.hash == sigHashDefault) $ Left "a Taproot signature does not use SIGHASH_DEFAULT, and the input names no sighash type"+  -- Every signature of the input, used or not, as Bitcoin Core checks them+  mapM_ sameType (maybeToList keySigM <> map snd scriptSigs)+  case keySigM of+    Just signature -> do+      spent <- maybe (Left "a Taproot signature needs the output every input spends") Right allCoins+      digest <- taprootSigHash tx spent i signature.hash Nothing Nothing+      unless (verifySchnorr ctx outKey signature.sig (hashToMsg digest)) $+        Left "the key path signature does not verify"+      return (Just (B.empty, [encodeTaprootSig signature]))+    Nothing -> do+      let candidates =+            [ (leaf, leafKey, signature)+            | leaf <- leaves,+              Just leafKey <- [singleKeyLeaf ctx leaf],+              Just signature <- [lookup (leafKey, leafHash leaf.leafVersion leaf.script) [((k, h), sig) | ((k, h), sig) <- scriptSigs, sameX k leafKey]]+            ]+      case candidates of+        [] -> return Nothing+        (leaf, leafKey, signature) : _ -> do+          spent <- maybe (Left "a Taproot signature needs the output every input spends") Right allCoins+          unless (commitsLeaf ctx outKey leaf) $+            Left "the control block of the leaf does not commit it to the output key"+          digest <- taprootSigHash tx spent i signature.hash Nothing (Just (leafHash leaf.leafVersion leaf.script, 0xffffffff))+          unless (verifySchnorr ctx leafKey signature.sig (hashToMsg digest)) $+            Left "the script path signature does not verify"+          return (Just (B.empty, [encodeTaprootSig signature, S.encode leaf.script, leaf.controlBlock]))+  where+    sameX a b = exportXOnlyPubKey ctx a == exportXOnlyPubKey ctx b
src/Haskoin/Transaction/Segwit.hs view
@@ -1,145 +1,175 @@-{-# LANGUAGE LambdaCase        #-}+{-# 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 TupleSections #-}+{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Types to represent segregated witness data and auxilliary functions to+-- manipulate it.  See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)+-- and [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) for+-- details. module Haskoin.Transaction.Segwit-    ( -- * Segwit-      WitnessProgram (..)-    , WitnessProgramPKH (..)-    , WitnessProgramSH (..)-    , isSegwit--    , viewWitnessProgram-    , decodeWitnessInput--    , calcWitnessProgram-    , simpleInputStack-    , toWitnessStack-    ) where+  ( -- * Segwit+    WitnessProgram (..),+    WitnessProgramPKH (..),+    WitnessProgramSH (..),+    isSegwit,+    viewWitnessProgram,+    decodeWitnessInput,+    calcWitnessProgram,+    simpleInputStack,+    toWitnessStack,+  )+where -import           Data.ByteString            (ByteString)-import qualified Data.Serialize             as S-import           Haskoin.Constants-import           Haskoin.Keys.Common-import           Haskoin.Script-import           Haskoin.Transaction.Common+import Crypto.Secp256k1+import Data.ByteString (ByteString)+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (Serial (deserialize, serialize))+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util.Marshal  -- | Test if a 'ScriptOutput' is P2WPKH or P2WSH -- -- @since 0.11.0.0 isSegwit :: ScriptOutput -> Bool isSegwit = \case-    PayWitnessPKHash{}     -> True-    PayWitnessScriptHash{} -> True-    _                      -> False+  PayWitnessPKHash {} -> True+  PayWitnessScriptHash {} -> True+  _ -> False  -- | High level represenation of a (v0) witness program -- -- @since 0.11.0.0 data WitnessProgram-    = P2WPKH WitnessProgramPKH-    | P2WSH WitnessProgramSH-    | EmptyWitnessProgram-    deriving (Eq, Show)+  = P2WPKH WitnessProgramPKH+  | P2WSH WitnessProgramSH+  | EmptyWitnessProgram+  deriving (Eq, Show)  -- | Encode a witness program -- -- @since 0.11.0.0-toWitnessStack :: WitnessProgram -> WitnessStack-toWitnessStack = \case-    P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, S.encode key]-    P2WSH (WitnessProgramSH stack scr) -> stack <> [S.encode scr]-    EmptyWitnessProgram                -> mempty+toWitnessStack :: Network -> Ctx -> WitnessProgram -> WitnessStack+toWitnessStack net ctx = \case+  P2WPKH (WitnessProgramPKH sig key) ->+    [encodeTxSig net ctx sig, marshal ctx key]+  P2WSH (WitnessProgramSH stack scr) ->+    stack <> [runPutS (serialize scr)]+  EmptyWitnessProgram ->+    mempty  -- | High level representation of a P2WPKH witness -- -- @since 0.11.0.0 data WitnessProgramPKH = WitnessProgramPKH-    { witnessSignature :: !TxSignature-    , witnessPubKey    :: !PubKeyI-    }-    deriving (Eq, Show)+  { signature :: !TxSignature,+    key :: !PublicKey+  }+  deriving (Eq, Show)  -- | High-level representation of a P2WSH witness -- -- @since 0.11.0.0 data WitnessProgramSH = WitnessProgramSH-    { witnessScriptHashStack  :: ![ByteString]-    , witnessScriptHashScript :: !Script-    }-    deriving (Eq, Show)+  { stack :: ![ByteString],+    script :: !Script+  }+  deriving (Eq, Show)  -- | Calculate the witness program from the transaction data -- -- @since 0.11.0.0 viewWitnessProgram ::-       Network -> ScriptOutput -> WitnessStack -> Either String WitnessProgram-viewWitnessProgram net so witness = case so of-    PayWitnessPKHash _ | length witness == 2 -> do-        sig    <- decodeTxSig net $ head witness-        pubkey <- S.decode $ witness !! 1-        return . P2WPKH $ WitnessProgramPKH sig pubkey-    PayWitnessScriptHash _ | not (null witness) -> do-        redeemScript <- S.decode $ last witness-        return . P2WSH $ WitnessProgramSH (init witness) redeemScript-    _ | null witness -> return EmptyWitnessProgram-      | otherwise    -> Left "viewWitnessProgram: Invalid witness program"+  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 <- decodePubKey ctx $ witness !! 1+    return . P2WPKH $ WitnessProgramPKH sig pubkey+  PayWitnessScriptHash _ | not (null witness) -> do+    redeemScript <- runGetS deserialize $ last witness+    return . P2WSH $ WitnessProgramSH (init witness) redeemScript+  _+    | null witness -> return EmptyWitnessProgram+    | otherwise -> Left "viewWitnessProgram: Invalid witness program"  -- | Analyze the witness, trying to match it with standard input structures -- -- @since 0.11.0.0 decodeWitnessInput ::-       Network-    -> 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 <*> S.decode keyBS-            (PayMulSig _ _, "" : sigsBS) ->-                SpendMulSig <$> traverse (decodeTxSig net) sigsBS-            _ -> Left "decodeWitnessInput: Non-standard script output"-    EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"+  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+          <*> decodePubKey ctx keyBS+      (PayMulSig _ _, "" : sigsBS) ->+        SpendMulSig+          <$> traverse (decodeTxSig net ctx) sigsBS+      _ -> Left "decodeWitnessInput: Non-standard script output"+  EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"  -- | Create the witness program for a standard input -- -- @since 0.11.0.0-calcWitnessProgram :: 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"+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) ->+    p2wsh i o+  (PayScriptHash {}, ScriptHashInput i o) ->+    p2wsh i o+  _ -> Left "calcWitnessProgram: Invalid segwit SigInput"   where-    p2wpkh sig = return . P2WPKH . WitnessProgramPKH sig-    p2wsh i o  = return . P2WSH $ WitnessProgramSH (simpleInputStack i) (encodeOutput o)+    p2wpkh sig =+      P2WPKH . WitnessProgramPKH sig+    p2wsh i o =+      P2WSH . WitnessProgramSH (simpleInputStack net ctx i) <$> encodeOutput ctx o  -- | Create the witness stack required to spend a standard P2WSH input -- -- @since 0.11.0.0-simpleInputStack :: SimpleInput -> [ByteString]-simpleInputStack = \case-    SpendPK sig       -> [f sig]-    SpendPKHash sig k -> [f sig, S.encode k]-    SpendMulSig sigs  -> "" : fmap f sigs+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
@@ -0,0 +1,735 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Taproot+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Taproot outputs, witnesses and signatures: the script trees and output+-- keys of BIP 341, the witness of a key path or script path spend, the+-- control block, and the digest a Taproot signature signs. For reference+-- see BIPs 340, 341 and 342.+module Haskoin.Transaction.Taproot+  ( -- * Keys+    XOnlyPubKey (..),+    keyParity,++    -- * Script trees+    TapLeafVersion,+    tapscriptLeafVersion,+    MAST (..),+    mastCommitment,+    getMerkleProofs,+    leafHash,+    leafHashBytes,+    checkTapTree,+    TapLeaf (..),+    tapTreeLeaves,+    tapTreeFromLeaves,++    -- * Outputs+    TaprootOutput (..),+    taprootOutputKey,+    taprootOutputSecKey,+    taprootScriptOutput,++    -- * Witnesses+    TaprootWitness (..),+    ScriptPathData (..),+    scriptPathScriptBytes,+    viewTaprootWitness,+    encodeTaprootWitness,+    verifyScriptPathData,+    maxControlBlockNodes,+    encodeControlBlock,+    decodeControlBlock,++    -- * Signatures+    TaprootSignature,+    pattern TaprootSignature,+    taprootSignature,+    isTaprootSigHash,+    taprootSigHash,+    encodeTaprootSig,+    decodeTaprootSig,+  )+where++import Control.Monad (forM_, unless, when, (<=<))+import Crypto.Hash+  ( Digest,+    SHA256,+    digestFromByteString,+    hashFinalize,+    hashUpdate,+    hashUpdates,+  )+import Crypto.Secp256k1+import Data.Aeson+  ( FromJSON (parseJSON),+    ToJSON (toJSON),+    Value (String),+    withText,+  )+import Data.Aeson.Types (Parser, Value)+import Data.Bits ((.&.), (.|.))+import Data.Bool (bool)+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get (MonadGet, getBytes, runGetS)+import Data.Bytes.Put (MonadPut, putByteString, putWord32le, putWord64le, putWord8, runPutL, runPutS)+import Data.Bytes.Serial (Serial (..), deserialize, serialize)+import Data.Foldable (foldl')+import Data.Maybe (fromMaybe, maybeToList)+import Data.Word (Word32, Word8)+import GHC.Records (HasField (..))+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Common (putVarIntBytes)+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util++{- Keys -}++-- | 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, and reads back as the point with the even y coordinate, as BIP 340+-- defines.+--+-- @since 0.21.0+newtype XOnlyPubKey = XOnlyPubKey {point :: PubKey}+  deriving (Read, Show)++instance Eq XOnlyPubKey where+  XOnlyPubKey k1 == XOnlyPubKey k2 = f k1 == f k2+    where+      f = BS.take 32 . (.get)++instance Marshal Ctx XOnlyPubKey where+  marshalPut ctx (XOnlyPubKey pk) =+    putByteString (exportXOnlyPubKey ctx pk)++  marshalGet ctx =+    maybe (fail "Not an x-only public key") (pure . XOnlyPubKey)+      . importXOnlyPubKey ctx+      =<< getBytes 32++instance MarshalJSON Ctx XOnlyPubKey where+  -- The hex must hold the 32 bytes of the key and nothing else. A longer+  -- value, such as a 33-byte compressed key, is another kind of key.+  unmarshalValue ctx =+    withText "XOnlyPubKey" $ either fail pure . (unmarshal ctx <=< hex)+    where+      hex = maybe (Left "Unable to decode hex") Right . decodeHex++  marshalValue ctx =+    String . encodeHex . marshal ctx++  marshalEncoding ctx =+    hexEncoding . runPutL . marshalPut ctx++-- | The parity of the y coordinate of a key: 0 when it is even, 1 when it+-- is odd. It is the parity bit of a control block.+keyParity :: Ctx -> PubKey -> Word8+keyParity ctx key =+  case BS.unpack . marshal ctx $ PublicKey key True of+    0x02 : _ -> 0x00+    _ -> 0x01++{- Script trees -}++-- | @since 0.21.0+type TapLeafVersion = Word8++-- | The leaf version of a Tapscript, @0xc0@, the only one BIP 342 defines.+tapscriptLeafVersion :: TapLeafVersion+tapscriptLeafVersion = 0xc0++-- | 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.+--+-- A leaf holds its script as opcodes. For a leaf whose bytes are not a list+-- of opcodes, put its hash in the tree: @MASTCommitment (leafHashBytes+-- version bytes)@. 'checkTapTree' tells whether every leaf of a tree can be+-- spent.+--+-- @since 0.21.0+data MAST+  = MASTBranch MAST MAST+  | MASTLeaf TapLeafVersion Script+  | MASTCommitment (Digest SHA256)+  deriving (Eq, Show)++-- | The inclusion proof of every leaf of the tree, depth first: the leaf+-- version, the script, and the hashes of the siblings on the way from the+-- leaf up to the root. This is the order a control block holds them in, the+-- one of @taproot_tree_helper@ in BIP 341 and of @TaprootBuilder::Combine@+-- in Bitcoin Core. A leaf at depth @d@ has a proof of @d@ hashes.+--+-- A proof of more than 'maxControlBlockNodes' hashes cannot be used: no+-- control block may hold it. 'checkTapTree' tells whether a tree has such+-- a leaf.+--+-- @since 0.21.0+getMerkleProofs :: MAST -> [(TapLeafVersion, Script, [Digest SHA256])]+getMerkleProofs = snd . go+  where+    -- The commitment of a subtree with the proofs of its leaves inside+    -- that subtree. Joining two subtrees adds the hash of the sibling at+    -- the end of every proof.+    go = \case+      MASTBranch left right ->+        let (hashL, proofsL) = go left+            (hashR, proofsR) = go right+         in ( hashBranch hashL hashR,+              [(v, s, proof <> [hashR]) | (v, s, proof) <- proofsL]+                <> [(v, s, proof <> [hashL]) | (v, s, proof) <- proofsR]+            )+      MASTLeaf v s -> (leafHash v s, [(v, s, [])])+      MASTCommitment commitment -> (commitment, [])++-- | Calculate the root hash for this tree.+--+-- @since 0.21.0+mastCommitment :: MAST -> Digest SHA256+mastCommitment = \case+  MASTBranch leftBranch rightBranch ->+    hashBranch (mastCommitment leftBranch) (mastCommitment rightBranch)+  MASTLeaf leafVersion leafScript -> leafHash leafVersion leafScript+  MASTCommitment theCommitment -> theCommitment++hashBranch :: Digest SHA256 -> Digest SHA256 -> Digest SHA256+hashBranch hashA hashB =+  hashFinalize $+    hashUpdates+      (initTaggedHash "TapBranch")+      [ min hashA hashB,+        max hashA hashB+      ]++-- | The hash of a leaf of the tree, the tapleaf hash of BIP 341:+-- @hash_TapLeaf(v || compact_size(size of s) || s)@, the tagged hash of the+-- leaf version and the script with its length as a CompactSize.+--+-- Versions up to 1.5.1 wrote the length as a base-128 varint, which is the+-- same byte below 128 only. For a script of 128 bytes or more they gave a+-- leaf hash, and so an output key and an address, that no other software+-- derives. The changelog of the release after 1.5.1 says how to move coins+-- that sit at such an address.+leafHash :: TapLeafVersion -> Script -> Digest SHA256+leafHash leafVersion = leafHashBytes leafVersion . runPutS . serialize++-- | The tapleaf hash of a script given as its bytes, as it is in the+-- witness of a script path spend. Use this for a script that is not a list+-- of opcodes, which a 'Script' cannot hold.+leafHashBytes :: TapLeafVersion -> ByteString -> Digest SHA256+leafHashBytes leafVersion scriptBytes =+  hashFinalize+    . hashUpdate (initTaggedHash "TapLeaf")+    . runPutS+    $ do+      putWord8 leafVersion+      putVarIntBytes scriptBytes++-- | Whether every leaf of a tree can be spent by its script path, as far as+-- the shape of the tree tells. It fails, with the reason, when:+--+-- * a node is deeper than 'maxControlBlockNodes' (128). The control block+--   of a leaf holds one hash per level, and BIP 341 allows at most 128.+--   Bitcoin Core's @TaprootBuilder@ refuses such a tree.+-- * a leaf version is odd or is @0x50@. A control block carries the leaf+--   version in the bits @0xfe@ of its first byte, so an odd version cannot+--   be expressed, and @0x50@ would read as an annex.+--+-- 'taprootOutputKey' and 'taprootScriptOutput' do not make this check: the+-- output of such a tree is well defined, and its other leaves and its key+-- path can still be spent. Check a tree with this function before you give+-- out its address, since a leaf it refuses can never be spent.+checkTapTree :: MAST -> Either String ()+checkTapTree = go (0 :: Int)+  where+    go depth node+      | depth > maxControlBlockNodes =+          Left ("checkTapTree: a node is deeper than " <> show maxControlBlockNodes)+      | otherwise = case node of+          MASTBranch left right -> go (depth + 1) left >> go (depth + 1) right+          MASTLeaf version _+            | odd version -> Left ("checkTapTree: the leaf version " <> show version <> " is odd")+            | version == 0x50 -> Left "checkTapTree: the leaf version 0x50 would read as an annex"+            | otherwise -> Right ()+          MASTCommitment _ -> Right ()++-- | A leaf of a script tree with its depth, as the field+-- @PSBT_OUT_TAP_TREE@ of BIP 371 lists the leaves, depth first+data TapLeaf = TapLeaf+  { depth :: !Word8,+    version :: !TapLeafVersion,+    script :: !Script+  }+  deriving (Eq, Show)++-- | The leaves of a tree, depth first, with their depth. 'Nothing' when a+-- branch of the tree is only known by its commitment, since its leaves are+-- not known, and when a leaf is deeper than 'maxControlBlockNodes' (128):+-- such a leaf can never be spent, @PSBT_OUT_TAP_TREE@ cannot hold it, and+-- 'tapTreeFromLeaves' refuses it.+tapTreeLeaves :: MAST -> Maybe [TapLeaf]+tapTreeLeaves = go (0 :: Int)+  where+    go depth = \case+      MASTBranch l r -> (<>) <$> go (depth + 1) l <*> go (depth + 1) r+      MASTLeaf v s+        | depth > maxControlBlockNodes -> Nothing+        | otherwise -> Just [TapLeaf (fromIntegral depth) v s]+      MASTCommitment _ -> Nothing++-- | The tree whose leaves these are, depth first with their depth, built+-- as @TaprootBuilder@ builds it in Bitcoin Core: two nodes at the same+-- depth join into their parent. 'Nothing' when the leaves do not make one+-- whole tree, or when a depth is above 128.+tapTreeFromLeaves :: [TapLeaf] -> Maybe MAST+tapTreeFromLeaves = go []+  where+    go :: [(Word8, MAST)] -> [TapLeaf] -> Maybe MAST+    go [(0, tree)] [] = Just tree+    go _ [] = Nothing+    go stack (leaf : rest)+      | leaf.depth > 128 = Nothing+      | otherwise = go (merge ((leaf.depth, MASTLeaf leaf.version leaf.script) : stack)) rest+    -- The newest node is first. Two nodes at the same depth become a+    -- branch at the depth above, the older one on the left.+    merge :: [(Word8, MAST)] -> [(Word8, MAST)]+    merge ((d1, right) : (d2, left) : more)+      | d1 == d2 && d1 > 0 = merge ((d1 - 1, MASTBranch left right) : more)+    merge stack = stack++{- Outputs -}++-- | Representation of a full taproot output: the internal key and the+-- script tree, if there is one. Only the x coordinate of the internal key+-- matters, as BIP 341 defines the output from the x-only internal key: a+-- key and its negation give the same output.+--+-- @since 0.21.0+data TaprootOutput = TaprootOutput+  { internalKey :: PubKey,+    mast :: Maybe MAST+  }++-- | The output key of BIP 341: the x-only internal key tweaked with the+-- hash of the internal key and the root of the tree. The parity of its y+-- coordinate is the one a control block of a script path spend carries.+--+-- Two things this function does not do:+--+-- * It does not look at the shape of the tree. A leaf deeper than 128, or+--   with a leaf version a control block cannot carry, gets an output like+--   any other and can never be spent. Run 'checkTapTree' on the tree first.+-- * It calls 'error' when the tweak is not below the order of the curve or+--   gives the point at infinity, which BIP 341 defines as a failure. The+--   tweak is a SHA256 output, so this happens for about one tree in 2^128,+--   and no input is known that reaches it. 'taprootOutputSecKey' returns+--   'Nothing' in the same case.+--+-- @since 0.21.0+taprootOutputKey :: Ctx -> TaprootOutput -> PubKey+taprootOutputKey ctx TaprootOutput {..} =+  fromMaybe keyFail $+    tweak commitment >>= tweakAddXOnlyPubKey ctx internalKey+  where+    commitment =+      taprootCommitment ctx internalKey $+        mastCommitment <$> mast+    keyFail = error "haskoin-core taprootOutputKey: key derivation failed"++-- | The secret key that signs a key path spend of the output of the secret+-- key given: the secret key, negated when its public key has an odd y+-- coordinate, plus the tweak of 'taprootOutputKey'. 'Nothing' in the+-- negligible case where the tweak gives no key.+taprootOutputSecKey :: Ctx -> SecKey -> Maybe MAST -> Maybe SecKey+taprootOutputSecKey ctx secret mast =+  tweak commitment >>= tweakAddXOnlySecKey ctx secret+  where+    commitment =+      taprootCommitment ctx (derivePubKey ctx secret) $+        mastCommitment <$> mast++taprootCommitment :: Ctx -> PubKey -> Maybe (Digest SHA256) -> ByteString+taprootCommitment ctx internalKey merkleRoot =+  BA.convert+    . hashFinalize+    . maybe id (flip hashUpdate) merkleRoot+    . (`hashUpdate` exportXOnlyPubKey ctx internalKey)+    $ initTaggedHash "TapTweak"++-- | The output script of a Taproot output: witness version 1 and the+-- x-only output key of 'taprootOutputKey', whose two caveats apply here+-- too. Check a script tree with 'checkTapTree' before you pay to it.+--+-- @since 0.21.0+taprootScriptOutput :: Ctx -> TaprootOutput -> ScriptOutput+taprootScriptOutput ctx =+  PayWitness 0x01+    . runPutS+    . marshalPut ctx+    . XOnlyPubKey+    . taprootOutputKey ctx++{- Witnesses -}++-- | Comprehension of taproot witness data+--+-- @since 0.21.0+data TaprootWitness+  = -- | A key path spend: the signature, and the annex if there is one+    KeyPathSpend ByteString (Maybe ByteString)+  | ScriptPathSpend ScriptPathData+  deriving (Eq, Show)++-- | A script path spend: the items the script runs on, the script, and+-- the control block that commits the script to the output key.+--+-- @since 0.21.0+data ScriptPathData = ScriptPathData+  { annex :: Maybe ByteString,+    stack :: [ByteString],+    -- | the script as opcodes. It is the empty script when 'opaqueScript'+    -- holds the bytes of a script that is not a list of opcodes.+    script :: Script,+    -- | the parity of the y coordinate of the output key+    extIsOdd :: Bool,+    -- | This value is masked by 0xFE+    leafVersion :: Word8,+    internalKey :: PubKey,+    -- | the Merkle proof of the leaf, leaf to root+    control :: [Digest SHA256],+    -- | the bytes of the script, when they are not the serialization of a+    -- 'Script'. A script path spend can be valid with such bytes: a+    -- Tapscript that holds an @OP_SUCCESSx@ opcode succeeds even if later+    -- bytes do not decode, and a leaf of a version other than @0xc0@ is not+    -- looked at. 'Nothing' for every script that is a list of opcodes, in+    -- which case 'script' holds it.+    opaqueScript :: Maybe ByteString+  }+  deriving (Eq, Show)++-- | The bytes of the script of a script path spend, as they are in the+-- witness: 'opaqueScript' when there is one, the serialization of 'script'+-- otherwise. The leaf hash is taken on these bytes.+scriptPathScriptBytes :: ScriptPathData -> ByteString+scriptPathScriptBytes d = fromMaybe (runPutS (serialize d.script)) d.opaqueScript++-- | The number of nodes a control block may hold at most, the depth of the+-- deepest leaf BIP 341 allows+maxControlBlockNodes :: Int+maxControlBlockNodes = 128++-- | The control block of a script path spend: the leaf version with the+-- parity bit of the output key, the x-only internal key, and the proof+-- of the leaf+encodeControlBlock :: Ctx -> Word8 -> PubKey -> [Digest SHA256] -> ByteString+encodeControlBlock ctx versionAndParity internalKey proof =+  BS.concat (BS.singleton versionAndParity : exportXOnlyPubKey ctx internalKey : map BA.convert proof)++-- | The parts of a control block, which must be 33 bytes plus 32 per node+-- of the proof, with at most 'maxControlBlockNodes' nodes, as BIP 341+-- requires+decodeControlBlock :: Ctx -> ByteString -> Either String (Word8, PubKey, [Digest SHA256])+decodeControlBlock ctx bytes+  | len < 33 || (len - 33) `mod` 32 /= 0 =+      Left ("decodeControlBlock: a control block is 33 bytes plus 32 per node, not " <> show len)+  | nodeCount > maxControlBlockNodes =+      Left ("decodeControlBlock: more than " <> show maxControlBlockNodes <> " nodes")+  | otherwise = do+      internalKey <- maybe (Left "decodeControlBlock: the internal key is not an x-only public key") Right $ importXOnlyPubKey ctx (BS.take 32 (BS.drop 1 bytes))+      proof <- traverse node (chunks (BS.drop 33 bytes))+      return (BS.head bytes, internalKey, proof)+  where+    len = BS.length bytes+    nodeCount = (len - 33) `div` 32+    chunks b+      | BS.null b = []+      | otherwise = BS.take 32 b : chunks (BS.drop 32 b)+    node = maybe (Left "decodeControlBlock: not a hash") Right . digestFromByteString @SHA256++-- | Try to interpret a 'WitnessStack' as taproot witness data, as BIP 341+-- reads it: the last item is the annex when there are at least two items+-- and it starts with @0x50@; then one item left is a key path signature,+-- and more are the stack, the script and the control block. The script is+-- read whatever its bytes are, see 'opaqueScript'. 'Nothing' for an empty+-- witness and for a control block that 'decodeControlBlock' refuses.+--+-- @since 0.21.0+viewTaprootWitness :: Ctx -> WitnessStack -> Maybe TaprootWitness+viewTaprootWitness ctx witnessStack =+  case reverse items of+    [sig] -> Just (KeyPathSpend sig annex)+    controlBytes : scriptBytes : reversedStack -> do+      (v, internalKey, control) <- eitherToMaybe $ decodeControlBlock ctx controlBytes+      -- The script is kept as opcodes when the bytes are exactly the+      -- serialization of a list of opcodes, and as bytes otherwise, so+      -- that the leaf hash is always the hash of the witness bytes.+      let (script, opaqueScript) = case runGetS deserialize scriptBytes of+            Right parsed | runPutS (serialize parsed) == scriptBytes -> (parsed, Nothing)+            _ -> (Script [], Just scriptBytes)+      pure $+        ScriptPathSpend+          ScriptPathData+            { annex,+              stack = reverse reversedStack,+              script,+              extIsOdd = odd v,+              leafVersion = v .&. 0xfe,+              internalKey,+              control,+              opaqueScript+            }+    [] -> Nothing+  where+    (annex, items) = case reverse witnessStack of+      a : rest+        | not (null rest), isAnnex a -> (Just a, reverse rest)+      _ -> (Nothing, witnessStack)++-- | The witness stack of a Taproot spend, in the order of BIP 341: the+-- signature of a key path spend, or the stack, the script and the control+-- block of a script path spend, and then the annex when there is one.+--+-- It fails for a witness that no node reads back as the same spend:+--+-- * an annex that is empty or does not start with @0x50@, which would be+--   read as a signature, a script or a control block;+-- * a proof of more than 'maxControlBlockNodes' hashes;+-- * a leaf version that is odd or is @0x50@, which a control block cannot+--   carry.+--+-- @since 0.21.0+encodeTaprootWitness :: Ctx -> TaprootWitness -> Either String WitnessStack+encodeTaprootWitness ctx = \case+  KeyPathSpend signature annex -> do+    mapM_ checkAnnex annex+    return (signature : maybeToList annex)+  ScriptPathSpend d -> do+    mapM_ checkAnnex d.annex+    when (length d.control > maxControlBlockNodes) $+      Left ("encodeTaprootWitness: a control block holds at most " <> show maxControlBlockNodes <> " nodes")+    when (odd d.leafVersion || d.leafVersion == annexTag) $+      Left ("encodeTaprootWitness: a control block cannot carry the leaf version " <> show d.leafVersion)+    return $+      d.stack+        <> [ scriptPathScriptBytes d,+             encodeControlBlock ctx (d.leafVersion .|. bool 0 1 d.extIsOdd) d.internalKey d.control+           ]+        <> maybeToList d.annex+  where+    checkAnnex annex =+      unless (isAnnex annex) $ Left "encodeTaprootWitness: an annex starts with 0x50"++-- | The first byte of an annex, @ANNEX_TAG@ in Bitcoin Core+annexTag :: Word8+annexTag = 0x50++-- | Whether bytes can be an annex: BIP 341 defines the annex as a last+-- witness item whose first byte is @0x50@+isAnnex :: ByteString -> Bool+isAnnex bytes = case BS.uncons bytes of+  Just (first, _) -> first == annexTag+  Nothing -> False++-- | Verify that the script path spend is valid, except for script execution:+-- the leaf and its proof commit to the output key, with the parity the+-- control block says, and the proof has at most 'maxControlBlockNodes'+-- hashes. The leaf hash is taken on 'scriptPathScriptBytes'.+--+-- @since 0.21.0+verifyScriptPathData ::+  Ctx ->+  -- | Output key+  PubKey ->+  ScriptPathData ->+  Bool+verifyScriptPathData ctx outkey spd = fromMaybe False $ do+  if length spd.control > maxControlBlockNodes+    then Nothing+    else+      tweak commitment+        >>= fmap onComputedKey+          . tweakAddXOnlyPubKey ctx spd.internalKey+  where+    onComputedKey computedKey =+      XOnlyPubKey outkey == XOnlyPubKey computedKey+        && expectedParity == keyParity ctx computedKey+    commitment =+      taprootCommitment ctx spd.internalKey (Just merkleRoot)+    merkleRoot = foldl' hashBranch theLeafHash spd.control+    theLeafHash = leafHashBytes (spd.leafVersion .&. 0xfe) (scriptPathScriptBytes spd)+    expectedParity = bool 0 1 spd.extIsOdd++{- Signatures -}++-- | A Schnorr signature with the hash type it signs: 64 bytes, then the+-- hash type byte unless it is 'sigHashDefault'.+--+-- The hash type is always one that 'isTaprootSigHash' accepts. Build a+-- value with 'taprootSignature' or 'decodeTaprootSig', which check it, and+-- take it apart with the pattern 'TaprootSignature' or with the fields+-- @sig@ and @hash@, as in @signature.hash@. The fields cannot be updated.+data TaprootSignature = MkTaprootSignature !SchnorrSig !SigHash+  deriving (Eq)++instance Show TaprootSignature where+  showsPrec d (MkTaprootSignature s h) =+    showParen (d > 10) $+      showString "TaprootSignature {sig = "+        . shows s+        . showString ", hash = "+        . shows h+        . showString "}"++instance HasField "sig" TaprootSignature SchnorrSig where+  getField (MkTaprootSignature s _) = s++instance HasField "hash" TaprootSignature SigHash where+  getField (MkTaprootSignature _ h) = h++-- | The signature and the hash type of a 'TaprootSignature'. This pattern+-- only matches. 'taprootSignature' builds a value.+pattern TaprootSignature :: SchnorrSig -> SigHash -> TaprootSignature+pattern TaprootSignature s h <- MkTaprootSignature s h++{-# COMPLETE TaprootSignature #-}++-- | A Taproot signature from a Schnorr signature and its hash type. Fails+-- for a hash type that 'isTaprootSigHash' refuses, such as @0x04@,+-- @0x41@ or a value above one byte: every node refuses such a signature,+-- and its bytes could not say the hash type it was made with.+taprootSignature :: SchnorrSig -> SigHash -> Either String TaprootSignature+taprootSignature s h+  | isTaprootSigHash h = Right (MkTaprootSignature s h)+  | otherwise = Left ("taprootSignature: invalid hash type " <> show (fromIntegral h :: Word32))++-- | Whether a hash type is one a Taproot signature may use: the default,+-- @ALL@, @NONE@ and @SINGLE@, the last three with or without+-- @ANYONECANPAY@. Bitcoin Core refuses every other value in+-- @SignatureHashSchnorr@.+isTaprootSigHash :: SigHash -> Bool+isTaprootSigHash sh = sh `elem` [0x00, 0x01, 0x02, 0x03, 0x81, 0x82, 0x83]++-- | The bytes of a Taproot signature as they go in a witness: the 64 bytes+-- of the signature, then the hash type byte unless it is 'sigHashDefault'.+-- 'decodeTaprootSig' reads these bytes back to the same value, since a+-- 'TaprootSignature' only holds a hash type that Taproot defines.+encodeTaprootSig :: TaprootSignature -> ByteString+encodeTaprootSig (MkTaprootSignature s h)+  | h == sigHashDefault = s.get+  | otherwise = s.get <> BS.singleton (fromIntegral h)++-- | A Taproot signature from its bytes: 64 bytes sign with+-- 'sigHashDefault', 65 end with a hash type byte that must be one+-- 'isTaprootSigHash' accepts and not zero, as BIP 341 requires+decodeTaprootSig :: ByteString -> Either String TaprootSignature+decodeTaprootSig bytes+  | BS.length bytes == 64 = (`MkTaprootSignature` sigHashDefault) <$> sigOf bytes+  | BS.length bytes == 65 = do+      let sh = fromIntegral (BS.last bytes)+      unless (isTaprootSigHash sh && sh /= sigHashDefault) $+        Left ("decodeTaprootSig: invalid hash type byte " <> show (BS.last bytes))+      (`MkTaprootSignature` sh) <$> sigOf (BS.init bytes)+  | otherwise = Left ("decodeTaprootSig: a signature is 64 or 65 bytes, not " <> show (BS.length bytes))+  where+    sigOf = maybe (Left "decodeTaprootSig: not a Schnorr signature") Right . schnorrSig++-- | The digest a Taproot signature signs, as BIP 341 and BIP 342 define it:+-- the tagged hash of the signature message, which commits to the whole+-- transaction and to the output every input spends, and, for a script path+-- spend, to the leaf. Fails when the hash type is not one of+-- 'isTaprootSigHash', when the input index is out of range, when the+-- spent outputs do not match the inputs, with @SIGHASH_SINGLE@ and no+-- output at the index of the input, or when the annex is empty or does not+-- start with @0x50@: no node takes such an item for an annex, so no node+-- computes that digest.+taprootSigHash ::+  -- | transaction to sign+  Tx ->+  -- | outputs spent, one per input of the transaction, in order+  [TxOut] ->+  -- | index of input being signed+  Int ->+  -- | what to sign+  SigHash ->+  -- | annex of the input, if there is one, with its @0x50@ prefix+  Maybe ByteString ->+  -- | for a script path spend: the leaf hash, and the position of the last+  -- @OP_CODESEPARATOR@ executed, @0xffffffff@ for none+  Maybe (Digest SHA256, Word32) ->+  Either String Hash256+taprootSigHash tx spent i sh annexM leafM = do+  unless (i >= 0 && i < length tx.inputs) $+    Left "taprootSigHash: input index out of range"+  unless (length spent == length tx.inputs) $+    Left "taprootSigHash: one spent output per input is required"+  unless (isTaprootSigHash sh) $+    Left ("taprootSigHash: invalid hash type " <> show (fromIntegral sh :: Word32))+  when (isSigHashSingle sh && i >= length tx.outputs) $+    Left "taprootSigHash: SIGHASH_SINGLE without an output at the input index"+  forM_ annexM $ \annex ->+    unless (isAnnex annex) $ Left "taprootSigHash: an annex starts with 0x50"+  return . taggedHash "TapSighash" . runPutS $ do+    putWord8 0x00 -- the epoch+    putWord8 (fromIntegral sh)+    putWord32le tx.version+    putWord32le tx.locktime+    unless (anyoneCanPay sh) $ do+      serialize $ sha256 (runPutS (mapM_ (serialize . (.outpoint)) tx.inputs))+      serialize $ sha256 (runPutS (mapM_ (putWord64le . (.value)) spent))+      serialize $ sha256 (runPutS (mapM_ (putVarIntBytes . (.script)) spent))+      serialize $ sha256 (runPutS (mapM_ (putWord32le . (.sequence)) tx.inputs))+    unless (isSigHashNone sh || isSigHashSingle sh) $+      serialize $ sha256 (runPutS (mapM_ serialize tx.outputs))+    putWord8 (bool 0 2 (isJust' leafM) .|. bool 0 1 (isJust' annexM))+    if anyoneCanPay sh+      then do+        serialize input.outpoint+        putWord64le coin.value+        putVarIntBytes coin.script+        putWord32le input.sequence+      else putWord32le (fromIntegral i)+    forM_ annexM $ \annex ->+      serialize $ sha256 (runPutS (putVarIntBytes annex))+    when (isSigHashSingle sh) $+      serialize $ sha256 (runPutS (serialize (tx.outputs !! i)))+    forM_ leafM $ \(leaf, codeSeparatorPosition) -> do+      putByteString (BA.convert leaf)+      putWord8 0x00 -- the key version+      putWord32le codeSeparatorPosition+  where+    input = tx.inputs !! i+    coin = spent !! i+    isJust' = maybe False (const True)++-- | The tagged hash of BIP 340: SHA256 of the hash of the tag twice, then+-- the message+taggedHash :: ByteString -> ByteString -> Hash256+taggedHash tag message = sha256 (tagHash <> tagHash <> message)+  where+    tagHash = runPutS (serialize (sha256 tag))
src/Haskoin/Util.hs view
@@ -1,207 +1,17 @@-{-# LANGUAGE CPP                   #-}-{-# 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+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Marshalling and helper functions. module Haskoin.Util-    (-      -- * ByteString Helpers-      bsToInteger-    , integerToBS-    , hexBuilder-    , encodeHex-    , decodeHex-    , getBits--      -- * Maybe & Either Helpers-    , eitherToMaybe-    , maybeToEither-    , liftEither-    , liftMaybe--      -- * Other Helpers-    , updateIndex-    , matchTemplate-    , convertBits--      -- * Triples-    , fst3-    , snd3-    , lst3--      -- * JSON Utilities-    , dropFieldLabel-    , dropSumLabels--    ) where--import           Control.Monad           (guard)-import           Control.Monad.Except    (ExceptT (..), liftEither)-import           Data.Aeson.Types        (Options (..), SumEncoding (..),-                                          defaultOptions, defaultTaggedObject)-import           Data.Bits-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import qualified Data.ByteString.Base16  as B16-import           Data.ByteString.Builder-import qualified Data.ByteString.Lazy    as BL-import           Data.Char               (toLower)-import           Data.List-import           Data.Text               (Text)-import qualified Data.Text.Encoding      as E-import           Data.Word               (Word8)---- ByteString helpers---- | Decode a big endian 'Integer' from a 'ByteString'.-bsToInteger :: ByteString -> Integer-bsToInteger = BS.foldr f 0 . BS.reverse-  where-    f w n = toInteger w .|. shiftL n 8---- | Encode an 'Integer' to a 'ByteString' as big endian.-integerToBS :: Integer -> ByteString-integerToBS 0 = BS.pack [0]-integerToBS i-    | i > 0     = BS.reverse $ BS.unfoldr f i-    | otherwise = error "integerToBS not defined for negative values"-  where-    f 0 = Nothing-    f x = Just (fromInteger x :: Word8, x `shiftR` 8)--hexBuilder :: ByteString -> Builder-hexBuilder = byteStringHex---- | Encode as string of human-readable hex characters.-encodeHex :: ByteString -> Text-encodeHex = E.decodeUtf8 . BL.toStrict . toLazyByteString . byteStringHex---- | Decode string of human-readable hex characters.-decodeHex :: Text -> Maybe ByteString-# if MIN_VERSION_base16_bytestring(1,0,0)-decodeHex = eitherToMaybe . B16.decode . E.encodeUtf8-# else-decodeHex text =-    let (x, b) = B16.decode (E.encodeUtf8 text)-    in guard (b == BS.empty) >> return x-# endif---- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'--- will be smallest required to hold that many bits, padded with zeroes to the--- right.-getBits :: Int -> ByteString -> ByteString-getBits b bs-    | r == 0 = BS.take q bs-    | otherwise = i `BS.snoc` l-  where-    (q, r) = b `quotRem` 8-    s = BS.take (q + 1) bs-    i = BS.init s-    l = BS.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits---- Maybe and Either monad helpers---- | Transform an 'Either' value into a 'Maybe' value. 'Right' is mapped to--- 'Just' and 'Left' is mapped to 'Nothing'. The value inside 'Left' is lost.-eitherToMaybe :: Either a b -> Maybe b-eitherToMaybe (Right b) = Just b-eitherToMaybe _         = Nothing---- | Transform a 'Maybe' value into an 'Either' value. 'Just' is mapped to--- 'Right' and 'Nothing' is mapped to 'Left'. Default 'Left' required.-maybeToEither :: b -> Maybe a -> Either b a-maybeToEither err = maybe (Left err) Right---- | Lift a 'Maybe' computation into the 'ExceptT' monad.-liftMaybe :: Monad m => b -> Maybe a -> ExceptT b m a-liftMaybe err = liftEither . maybeToEither err---- Various helpers---- | Applies a function to only one element of a list defined by its index.  If--- the index is out of the bounds of the list, the original list is returned.-updateIndex :: Int      -- ^ index of the element to change-            -> [a]      -- ^ list of elements-            -> (a -> a) -- ^ function to apply-            -> [a]      -- ^ result with one element changed-updateIndex i xs f-    | i < 0 || i >= length xs = xs-    | otherwise = l ++ (f h : r)-  where-    (l,h:r) = splitAt i xs---- | Use the list @[b]@ as a template and try to match the elements of @[a]@--- against it. For each element of @[b]@ return the (first) matching element of--- @[a]@, or 'Nothing'. Output list has same size as @[b]@ and contains results--- in same order. Elements of @[a]@ can only appear once.-matchTemplate :: [a]              -- ^ input list-              -> [b]              -- ^ list to serve as a template-              -> (a -> b -> Bool) -- ^ comparison function-              -> [Maybe a]-matchTemplate [] bs _ = replicate (length bs) Nothing-matchTemplate _  [] _ = []-matchTemplate as (b:bs) f = case break (`f` b) as of-    (l,r:rs) -> Just r  : matchTemplate (l ++ rs) bs f-    _        -> Nothing : matchTemplate as bs f---- | Returns the first value of a triple.-fst3 :: (a,b,c) -> a-fst3 (a,_,_) = a---- | Returns the second value of a triple.-snd3 :: (a,b,c) -> b-snd3 (_,b,_) = b---- | Returns the last value of a triple.-lst3 :: (a,b,c) -> c-lst3 (_,_,c) = c---- | Field label goes lowercase and first @n@ characters get removed.-dropFieldLabel :: Int -> Options-dropFieldLabel n = defaultOptions-    { fieldLabelModifier = map toLower . drop n-    }---- | Transformation from 'dropFieldLabel' is applied with argument @f@, plus--- constructor tags are lowercased and first @c@ characters removed. @tag@ is--- used as the name of the object field name that will hold the transformed--- constructor tag as its value.-dropSumLabels :: Int -> Int -> String -> Options-dropSumLabels c f tag = (dropFieldLabel f)-    { constructorTagModifier = map toLower . drop c-    , sumEncoding = defaultTaggedObject { tagFieldName = tag }-    }+  ( module Marshal,+    module Helpers,+  )+where --- | 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)+import Haskoin.Util.Helpers as Helpers+import Haskoin.Util.Marshal as Marshal
src/Haskoin/Util/Arbitrary.hs view
@@ -1,23 +1,23 @@-{-|-Module      : Haskoin.Test-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Arbitrary instances for testing.--}+-- |+-- Module      : Haskoin.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 X,+  )+where -import           Haskoin.Util.Arbitrary.Address     as X-import           Haskoin.Util.Arbitrary.Block       as X-import           Haskoin.Util.Arbitrary.Crypto      as X-import           Haskoin.Util.Arbitrary.Keys        as X-import           Haskoin.Util.Arbitrary.Message     as X-import           Haskoin.Util.Arbitrary.Network     as X-import           Haskoin.Util.Arbitrary.Script      as X-import           Haskoin.Util.Arbitrary.Transaction as X-import           Haskoin.Util.Arbitrary.Util        as X+import Haskoin.Util.Arbitrary.Address as X+import Haskoin.Util.Arbitrary.Block as X+import Haskoin.Util.Arbitrary.Crypto as X+import Haskoin.Util.Arbitrary.Keys as X+import Haskoin.Util.Arbitrary.Message as X+import Haskoin.Util.Arbitrary.Network as X+import Haskoin.Util.Arbitrary.Script as X+import Haskoin.Util.Arbitrary.Transaction as X+import Haskoin.Util.Arbitrary.Util as X
src/Haskoin/Util/Arbitrary/Address.hs view
@@ -1,63 +1,119 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# 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.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Util-import           Test.QuickCheck+import Data.Maybe (isJust)+import Haskoin.Address+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck --- | Arbitrary pay-to-public-key-hash or pay-to-script-hash address.-arbitraryAddress :: Gen Address-arbitraryAddress = oneof [arbitraryPubKeyAddress, arbitraryScriptAddress]+-- | Arbitrary address of a Bitcoin Cash network: pay-to-public-key-hash,+-- pay-to-script-hash with either hash size, a token-aware address, or any+-- other CashAddr kind.+arbitraryBitcoinCashAddress :: Gen Address+arbitraryBitcoinCashAddress =+  oneof+    [ arbitraryPubKeyAddress,+      arbitraryScriptAddress,+      arbitraryScript32Address,+      arbitraryTokenAddress,+      arbitraryCashAddress+    ] --- | Arbitrary address including pay-to-witness-arbitraryAddressAll :: Gen Address-arbitraryAddressAll =-    oneof [ arbitraryPubKeyAddress-          , arbitraryScriptAddress-          , arbitraryWitnessPubKeyAddress-          , arbitraryWitnessScriptAddress-          , arbitraryWitnessAddress-          ]+-- | Arbitrary address of a Bitcoin network, including pay-to-witness and+-- Taproot.+arbitraryBitcoinAddress :: Gen Address+arbitraryBitcoinAddress =+  oneof+    [ arbitraryPubKeyAddress,+      arbitraryScriptAddress,+      arbitraryWitnessPubKeyAddress,+      arbitraryWitnessScriptAddress,+      arbitraryTaprootAddress,+      arbitraryWitnessAddress+    ] +arbitraryAddress :: Network -> Gen Address+arbitraryAddress net =+  if isJust net.cashAddrPrefix+    then arbitraryBitcoinCashAddress+    else arbitraryBitcoinAddress+ -- | Arbitrary valid combination of (Network, Address) arbitraryNetAddress :: Gen (Network, Address) arbitraryNetAddress = do-    net <- arbitraryNetwork-    if net `elem` [bch, bchTest, bchRegTest]-        then (net, ) <$> arbitraryAddress-        else (net, ) <$> arbitraryAddressAll+  net <- arbitraryNetwork+  addr <- arbitraryAddress net+  return (net, addr)  -- | Arbitrary pay-to-public-key-hash address. arbitraryPubKeyAddress :: Gen Address-arbitraryPubKeyAddress = PubKeyAddress <$> arbitraryHash160+arbitraryPubKeyAddress = pubKeyAddress <$> arbitraryHash160  -- | Arbitrary pay-to-script-hash address. arbitraryScriptAddress :: Gen Address-arbitraryScriptAddress = ScriptAddress <$> arbitraryHash160+arbitraryScriptAddress = scriptAddress <$> arbitraryHash160  -- | Arbitrary pay-to-witness public key hash arbitraryWitnessPubKeyAddress :: Gen Address-arbitraryWitnessPubKeyAddress = WitnessPubKeyAddress <$> arbitraryHash160+arbitraryWitnessPubKeyAddress = witnessPubKeyAddress <$> arbitraryHash160  -- | Arbitrary pay-to-witness script hash arbitraryWitnessScriptAddress :: Gen Address-arbitraryWitnessScriptAddress = WitnessPubKeyAddress <$> arbitraryHash160+arbitraryWitnessScriptAddress = witnessScriptAddress <$> arbitraryHash256  arbitraryWitnessAddress :: Gen Address arbitraryWitnessAddress = do-    ver <- choose (0, 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+  case witnessAddress ver bs of+    Just a -> return a+    Nothing -> error "Error generating arbitrary WitnessAddress"++-- | Arbitrary Taproot address: witness version 1 with a 32-byte program.+-- 'arbitraryWitnessAddress' almost never draws that combination.+arbitraryTaprootAddress :: Gen Address+arbitraryTaprootAddress = do+  bs <- B.pack <$> vectorOf 32 arbitrary+  case witnessAddress 1 bs of+    Just a -> return a+    Nothing -> error "Error generating arbitrary Taproot address"++arbitraryScript32Address :: Gen Address+arbitraryScript32Address = script32Address <$> arbitraryHash256++-- | Arbitrary token-aware CashAddr that can be paid: type 2 with a 20-byte+-- hash, or type 3 with a 20-byte or a 32-byte hash.+arbitraryTokenAddress :: Gen Address+arbitraryTokenAddress = do+  (ver, len) <- elements [(2, 20), (3, 20), (3, 32)]+  bs <- B.pack <$> vectorOf len arbitrary+  case cashAddress ver bs of+    Just a -> return a+    Nothing -> error "Error generating arbitrary token-aware address"++arbitraryCashAddress :: Gen Address+arbitraryCashAddress = do+  ver <- choose (0, 15)+  len <- choose (0, 7)+  let lbs = [20, 24, 28, 32, 40, 48, 56, 64] !! len+  ws <- vectorOf lbs arbitrary+  let bs = B.pack ws+  case cashAddress ver bs of+    Just a -> return a+    Nothing -> error "Error generating arbitrary CashAddress"
src/Haskoin/Util/Arbitrary/Block.hs view
@@ -1,39 +1,56 @@-{-|-Module      : Haskoin.Test.Block-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}++-- |+-- 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.Constants-import           Haskoin.Util.Arbitrary.Util-import           Haskoin.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Network-import           Haskoin.Util.Arbitrary.Transaction-import           Test.QuickCheck+import qualified Data.HashMap.Strict as HashMap+import Haskoin.Block+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Data+import Haskoin.Transaction.Common (Tx, TxHash (..), txHash)+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Network+import Haskoin.Util.Arbitrary.Transaction+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck --- | Block full or arbitrary transactions.-arbitraryBlock :: Network -> Gen Block-arbitraryBlock net = do-    h   <- arbitraryBlockHeader-    c   <- choose (0,10)-    txs <- vectorOf c (arbitraryTx net)-    return $ Block h txs+-- | Block of arbitrary transactions. The header has the Merkle root of the+-- transactions when there are some. On a network without SegWit the+-- transactions have no witness, as a block of that network has none.+arbitraryBlock :: Network -> Ctx -> Gen Block+arbitraryBlock net ctx = do+  h <- arbitraryBlockHeader+  c <- choose (0, 10)+  txs <- vectorOf c (arbitraryNetworkTx net ctx)+  let header = case buildMerkleRoot (map txHash txs) of+        Right root -> (h :: BlockHeader) {merkle = root}+        Left _ -> h+  return $ Block header txs +-- | Arbitrary transaction of the kind a peer of the network sends: with or+-- without a witness where the network has SegWit, without one elsewhere.+arbitraryNetworkTx :: Network -> Ctx -> Gen Tx+arbitraryNetworkTx net ctx+  | net.segWit = arbitraryTx net ctx+  | otherwise = arbitraryLegacyTx net ctx+ -- | 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,48 +59,70 @@ -- | 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.+-- | Arbitrary 'MerkleBlock' that holds a valid proof: a partial Merkle tree+-- over one to thirty distinct transaction hashes, with any subset of them+-- matching, under a header that has the Merkle root of those hashes. The+-- flags are padded with zero bits to whole bytes, as they are on the wire. 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+  n <- choose (1, 30)+  hashes <- (TxHash <$> arbitraryHash256) `vectorOfDistinct` n+  matches <- vectorOf n arbitrary+  let (flags, partial) = buildPartialMerkle (zip hashes matches)+      padding = (8 - length flags `mod` 8) `mod` 8+      root = either (const bh.merkle) id (buildMerkleRoot hashes)+  return $+    MerkleBlock+      (bh :: BlockHeader) {merkle = root}+      (fromIntegral n)+      partial+      (flags ++ replicate padding False)+  where+    vectorOfDistinct gen k = go k []+      where+        go 0 acc = return acc+        go i acc = do+          x <- gen `suchThat` (`notElem` acc)+          go (i - 1 :: Int) (x : acc) --- | Arbitrary 'BlockNode'+-- | Arbitrary 'BlockNode'. One in ten is at height zero, like a genesis+-- node, whose skip hash is its own hash and is not serialized. arbitraryBlockNode :: Gen BlockNode arbitraryBlockNode =-    oneof-    [ BlockNode+  frequency [(1, genesisLike), (9, above)]+  where+    genesisLike = do+      h <- arbitraryBlockHeader+      w <- arbitrarySizedNatural+      return $ BlockNode h 0 w (headerHash h)+    above =+      BlockNode         <$> arbitraryBlockHeader-        <*> choose (0, maxBound)+        <*> choose (1, maxBound)         <*> arbitrarySizedNatural         <*> arbitraryBlockHash-    ]  -- | Arbitrary 'HeaderMemory' arbitraryHeaderMemory :: Gen HeaderMemory arbitraryHeaderMemory = do-    ls <- listOf $ (,) <$> arbitrary <*> arbitraryBSS-    HeaderMemory-        <$> (return $ HashMap.fromList ls)-        <*> arbitraryBlockNode-+  ls <- listOf $ (,) <$> arbitrary <*> arbitraryBSS+  HeaderMemory (HashMap.fromList ls) <$> arbitraryBlockNode
src/Haskoin/Util/Arbitrary/Crypto.hs view
@@ -1,33 +1,32 @@-{-|-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-import           Haskoin.Util.Arbitrary.Util-import           Test.QuickCheck+import Haskoin.Crypto.Hash+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck  -- | 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,44 +1,65 @@-{-|-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           Data.Bits             (clearBit)-import           Data.List             (foldl')-import           Data.Word             (Word32)-import           Haskoin.Crypto-import           Haskoin.Keys.Common-import           Haskoin.Keys.Extended-import           Haskoin.Util.Arbitrary.Crypto-import           Test.QuickCheck+import Crypto.Secp256k1+import Data.Bits (clearBit)+import Data.Coerce (coerce)+import Data.List (foldl')+import Data.Word (Word32)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Crypto.Keys.Extended.Internal (Fingerprint (..))+import Haskoin.Crypto.Signature+import Haskoin.Util.Arbitrary.Crypto+import Test.QuickCheck  -- | Arbitrary private key with arbitrary compressed flag.-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) --- | Arbitrary extended private key.+-- | Arbitrary private key with its public key, both in compressed form: the+-- only form of key that a SegWit version 0 input is signed with+arbitraryKeyPairC :: Ctx -> Gen (PrivateKey, PublicKey)+arbitraryKeyPairC ctx = do+  k <- wrapSecKey True <$> arbitrary+  return (k, derivePublicKey ctx k)++arbitraryFingerprint :: Gen Fingerprint+arbitraryFingerprint = Fingerprint <$> arbitrary++-- | Arbitrary extended private key. A key at depth 0 is a master key, and+-- BIP32 requires it to have a zero parent fingerprint and index. arbitraryXPrvKey :: Gen XPrvKey-arbitraryXPrvKey =-    XPrvKey <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryHash256-            <*> arbitrary+arbitraryXPrvKey = do+  depth <- arbitrary+  parent <- if depth == 0 then pure (Fingerprint 0) else arbitraryFingerprint+  index <- if depth == 0 then pure 0 else arbitrary+  XPrvKey depth parent index <$> 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 -} @@ -49,9 +70,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@@ -69,17 +91,18 @@ -- '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)+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,52 +1,147 @@-{-|-Module      : Haskoin.Test.Message-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}++-- |+-- 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.Constants-import           Haskoin.Network.Message-import           Haskoin.Util.Arbitrary.Block-import           Haskoin.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Network-import           Haskoin.Util.Arbitrary.Transaction-import           Test.QuickCheck+import Data.List (nub, sort)+import Data.Word (Word32)+import Haskoin.Crypto (Ctx)+import Haskoin.Network.BlockFilter+import Haskoin.Network.CompactBlock+import Haskoin.Network.Data+import Haskoin.Network.Message+import Haskoin.Util.Arbitrary.Block+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Network+import Haskoin.Util.Arbitrary.Transaction+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck  -- | 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-        ]+-- | Arbitrary 'SendCompact', with the versions 1 and 2 of BIP 152 and any+-- other number.+arbitrarySendCompact :: Gen SendCompact+arbitrarySendCompact = SendCompact <$> arbitrary <*> oneof [elements [1, 2], arbitrary]++-- | Strictly increasing block positions, none above the given one. The+-- first and the last possible position each come up often.+arbitraryPositionsUpTo :: Word32 -> Gen [Word32]+arbitraryPositionsUpTo largest =+  nub . sort <$> listOf (frequency [(1, elements [0, largest]), (8, choose (0, largest))])++-- | Strictly increasing block positions of up to 32 bits.+arbitraryPositions :: Gen [Word32]+arbitraryPositions = arbitraryPositionsUpTo maxBound++-- | Arbitrary 'CompactBlock' that a peer of the network can send: positions+-- within the 'maxCompactBlockIndex' of the network, and transactions as+-- 'arbitraryNetworkTx' makes them.+arbitraryCompactBlock :: Network -> Ctx -> Gen CompactBlock+arbitraryCompactBlock net ctx = do+  positions <- arbitraryPositionsUpTo net.maxCompactBlockIndex+  CompactBlock+    <$> arbitraryBlockHeader+    <*> arbitrary+    <*> listOf (choose (0, maxShortId))+    <*> mapM (\i -> PrefilledTx i <$> arbitraryNetworkTx net ctx) positions++-- | Arbitrary 'GetBlockTxn' with positions of up to 32 bits.+arbitraryGetBlockTxn :: Gen GetBlockTxn+arbitraryGetBlockTxn = GetBlockTxn <$> arbitraryBlockHash <*> arbitraryPositions++-- | Arbitrary 'GetBlockTxn' that a peer of the network can send.+arbitraryNetworkGetBlockTxn :: Network -> Gen GetBlockTxn+arbitraryNetworkGetBlockTxn net =+  GetBlockTxn <$> arbitraryBlockHash <*> arbitraryPositionsUpTo net.maxCompactBlockIndex++-- | Arbitrary 'BlockTxn' that a peer of the network can send.+arbitraryBlockTxn :: Network -> Ctx -> Gen BlockTxn+arbitraryBlockTxn net ctx = BlockTxn <$> arbitraryBlockHash <*> listOf (arbitraryNetworkTx net ctx)++-- | Arbitrary 'GetCFilters'.+arbitraryGetCFilters :: Gen GetCFilters+arbitraryGetCFilters = GetCFilters <$> arbitrary <*> arbitrary <*> arbitraryBlockHash++-- | Arbitrary 'CFilter'.+arbitraryCFilter :: Gen CFilter+arbitraryCFilter = CFilter <$> arbitrary <*> arbitraryBlockHash <*> arbitraryBS++-- | Arbitrary 'GetCFHeaders'.+arbitraryGetCFHeaders :: Gen GetCFHeaders+arbitraryGetCFHeaders = GetCFHeaders <$> arbitrary <*> arbitrary <*> arbitraryBlockHash++-- | Arbitrary 'CFHeaders'.+arbitraryCFHeaders :: Gen CFHeaders+arbitraryCFHeaders =+  CFHeaders+    <$> arbitrary+    <*> arbitraryBlockHash+    <*> arbitraryHash256+    <*> listOf arbitraryHash256++-- | Arbitrary 'GetCFCheckpt'.+arbitraryGetCFCheckpt :: Gen GetCFCheckpt+arbitraryGetCFCheckpt = GetCFCheckpt <$> arbitrary <*> arbitraryBlockHash++-- | Arbitrary 'CFCheckpt'.+arbitraryCFCheckpt :: Gen CFCheckpt+arbitraryCFCheckpt = CFCheckpt <$> arbitrary <*> arbitraryBlockHash <*> listOf arbitraryHash256++-- | Arbitrary 'Message' of every kind, including one of an unknown command.+arbitraryMessage :: Network -> Ctx -> Gen Message+arbitraryMessage net ctx =+  oneof+    [ MVersion <$> arbitraryVersion,+      return MVerAck,+      MAddr <$> arbitraryAddr1,+      MAddrV2 <$> arbitraryAddrV2,+      return MSendAddrV2,+      MInv <$> arbitraryInv1,+      MGetData <$> arbitraryGetData,+      MNotFound <$> arbitraryNotFound,+      MGetBlocks <$> arbitraryGetBlocks,+      MGetHeaders <$> arbitraryGetHeaders,+      MTx <$> arbitraryNetworkTx net ctx,+      MBlock <$> arbitraryBlock net ctx,+      MMerkleBlock <$> arbitraryMerkleBlock,+      MHeaders <$> arbitraryHeaders,+      return MGetAddr,+      MFilterLoad <$> arbitraryFilterLoad,+      MFilterAdd <$> arbitraryFilterAdd,+      return MFilterClear,+      MPing <$> arbitraryPing,+      MPong <$> arbitraryPong,+      MAlert <$> arbitraryAlert,+      return MMempool,+      MReject <$> arbitraryReject,+      return MSendHeaders,+      MFeeFilter <$> arbitraryFeeFilter,+      MSendCompact <$> arbitrarySendCompact,+      MCompactBlock <$> arbitraryCompactBlock net ctx,+      MGetBlockTxn <$> arbitraryNetworkGetBlockTxn net,+      MBlockTxn <$> arbitraryBlockTxn net ctx,+      MGetCFilters <$> arbitraryGetCFilters,+      MCFilter <$> arbitraryCFilter,+      MGetCFHeaders <$> arbitraryGetCFHeaders,+      MCFHeaders <$> arbitraryCFHeaders,+      MGetCFCheckpt <$> arbitraryGetCFCheckpt,+      MCFCheckpt <$> arbitraryCFCheckpt,+      return MWtxidRelay,+      MSendTxRcncl <$> arbitrarySendTxRcncl,+      MOther <$> arbitraryOtherCommand <*> arbitraryBS+    ]
src/Haskoin/Util/Arbitrary/Network.hs view
@@ -1,25 +1,35 @@-{-|-Module      : Haskoin.Test.Network-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Haskoin.Test.Network+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX module Haskoin.Util.Arbitrary.Network where -import qualified Data.ByteString               as BS (empty, pack)-import qualified Data.ByteString.Char8         as C8-import           Data.Word                     (Word16, Word32)-import           Haskoin.Network-import           Haskoin.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Util-import           Network.Socket                (SockAddr (..))-import           Test.QuickCheck+import Data.Bits (shiftL)+import Data.ByteString qualified as BS+import Data.ByteString.Char8 qualified as C8+import Data.Sequence qualified as Seq+import Data.Word (Word16, Word32)+import Haskoin.Network+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Util+import Network.Socket (SockAddr (..))+import Test.QuickCheck  -- | Arbitrary 'VarInt'. arbitraryVarInt :: Gen VarInt-arbitraryVarInt = VarInt <$> arbitrary+arbitraryVarInt =+  VarInt+    <$> frequency+      [ -- the last value of each encoding length and the first of the next+        (1, elements [0, 0xfc, 0xfd, 0xffff, 0x10000, 0xffffffff, 0x100000000, maxBound]),+        (3, arbitrary)+      ]  -- | Arbitrary 'VarString'. arbitraryVarString :: Gen VarString@@ -28,27 +38,70 @@ -- | 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) arbitraryNetworkAddressTime = (,) <$> arbitrary <*> arbitraryNetworkAddress --- | Arbitrary 'InvType'.+-- | Arbitrary 'AddressNetwork', known or unknown.+arbitraryAddressNetwork :: Gen AddressNetwork+arbitraryAddressNetwork =+  frequency+    [ (7, elements [AddrIPv4, AddrIPv6, AddrTorV2, AddrTorV3, AddrI2P, AddrCJDNS, AddrYggdrasil]),+      (1, AddrUnknown <$> choose (8, 255))+    ]++-- | Arbitrary 'NetworkAddressV2' with as many address bytes as its network+-- requires. An unknown network gets up to 'maxAddrV2Size' bytes.+arbitraryNetworkAddressV2 :: Gen NetworkAddressV2+arbitraryNetworkAddressV2 = do+  network <- arbitraryAddressNetwork+  len <- maybe (choose (0, fromIntegral maxAddrV2Size)) return (addressNetworkLength network)+  NetworkAddressV2+    <$> arbitrary+    <*> arbitrary+    <*> pure network+    <*> arbitraryBSn len+    <*> arbitrary++-- | Arbitrary 'AddrV2'.+arbitraryAddrV2 :: Gen AddrV2+arbitraryAddrV2 = AddrV2 <$> listOf arbitraryNetworkAddressV2++-- | Arbitrary 'InvType', including unknown types. arbitraryInvType :: Gen InvType-arbitraryInvType = elements [InvError, InvTx, InvBlock, InvMerkleBlock]+arbitraryInvType =+  frequency+    [ ( 9,+        elements+          [ InvError,+            InvTx,+            InvBlock,+            InvMerkleBlock,+            InvCompactBlock,+            InvWTx,+            InvWitnessTx,+            InvWitnessBlock,+            InvWitnessMerkleBlock+          ]+      ),+      -- Types between the last plain one and the witness flag are unknown.+      (1, InvType <$> choose (6, 1 `shiftL` 30))+    ]  -- | Arbitrary 'InvVector'. arbitraryInvVector :: Gen InvVector@@ -58,18 +111,19 @@ arbitraryInv1 :: Gen Inv arbitraryInv1 = Inv <$> listOf1 arbitraryInvVector --- | Arbitrary 'Version'.+-- | Arbitrary 'Version' with a user agent that fits 'maxUserAgentLength'. arbitraryVersion :: Gen Version arbitraryVersion =-    Version <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryNetworkAddress-            <*> arbitraryNetworkAddress-            <*> arbitrary-            <*> arbitraryVarString-            <*> arbitrary-            <*> arbitrary+  Version+    <$> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitraryNetworkAddress+    <*> arbitraryNetworkAddress+    <*> arbitrary+    <*> (VarString <$> (arbitraryBSn =<< choose (0, maxUserAgentLength)))+    <*> arbitrary+    <*> arbitrary  -- | Arbitrary non-empty 'Addr'. arbitraryAddr1 :: Gen Addr@@ -80,30 +134,37 @@ arbitraryAlert :: Gen Alert arbitraryAlert = Alert <$> arbitraryVarString <*> arbitraryVarString +-- | Arbitrary 'FeeFilter'.+arbitraryFeeFilter :: Gen FeeFilter+arbitraryFeeFilter = FeeFilter <$> arbitrary++-- | Arbitrary 'SendTxRcncl'.+arbitrarySendTxRcncl :: Gen SendTxRcncl+arbitrarySendTxRcncl = SendTxRcncl <$> arbitrary <*> arbitrary+ -- | 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+arbitraryReject =+  Reject+    -- A reject can name any bytes as its command, of any length.+    <$> frequency [(4, arbitraryMessageCommand), (1, stringToCommand <$> arbitraryBS)]+    <*> arbitraryRejectCode+    <*> arbitraryVarString+    <*> arbitraryMaybe arbitraryHash256 --- | Arbitrary 'RejectCode'.+-- | Arbitrary 'RejectCode', any of the eight. arbitraryRejectCode :: Gen RejectCode arbitraryRejectCode =-    elements-        [ RejectMalformed-        , RejectInvalid-        , RejectInvalid-        , RejectDuplicate-        , RejectNonStandard-        , RejectDust-        , RejectInsufficientFee-        , RejectCheckpoint-        ]+  elements+    [ RejectMalformed,+      RejectInvalid,+      RejectObsolete,+      RejectDuplicate,+      RejectNonStandard,+      RejectDust,+      RejectInsufficientFee,+      RejectCheckpoint+    ]  -- | Arbitrary non-empty 'GetData'. arbitraryGetData :: Gen GetData@@ -124,55 +185,88 @@ -- | 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.-arbitraryBloomFilter :: Gen (Int, Double, BloomFilter)+-- | Arbitrary bloom filter with random bits, within the limits a peer+-- accepts.+arbitraryBloomFilter :: Gen BloomFilter arbitraryBloomFilter = do-    n     <- choose (0,100000)-    fp    <- choose (1e-8,1)-    tweak <- arbitrary-    fl    <- arbitraryBloomFlags-    return (n, fp, bloomCreate n fp tweak fl)+  len <- choose (0, 1000)+  bytes <- vectorOf len arbitrary+  BloomFilter (Seq.fromList bytes)+    <$> choose (0, maxHashFuncs)+    <*> arbitrary+    <*> arbitraryBloomFlags  -- | Arbitrary 'FilterLoad'. arbitraryFilterLoad :: Gen FilterLoad-arbitraryFilterLoad = do-    (_, _, bf) <- arbitraryBloomFilter-    return $ FilterLoad bf+arbitraryFilterLoad = FilterLoad <$> arbitraryBloomFilter --- | Arbitrary 'FilterAdd'.+-- | Arbitrary 'FilterAdd' that fits 'maxFilterAddSize'. arbitraryFilterAdd :: Gen FilterAdd-arbitraryFilterAdd = FilterAdd <$> arbitraryBS+arbitraryFilterAdd = FilterAdd <$> (arbitraryBSn =<< choose (0, maxFilterAddSize)) --- | Arbitrary 'MessageCommand'.+-- | Arbitrary 'MessageCommand', known or not. An unknown command has one to+-- twelve printable ASCII characters and is not the name of a known one. 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)))-        ]+arbitraryMessageCommand =+  frequency+    [ (36, elements knownCommands),+      (4, MCOther <$> arbitraryOtherCommand)+    ]+  where+    knownCommands =+      [ MCVersion,+        MCVerAck,+        MCAddr,+        MCAddrV2,+        MCSendAddrV2,+        MCInv,+        MCGetData,+        MCNotFound,+        MCGetBlocks,+        MCGetHeaders,+        MCTx,+        MCBlock,+        MCMerkleBlock,+        MCHeaders,+        MCGetAddr,+        MCFilterLoad,+        MCFilterAdd,+        MCFilterClear,+        MCPing,+        MCPong,+        MCAlert,+        MCMempool,+        MCReject,+        MCSendHeaders,+        MCFeeFilter,+        MCSendCmpct,+        MCCmpctBlock,+        MCGetBlockTxn,+        MCBlockTxn,+        MCGetCFilters,+        MCCFilter,+        MCGetCFHeaders,+        MCCFHeaders,+        MCGetCFCheckpt,+        MCCFCheckpt,+        MCWtxidRelay,+        MCSendTxRcncl+      ]++-- | Arbitrary command name that no known command uses: one to twelve+-- printable ASCII characters.+arbitraryOtherCommand :: Gen BS.ByteString+arbitraryOtherCommand = name `suchThat` unknown+  where+    name = do+      n <- choose (1, 12)+      C8.pack <$> vectorOf n (choose (' ', '~'))+    unknown s = case stringToCommand s of+      MCOther _ -> True+      _ -> False
src/Haskoin/Util/Arbitrary/Script.hs view
@@ -1,175 +1,208 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-}-{-|-Module      : Haskoin.Test.Script-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}+{-# LANGUAGE OverloadedRecordDot #-}++-- |+-- Module      : Haskoin.Test.Script+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX module Haskoin.Util.Arbitrary.Script where -import           Crypto.Secp256k1-import qualified Data.ByteString                as B-import           Data.Maybe-import           Data.Word-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys.Common-import           Haskoin.Script-import           Haskoin.Transaction.Common-import           Haskoin.Util-import           Haskoin.Util.Arbitrary.Address-import           Haskoin.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Keys-import           Haskoin.Util.Arbitrary.Util-import           Test.QuickCheck+import Crypto.Secp256k1+import Data.ByteString qualified as B+import Data.Maybe+import Data.Word+import Haskoin.Address+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Common+import Haskoin.Util+import Haskoin.Util.Arbitrary.Address+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Keys+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck  -- | Arbitrary 'Script' with random script ops. arbitraryScript :: Gen Script arbitraryScript = Script <$> listOf arbitraryScriptOp +-- | Arbitrary push of random data. Most pushes are minimal, see+-- 'opPushData'. Some use a longer 'PushDataType' than the data needs, and the+-- lengths reach every 'PushDataType', so that every encoding is exercised.+arbitraryPushOp :: Gen ScriptOp+arbitraryPushOp = do+  len <- frequency [(6, choose (0, 75)), (2, choose (76, 255)), (1, choose (256, 300))]+  bs <- arbitraryBSn len+  let -- An empty payload with OPCODE is the byte of OP_0, so it is not kept.+      types =+        [OPCODE | len >= 1, len <= 75]+          ++ [OPDATA1 | len <= 255]+          ++ [OPDATA2, OPDATA4]+  frequency+    [ (3, return $ opPushData bs),+      (1, OP_PUSHDATA bs <$> elements types)+    ]+ -- | 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+    [ arbitraryPushOp,+      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@@ -179,86 +212,126 @@ arbitrarySigHash :: Gen SigHash arbitrarySigHash = fromIntegral <$> (arbitrary :: Gen Word32) --- | Arbitrary valid 'SigHash'.+-- | Arbitrary valid 'SigHash'. On a network with a fork id, the+-- 'SIGHASH_FORKID' flag is always set, since every signature there must+-- carry it. 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]+  let f1+        | isJust net.sigHashForkId = setForkIdFlag+        | otherwise = 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)+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+    filterBad sh =+      not $+        isSigHashUnknown sh+          || isNothing net.sigHashForkId && hasForkIdFlag sh+          || isJust net.sigHashForkId && not (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.+-- | Arbitrary m of n parameters, with n up to 'maxMultisigKeys'. Such a+-- multisig fits a bare output or a pay-to-witness-script-hash output. For a+-- pay-to-script-hash output use 'arbitraryP2SHMSParam'. arbitraryMSParam :: Gen (Int, Int) arbitraryMSParam = do-    m <- choose (1,16)-    n <- choose (m,16)-    return (m, n)+  m <- choose (1, maxMultisigKeys)+  n <- choose (m, maxMultisigKeys)+  return (m, n) +-- | The most keys of a pay-to-script-hash multisig whatever the form of its+-- keys. A redeem script is at most 520 bytes, and an uncompressed key takes+-- 66 of them with its push: 3 + 7 × 66 = 465, and 8 keys would be 531.+maxP2SHMultisigKeys :: Int+maxP2SHMultisigKeys = 7++-- | The most compressed keys of a pay-to-script-hash multisig. A compressed+-- key takes 34 bytes with its push: 3 + 15 × 34 = 513, and 16 keys would be+-- 547.+maxP2SHMultisigKeysC :: Int+maxP2SHMultisigKeysC = 15++-- | Arbitrary m of n parameters of a multisig whose script fits a+-- pay-to-script-hash redeem script with keys of any form: n up to+-- 'maxP2SHMultisigKeys'.+arbitraryP2SHMSParam :: Gen (Int, Int)+arbitraryP2SHMSParam = do+  m <- choose (1, maxP2SHMultisigKeys)+  n <- choose (m, maxP2SHMultisigKeys)+  return (m, n)++-- | Arbitrary m of n parameters of a multisig of compressed keys whose+-- script fits a pay-to-script-hash redeem script: n up to+-- 'maxP2SHMultisigKeysC'.+arbitraryP2SHMSParamC :: Gen (Int, Int)+arbitraryP2SHMSParamC = do+  m <- choose (1, maxP2SHMultisigKeysC)+  n <- choose (m, maxP2SHMultisigKeysC)+  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 []+         )+      -- Pay-to-script-hash with a 256-bit hash exists on the networks that+      -- have CashAddr.+      ++ [arbitrarySH32Output | isJust net.cashAddrPrefix]  -- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS' -- (Not 'PayScriptHash', 'DataCarrier', or SegWit)-arbitrarySimpleOutput :: Gen ScriptOutput-arbitrarySimpleOutput =-    oneof-        [ arbitraryPKOutput-        , arbitraryPKHashOutput-        , arbitraryMSOutput-        ]+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@@ -274,128 +347,179 @@  arbitraryWitOutput :: Gen ScriptOutput arbitraryWitOutput = do-    ver <- choose (0, 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 multisig that fits a pay-to-script-hash redeem script: up to+-- 7 keys of any form, or up to 15 compressed keys.+arbitraryP2SHMSOutput :: Ctx -> Gen ScriptOutput+arbitraryP2SHMSOutput ctx =+  oneof+    [ do+        (m, n) <- arbitraryP2SHMSParam+        keys <- map snd <$> vectorOf n (arbitraryKeyPair ctx)+        return $ PayMulSig keys m,+      arbitraryP2SHMSOutputC ctx+    ]++-- | Arbitrary multisig of compressed keys that fits a pay-to-script-hash+-- redeem script: up to 15 keys.+arbitraryP2SHMSOutputC :: Ctx -> Gen ScriptOutput+arbitraryP2SHMSOutputC ctx = do+  (m, n) <- arbitraryP2SHMSParamC+  keys <-+    map snd+      <$> vectorOf n (arbitraryKeyPair ctx `suchThat` ((.compress) . snd))+  return $ PayMulSig keys m++-- | Arbitrary 'ScriptOutput' of type 'PayScript32Hash'.+arbitrarySH32Output :: Gen ScriptOutput+arbitrarySH32Output = PayScript32Hash <$> arbitraryHash256+ -- | Arbitrary 'ScriptOutput' of type 'PayScriptHash'. arbitrarySHOutput :: Gen ScriptOutput-arbitrarySHOutput = PayScriptHash . getAddrHash160 <$> arbitraryScriptAddress+arbitrarySHOutput = do+  address <- arbitraryScriptAddress+  case addressHash160 address of+    Just h -> return $ PayScriptHash h+    Nothing -> error "Could not get Hash160 from address" --- | Arbitrary 'ScriptOutput' of type 'DataCarrier'.+-- | Arbitrary 'ScriptOutput' of type 'DataCarrier', with any number of+-- pushes, including none and empty ones. arbitraryDCOutput :: Gen ScriptOutput-arbitraryDCOutput = DataCarrier <$> arbitraryBS1+arbitraryDCOutput = DataCarrier <$> listOf arbitraryBS --- | Arbitrary 'ScriptInput'.-arbitraryScriptInput :: Network -> Gen ScriptInput-arbitraryScriptInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        , arbitrarySHInput net-        ]+-- | Arbitrary 'ScriptInput'. A nested witness input is only drawn on a+-- network that has SegWit.+arbitraryScriptInput :: Network -> Ctx -> Gen ScriptInput+arbitraryScriptInput net ctx =+  oneof $+    [ arbitraryPKInput net ctx,+      arbitraryPKHashInput net ctx,+      arbitraryMSInput net ctx,+      arbitrarySHInput net ctx+    ]+      ++ [arbitraryNestedWitnessInput | net.segWit] +-- | Arbitrary 'ScriptInput' of type 'NestedWitnessInput', wrapping a+-- 'PayWitnessPKHash' or a 'PayWitnessScriptHash'.+arbitraryNestedWitnessInput :: Gen ScriptInput+arbitraryNestedWitnessInput =+  NestedWitnessInput <$> oneof [arbitraryWPKHashOutput, arbitraryWSHOutput]+ -- | Arbitrary 'ScriptInput' of type 'SpendPK', 'SpendPKHash' or 'SpendMulSig' -- (not 'ScriptHashInput')-arbitrarySimpleInput :: Network -> Gen ScriptInput-arbitrarySimpleInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        ]+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+-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput', whose input fits its+-- redeem script: a 'SpendPK' for a 'PayPK', a 'SpendPKHash' for a+-- 'PayPKHash', a 'SpendMulSig' for a 'PayMulSig'.+arbitrarySHInput :: Network -> Ctx -> Gen ScriptInput+arbitrarySHInput net ctx =+  oneof+    [ do+        sig <- arbitraryTxSignatureEmpty net ctx+        ScriptHashInput (SpendPK sig) <$> arbitraryPKOutput ctx,+      do+        sig <- arbitraryTxSignatureEmpty net ctx+        key <- snd <$> arbitraryKeyPair ctx+        ScriptHashInput (SpendPKHash sig key) <$> arbitraryPKHashOutput,+      arbitraryMulSigSHInput net 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+-- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'. The+-- redeem script is at most 520 bytes long, as a pay-to-script-hash redeem+-- script must be.+arbitraryMulSigSHInput :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInput net ctx =+  arbitraryP2SHMSOutput 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+arbitraryMulSigSHInputC :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInputC net ctx =+  arbitraryP2SHMSOutputC 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 =+  arbitraryP2SHMSOutput 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 =+  arbitraryP2SHMSOutputC 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,270 +1,548 @@-{-|-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.Either         (fromRight)-import           Data.List           (nub, nubBy, permutations)-import           Data.Word           (Word64)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys.Common-import           Haskoin.Script-import           Haskoin.Util.Arbitrary.Crypto-import           Haskoin.Util.Arbitrary.Keys-import           Haskoin.Util.Arbitrary.Script-import           Haskoin.Util.Arbitrary.Util-import           Haskoin.Transaction-import           Test.QuickCheck+import Control.Monad+import Data.ByteString qualified as BS+import Data.Either (fromRight)+import Data.List (findIndex, nub, nubBy, permutations)+import Data.Maybe (fromMaybe, isNothing)+import Data.Map.Strict qualified as Map+import Data.Word (Word32, Word64)+import Haskoin.Address+import Haskoin.Crypto (Ctx)+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended (XPubKey (..), listToPath)+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Transaction.Segwit (isSegwit)+import Haskoin.Util+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Keys+import Haskoin.Util.Arbitrary.Script+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck --- | Wrapped coin value for testing.-newtype TestCoin = TestCoin { getTestCoin :: Word64 }-    deriving (Eq, Show)+-- | A coin for testing: its value, and the kind of input it is spent with.+data TestCoin = TestCoin+  { getTestCoin :: Word64,+    getTestCoinKind :: InputKind+  }+  deriving (Eq, Show)  instance Coin TestCoin where-    coinValue = getTestCoin+  coinValue = (.getTestCoin)+  coinInputKind = (.getTestCoinKind)  -- | Arbitrary transaction hash (for non-existent transaction). arbitraryTxHash :: Gen TxHash arbitraryTxHash = TxHash <$> arbitraryHash256 --- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14)+-- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14), spent+-- with an arbitrary kind of input. A third of the amounts are below 10,000+-- satoshi, near the fees and the dust thresholds, a third below one coin,+-- and a third anywhere up to the 21 million coins. arbitrarySatoshi :: Network -> Gen TestCoin-arbitrarySatoshi net = TestCoin <$> choose (1, getMaxSatoshi net)+arbitrarySatoshi net = TestCoin <$> amount <*> arbitraryInputKind+  where+    amount = oneof [choose (1, 10000), choose (1, 100000000), choose (1, net.maxSatoshi)] +-- | Arbitrary kind of input that can exist. A SegWit multisig has+-- parameters from 'arbitraryMSParam', up to 20 keys. A pay-to-script-hash+-- multisig has them from 'arbitraryP2SHMSParamC', up to the 15 compressed+-- keys that fit a redeem script.+arbitraryInputKind :: Gen InputKind+arbitraryInputKind = do+  (m, n) <- arbitraryMSParam+  (mP2SH, nP2SH) <- arbitraryP2SHMSParamC+  elements+    [ InputPK,+      InputPKHash,+      InputUncompressedPKHash,+      InputMulSig mP2SH nP2SH,+      InputWitnessPKHash,+      InputWitnessMulSig m n,+      InputNestedWitnessPKHash,+      InputNestedWitnessMulSig m n,+      InputTaproot+    ]++-- | Arbitrary kind of output. An 'OutputScript' is up to 10000 bytes, the+-- most a script may have.+arbitraryOutputKind :: Gen OutputKind+arbitraryOutputKind =+  oneof+    [ elements+        [ OutputPKHash,+          OutputScriptHash,+          OutputScript32Hash,+          OutputWitnessPKHash,+          OutputWitnessScriptHash,+          OutputTaproot+        ],+      OutputScript <$> choose (0, 10000)+    ]+ -- | 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+-- | Arbitrary witness transaction (witness data is fake). At least one+-- input has a witness item, since a transaction whose stacks are all+-- empty has no witness.+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 (:) <$> listOf1 arbitraryBS <*> vectorOf (length uniqueInps - 1) (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+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+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+-- to generate the 'ScriptOutput' or 'RedeemScript'. On a network with+-- SegWit, it may be a SegWit version 0 output, or a Taproot output spent+-- by its key path. The signer refuses those on a network without SegWit.+arbitrarySigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitrarySigInput net ctx =+  oneof $+    [ wrapKey <$> arbitraryPKSigInput net ctx,+      wrapKey <$> arbitraryPKHashSigInput net ctx,+      arbitraryMSSigInput net ctx,+      arbitrarySHSigInput net ctx+    ]+      <> concat+        [ [ wrapKey <$> arbitraryWPKHSigInput net ctx,+            arbitraryWSHSigInput net ctx,+            wrapKey <$> arbitraryTaprootSigInput net ctx+          ]+        | net.segWit         ] +-- | Arbitrary 'SigInput' of a Taproot output without a script tree, spent+-- by its key path with the key of the internal key, as a wallet of BIP 86+-- spends. The sighash is one a Taproot signature may use, other than+-- SIGHASH_SINGLE, which needs an output at the index of the input.+arbitraryTaprootSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryTaprootSigInput net ctx = do+  (k, p) <- arbitraryKeyPair ctx+  val <- getTestCoin <$> arbitrarySatoshi net+  op <- arbitraryOutPoint+  sh <- elements [sigHashDefault, sigHashAll, sigHashNone, setAnyoneCanPay sigHashAll, setAnyoneCanPay sigHashNone]+  let out = taprootScriptOutput ctx (TaprootOutput p.point Nothing)+  return (SigInput out val op sh Nothing, k)+ -- | 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)+-- | Arbitrary 'SigInput' of a key in either form.+arbitraryAnyInput :: Network -> Ctx -> Bool -> Gen (SigInput, PrivateKey)+arbitraryAnyInput net ctx = arbitraryAnyInputOf (arbitraryKeyPair ctx) net ctx +-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash', when+-- the flag is set, or 'PayPK', of a key from the generator given.+arbitraryAnyInputOf ::+  Gen (PrivateKey, PublicKey) -> Network -> Ctx -> Bool -> Gen (SigInput, PrivateKey)+arbitraryAnyInputOf keyPair net ctx pkh = do+  (k, p) <- keyPair+  let out+        | pkh = case addressHash160 (pubKeyAddr ctx p) of+            Just h -> PayPKHash h+            Nothing -> error "Could not get Hash160 from address"+        | 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 = arbitraryMSSigInputOf arbitraryMSParam +-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig', whose m+-- of n parameters come from the given generator.+arbitraryMSSigInputOf ::+  Gen (Int, Int) -> Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitraryMSSigInputOf params net ctx = arbitraryMSSigInputWith (arbitraryKeyPair ctx) params net++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig', whose+-- keys and m of n parameters come from the given generators.+arbitraryMSSigInputWith ::+  Gen (PrivateKey, PublicKey) -> Gen (Int, Int) -> Network -> Gen (SigInput, [PrivateKey])+arbitraryMSSigInputWith keyPair params net = do+  (m, n) <- params+  ks <- vectorOf n keyPair+  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)+-- 'RedeemScript'. A multisig redeem script has at most 7 keys, so that it+-- fits the 520 bytes of a pay-to-script-hash redeem script with keys of any+-- form.+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,+        arbitraryMSSigInputOf arbitraryP2SHMSParam net ctx+      ]+  let out = case either (const Nothing) addressHash160 (payToScriptAddress ctx rdm) of+        Just h -> PayScriptHash h+        Nothing -> error "Could not get Hash160 from address"+  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)+-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayWitnessPKHash'.+-- The key is compressed, as nodes require of a SegWit version 0 input.+arbitraryWPKHSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryWPKHSigInput net ctx = do+  (k, p) <- arbitraryKeyPairC ctx+  (val, op, sh) <- arbitraryInputStuff net+  let out = case addressHash160 (pubKeyAddr ctx p) of+        Just h -> PayWitnessPKHash h+        Nothing -> error "Could not get Hash160 from address"+  return (SigInput out val op sh Nothing, k) -arbitraryWSHSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitraryWSHSigInput net = do-    (SigInput rdm val op sh _, ks) <- oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        ]-    let out = PayWitnessScriptHash . getAddrHash256 $ payToWitnessScriptAddress rdm-    return (SigInput out val op sh $ Just rdm, ks)+-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type+-- 'PayWitnessScriptHash' and its witness script. The keys of the witness+-- script are compressed, as nodes require of a SegWit version 0 input.+arbitraryWSHSigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitraryWSHSigInput net ctx = do+  (SigInput rdm val op sh _, ks) <-+    oneof+      [ wrapKey <$> arbitraryAnyInputOf (arbitraryKeyPairC ctx) net ctx False,+        wrapKey <$> arbitraryAnyInputOf (arbitraryKeyPairC ctx) net ctx True,+        arbitraryMSSigInputWith (arbitraryKeyPairC ctx) arbitraryMSParam net+      ]+  let out = case either (const Nothing) addressHash256 (payToWitnessScriptAddress ctx rdm) of+        Just h -> PayWitnessScriptHash h+        Nothing -> error "Could not get Hash256 from address"+  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)+-- passed to 'signTx' or 'detSignTx' to fully sign the 'Tx'. The outputs+-- spend no more than the inputs hold.+arbitrarySigningData :: Network -> Ctx -> Gen (Tx, [SigInput], [PrivateKey])+arbitrarySigningData net ctx = do+  v <- arbitrary+  ni <- choose (1, 5)+  sigis <- vectorOf ni (arbitrarySigInput net ctx)+  let uSigis = nubBy (\(a, _) (b, _) -> a.outpoint == b.outpoint) sigis+  -- The signer refuses SIGHASH_SINGLE on a legacy input without an output+  -- at its index, so every input gets an output when one asks for it+  no <- choose (if any (legacySingle net . fst) uSigis then length uSigis else 1, 5)+  inps <- forM uSigis $ \(s, _) -> TxIn s.outpoint BS.empty <$> arbitrary+  outs <- arbitraryOutputsWithin net ctx (sum (map ((.value) . fst) uSigis)) no+  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) +-- | Whether the input is to be signed with SIGHASH_SINGLE over the legacy+-- digest, which is the constant 1 when the input has no output at its+-- index. 'makeSignature' refuses to sign that.+legacySingle :: Network -> SigInput -> Bool+legacySingle net si =+  isSigHashSingle si.sighash && not (isSegwit si.script) && isNothing net.sigHashForkId++-- | Arbitrary outputs whose values add up to at most the amount given+arbitraryOutputsWithin :: Network -> Ctx -> Word64 -> Int -> Gen [TxOut]+arbitraryOutputsWithin net ctx total count =+  vectorOf count $+    TxOut+      <$> choose (0, total `div` fromIntegral count)+      <*> (marshal ctx <$> arbitraryScriptOutput net ctx)+ -- | 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.+-- | Arbitrary partially-signed transactions. The outputs spend no more+-- than the coins hold. 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+  empty <- arbitraryEmptyTx net ctx+  coins <- forM (map (.outpoint) empty.inputs) $ \op -> (,) op <$> arbitraryData+  outs <- arbitraryOutputsWithin net ctx (sum [val | (_, (_, val, _, _, _, _)) <- coins]) (length empty.outputs)+  let tx = Tx empty.version empty.inputs outs [] empty.locktime+  res <-+    forM coins $ \(op, (so, val, rdmM, prvs, m, n)) -> do+      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]+      -- The signer refuses SIGHASH_SINGLE on a legacy input without an+      -- output at its index+      let index = fromMaybe 0 (findIndex ((== op) . (.outpoint)) tx.inputs)+          refused sh = index >= length tx.outputs && legacySingle net (SigInput so val op sh rdmM)+      sh <- arbitraryValidSigHash net `suchThat` (not . refused)+      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)-            ]+      -- At most 7 keys, so that the script also fits a pay-to-script-hash+      -- redeem script+      (m, n) <- arbitraryP2SHMSParam+      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+      let h =+            fromMaybe+              (error "Could not get Hash160 from address")+              (either (const Nothing) addressHash160 (payToScriptAddress ctx so))+      elements+        [ (so, val, Nothing, prvKeys, m, n),+          ( PayScriptHash h,+            val,+            Just so,+            prvKeys,+            m,+            n+          )+        ] -wrapKey :: (SigInput, SecKeyI) -> (SigInput, [SecKeyI])+wrapKey :: (SigInput, PrivateKey) -> (SigInput, [PrivateKey]) wrapKey (s, k) = (s, [k])++-- | Arbitrary PSBT of a transaction of the network, of version 0 or 2,+-- with fields of every kind: previous transactions, witness outputs,+-- partial signatures, sighash types, scripts, final scripts, derivation+-- records, extended public keys, the Taproot fields, proprietary fields,+-- and keys the spec does not define. An input that carries a previous+-- transaction spends an output of it, as 'decodePsbt' requires. One PSBT+-- in ten has no input. One of version 2 may have its flags, and lock+-- times its inputs require.+arbitraryPsbt :: Network -> Ctx -> Gen Psbt+arbitraryPsbt net ctx = do+  drawn <- arbitraryTx net ctx+  inputs0 <- frequency [(9, return drawn.inputs), (1, return [])]+  parents <- mapM (const (oneof [return Nothing, Just <$> spendable])) inputs0+  let spend input = maybe input (\(parent, index) -> TxIn (OutPoint (txHash parent) index) input.script input.sequence)+      base = psbtOf (Tx drawn.version (zipWith spend inputs0 parents) drawn.outputs [] drawn.locktime)+  globals <- globalFields+  inputs <- mapM inputFields parents+  outputs <- mapM (const outputFields) base.outputs+  let version0 = Psbt Version0 base.unsignedTx globals inputs outputs+  oneof [return version0, version2 version0]+  where+    -- A transaction with at least one output, and the index of one of them+    spendable = do+      parent <- arbitraryTx net ctx+      extra <- arbitraryTxOut net ctx+      let outputs = if null parent.outputs then [extra] else parent.outputs+      index <- choose (0, length outputs - 1)+      return (Tx parent.version parent.inputs outputs parent.witness parent.locktime, fromIntegral index)+    -- The same PSBT in version 2, with flags at times, and lock times the+    -- inputs require: all heights, all times, or both in every input, so+    -- that one fits them all. The PSBT is rebuilt from its maps for the+    -- lock time they determine.+    version2 version0 = do+      let converted = withVersion Version2 version0+      -- The bits the spec does not define are those from the fourth on: a multiple of 8+      flagsM <- oneof [return Nothing, Just <$> (Modifiable <$> arbitrary <*> arbitrary <*> arbitrary <*> ((8 *) <$> choose (0, 31)))]+      kind <- choose (0 :: Int, 2)+      inputs <- forM converted.inputs $ \m -> do+        asks <- arbitrary+        height <- choose (1, 499999999)+        time <- choose (500000000, maxBound)+        return $+          if not asks+            then m+            else case kind of+              0 -> insertRequiredHeightLockTime height m+              1 -> insertRequiredTimeLockTime time m+              _ -> insertRequiredHeightLockTime height (insertRequiredTimeLockTime time m)+      let globals = maybe id insertTxModifiable flagsM converted.globals+      either (error . ("arbitraryPsbt: " <>)) return (psbtOfMaps globals inputs converted.outputs)+    -- Types from 0x20 to 0x7f are none the spec defines for any map. A+    -- type from 253 on is written over three bytes: 0xfd, then the type+    -- over two bytes, little-endian, which are the start of the key data.+    unknownFields = Map.fromList <$> resize 3 (listOf (oneof [unknownField, wideTypeField, proprietaryField]))+    unknownField = (,) <$> (Key <$> choose (0x20, 0x7f) <*> arbitraryBS) <*> arbitraryBS+    wideTypeField = do+      wide <- choose (0xfd, 0xffff :: Int)+      rest <- arbitraryBS+      (,) (Key 0xfd (BS.pack [fromIntegral (wide `mod` 256), fromIntegral (wide `div` 256)] <> rest)) <$> arbitraryBS+    proprietaryField = (,) <$> (proprietaryKey <$> (Proprietary <$> arbitraryBS <*> arbitrary <*> arbitraryBS)) <*> arbitraryBS+    globalFields = do+      m <- unknownFields+      xpubs <- resize 2 (listOf xpubRecord)+      return (foldr ($) m xpubs)+    -- The path of an extended key has one index per level of its depth+    xpubRecord = do+      xpub <- arbitraryXPubKey ctx+      path <- listToPath <$> vectorOf (fromIntegral xpub.depth) arbitrary+      (\fingerprint -> insertXpubRecord net ctx xpub (Derivation fingerprint path)) <$> arbitraryFingerprint+    inputFields parentM = do+      m <- unknownFields+      fields <-+        sequence+          [ return (maybe id (insertNonWitnessUtxo . fst) parentM),+            maybeField (insertWitnessUtxo <$> arbitraryTxOut net ctx),+            maybeField (insertSigHashType . fromIntegral <$> (arbitrary :: Gen Word32)),+            maybeField (insertInputRedeemScript <$> arbitraryScript),+            maybeField (insertInputWitnessScript <$> arbitraryScript),+            maybeField (insertFinalScriptSig <$> arbitraryBS),+            maybeField (insertFinalScriptWitness <$> resize 3 (listOf arbitraryBS)),+            derivations (insertInputDerivation ctx),+            partialSignatures,+            maybeField (insertInputTapInternalKey ctx <$> point),+            maybeField (insertInputTapMerkleRoot <$> digest),+            maybeField (insertTapKeySig <$> arbitraryTapSig),+            maybeField (insertTapScriptSig ctx <$> point <*> digest <*> arbitraryTapSig),+            maybeField (insertTapLeafScript <$> leafScript),+            maybeField (insertInputTapDerivation ctx <$> point <*> tapDerivation)+          ]+      return (foldr ($) m fields)+    outputFields = do+      m <- unknownFields+      fields <-+        sequence+          [ maybeField (insertOutputRedeemScript <$> arbitraryScript),+            maybeField (insertOutputWitnessScript <$> arbitraryScript),+            derivations (insertOutputDerivation ctx),+            maybeField (insertOutputTapInternalKey ctx <$> point),+            maybeField (insertOutputTapTree <$> scriptTree),+            maybeField (insertOutputTapDerivation ctx <$> point <*> tapDerivation)+          ]+      return (foldr ($) m fields)+    maybeField gen = oneof [return id, gen]+    derivations insert = do+      records <- resize 2 (listOf ((,) <$> (snd <$> arbitraryKeyPair ctx) <*> (Derivation <$> arbitraryFingerprint <*> arbitraryDerivPath)))+      return $ foldr (\(key, derivation) inserted -> insert key derivation . inserted) id records+    -- Signatures of a sighash type the network defines, under keys that+    -- need not be theirs: a PSBT is read without verifying them+    partialSignatures = do+      signatures <- resize 2 (listOf ((,) <$> (snd <$> arbitraryKeyPair ctx) <*> ((\(_, _, signature) -> encodeTxSig net ctx signature) <$> arbitraryTxSignature net ctx)))+      return $ foldr (\(key, signature) inserted -> insertPartialSignature ctx key signature . inserted) id signatures+    point = (.point) . snd <$> arbitraryKeyPair ctx+    digest = leafHash 0xc0 <$> arbitraryScript+    -- 64 bytes, alone or with one of the six sighash bytes of BIP 341+    arbitraryTapSig = do+      bytes <- arbitraryBSn 64+      sighash <- elements [[], [0x01], [0x02], [0x03], [0x81], [0x82], [0x83]]+      return (fromRight (error "arbitraryPsbt: Taproot signature") (decodeTaprootSig (bytes <> BS.pack sighash)))+    leafScript = do+      internal <- point+      nodes <- resize 3 (listOf digest)+      (\script -> TapLeafScript (encodeControlBlock ctx 0xc0 internal nodes) script 0xc0) <$> arbitraryScript+    tapDerivation = TapDerivation <$> resize 2 (listOf digest) <*> (Derivation <$> arbitraryFingerprint <*> arbitraryDerivPath)+    -- The leaves of a whole tree: one leaf alone, or two under the root+    scriptTree =+      oneof+        [ (\script -> [TapLeaf 0 0xc0 script]) <$> arbitraryScript,+          (\a b -> [TapLeaf 1 0xc0 a, TapLeaf 1 0xc0 b]) <$> arbitraryScript <*> arbitraryScript+        ]
src/Haskoin/Util/Arbitrary/Util.hs view
@@ -1,55 +1,61 @@ {-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE OverloadedStrings         #-}-{-|-Module      : Haskoin.Test.Util-Copyright   : No rights reserved-License     : MIT-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Haskoin.Test.Util+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX module Haskoin.Util.Arbitrary.Util-( arbitraryBS-, arbitraryBS1-, arbitraryBSn-, arbitraryBSS-, arbitraryBSS1-, arbitraryBSSn-, arbitraryMaybe-, arbitraryNetwork-, arbitraryUTCTime-, SerialBox(..)-, JsonBox(..)-, NetBox(..)-, ReadBox(..)-, testIdentity-, testSerial-, testRead-, testJson-, testNetJson-, arbitraryNetData-, genNetData-)+  ( arbitraryBS,+    arbitraryBS1,+    arbitraryBSn,+    arbitraryBSS,+    arbitraryBSS1,+    arbitraryBSSn,+    arbitraryMaybe,+    arbitraryNetwork,+    arbitraryUTCTime,+    ReadBox (..),+    JsonBox (..),+    MarshalJsonBox (..),+    SerialBox (..),+    MarshalBox (..),+    IdentityTests (..),+    testIdentity,+    testSerial,+    testRead,+    testJson,+  ) where -import           Control.Monad           (forM_, (<=<))-import qualified Data.Aeson              as A-import qualified Data.Aeson.Encoding     as A-import qualified Data.Aeson.Types        as A-import           Data.ByteString         (ByteString, pack)-import qualified Data.ByteString.Short   as BSS-import qualified Data.Map.Strict         as Map-import           Data.Proxy-import qualified Data.Serialize          as S-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           Test.Hspec              (Spec, describe, shouldBe,-                                          shouldSatisfy)-import           Test.Hspec.QuickCheck   (prop)-import           Test.QuickCheck+import Control.Monad (forM_, (<=<))+import qualified Data.Aeson as A+import qualified Data.Aeson.Encoding as A+import qualified Data.Aeson.Types as A+import Data.ByteString (ByteString, pack)+import Data.ByteString.Lazy (fromStrict, toStrict)+import qualified Data.ByteString.Short as BSS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default+import qualified Data.Map.Strict as Map+import Data.Proxy+import Data.Time.Clock (UTCTime (..))+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified Data.Typeable as T+import Data.Word (Word32)+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Util+import Test.Hspec (Spec, describe, shouldBe, shouldSatisfy)+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck  -- | Arbitrary strict 'ByteString'. arbitraryBS :: Gen ByteString@@ -78,14 +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)-                             ]+arbitraryMaybe g =+  frequency+    [ (1, return Nothing),+      (5, Just <$> g)+    ]  -- | Generate an Network arbitraryNetwork :: Gen Network@@ -93,115 +101,153 @@  -- Helpers for creating Serial and JSON Identity tests -data SerialBox =-    forall a. (Show a, Eq a, T.Typeable a, S.Serialize 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)+data ReadBox+  = forall a.+    (Read a, Show a, Eq a, T.Typeable a) =>+    ReadBox (Gen a) -data JsonBox =-    forall a. (Show a, Eq a, T.Typeable a, A.ToJSON a, A.FromJSON a) =>-              JsonBox (Gen a)+data JsonBox+  = 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 "Data.Serialize 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) --- | Generate Data.Serialize identity tests+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, S.Serialize a) => Gen a -> Spec+  (Eq a, Show a, T.Typeable a, Serial a) => Gen a -> Spec testSerial gen =-    prop ("Data.Serialize encoding/decoding identity for " <> name) $-    forAll gen $ \x -> (S.decode . S.encode) 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` Right 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) -> val s a `shouldBe` Just a+  prop ("MarshalJSON marshalEncoding/unmarshalValue identity for " <> name) $+    forAll gen $+      \(s, a) -> enc s a `shouldBe` Just a   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)+    val s a = A.parseMaybe (unmarshalValue s) (marshalValue s a)+    enc s a = unmarshalJSON s (marshalJSON s a)  toMap :: a -> Map.Map String a toMap = Map.singleton "object"  toMapE :: A.Encoding -> A.Encoding-toMapE = A.pairs . (A.pair "object")+toMapE = A.pairs . A.pair "object"  fromMap :: Map.Map String a -> a fromMap = (Map.! "object")-
+ src/Haskoin/Util/Helpers.hs view
@@ -0,0 +1,480 @@+{-# 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 =+  case parseBase16 u8 of+    Right b16 -> Just $ decodeBase16 b16+    Left _ -> Nothing+  where+    u8 = T.encodeUtf8 t++-- | Decode string of human-readable hex characters.+decodeHexLazy :: LT.Text -> Maybe LB.ByteString+decodeHexLazy t =+  case LB16.parseBase16 u8 of+    Right b16 -> Just $ LB16.decodeBase16 b16+    Left _ -> 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. Raises an error when the 'ByteString' does not hold that many+-- bits, instead of returning fewer bits than asked.+getBits :: Int -> ByteString -> ByteString+getBits b bs+  | b < 0 || b > 8 * B.length bs =+      error ("getBits: " <> show b <> " bits asked of " <> show (B.length bs) <> " bytes")+  | 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'. With padding, leftover bits fill one more output digit. Without+-- padding, the conversion fails when the leftover bits could hold a whole+-- input digit, or when they are not all zero. That is the rule Bitcoin Core+-- applies. It rejects strings that are not the canonical encoding of their+-- payload.+--+-- The conversion also fails for a digit that does not fit the width it is+-- converted from, as the reference code of BIP 173 does, and for widths that+-- are not positive or whose sum is above the 64 bits of a 'Word'.+convertBits :: Bool -> Int -> Int -> [Word] -> Maybe [Word]+convertBits pad frombits tobits i+  | frombits < 1 || tobits < 1 || frombits + tobits > 64 = Nothing+  | any (>= 1 `shiftL` frombits) i = Nothing+  | pad = Just . reverse $ if xbits == 0 then xout else leftover : xout+  | xbits >= frombits = Nothing+  | leftover /= 0 = Nothing+  | otherwise = Just (reverse xout)+  where+    (xacc, xbits, xout) = foldl' outer (0, 0, []) i+    -- The leftover bits, moved to the top of one output digit.+    leftover = (xacc `shiftL` (tobits - xbits)) .&. maxv+    maxv = 1 `shiftL` tobits - 1+    -- Only this many bits of the accumulator are ever read. Masking to them+    -- keeps the accumulator from overflowing on long inputs.+    maxAcc = 1 `shiftL` (frombits + tobits - 1) - 1+    outer (acc, bits, out) it =+      let acc' = ((acc `shiftL` frombits) .|. it) .&. maxAcc+          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)++-- | Read an 'Integer' that 'putInteger' wrote. A number has one encoding, and+-- any other way to write it is refused:+--+-- * the tag byte is 0x00 for a number that fits 32 bits, and 0x01 otherwise+-- * the sign byte of the wide form is 0x01, or 0xff for a negative number+-- * the bytes of the wide form are the shortest that hold the number, so the+--   last one is not zero+--+-- This is the encoding that 'putInteger' has always written, so stored data+-- reads as before. The bytes are read in one piece and turned into a number+-- in close to linear time, so a long number cannot stall the reader.+getInteger :: (MonadGet m) => m Integer+getInteger =+  getWord8 >>= \case+    0x00 -> fromIntegral <$> getInt32be+    0x01 -> do+      sign <-+        getWord8 >>= \case+          0x01 -> return 1+          0xff -> return (-1)+          _ -> fail "getInteger: the sign byte is not 0x01 or 0xff"+      bytes <- getLengthBytes+      let n = sign * littleEndianToInteger bytes+      when (B.null bytes || B.last bytes == 0x00 || fitsInt32 n) $+        fail "getInteger: the number is not in its shortest form"+      return n+    _ -> fail "getInteger: the tag byte is not 0x00 or 0x01"+  where+    fitsInt32 n =+      n >= fromIntegral (minBound :: Int32) && n <= fromIntegral (maxBound :: Int32)++-- | The number that a string of bytes spells, least significant byte first.+-- The string is split in halves and the two numbers are joined. Adding one+-- byte at a time would move the whole number at every step, which takes time+-- that grows with the square of the length.+littleEndianToInteger :: ByteString -> Integer+littleEndianToInteger bs+  | B.length bs <= 8 = B.foldr (\byte n -> n `shiftL` 8 .|. toInteger byte) 0 bs+  | otherwise = low .|. (high `shiftL` (8 * B.length lowBytes))+  where+    (lowBytes, highBytes) = B.splitAt (B.length bs `div` 2) bs+    low = littleEndianToInteger lowBytes+    high = littleEndianToInteger highBytes++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 = getByteString =<< getCount++-- | A 64-bit count that must fit an 'Int'. A larger one fails the parse+-- instead of wrapping around to a negative number.+getCount :: (MonadGet m) => m Int+getCount = do+  n <- getWord64be+  when (n > fromIntegral (maxBound :: Int)) $+    fail ("Count too large: " <> show n)+  return (fromIntegral n)++--+-- 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)++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 a map as a count and that many pairs of key and element. The keys+-- must come in ascending order, each one once, which is how 'putIntMap'+-- writes them. A key given twice is refused. It used to be read, and the+-- second element silently replaced the first.+getIntMap :: (MonadGet m) => m Int -> m a -> m (IntMap a)+getIntMap i m = do+  pairs <- getList (getTwo i m)+  let keys = map fst pairs+  unless (and (zipWith (<) keys (drop 1 keys))) $+    fail "getIntMap: the keys are not in ascending order"+  return (IntMap.fromDistinctAscList pairs)++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 <- getCount+  replicateM l f++--+-- Test Helpers+--++prepareContext :: (Ctx -> SpecWith a) -> SpecWith a+prepareContext go = go =<< runIO createContext+  ++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,57 @@+{-# 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++-- | Read a value from bytes that hold the value and nothing else. Bytes left+-- over after the value are an error, so that two different byte strings never+-- read as the same value. To read a value from the start of a longer stream,+-- run 'marshalGet' yourself.+unmarshal :: (Marshal s a) => s -> ByteString -> Either String a+unmarshal s = runGetS (marshalGetAll s)++-- | Like 'unmarshal', for lazy bytes.+unmarshalLazy :: (Marshal s a) => s -> Lazy.ByteString -> Either String a+unmarshalLazy s = runGetS (marshalGetAll s) . Lazy.toStrict++-- | Read a value and require that the input ends there.+marshalGetAll :: (Marshal s a, MonadGet m) => s -> m a+marshalGetAll s = do+  value <- marshalGet s+  done <- isEmpty+  unless done $ fail "unmarshal: bytes left over after the value"+  return value++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,154 +1,323 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-}-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.Char               (chr, ord, toLower)-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text               (Text, append, pack, snoc, uncons)-import qualified Data.Text               as T-import           Data.Word               (Word8)-import           Haskoin.Address-import           Haskoin.Address.Bech32-import           Haskoin.Util-import           Test.Hspec-import           Test.HUnit+module Haskoin.Address.Bech32Spec (spec) where -spec = do-    describe "bech32 checksum" $ do-        it "should have valid checksum" $ forM_ validChecksums testValidChecksum-        it "should have invalid checksum" $-            forM_ invalidChecksums testInvalidChecksum-        it "should be a valid address" $ forM_ validAddresses testValidAddress-        it "should be an invalid address" $-            forM_ invalidAddresses testInvalidAddress-        it "should be same for the input in different case" $-            all (== Just "test12hrzfj") . map (flip bech32Encode []) $ hrpCaseVariants-    describe "more encoding/decoding cases" $ do-        it "length > 90" $-            assert $-            isNothing $-            bech32Encode "bc" (replicate 82 (word5 (1 :: Word8)))-        it "segwit version bounds" $-            assert $ isNothing $ segwitEncode "bc" 17 []-        it "segwit prog len version 0" $-            assert $ isNothing $ segwitEncode "bc" 0 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isJust $ segwitEncode "bc" 1 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isNothing $ segwitEncode "bc" 1 (replicate 41 1)-        it "empty HRP encode" $ assert $ isNothing $ bech32Encode "" []-        it "empty HRP encode" $-            assert $ isNothing $ bech32Decode "10a06t8"-        it "hrp lowercased" $-            bech32Encode "HRP" [] `shouldBe` bech32Encode "hrp" []+import Control.Monad+import Data.Bits (xor)+import Data.ByteString (ByteString)+import Data.ByteString qualified as ByteString+import Data.Char (chr, ord, toLower)+import Data.Maybe+import Data.String.Conversions+import Data.Text (Text, append, pack, snoc, uncons)+import Data.Text qualified as Text+import Data.Word (Word8)+import Haskoin.Address+import Haskoin.Address.Bech32+import Haskoin.Util+import Test.HUnit+import Test.Hspec +spec :: Spec+spec = do+  describe "bech32 checksum" $ do+    it "should be valid" $+      forM_ validChecksums (uncurry testValidChecksum)+    it "should be invalid" $+      forM_ invalidChecksums testInvalidChecksum+    it "should be case-insensitive" $+      all ((== Just "test12hrzfj") . flip (bech32Encode Bech32) []) hrpCaseVariants+  describe "bech32 address" $ do+    it "passes the BIP-173 and BIP-350 valid address vectors" $+      forM_ validAddresses testValidAddress+    it "rejects the BIP-173 and BIP-350 invalid address vectors" $+      forM_ invalidAddresses testInvalidAddress+    it "rejects an uppercase address with a look-alike of an uppercase letter" $+      -- U+212A KELVIN SIGN in place of K. Its lowercase form is the letter k,+      -- so only a check for ASCII refuses it. The address is the first+      -- valid vector of BIP 173.+      segwitDecode "bc" "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7\x212AV8F3T4"+        `shouldBe` Nothing+    it "rejects an address with a non-ASCII look-alike character" $+      -- U+017F in place of s+      segwitDecode "tb" "tb1qrp33g0q5c5tx\x017Fp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"+        `shouldBe` Nothing+    it "rejects an address whose padding bits are not zero" $+      segwitDecode "bc" "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf308468nf"+        `shouldBe` Nothing+    it "rejects an address with an extra zero group" $+      segwitDecode "bc" "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qqnuhcm0"+        `shouldBe` Nothing+    it "rejects an address for another human-readable part" $+      segwitDecode "tb" "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" `shouldBe` Nothing+    it "decodes the version and program of a P2WPKH address" $+      segwitDecode "bc" "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"+        `shouldBe` Just (0, ByteString.unpack (fromJust (decodeHex "751e76e8199196d454941c45d1b3a323f1433bd6")))+  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" []+    it "rejects a human-readable part with a non-ASCII character" $+      bech32Encode Bech32 "b\x00e9" [] `shouldBe` Nothing+    it "refuses a five-bit word above 31 instead of raising an error" $+      -- UnsafeWord5 can hold any byte. Only 0 to 31 have a character.+      bech32Encode Bech32 "bc" [UnsafeWord5 99] `shouldBe` Nothing+    it "rejects a string with a non-ASCII character" $+      isNothing (bech32Decode "a12uel5l\x00e9") `shouldBe` True+    it "rejects a string longer than 90 characters" $ do+      -- The vectors of BIP 173 and BIP 350 with a valid checksum and 91+      -- characters, so that only the length can refuse them+      isNothing (bech32Decode "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx")+        `shouldBe` True+      isNothing (bech32Decode "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4")+        `shouldBe` True+    it "accepts a string of exactly 90 characters" $+      -- The valid vector of BIP 173 with an 83-character human-readable part+      (fmap (\(enc, hrp, _) -> (enc, Text.length hrp)) (bech32Decode "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"))+        `shouldBe` Just (Bech32, 83)+  describe "base conversion" $ do+    it "toBase32 splits bytes into five-bit groups and pads with zero bits" $+      -- 11111111 becomes 11111 and 11100+      map fromWord5 (toBase32 [0xff]) `shouldBe` [31, 28 :: Int]+    it "toBase32 needs no padding for five bytes" $+      map fromWord5 (toBase32 [1, 2, 3, 4, 5]) `shouldBe` [0, 4, 1, 0, 6, 1, 0, 5 :: Int]+    it "toBase256 reverses toBase32" $+      toBase256 (map word5 [31, 28 :: Int]) `shouldBe` Just [0xff]+    it "toBase256 rejects padding bits that are not zero" $+      toBase256 (map word5 [31, 31 :: Int]) `shouldBe` Nothing+    it "toBase256 rejects padding wide enough to hold a whole group" $+      toBase256 (map word5 [31, 28, 0 :: Int]) `shouldBe` Nothing+    it "word5 keeps the low five bits" $+      fromWord5 (word5 (33 :: Int)) `shouldBe` (1 :: Int)+    it "bech32Const tells the two encodings apart" $ do+      bech32Const Bech32 `shouldBe` 1+      bech32Const Bech32m `shouldBe` 0x2bc830a3 -testValidChecksum :: Bech32 -> Assertion-testValidChecksum checksum = case bech32Decode checksum of-    Nothing                      -> assertFailure (show checksum)-    Just (resultHRP, resultData) -> do-        -- test that a corrupted checksum fails decoding.-        let (hrp, rest)         = T.breakOnEnd "1" checksum-            Just (first, rest') = uncons rest-            checksumCorrupted   = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'-        assertBool (show checksum ++ " corrupted")-            $ isNothing (bech32Decode checksumCorrupted)-        -- test that re-encoding the decoded checksum results in the same checksum.-        let checksumEncoded  = bech32Encode resultHRP resultData-            expectedChecksum = Just $ T.toLower checksum-        assertEqual (show checksum ++ " re-encode")-                    expectedChecksum-                    checksumEncoded+testValidChecksum :: Bech32Encoding -> Bech32 -> Assertion+testValidChecksum enc checksum = case bech32Decode checksum of+  Nothing -> assertFailure (show checksum)+  Just (enc', resultHRP, resultData) -> do+    assertEqual (show checksum ++ " encoding incorrect") enc enc'+    -- test that a corrupted checksum fails decoding.+    let (hrp, rest) = Text.breakOnEnd "1" checksum+        Just (first, rest') = uncons rest+        checksumCorrupted = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'+    assertBool (show checksum ++ " corrupted") $+      isNothing (bech32Decode checksumCorrupted)+    -- test that re-encoding the decoded checksum results in the same checksum.+    let checksumEncoded = bech32Encode enc' resultHRP resultData+        expectedChecksum = Just $ Text.toLower checksum+    assertEqual+      (show checksum ++ " re-encode")+      expectedChecksum+      checksumEncoded  testInvalidChecksum :: Bech32 -> Assertion testInvalidChecksum checksum =-    assertBool (show checksum) (isNothing $ bech32Decode checksum)+  assertBool (show checksum) (isNothing $ bech32Decode checksum)  testValidAddress :: (Text, Text) -> Assertion testValidAddress (address, hexscript) = do-    let address' = T.toLower address-        hrp      = T.take 2 address'-    case segwitDecode hrp address of-        Nothing                -> assertFailure "decode failed"-        Just (witver, witprog) -> do-            assertEqual (show address)-                        (decodeHex hexscript)-                        (Just $ segwitScriptPubkey witver witprog)-            assertEqual (show address)-                        (Just address')-                        (segwitEncode hrp witver witprog)+  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-    where witver' = if witver == 0 then 0 else witver + 0x50+  ByteString.pack $ witver' : fromIntegral (length witprog) : witprog+  where+    witver' = if witver == 0 then 0 else witver + 0x50 -validChecksums :: [Text]+validChecksums :: [(Bech32Encoding, Text)] validChecksums =-    [ "A12UEL5L"-    , "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"-    , "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"-    , "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"-    , "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"-    ]+  [ ( Bech32,+      "A12UEL5L"+    ),+    ( Bech32,+      "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"+    ),+    ( Bech32,+      "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"+    ),+    ( Bech32,+      "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"+    ),+    ( Bech32,+      "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"+    ),+    ( Bech32,+      "?1ezyfcl"+    ),+    ( 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",+    -- The rest of the invalid strings of BIP 173+    "A1G7SGD8",+    "10a06t8",+    "1qzzfhee",+    -- The invalid strings of BIP 350+    "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4",+    "qyrz8wqd2c9m",+    "1qyrz8wqd2c9m",+    "y1b0jsk6g",+    "lt1igcx5c0",+    "in1muywd",+    "mm1crxm3i",+    "au1s5cgom",+    "M1VUXWEZ",+    "16plkw9",+    "1p2gdwpf"+  ]  validAddresses :: [(Text, Text)] validAddresses =-    [ ("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"-      ,"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262")-    , ("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"-      ,"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("BC1SW50QA3JX3S", "6002751e")-    , ("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323")-    , ("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"-      ,"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433")-    ]+  [ ( "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"-    ]+  [ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",+    "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",+    "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",+    "bc1rw5uspcuh",+    "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",+    "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",+    "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",+    "bc1gmk9yu",+    "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",+    "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",+    "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",+    "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",+    "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",+    "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",+    "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",+    "bc1pw5dgrnzv",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",+    "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",+    "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j",+    -- The three addresses of witness version 1 and above that BIP 173 lists+    -- as valid. They use the Bech32 checksum, and BIP 350 made them invalid.+    "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",+    "BC1SW50QA3JX3S",+    "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj"+  ] -hrpCaseVariants:: [Text]-hrpCaseVariants = map T.pack hrpTestPermutations+hrpCaseVariants :: [Text]+hrpCaseVariants = map Text.pack hrpTestPermutations  hrpTestPermutations :: [String] hrpTestPermutations = do-    a <- ['t', 'T']-    b <- ['e', 'E']-    c <- ['s', 'S']-    d <- ['t', 'T']-    return [a, b, c, d]+  a <- ['t', 'T']+  b <- ['e', 'E']+  c <- ['s', 'S']+  d <- ['t', 'T']+  return [a, b, c, d]
test/Haskoin/Address/CashAddrSpec.hs view
@@ -1,145 +1,304 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-}+ module Haskoin.Address.CashAddrSpec (spec) where -import           Control.Monad-import qualified Data.ByteString.Char8    as C-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text                (Text)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Util-import           Test.Hspec-import           Test.HUnit+import Control.Exception (evaluate)+import Control.Monad+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as Char8+import Data.Maybe+import Data.String.Conversions+import Data.Text (Text)+import Data.Text qualified as T+import Haskoin.Address+import Haskoin.Network.Constants+import Haskoin.Util+import System.Timeout (timeout)+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+  describe "cashAddrDecode" $ do+    it "decodes a lowercase address with its prefix" $+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        `shouldBe` Just (0, hash20)+    it "decodes an address without prefix" $+      cashAddrDecode bch "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        `shouldBe` Just (0, hash20)+    it "decodes an address in all uppercase" $+      cashAddrDecode bch "BITCOINCASH:QPM2QSZNHKS23Z7629MMS6S4CWEF74VCWVY22GDX6A"+        `shouldBe` Just (0, hash20)+    it "decodes a token-aware address with its type" $+      cashAddrDecode bch "bitcoincash:zpm2qsznhks23z7629mms6s4cwef74vcwvrqekrq9w"+        `shouldBe` Just (2, hash20)+    it "rejects a mixed case prefix" $+      cashAddrDecode bch "BitcoinCash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        `shouldBe` Nothing+    it "rejects mixed case data" $+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdX6A"+        `shouldBe` Nothing+    it "rejects the prefix of another network" $+      cashAddrDecode bch "bchtest:qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"+        `shouldBe` Nothing+    it "rejects another prefix in front of the data of a valid address" $+      -- The data and the checksum are those of the mainnet address, so only+      -- the comparison of the prefix text can refuse it+      cashAddrDecode bch "xyz:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        `shouldBe` Nothing+    it "rejects a hash that is longer than its version byte says" $+      -- Version byte 0x00 says 20 bytes, and 24 follow. The checksum is+      -- valid (Python transcription of the Bitcoin Cash Node encoder).+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvqsyqcy689j2373"+        `shouldBe` Nothing+    it "decodes the longest address, with a 64-byte hash" $+      -- 11 characters of prefix, the colon, 104 of data and 8 of checksum+      cashAddrDecode bch "bitcoincash:quqqzqsrqszsvpcgpy9qkrqdpc83qygjzv2p29shrqv35xcur50p7gppyg3jgffxyu5zj23t9skjutesxyerxdp4xcmnswf68v7r603lr3frdfp5"+        `shouldBe` Just (0, B.pack [0 .. 63])+    it "rejects a string one character longer than the longest address" $+      cashAddrDecode bch "bitcoincash:quqqzqsrqszsvpcgpy9qkrqdpc83qygjzv2p29shrqv35xcur50p7gppyg3jgffxyu5zj23t9skjutesxyerxdp4xcmnswf68v7r603lr3frdfp5q"+        `shouldBe` Nothing+    it "rejects a string of megabytes at once" $+      timeout 2000000 (evaluate (cashAddrDecode bch ("bitcoincash:" <> T.replicate 20000000 "q")))+        `shouldReturn` Just Nothing+    it "rejects an address of another network given without prefix" $+      cashAddrDecode bch "qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"+        `shouldBe` Nothing+    it "rejects a non-ASCII look-alike character" $+      -- U+212A in place of K+      cashAddrDecode bch "BITCOINCASH:QPM2QSZNH\x212AS23Z7629MMS6S4CWEF74VCWVY22GDX6A"+        `shouldBe` Nothing+    it "rejects padding bits that are not zero" $+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcw0c0g40rx7"+        `shouldBe` Nothing+    it "rejects an extra zero group" $+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvq5xhgekz9"+        `shouldBe` Nothing+    it "rejects a bad checksum" $+      cashAddrDecode bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6q"+        `shouldBe` Nothing+    it "has no result on a network without CashAddr" $+      cashAddrDecode btc "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        `shouldBe` Nothing+  describe "cashAddrEncode" $ do+    it "encodes a P2PKH hash" $+      cashAddrEncode bch 0 hash20+        `shouldBe` Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+    it "encodes a token-aware P2SH32 hash" $+      cashAddrEncode bch 3 hash32+        `shouldBe` Just "bitcoincash:rvvxx9puznz3v6qyh5vjqv6kmgfkexzk0rx56faphrrr99syjqexy30vcqxv4"+    it "uses the prefix of the network" $ do+      cashAddrEncode bchTest 0 hash20+        `shouldBe` Just "bchtest:qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"+      cashAddrEncode bchRegTest 0 hash20+        `shouldBe` Just "bchreg:qpm2qsznhks23z7629mms6s4cwef74vcwv6ycwvz78"+    it "rejects a type above 15" $+      cashAddrEncode bch 16 hash20 `shouldBe` Nothing+    it "rejects a hash with a length the standard does not allow" $+      cashAddrEncode bch 0 (B.replicate 21 1) `shouldBe` Nothing+    it "has no result on a network without CashAddr" $+      cashAddrEncode btc 0 hash20 `shouldBe` Nothing+  describe "cash32 low level" $ do+    it "cash32encode writes the prefix, the data and the checksum" $ do+      cash32encode "prefix" "" `shouldBe` Just "prefix:x64nx6hz"+      cash32encode "bitcoincash" (B.cons 0 hash20)+        `shouldBe` Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+    it "cash32encode writes an uppercase prefix in lowercase, so that the string is of one case" $+      -- 21 zero bytes+      cash32encode "BitcoinCash" (B.replicate 21 0)+        `shouldBe` Just "bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfnhks603"+    it "cash32encode refuses a prefix that is not made of letters" $ do+      cash32encode "" "" `shouldBe` Nothing+      cash32encode "pre1fix" "" `shouldBe` Nothing+      cash32encode "a:b" "" `shouldBe` Nothing+      cash32encode "pre-fix" "" `shouldBe` Nothing+      -- U+212A KELVIN SIGN lowers to the letter k+      cash32encode "\x212A" "" `shouldBe` Nothing+    it "cash32decode refuses a prefix with a digit, a second colon or another character" $ do+      -- Each string has a valid checksum for its prefix. Bitcoin Cash Node+      -- refuses all three.+      cash32decode "pre1fix:fe8gx7xv" `shouldBe` Nothing+      cash32decode "a:b:wdr5stq6" `shouldBe` Nothing+      cash32decode "pre-fix:c0c507qj" `shouldBe` Nothing+    it "cash32decode needs a prefix" $ do+      cash32decode "x64nx6hz" `shouldBe` Nothing+      cash32decode ":x64nx6hz" `shouldBe` Nothing+    it "cash32decode rejects mixed case" $+      cash32decode "Prefix:x64nx6hz" `shouldBe` Nothing+    it "cash32decode rejects a non-ASCII prefix" $+      cash32decode "pr\x00e9fix:x64nx6hz" `shouldBe` Nothing+    it "cash32decode rejects data shorter than a checksum" $+      cash32decode "prefix:x64nx6h" `shouldBe` Nothing+    it "cash32encodeType and cash32decodeType carry the type and the length" $ do+      cash32encodeType "pref" 1 hashF5+        `shouldBe` Just "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5"+      cash32decodeType "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5"+        `shouldBe` Just ("pref", 1, hashF5)+    it "cash32encodeType rejects a length the standard does not allow" $+      cash32encodeType "pref" 1 (B.replicate 21 1) `shouldBe` Nothing+    it "cash32decodeType rejects mixed case" $+      cash32decodeType "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtkS5" `shouldBe` Nothing+  describe "version byte" $ do+    it "decodeVersionByte splits the type and the hash size" $ do+      -- type in the high four bits, size code in the low three bits+      decodeVersionByte 0 `shouldBe` Just (0, 20)+      decodeVersionByte 11 `shouldBe` Just (1, 32)+      decodeVersionByte 16 `shouldBe` Just (2, 20)+      decodeVersionByte 127 `shouldBe` Just (15, 64)+    it "decodeVersionByte rejects a set high bit" $+      decodeVersionByte 128 `shouldBe` Nothing+    it "encodeVersionByte packs the type and the hash size" $ do+      encodeVersionByte 0 20 `shouldBe` Just 0+      encodeVersionByte 1 32 `shouldBe` Just 11+      encodeVersionByte 15 64 `shouldBe` Just 127+    it "encodeVersionByte rejects a type above 15" $+      encodeVersionByte 16 20 `shouldBe` Nothing+    it "encodeVersionByte rejects a hash size the standard does not allow" $+      encodeVersionByte 0 21 `shouldBe` Nothing +{- Fixtures -}++-- | HASH160 behind the Bitcoin Cash translation vectors above.+hash20 :: ByteString+hash20 = fromJust $ decodeHex "76a04053bda0a88bda5177b86a15c3b29f559873"++-- | SHA256 behind the BIP-173 P2WSH vector.+hash32 :: ByteString+hash32 = fromJust $ decodeHex "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"++-- | Hash from the CashAddr specification vectors.+hashF5 :: ByteString+hashF5 = fromJust $ decodeHex "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"+ {- 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@@ -148,132 +307,164 @@ -- 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,189 +1,840 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+ module Haskoin.AddressSpec (spec) where -import           Data.ByteString        (ByteString)-import qualified Data.ByteString        as BS (append, empty, pack)-import           Data.Maybe             (fromJust, isJust)-import           Data.Text              (Text)-import qualified Data.Text              as T-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit-import           Test.QuickCheck+import Control.DeepSeq (force)+import Control.Exception (evaluate)+import Control.Monad+import Data.Aeson (Value (String))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (deserialize, serialize)+import Data.Default (def)+import Data.Either (isLeft)+import Data.Maybe (fromJust, isJust)+import Data.Text (Text)+import Data.Text qualified as T+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Util+import Haskoin.Util.Arbitrary+import System.Timeout (timeout)+import Text.Read (readMaybe)+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck -serialVals :: [SerialBox]-serialVals = [SerialBox arbitraryAddressAll]+identityTests :: Network -> IdentityTests+identityTests net =+  def+    { readTests = [ReadBox (arbitraryAddress net)],+      serialTests = [SerialBox (arbitraryAddress net)],+      marshalJsonTests = [MarshalJsonBox arbitraryNetAddress]+    } -readVals :: [ReadBox]-readVals = [ReadBox arbitraryAddressAll]+spec :: Spec+spec = prepareContext $ \ctx -> do+  forM_ allNets $ \net -> testIdentity $ identityTests net+  describe "Address properties" $ do+    prop "encodes and decodes base58 bytestring" $+      forAll arbitraryBS $ \bs ->+        decodeBase58 (encodeBase58 bs) `shouldBe` Just bs+    prop "encodes and decodes base58 bytestring with checksum" $+      forAll arbitraryBS $ \bs ->+        decodeBase58Check (encodeBase58Check bs) `shouldBe` Just bs+    prop "textToAddr . addrToText identity" $+      forAll arbitraryNetAddress $ \(net, a) ->+        (textToAddr net =<< addrToText net a) `shouldBe` Just a+    prop "an address of a network is the address of its own output script on that network" $+      -- A generic CashAddr is left out. A token-aware one pays the script of+      -- its plain form, and the others have no script. Both have a test below.+      forAll arbitraryNetAddress $ \(net, a) ->+        not (isCashAddress a) ==>+          (scriptToAddressBS net ctx =<< maybeToEither "no script" (addressToScriptBS ctx a))+            `shouldBe` Right a+    prop "an address that outputAddress gives always has a text form on that network" $+      -- The output is of a kind of any network, not only of the one asked+      forAll ((,) <$> arbitraryNetwork <*> (arbitraryNetwork >>= \other -> arbitraryScriptOutput other ctx)) $ \(net, out) ->+        case outputAddress net ctx out of+          Nothing -> property True+          Just a -> property (isJust (addrToText net 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+  describe "Base58" $ do+    it "decodes digits by their position in the alphabet" $+      -- 2 is digit 1 and 1 is digit 0, so 1 × 58 + 0 = 58+      decodeBase58 "21" `shouldBe` Just (B.pack [58])+    it "rejects a character outside the alphabet" $+      decodeBase58 "0" `shouldBe` Nothing+    it "rejects a non-ASCII character whose code point ends in a digit byte" $+      -- U+0131 is 0x131. Its low byte 0x31 is the digit 1.+      decodeBase58 "2\x0131" `shouldBe` Nothing+    it "rejects a checked string shorter than a checksum" $+      decodeBase58Check "1" `shouldBe` Nothing+  describe "Smart constructors" $ do+    it "witnessAddress builds a P2WPKH address from version 0 and 20 bytes" $+      witnessAddress 0 hash20Bytes `shouldBe` Just (WitnessPubKeyAddress hash20)+    it "witnessAddress builds a P2WSH address from version 0 and 32 bytes" $+      witnessAddress 0 hash32Bytes `shouldBe` Just (WitnessScriptAddress hash32)+    it "witnessAddress rejects version 0 with another program length" $+      witnessAddress 0 (B.replicate 21 1) `shouldBe` Nothing+    it "witnessAddress keeps version 1 with 32 bytes as a Taproot address" $ do+      let Just a = witnessAddress 1 hash32Bytes+      isWitnessAddress a `shouldBe` True+      addressVersion a `shouldBe` 1+      addressBytes a `shouldBe` hash32Bytes+    it "witnessAddress rejects a version above 16" $+      witnessAddress 17 hash32Bytes `shouldBe` Nothing+    it "witnessAddress rejects a program shorter than 2 bytes" $+      witnessAddress 1 (B.pack [1]) `shouldBe` Nothing+    it "witnessAddress rejects a program longer than 40 bytes" $+      witnessAddress 1 (B.replicate 41 1) `shouldBe` Nothing+    it "cashAddress builds a P2PKH address from type 0 and 20 bytes" $+      cashAddress 0 hash20Bytes `shouldBe` Just (PubKeyAddress hash20)+    it "cashAddress builds a P2SH address from type 1 and 20 bytes" $+      cashAddress 1 hash20Bytes `shouldBe` Just (ScriptAddress hash20)+    it "cashAddress builds a P2SH32 address from type 1 and 32 bytes" $+      cashAddress 1 hash32Bytes `shouldBe` Just (Script32Address hash32)+    it "cashAddress keeps a token-aware address as a generic CashAddr" $ do+      let Just a = cashAddress 2 hash20Bytes+      isCashAddress a `shouldBe` True+      addressVersion a `shouldBe` 2+      addressBytes a `shouldBe` hash20Bytes+    it "cashAddress rejects a type above 15" $+      cashAddress 16 hash20Bytes `shouldBe` Nothing+    it "cashAddress rejects a hash length the standard does not allow" $+      cashAddress 0 (B.replicate 21 1) `shouldBe` Nothing+  describe "Address predicates" $+    it "each predicate is true only for its own kind of address" $+      forM_ predicateTable $ \(name, a, kind) ->+        assertEqual name [k == kind | k <- [0 .. 6]] (map ($ a) predicates)+  describe "Address accessors" $ do+    it "addressHash160 gives the hash of every address paying to a 160-bit hash" $ do+      addressHash160 p2pkh `shouldBe` Just hash20+      addressHash160 p2sh `shouldBe` Just hash20+      addressHash160 p2wpkh `shouldBe` Just hash20+      addressHash160 tokenP2pkh `shouldBe` Just hash20+      addressHash160 tokenP2sh `shouldBe` Just hash20+    it "addressHash160 gives nothing for other kinds" $ do+      addressHash160 p2wsh `shouldBe` Nothing+      addressHash160 p2tr `shouldBe` Nothing+      addressHash160 cash15 `shouldBe` Nothing+    it "addressHash256 gives the hash of every address paying to a 256-bit hash" $ do+      addressHash256 p2wsh `shouldBe` Just hash32+      addressHash256 p2sh32 `shouldBe` Just hash32+      addressHash256 tokenP2sh32 `shouldBe` Just hash32+    it "addressHash256 gives nothing for other kinds" $ do+      addressHash256 p2pkh `shouldBe` Nothing+      addressHash256 p2tr `shouldBe` Nothing+    it "addressVersion is the witness version of SegWit kinds" $ do+      addressVersion p2wpkh `shouldBe` 0+      addressVersion p2wsh `shouldBe` 0+      addressVersion p2tr `shouldBe` 1+      addressVersion witness16 `shouldBe` 16+    it "addressVersion is the CashAddr type of other kinds" $ do+      addressVersion p2pkh `shouldBe` 0+      addressVersion p2sh `shouldBe` 1+      addressVersion p2sh32 `shouldBe` 1+      addressVersion tokenP2pkh `shouldBe` 2+      addressVersion tokenP2sh `shouldBe` 3+      addressVersion cash15 `shouldBe` 15+    it "addressBytes is the raw hash or program without any prefix" $ do+      addressBytes p2pkh `shouldBe` hash20Bytes+      addressBytes p2sh `shouldBe` hash20Bytes+      addressBytes p2wpkh `shouldBe` hash20Bytes+      addressBytes p2wsh `shouldBe` hash32Bytes+      addressBytes p2sh32 `shouldBe` hash32Bytes+      addressBytes p2tr `shouldBe` hash32Bytes+      addressBytes witness16 `shouldBe` B.pack [1 .. 40]+      addressBytes tokenP2pkh `shouldBe` hash20Bytes+      addressBytes cash15 `shouldBe` B.pack [1 .. 24]+  describe "addrToText" $ do+    it "encodes every address kind on every network" $+      forM_ textTable $ \(net, a, txt) ->+        assertEqual (net.name <> " " <> T.unpack txt) (Just txt) (addrToText net a)+    it "uses the testnet prefixes on every test and regression network" $ do+      addrToText btcTest4 p2pkh `shouldBe` Just "mrLC19Je2BuWQDkWSTriGYPyQJXKkkBmCx"+      addrToText btcRegTest p2sh `shouldBe` Just "2N44ThNe8NXHyv4bsX8AoVCXquBRW94Ls7W"+      addrToText btcTest4 p2wpkh `shouldBe` Just "tb1qw6syq5aa5z5ghkj3w7ux59wrk204txrn0swsqg"+      addrToText btcRegTest p2wpkh `shouldBe` Just "bcrt1qw6syq5aa5z5ghkj3w7ux59wrk204txrndehahp"+      addrToText bchTest4 p2pkh `shouldBe` Just "bchtest:qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"+      addrToText bchRegTest p2pkh `shouldBe` Just "bchreg:qpm2qsznhks23z7629mms6s4cwef74vcwv6ycwvz78"+    it "has no text for a SegWit address on a Bitcoin Cash network" $ do+      addrToText bch p2wpkh `shouldBe` Nothing+      addrToText bch p2wsh `shouldBe` Nothing+      addrToText bch p2tr `shouldBe` Nothing+    it "has no text for P2SH32 or a CashAddr kind on a Bitcoin network" $ do+      addrToText btc p2sh32 `shouldBe` Nothing+      addrToText btc tokenP2pkh `shouldBe` Nothing+      addrToText btc cash15 `shouldBe` Nothing+  describe "addrToBase58, addrToBech32 and addrToCash" $ do+    it "addrToBase58 gives the legacy form of a Bitcoin Cash address" $ do+      addrToBase58 bch p2pkh `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+      addrToBase58 bch p2sh `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+      addrToBase58 bchTest p2sh `shouldBe` Just "2N44ThNe8NXHyv4bsX8AoVCXquBRW94Ls7W"+    it "addrToBase58 has no form for other kinds" $ do+      addrToBase58 btc p2wpkh `shouldBe` Nothing+      addrToBase58 bch p2sh32 `shouldBe` Nothing+      addrToBase58 bch tokenP2pkh `shouldBe` Nothing+    it "addrToBech32 encodes SegWit kinds on SegWit networks only" $ do+      addrToBech32 btc p2wpkh `shouldBe` Just "bc1qw6syq5aa5z5ghkj3w7ux59wrk204txrn9k4rmm"+      addrToBech32 btc p2pkh `shouldBe` Nothing+      addrToBech32 bch p2wpkh `shouldBe` Nothing+    it "addrToCash encodes on Bitcoin Cash networks only" $ do+      addrToCash bch p2pkh `shouldBe` Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+      addrToCash btc p2pkh `shouldBe` Nothing+      addrToCash bch p2wpkh `shouldBe` Nothing+  describe "textToAddr" $ do+    it "decodes every form that addrToText encodes" $+      forM_ textTable $ \(net, a, txt) ->+        assertEqual (net.name <> " " <> T.unpack txt) (Just a) (textToAddr net txt)+    it "accepts a CashAddr without prefix" $+      textToAddr bch "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" `shouldBe` Just p2pkh+    it "accepts a CashAddr in all uppercase" $+      textToAddr bch "BITCOINCASH:QPM2QSZNHKS23Z7629MMS6S4CWEF74VCWVY22GDX6A" `shouldBe` Just p2pkh+    it "accepts a Bech32 address in all uppercase" $+      textToAddr btc "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4"+        `shouldBe` Just (WitnessPubKeyAddress "751e76e8199196d454941c45d1b3a323f1433bd6")+    it "accepts a legacy Base58 address on a Bitcoin Cash network" $+      textToAddr bch "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" `shouldBe` Just p2pkh+    it "rejects a CashAddr with a mixed case prefix" $+      textToAddr bch "BitcoinCash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" `shouldBe` Nothing+    it "rejects a CashAddr with mixed case data" $+      textToAddr bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdX6A" `shouldBe` Nothing+    it "rejects a Bech32 address with mixed case" $+      textToAddr btcTest "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" `shouldBe` Nothing+    it "rejects a CashAddr whose padding bits are not zero" $+      textToAddr bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcw0c0g40rx7" `shouldBe` Nothing+    it "rejects a CashAddr with an extra zero group" $+      textToAddr bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvq5xhgekz9" `shouldBe` Nothing+    it "rejects a Bech32 address whose padding bits are not zero" $+      textToAddr btc "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf308468nf" `shouldBe` Nothing+    it "rejects a Bech32 address with an extra zero group" $+      textToAddr btc "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qqnuhcm0" `shouldBe` Nothing+    it "rejects a Base58 address with a non-ASCII look-alike character" $+      -- U+0141 in place of A+      textToAddr btc "1BpEi6DfD\x0141UFd7GtittLSdBeYJvcoaVggu" `shouldBe` Nothing+    it "rejects a Bech32 address with a non-ASCII look-alike character" $+      -- U+017F in place of s+      textToAddr btcTest "tb1qrp33g0q5c5tx\x017Fp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7" `shouldBe` Nothing+    it "rejects a CashAddr with a non-ASCII look-alike character" $+      -- U+212A in place of K+      textToAddr bch "BITCOINCASH:QPM2QSZNH\x212AS23Z7629MMS6S4CWEF74VCWVY22GDX6A" `shouldBe` Nothing+    it "rejects an address of another network" $+      forM_ otherNetworkTable $ \(net, txt) ->+        assertEqual (net.name <> " " <> T.unpack txt) Nothing (textToAddr net txt)+    it "rejects a Base58 address with a bad checksum" $+      textToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggv" `shouldBe` Nothing+    it "rejects a Base58 payload that is not 21 bytes" $ do+      -- version byte, the hash and one extra byte+      textToAddr btc "1pjVWN2segoHYNh9Hv1ap6hzhC7eqEpz8oy" `shouldBe` Nothing+      -- version byte and 19 bytes of hash+      textToAddr btc "13T7H7AfZfPwWvz51tBGjWVhDz5TJEMr9" `shouldBe` Nothing+    it "rejects a Base58 version byte that is neither P2PKH nor P2SH" $+      -- version byte 1 and the hash+      textToAddr btc "b9qhCWwvLw8SYQykKDevkTSApBZZqXzeV" `shouldBe` Nothing+    it "rejects surrounding whitespace" $+      textToAddr btc " 1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu\n" `shouldBe` Nothing+    it "reads a CashAddr kind that has no output script, which is not payable" $ do+      -- type 4 with the 20-byte hash 01 … 01: Bitcoin Cash Node gives it no+      -- destination. Encoded with a Python transcription of its encoder.+      let unknownKind = textToAddr bch "bitcoincash:yqqszqgpqyqszqgpqyqszqgpqyqszqgpqyds8d3dsw"+      (isCashAddress <$> unknownKind) `shouldBe` Just True+      (addressToOutput =<< unknownKind) `shouldBe` Nothing+    it "reads the longest CashAddr, with a 64-byte hash" $+      -- 11 characters of prefix, the colon, and 112 of data and checksum+      (addressBytes <$> textToAddr bch longestCashAddr) `shouldBe` Just (B.pack [0 .. 63])+    it "refuses a string longer than any CashAddr without decoding it" $ do+      -- One character more than the longest address: 125+      textToAddr bch (longestCashAddr <> "q") `shouldBe` Nothing+      timeout 2000000 (evaluate (textToAddr bch ("bitcoincash:" <> T.replicate 20000000 "q")))+        `shouldReturn` Just Nothing+    it "rejects a long string of Base58 characters without decoding it" $+      timeout 2000000 (evaluate (textToAddr btc (T.replicate 500000 "z")))+        `shouldReturn` Just Nothing+  describe "Addresses from public keys" $ do+    it "pubKeyAddr hashes a compressed key" $+      addrToText btc (pubKeyAddr ctx (compressedKey ctx))+        `shouldBe` Just "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"+    it "pubKeyAddr hashes an uncompressed key in its uncompressed form" $+      addrToText btc (pubKeyAddr ctx (uncompressedKey ctx))+        `shouldBe` Just "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm"+    it "pubKeyWitnessAddr gives the P2WPKH address of a compressed key" $+      (addrToText btc =<< pubKeyWitnessAddr ctx (compressedKey ctx))+        `shouldBe` Just "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"+    it "pubKeyWitnessAddr refuses an uncompressed key" $+      pubKeyWitnessAddr ctx (uncompressedKey ctx) `shouldBe` Nothing+    it "pubKeyCompatWitnessAddr gives the P2SH-P2WPKH address of a compressed key" $+      (addrToText btc =<< pubKeyCompatWitnessAddr ctx (compressedKey ctx))+        `shouldBe` Just "3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN"+    it "pubKeyCompatWitnessAddr refuses an uncompressed key" $+      pubKeyCompatWitnessAddr ctx (uncompressedKey ctx) `shouldBe` Nothing+    it "p2pkhAddr, p2shAddr, p2wpkhAddr and p2wshAddr wrap a hash" $ do+      addrToText btc (p2pkhAddr hash20) `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+      addrToText btc (p2shAddr hash20) `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+      addrToText btc (p2wpkhAddr hash20) `shouldBe` Just "bc1qw6syq5aa5z5ghkj3w7ux59wrk204txrn9k4rmm"+      addrToText btc (p2wshAddr hash32) `shouldBe` Just "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"+  describe "Addresses from scripts" $ do+    it "payToScriptAddress hashes the output script with HASH160" $+      (addrToText btc <$> payToScriptAddress ctx (multisig ctx))+        `shouldBe` Right (Just "38fEX6RbBBMmpu3nbbuULku1xyrrzqqqnE")+    it "payToWitnessScriptAddress hashes the output script with SHA256" $+      (addrToText btc <$> payToWitnessScriptAddress ctx (multisig ctx))+        `shouldBe` Right (Just "bc1qd6e6c86xp56gw8pty8suuqhscpttea2c5m2fggzjkxzk5nl9faks5pehxu")+    it "payToNestedScriptAddress wraps the P2WSH script in P2SH" $+      (addrToText btc <$> payToNestedScriptAddress ctx (multisig ctx))+        `shouldBe` Right (Just "3JSuZ22mPR2aD5bf77KsGi36V5ar3p87LX")+    it "p2shP2wpkhAddr is the P2SH address of the witness program of a key hash" $+      -- The P2SH-P2WPKH example of BIP 143: redeem script 0014 79091972…,+      -- script hash 4733f37cf4db86fbc2efed2500b4f4e49f312023.+      addrToText btc (p2shP2wpkhAddr "79091972186c449eb1ded22b78e40d009bdf0089")+        `shouldBe` Just "38BW8nqpHSWpkf5sXrQd2xYwvnPJwP59ic"+  describe "Script size limits of script hash addresses" $ do+    -- The expected addresses were computed with Python (hashlib, embit,+    -- base58) from the script bytes, not with this library.+    it "gives the P2SH address of a 1-of-15 multisig of compressed keys" $+      -- 3 + 15 × 34 = 513 bytes, which fits the 520 bytes of a push+      (addrToText btc <$> payToScriptAddress ctx (multisigOf ctx 15))+        `shouldBe` Right (Just "32vPTRtH3XPKgBxBtEoiQdGzVEZydYKduQ")+    it "has no P2SH address for a 1-of-16 multisig of compressed keys" $+      -- 3 + 16 × 34 = 547 bytes: no input can push that redeem script+      payToScriptAddress ctx (multisigOf ctx 16)+        `shouldBe` Left "The redeem script is 547 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "has no P2SH address for a 1-of-20 multisig of compressed keys" $+      -- 3 + 20 × 34 + 1 = 684 bytes: the count 20 takes a one-byte push+      payToScriptAddress ctx (multisigOf ctx 20)+        `shouldBe` Left "The redeem script is 684 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "gives the P2SH address of a redeem script of exactly 520 bytes" $+      -- OP_RETURN, the three bytes of OP_PUSHDATA2 and its length, 516 bytes+      (addrToText btc <$> payToScriptAddress ctx (DataCarrier [B.replicate 516 0xab]))+        `shouldBe` Right (Just "35xAkh7KgcziTpgBWPAoXGzGDgLujKCnLL")+    it "has no P2SH address for a redeem script of 521 bytes" $+      payToScriptAddress ctx (DataCarrier [B.replicate 517 0xab])+        `shouldBe` Left "The redeem script is 521 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "gives the P2WSH and the P2SH-P2WSH address of a 1-of-20 multisig" $ do+      (addrToText btc <$> payToWitnessScriptAddress ctx (multisigOf ctx 20))+        `shouldBe` Right (Just "bc1q5yfkferfc5w4t3chd0ghw6ume447rey75v8df4z6sjrhnegv50ysg5v7a5")+      (addrToText btc <$> payToNestedScriptAddress ctx (multisigOf ctx 20))+        `shouldBe` Right (Just "3KHeFzKKQySyJSVTdqkkxxaMGRUCNdPfay")+    it "gives the P2WSH and the P2SH-P2WSH address of a witness script of exactly 10,000 bytes" $ do+      -- OP_RETURN, the three bytes of OP_PUSHDATA2 and its length, 9996 bytes+      (addrToText btc <$> payToWitnessScriptAddress ctx (DataCarrier [B.replicate 9996 0xab]))+        `shouldBe` Right (Just "bc1q5w9futq5wnymeh5wfrak2388u0cmup9nhkwxsq56rraaxvfy22pqrzp44n")+      (addrToText btc <$> payToNestedScriptAddress ctx (DataCarrier [B.replicate 9996 0xab]))+        `shouldBe` Right (Just "34v1ZTik6F9PmGBTcXnmbHF1NfTjufPgvv")+    it "has no P2WSH and no P2SH-P2WSH address for a witness script of 10,001 bytes" $ do+      payToWitnessScriptAddress ctx (DataCarrier [B.replicate 9997 0xab])+        `shouldBe` Left "The witness script is 10001 bytes long, and a pay-to-witness-script-hash witness script can have at most 10000"+      payToNestedScriptAddress ctx (DataCarrier [B.replicate 9997 0xab])+        `shouldBe` Left "The witness script is 10001 bytes long, and a pay-to-witness-script-hash witness script can have at most 10000"+    it "gives an error value, not an exception, for a multisig that no script can express" $ do+      payToScriptAddress ctx (PayMulSig [compressedKey ctx, secondKey ctx] 3)+        `shouldBe` Left "encodeOutput: multisig requires more signatures than it has keys"+      payToScriptAddress ctx (PayMulSig [compressedKey ctx] 0)+        `shouldBe` Left "encodeOutput: multisig must require at least one signature"+      payToWitnessScriptAddress ctx (PayMulSig [] 1)+        `shouldBe` Left "encodeOutput: multisig requires more signatures than it has keys"+      payToNestedScriptAddress ctx (PayMulSig (replicate 21 (compressedKey ctx)) 1)+        `shouldBe` Left "encodeOutput: multisig has more than 20 keys"+  describe "addressToOutput, addressToScript and addressToScriptBS" $ do+    it "builds the locking script of every address kind" $+      forM_ scriptTable $ \(_, a, hex) ->+        assertEqual (T.unpack hex) (Just hex) (encodeHex <$> addressToScriptBS ctx a)+    it "pays a token-aware address and its plain form with the same script" $ do+      let script = Just "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac"+      (encodeHex <$> (addressToScriptBS ctx =<< textToAddr bch "bitcoincash:zpm2qsznhks23z7629mms6s4cwef74vcwvrqekrq9w"))+        `shouldBe` script+      (encodeHex <$> (addressToScriptBS ctx =<< textToAddr bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"))+        `shouldBe` script+    it "the script of a token-aware address has the plain address, which is another value" $ do+      -- An index keyed by address must look a token-aware address up by its+      -- script, or by its plain form+      (scriptToAddressBS bch ctx =<< maybeToEither "no script" (addressToScriptBS ctx tokenP2pkh))+        `shouldBe` Right p2pkh+      tokenP2pkh `shouldNotBe` p2pkh+    it "pays a token-aware P2PKH address with the plain P2PKH script" $+      (encodeHex <$> addressToScriptBS ctx tokenP2pkh)+        `shouldBe` Just "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac"+    it "pays a token-aware P2SH address with the plain P2SH script" $+      (encodeHex <$> addressToScriptBS ctx tokenP2sh)+        `shouldBe` Just "a91476a04053bda0a88bda5177b86a15c3b29f55987387"+    it "pays a token-aware P2SH32 address with the plain P2SH32 script" $+      (encodeHex <$> addressToScriptBS ctx tokenP2sh32)+        `shouldBe` Just "aa201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326287"+    it "has no script for a CashAddr type with no known script" $ do+      addressToOutput cash15 `shouldBe` Nothing+      addressToOutput (fromJust (cashAddress 0 hash32Bytes)) `shouldBe` Nothing+      addressToOutput (fromJust (cashAddress 2 hash32Bytes)) `shouldBe` Nothing+    it "addressToOutput gives the matching ScriptOutput" $ do+      addressToOutput p2pkh `shouldBe` Just (PayPKHash hash20)+      addressToOutput p2wsh `shouldBe` Just (PayWitnessScriptHash hash32)+      addressToOutput p2tr `shouldBe` Just (PayWitness 1 hash32Bytes)+      addressToOutput tokenP2sh32 `shouldBe` Just (PayScript32Hash hash32)+    it "addressToScript gives the script AST" $+      ((.ops) <$> addressToScript ctx p2sh)+        `shouldBe` Just [OP_HASH160, OP_PUSHDATA hash20Bytes OPCODE, OP_EQUAL]+  describe "outputAddress, scriptToAddress and scriptToAddressBS" $ do+    it "recovers the address of every standard locking script on a network that has its kind" $+      forM_ scriptTable $ \(net, a, hex) ->+        assertEqual (T.unpack hex) (Right a) (scriptToAddressBS net ctx (fromJust (decodeHex hex)))+    it "scriptToAddress works on the script AST" $+      (scriptToAddress btc ctx =<< runGetS deserialize (fromJust (decodeHex "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac")))+        `shouldBe` Right p2pkh+    it "has no address for a pay-to-public-key script" $+      scriptToAddressBS btc ctx (fromJust (decodeHex p2pkScript))+        `shouldBe` Left "Could not decode address"+    it "has no address for a bare multisig script" $+      scriptToAddressBS btc ctx (fromJust (decodeHex multisigScript))+        `shouldBe` Left "Could not decode address"+    it "has no address for a data carrier script" $+      scriptToAddressBS btc ctx (fromJust (decodeHex "6a04deadbeef"))+        `shouldBe` Left "Could not decode address"+    it "fails on a script that is not standard" $+      scriptToAddressBS btc ctx (fromJust (decodeHex "51")) `shouldSatisfy` isLeft+    it "outputAddress has no address for a pay-to-public-key output" $+      outputAddress btc ctx (PayPK (compressedKey ctx)) `shouldBe` Nothing+    it "a script of the 256-bit script hash shape has no address on Bitcoin" $ do+      -- Output 0 of mainnet transaction a4bfa8ab…bc31b, in block 211997.+      -- Bitcoin Core reports it as non-standard, with no address.+      let puzzle = fromJust (decodeHex "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087")+      forM_ [btc, btcTest, btcTest4, btcRegTest] $ \net ->+        assertEqual net.name (Left "Could not decode address") (scriptToAddressBS net ctx puzzle)+      -- On Bitcoin Cash the same bytes are pay-to-script-hash with a 32-byte hash+      (addrToText bch =<< eitherToMaybe (scriptToAddressBS bch ctx puzzle))+        `shouldBe` Just "bitcoincash:pdh79rq2kmcmxukp563ydtnr7a8ex85rvhs45zyudrtpjqqqqqqqqgss5hn0j"+    it "a witness program has no address on Bitcoin Cash" $+      forM_ [bch, bchTest, bchTest4, bchRegTest] $ \net -> do+        -- version 0 with 20 bytes, version 0 with 32 bytes, and Taproot+        assertEqual net.name (Left "Could not decode address") $+          scriptToAddressBS net ctx (fromJust (decodeHex "001476a04053bda0a88bda5177b86a15c3b29f559873"))+        assertEqual net.name (Left "Could not decode address") $+          scriptToAddressBS net ctx (fromJust (decodeHex "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"))+        assertEqual net.name (Left "Could not decode address") $+          scriptToAddressBS net ctx (fromJust (decodeHex "51201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"))+    it "outputAddress gives an output kind an address only where the network has that kind" $ do+      outputAddress btc ctx (PayScript32Hash hash32) `shouldBe` Nothing+      outputAddress bch ctx (PayScript32Hash hash32) `shouldBe` Just p2sh32+      outputAddress bch ctx (PayWitnessPKHash hash20) `shouldBe` Nothing+      outputAddress bch ctx (PayWitnessScriptHash hash32) `shouldBe` Nothing+      outputAddress bch ctx (PayWitness 1 hash32Bytes) `shouldBe` Nothing+      outputAddress btc ctx (PayWitnessPKHash hash20) `shouldBe` Just p2wpkh+      outputAddress btc ctx (PayWitness 1 hash32Bytes) `shouldBe` Just p2tr+    it "the address of the mainnet 256-bit hash lock script is not written as JSON, because it has none" $+      -- This is the value an indexer encodes for that output+      marshalValue btc <$> eitherToMaybe (scriptToAddressBS btc ctx (fromJust (decodeHex "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087")))+        `shouldBe` Nothing+  describe "inputAddress" $ do+    it "gives the P2PKH address of the key in a public key hash input" $+      (addrToText btc =<< inputAddress ctx (RegularInput (SpendPKHash TxSignatureEmpty (compressedKey ctx))))+        `shouldBe` Just "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"+    it "gives the P2SH address of the redeem script in a script hash input" $+      (addrToText btc =<< inputAddress ctx (ScriptHashInput (SpendMulSig [TxSignatureEmpty]) (multisig ctx)))+        `shouldBe` Just "38fEX6RbBBMmpu3nbbuULku1xyrrzqqqnE"+    it "gives the hash of the redeem script bytes of a decoded P2SH input" $ do+      -- OP_0, an empty signature, and the push of the 37-byte redeem script+      -- 51 21 <key of private key 1> 51 ae. Its HASH160 is+      -- 83eebb7d79aa1d388e3b0ac65b98ac580c4da01a (Python, hashlib).+      let script = fromJust (decodeHex "00002551210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179851ae")+      (addrToText btc =<< inputAddress ctx =<< eitherToMaybe (decodeInput btc ctx =<< runGetS deserialize script))+        `shouldBe` Just "3DicS6C8JZm59RsrgXr56iVHzYdQngiehV"+    it "gives no address for a P2SH input whose redeem script pushes its key with OP_PUSHDATA1" $ do+      -- The redeem script 51 4c 21 <key> 51 ae hashes to+      -- 6b5837f2dea4723d69dc0911013387692b970cfa, the address+      -- 3BUbubs2pJzko1Xg5KBVDpoRa7V3kRAsVP. Written back in the form of this+      -- library it would hash to 3DicS6C8…, an address that held no coin.+      let script = fromJust (decodeHex "000026514c210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179851ae")+      (inputAddress ctx =<< eitherToMaybe (decodeInput btc ctx =<< runGetS deserialize script))+        `shouldBe` Nothing+    it "has no address for a redeem script that is too long to be pushed" $+      inputAddress ctx (ScriptHashInput (SpendMulSig [TxSignatureEmpty]) (multisigOf ctx 16))+        `shouldBe` Nothing+    it "gives the P2SH address of the wrapped program in a nested witness input" $+      -- The P2SH-P2WPKH example of BIP 143: redeem script 0014 79091972…,+      -- script hash 4733f37cf4db86fbc2efed2500b4f4e49f312023.+      (addrToText btc =<< inputAddress ctx (NestedWitnessInput (PayWitnessPKHash "79091972186c449eb1ded22b78e40d009bdf0089")))+        `shouldBe` Just "38BW8nqpHSWpkf5sXrQd2xYwvnPJwP59ic"+    it "has no address for a pay-to-public-key input" $+      inputAddress ctx (RegularInput (SpendPK TxSignatureEmpty)) `shouldBe` Nothing+    it "has no address for a bare multisig input" $+      inputAddress ctx (RegularInput (SpendMulSig [TxSignatureEmpty])) `shouldBe` Nothing+  describe "Binary serialization" $ do+    it "writes a P2PKH address as a type byte and the hash" $+      encodeHex (runPutS (serialize p2pkh))+        `shouldBe` "0076a04053bda0a88bda5177b86a15c3b29f559873"+    it "writes a Taproot address as a type byte, the version and the sized program" $+      encodeHex (runPutS (serialize p2tr))+        `shouldBe` "0401" <> "0000000000000020" <> "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"+    it "writes a token-aware address as a type byte, the CashAddr type and the sized hash" $+      encodeHex (runPutS (serialize tokenP2pkh))+        `shouldBe` "0602" <> "0000000000000014" <> "76a04053bda0a88bda5177b86a15c3b29f559873"+    it "rejects an unknown type byte" $+      deserializeAddress "0776a04053bda0a88bda5177b86a15c3b29f559873" `shouldSatisfy` isLeft+    it "rejects a witness version above 16" $+      deserializeAddress ("0411" <> "0000000000000002" <> "0102") `shouldSatisfy` isLeft+    it "rejects a witness program longer than 40 bytes" $+      deserializeAddress ("0401" <> "0000000000000029" <> T.replicate 41 "01") `shouldSatisfy` isLeft+    it "rejects a declared length above the longest payload before reading the payload" $ do+      -- 41 for a witness program, whose longest is 40+      deserializeAddress ("0401" <> "0000000000000029" <> T.replicate 41 "01")+        `shouldBe` Left "Failed reading: Address payload too long\nEmpty call stack\n"+      -- 65 for a CashAddr hash, whose longest is 64+      deserializeAddress ("0600" <> "0000000000000041" <> T.replicate 65 "01")+        `shouldBe` Left "Failed reading: Address payload too long\nEmpty call stack\n"+    it "rejects a sized payload whose length does not fit in memory" $+      deserializeAddress ("0401" <> "ffffffffffffffff" <> "0102") `shouldSatisfy` isLeft+    it "reads a version 0 witness program as the P2WPKH constructor" $+      deserializeAddress ("0400" <> "0000000000000014" <> "76a04053bda0a88bda5177b86a15c3b29f559873")+        `shouldBe` Right (WitnessPubKeyAddress hash20)+    it "reads a CashAddr of type 0 with 20 bytes as the P2PKH constructor" $+      deserializeAddress ("0600" <> "0000000000000014" <> "76a04053bda0a88bda5177b86a15c3b29f559873")+        `shouldBe` Right (PubKeyAddress hash20)+    it "rejects a CashAddr with a hash size the standard does not allow" $+      deserializeAddress ("0602" <> "0000000000000015" <> T.replicate 21 "01") `shouldSatisfy` isLeft+  describe "JSON" $ do+    it "encodes an address as its text form" $ do+      marshalValue btc p2pkh `shouldBe` String "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+      marshalJSON btc p2pkh `shouldBe` "\"1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu\""+      marshalJSON bch tokenP2pkh `shouldBe` "\"bitcoincash:zpm2qsznhks23z7629mms6s4cwef74vcwvrqekrq9w\""+    it "decodes the text form" $+      unmarshalJSON btc "\"1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu\"" `shouldBe` Just p2pkh+    it "rejects text that is not an address" $ do+      unmarshalJSON btc "\"hello\"" `shouldBe` (Nothing :: Maybe Address)+      unmarshalJSON btc "null" `shouldBe` (Nothing :: Maybe Address)+    it "fails loudly instead of writing null when the address has no text form" $ do+      evaluate (force (marshalValue bch p2wpkh)) `shouldThrow` anyErrorCall+      evaluate (force (marshalJSON btc p2sh32)) `shouldThrow` anyErrorCall+  describe "Read" $ do+    it "reads what Show writes for the generic kinds" $ do+      read (show p2tr) `shouldBe` p2tr+      read (show tokenP2pkh) `shouldBe` tokenP2pkh+    it "does not read a witness version above 16" $+      (readMaybe "OtherWitnessAddress {version = 99, bytes = \"ab\"}" :: Maybe Address)+        `shouldBe` Nothing+    it "does not read a witness program shorter than two bytes" $+      (readMaybe "OtherWitnessAddress {version = 1, bytes = \"\"}" :: Maybe Address)+        `shouldBe` Nothing+    it "does not read a CashAddr type above 15" $+      (readMaybe "OtherCashAddress {version = 200, bytes = \"abcdefghijklmnopqrst\"}" :: Maybe Address)+        `shouldBe` Nothing+    it "does not read a CashAddr hash of a length the standard does not allow" $+      (readMaybe "OtherCashAddress {version = 2, bytes = \"abc\"}" :: Maybe Address)+        `shouldBe` Nothing+    it "does not read a generic kind that has a dedicated constructor" $ do+      -- version 0 with 20 bytes is a WitnessPubKeyAddress+      (readMaybe "OtherWitnessAddress {version = 0, bytes = \"abcdefghijklmnopqrst\"}" :: Maybe Address)+        `shouldBe` Nothing+      -- CashAddr type 0 with 20 bytes is a PubKeyAddress+      (readMaybe "OtherCashAddress {version = 0, bytes = \"abcdefghijklmnopqrst\"}" :: Maybe Address)+        `shouldBe` Nothing -netVals :: [NetBox]-netVals =-    [NetBox (addrToJSON, addrToEncoding, addrFromJSON, arbitraryNetAddress)]+{- Fixtures -} -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+-- | HASH160 behind the CashAddr translation vectors.+hash20 :: Hash160+hash20 = "76a04053bda0a88bda5177b86a15c3b29f559873" +hash20Bytes :: ByteString+hash20Bytes = fromJust $ decodeHex "76a04053bda0a88bda5177b86a15c3b29f559873"++-- | SHA256 behind the BIP-173 P2WSH vector.+hash32 :: Hash256+hash32 = "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"++hash32Bytes :: ByteString+hash32Bytes = fromJust $ decodeHex "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"++p2pkh, p2sh, p2wpkh, p2wsh, p2sh32, p2tr, witness16 :: Address+p2pkh = pubKeyAddress hash20+p2sh = scriptAddress hash20+p2wpkh = witnessPubKeyAddress hash20+p2wsh = witnessScriptAddress hash32+p2sh32 = script32Address hash32+p2tr = fromJust $ witnessAddress 1 hash32Bytes+witness16 = fromJust $ witnessAddress 16 (B.pack [1 .. 40])++tokenP2pkh, tokenP2sh, tokenP2sh32, cash15 :: Address+tokenP2pkh = fromJust $ cashAddress 2 hash20Bytes+tokenP2sh = fromJust $ cashAddress 3 hash20Bytes+tokenP2sh32 = fromJust $ cashAddress 3 hash32Bytes+cash15 = fromJust $ cashAddress 15 (B.pack [1 .. 24])++predicates :: [Address -> Bool]+predicates =+  [ isPubKeyAddress,+    isScriptAddress,+    isWitnessPubKeyAddress,+    isWitnessScriptAddress,+    isWitnessAddress,+    isScript32Address,+    isCashAddress+  ]++-- | Each address with the index of the only predicate that holds for it.+predicateTable :: [(String, Address, Int)]+predicateTable =+  [ ("P2PKH", p2pkh, 0),+    ("P2SH", p2sh, 1),+    ("P2WPKH", p2wpkh, 2),+    ("P2WSH", p2wsh, 3),+    ("P2TR", p2tr, 4),+    ("P2SH32", p2sh32, 5),+    ("token-aware P2PKH", tokenP2pkh, 6)+  ]++-- | Text form of every address kind on the main networks.+textTable :: [(Network, Address, Text)]+textTable =+  [ (btc, p2pkh, "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"),+    (btc, p2sh, "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"),+    (btc, p2wpkh, "bc1qw6syq5aa5z5ghkj3w7ux59wrk204txrn9k4rmm"),+    (btc, p2wsh, "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"),+    (btc, p2tr, "bc1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qj0fj5d"),+    (btc, witness16, "bc1sqypqxpq9qcrsszg2pvxq6rs0zqg3yyc5z5tpwxqergd3c8g7ruszzg3rysjjvfeg7dt0md"),+    (btcTest, p2pkh, "mrLC19Je2BuWQDkWSTriGYPyQJXKkkBmCx"),+    (btcTest, p2sh, "2N44ThNe8NXHyv4bsX8AoVCXquBRW94Ls7W"),+    (btcTest, p2wpkh, "tb1qw6syq5aa5z5ghkj3w7ux59wrk204txrn0swsqg"),+    (btcTest, p2wsh, "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"),+    (btcTest, p2tr, "tb1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q98lawz"),+    (bch, p2pkh, "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"),+    (bch, p2sh, "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"),+    (bch, p2sh32, "bitcoincash:pvvxx9puznz3v6qyh5vjqv6kmgfkexzk0rx56faphrrr99syjqexyrusee847"),+    (bch, tokenP2pkh, "bitcoincash:zpm2qsznhks23z7629mms6s4cwef74vcwvrqekrq9w"),+    (bch, tokenP2sh, "bitcoincash:rpm2qsznhks23z7629mms6s4cwef74vcwv59yeyr7n"),+    (bch, tokenP2sh32, "bitcoincash:rvvxx9puznz3v6qyh5vjqv6kmgfkexzk0rx56faphrrr99syjqexy30vcqxv4"),+    (bch, cash15, "bitcoincash:0yqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9cc7akt68km"),+    (bchTest, p2pkh, "bchtest:qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"),+    (bchTest, p2sh, "bchtest:ppm2qsznhks23z7629mms6s4cwef74vcwvhanqgjxu")+  ]++-- | Valid addresses of one network that another network must reject.+otherNetworkTable :: [(Network, Text)]+otherNetworkTable =+  [ (btc, "mrLC19Je2BuWQDkWSTriGYPyQJXKkkBmCx"),+    (btc, "tb1qw6syq5aa5z5ghkj3w7ux59wrk204txrn0swsqg"),+    (btc, "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"),+    (btc, "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"),+    (btcTest, "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"),+    (btcTest, "bc1qw6syq5aa5z5ghkj3w7ux59wrk204txrn9k4rmm"),+    (btcRegTest, "tb1qw6syq5aa5z5ghkj3w7ux59wrk204txrn0swsqg"),+    (bch, "bc1qw6syq5aa5z5ghkj3w7ux59wrk204txrn9k4rmm"),+    (bch, "bchtest:qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"),+    (bch, "qpm2qsznhks23z7629mms6s4cwef74vcwvqcw003ap"),+    (bchTest, "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")+  ]++-- | Locking script of every address kind that has one, with a network that+-- has that kind of output.+scriptTable :: [(Network, Address, Text)]+scriptTable =+  [ (btc, p2pkh, "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac"),+    (bch, p2pkh, "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac"),+    (btc, p2sh, "a91476a04053bda0a88bda5177b86a15c3b29f55987387"),+    (bch, p2sh, "a91476a04053bda0a88bda5177b86a15c3b29f55987387"),+    (bch, p2sh32, "aa201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326287"),+    (btc, p2wpkh, "001476a04053bda0a88bda5177b86a15c3b29f559873"),+    (btc, p2wsh, "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"),+    (btc, p2tr, "51201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"),+    (btc, witness16, "6028" <> "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728")+  ]++-- | The longest CashAddr: type 0 with the 64-byte hash 00 01 … 3f. Encoded+-- with a Python transcription of the Bitcoin Cash Node encoder.+longestCashAddr :: Text+longestCashAddr = "bitcoincash:quqqzqsrqszsvpcgpy9qkrqdpc83qygjzv2p29shrqv35xcur50p7gppyg3jgffxyu5zj23t9skjutesxyerxdp4xcmnswf68v7r603lr3frdfp5"++-- | A 1-of-n multisig of the compressed public key of private key 1.+multisigOf :: Ctx -> Int -> ScriptOutput+multisigOf ctx n = PayMulSig (replicate n (compressedKey ctx)) 1++-- | Public key of private key 1, in both forms.+compressedKey, uncompressedKey :: Ctx -> PublicKey+compressedKey ctx =+  derivePublicKey ctx . fromJust $+    fromWif btc "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"+uncompressedKey ctx =+  derivePublicKey ctx . fromJust $+    fromWif btc "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf"++-- | Public key of private key 2.+secondKey :: Ctx -> PublicKey+secondKey ctx =+  either error id . unmarshal ctx . fromJust $+    decodeHex "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"++-- | 1-of-2 multisig of the keys of private keys 1 and 2.+multisig :: Ctx -> ScriptOutput+multisig ctx = PayMulSig [compressedKey ctx, secondKey ctx] 1++multisigScript :: Text+multisigScript =+  "5121"+    <> "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"+    <> "21"+    <> "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"+    <> "52ae"++-- | Pay to the public key of private key 1.+p2pkScript :: Text+p2pkScript = "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac"++deserializeAddress :: Text -> Either String Address+deserializeAddress = runGetS deserialize . fromJust . decodeHex++{- Vectors -}+ 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+    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,404 +1,1727 @@-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.BlockSpec-    ( spec-    ) where--import           Control.Monad.State.Strict-import           Data.Either                (fromRight)-import           Data.Maybe                 (fromJust)-import           Data.String                (fromString)-import           Data.String.Conversions    (cs)-import           Data.Text                  (Text)-import           Data.Word                  (Word32)-import           Haskoin.Block-import           Haskoin.Constants-import           Haskoin.Transaction-import           Haskoin.Util.Arbitrary-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit                 hiding (State)-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-    ]--myTime :: Timestamp-myTime = 1499083075--withChain :: Network -> State HeaderMemory a -> a-withChain net f = evalState f (initialChain net)--chain :: BlockHeaders m => Network -> BlockHeader -> Int -> m ()-chain net bh i = do-    bnsE <- connectBlocks net myTime bhs-    either error (const $ return ()) bnsE-  where-    bhs = appendBlocks net 6 bh i--spec :: Spec-spec = do-    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)---- 0 → → 2015 → → → → → → → 4031---       ↓---       → → 2035 → → → → → → 4035*---           ↓---           → → 2185-splitChain :: Network -> State HeaderMemory ()-splitChain net = do-    start <- go 1 (getGenesisHeader net) 2015-    e 2015 (head start)-    tail1 <- go 2 (nodeHeader $ head start) 2016-    e 4031 (head tail1)-    tail2 <- go 3 (nodeHeader $ head start) 20-    e 2035 (head tail2)-    tail3 <- go 4 (nodeHeader $ head tail2) 2000-    e 4035 (head tail3)-    tail4 <- go 5 (nodeHeader $ head tail2) 150-    e 2185 (head tail4)-    sp1 <- splitPoint (head tail1) (head tail3)-    unless (sp1 == head start) $-        error $-        "Split point wrong between blocks 4031 and 4035: " ++-        show (nodeHeight sp1)-    sp2 <- splitPoint (head tail4) (head tail3)-    unless (sp2 == head tail2) $-        error $-        "Split point wrong between blocks 2185 and 4035: " ++-        show (nodeHeight sp2)-  where-    e n bn =-        unless (nodeHeight bn == n) $-        error $-        "Node height " ++-        show (nodeHeight bn) ++ " of first chunk should be " ++ show n-    go seed start n = do-        let bhs = appendBlocks net seed start n-        bnE <- connectBlocks net myTime bhs-        case bnE of-            Right bn -> return bn-            Left ex  -> error ex--{- Merkle Trees -}--testTreeWidth :: Int -> Property-testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1--testBaseWidth :: Int -> Property-testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i--buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool-buildExtractTree net txs =-    r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)-  where-    (f, h) = buildPartialMerkle txs-    (r, m) =-        fromRight (error "Could not extract matches from Merkle tree") $-        extractMatches net f h (length txs)--testCompact :: Assertion-testCompact = do-    assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)-    assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)-    assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)-    assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)-    assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))-    assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)--testCompactBitcoinCore :: Assertion-testCompactBitcoinCore = do-    assertEqual "zero" (0, False) (decodeCompact 0x00000000)-    assertEqual-        "zero (encode · decode)"-        0x00000000-        (encodeCompact . fst $ decodeCompact 0x00000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)-    assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)-    assertEqual-        "vector 1 (encode · decode)"-        0x01120000-        (encodeCompact . fst $ decodeCompact 0x01123456)-    assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)-    assertEqual-        "vector 2 (negative) (decode)"-        (-0x7e, False)-        (decodeCompact 0x01fedcba)-    assertEqual-        "vector 2 (negative) (encode · decode)"-        0x01fe0000-        (encodeCompact . fst $ decodeCompact 0x01fedcba)-    assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)-    assertEqual-        "vector 3 (encode · decode)"-        0x02123400-        (encodeCompact . fst $ decodeCompact 0x02123456)-    assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)-    assertEqual-        "vector 4 (encode · decode)"-        0x03123456-        (encodeCompact . fst $ decodeCompact 0x03123456)-    assertEqual-        "vector 5 (decode)"-        (0x12345600, False)-        (decodeCompact 0x04123456)-    assertEqual-        "vector 5 (encode · decode)"-        0x04123456-        (encodeCompact . fst $ decodeCompact 0x04123456)-    assertEqual-        "vector 6 (decode)"-        (-0x12345600, False)-        (decodeCompact 0x04923456)-    assertEqual-        "vector 6 (encode · decode)"-        0x04923456-        (encodeCompact . fst $ decodeCompact 0x04923456)-    assertEqual-        "vector 7 (decode)"-        (0x92340000, False)-        (decodeCompact 0x05009234)-    assertEqual-        "vector 7 (encode · decode)"-        0x05009234-        (encodeCompact . fst $ decodeCompact 0x05009234)-    assertEqual-        "vector 8 (decode)"-        ( 0x1234560000000000000000000000000000000000000000000000000000000000-        , False)-        (decodeCompact 0x20123456)-    assertEqual-        "vector 8 (encode · decode)"-        0x20123456-        (encodeCompact . fst $ decodeCompact 0x20123456)-    assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)-    assertBool-        "vector 9 (decode) (positive)"-        ((> 0) . fst $ decodeCompact 0xff123456)--runMerkleVector :: (Text, [Text]) -> Assertion-runMerkleVector (r, hs) =-    assertBool "merkle vector" $-        buildMerkleRoot (map f hs) == getTxHash (f r)-  where-    f = fromJust . hexToTxHash--merkleVectors :: [(Text, [Text])]-merkleVectors =-      -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1-    [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f"-      , [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52"-        , "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9"-        , "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41"-        , "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f"-        , "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6"-        , "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc"-        , "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3"-        , "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66"-        , "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f"-        , "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4"-        , "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"-        ]-      )-      -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4-    , ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c"-      , [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e"-        , "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51"-        , "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a"-        , "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e"-        , "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471"-        , "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3"-        , "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030"-        , "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13"-        , "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2"-        , "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"-        ]-      )-      -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048-    , ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"-      , [ "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" ]-      )-      -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c-    , ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c"-      , [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028"-        , "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"-        ]-      )-      -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e-    , ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c"-      , [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549"-        , "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb"-        , "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"-        ]-      )-      -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021-    , ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2"-      , [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81"-        , "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92"-        , "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27"-        , "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a"-        , "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924"-        , "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2"-        , "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95"-        , "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4"-        , "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d"-        , "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f"-        , "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909"-        , "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d"-        , "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53"-        , "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b"-        , "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174"-        , "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494"-        , "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4"-        , "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa"-        , "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d"-        , "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6"-        , "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9"-        , "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55"-        , "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776"-        , "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d"-        , "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666"-        , "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f"-        , "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9"-        , "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4"-        , "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"-        ]-      )-    ]--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)--data AsertBlock = AsertBlock Int Integer Integer Word32--data AsertVector = AsertVector String Integer Integer Word32 [AsertBlock]--readAsertVector :: FilePath -> IO AsertVector-readAsertVector p = do-    (d:ah:apt:ab:_:_:_:_:xs) <- lines <$> readFile ("data/" ++ p)-    let desc = drop 16 d-        anchor_height = read (words ah !! 3)-        anchor_parent_time = read (words apt !! 4)-        anchor_nbits = read (words ab !! 3)-        blocks = map (f . words) (init xs)-    return $-        AsertVector-        desc-        anchor_height-        anchor_parent_time-        anchor_nbits-        blocks-  where-    f [i,h,t,g] = AsertBlock (read i) (read h) (read t) (read g)-    f _ = undefined--asertTests :: FilePath -> SpecWith ()-asertTests file = do-    v@(AsertVector d _ _ _ _) <- runIO $ readAsertVector file-    it d $ testAsertBits v--testAsertBits :: AsertVector -> Assertion-testAsertBits (AsertVector _ anchor_height anchor_parent_time anchor_bits blocks) =-    forM_ blocks $ \(AsertBlock _ h t g) ->-        computeAsertBits-            (2 * 24 * 60 * 60)-            anchor_bits-            (t - anchor_parent_time)-            (h - anchor_height)-        `shouldBe`-            g+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Haskoin.BlockSpec+  ( spec,+  )+where++import Control.Exception (evaluate)+import Control.Monad+import Control.Monad.State.Strict+import Data.Aeson (Value, decode, encode, object, toJSON, (.=))+import qualified Data.ByteString as B+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (deserialize, serialize)+import Data.Default (def)+import Data.Either (isLeft)+import Data.Hashable (hash)+import Data.List (isInfixOf, isPrefixOf)+import Data.Maybe (fromJust)+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Word (Word32)+import Haskoin.Block+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit hiding (State)+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Printf (printf)++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          ReadBox arbitraryBlockHash,+          ReadBox arbitraryBlockHeader,+          ReadBox arbitraryGetBlocks,+          ReadBox arbitraryGetHeaders,+          ReadBox arbitraryHeaders,+          ReadBox arbitraryMerkleBlock,+          ReadBox arbitraryBlockNode,+          ReadBox arbitraryHeaderMemory+        ],+      jsonTests =+        [ JsonBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          JsonBox arbitraryBlockHash,+          JsonBox arbitraryBlockHeader+        ],+      serialTests =+        [ SerialBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          SerialBox arbitraryBlockHash,+          SerialBox arbitraryBlockHeader,+          SerialBox arbitraryGetBlocks,+          SerialBox arbitraryGetHeaders,+          SerialBox arbitraryHeaders,+          SerialBox arbitraryMerkleBlock,+          SerialBox arbitraryBlockNode+        ]+    }++-- | Current time given to the validation functions. It is years after the+-- regtest genesis block, so the two-hour rule never interferes with the test+-- chains unless a test passes its own time.+myTime :: Timestamp+myTime = 1499083075++withChain :: Network -> State HeaderMemory a -> a+withChain net f = evalState f (initialChain net)++chain :: (BlockHeaders m) => Network -> BlockHeader -> Int -> m ()+chain net bh i = do+  bnsE <- connectBlocks net myTime bhs+  either error (const $ return ()) bnsE+  where+    bhs = appendBlocks net 6 bh i++-- | Regtest with a retarget every ten blocks and no min-difficulty rule, so+-- that retargets can be tested on short chains.+retargetNet :: Network+retargetNet =+  (btcRegTest :: Network)+    { powNoRetarget = False,+      minDiffBlocks = False,+      targetTimespan = 10 * 600+    }++-- | Regtest with the BIP94 rules, the min-difficulty rule and a retarget every+-- ten blocks.+bip94Net :: Network+bip94Net =+  (btcRegTest :: Network)+    { bip94 = True,+      powNoRetarget = False,+      targetTimespan = 10 * 600+    }++-- | The same network without the BIP94 rules.+noBip94Net :: Network+noBip94Net = (bip94Net :: Network) {bip94 = False}++-- | Bitcoin Cash regtest that retargets, with no Bitcoin Cash algorithm+-- enabled and no checkpoint on its genesis block. Each test enables the+-- algorithm it needs.+cashNet :: Network+cashNet =+  (bchRegTest :: Network)+    { powNoRetarget = False,+      minDiffBlocks = False,+      edaHeight = Nothing,+      daaHeight = Nothing,+      asertActivationTime = Nothing,+      checkpoints = []+    }++edaNet :: Network+edaNet = (cashNet :: Network) {edaHeight = Just 1}++daaNet :: Network+daaNet = (cashNet :: Network) {daaHeight = Just 1}++-- | ASERT activates when the median time past reaches the genesis time plus+-- 6000 seconds. With one block every 600 seconds that is the median time past+-- of block 15.+asertActivation :: Timestamp+asertActivation = cashNet.genesisHeader.timestamp + 6000++asertNet :: Network+asertNet =+  (cashNet :: Network)+    { asertActivationTime = Just asertActivation,+      asertHalfLife = 2 * 24 * 60 * 60+    }++-- | A version 4 header on top of the parent with the given timestamp and+-- bits, and a zero nonce. Version 4 passes the BIP34, BIP65 and BIP66 version+-- rules at any height, which regtest applies from height 1 like Bitcoin Core.+headerOn :: BlockHeader -> Timestamp -> Word32 -> BlockHeader+headerOn parent time bits =+  (parent :: BlockHeader) {version = 4, prev = headerHash parent, timestamp = time, bits = bits, nonce = 0}++-- | Mine a header on top of the parent with the given timestamp and bits.+mineOn :: Network -> BlockHeader -> Timestamp -> Word32 -> BlockHeader+mineOn net parent time bits = mineBlock net 0 (headerOn parent time bits)++-- | The same header with another version number.+headerWithVersion :: Word32 -> BlockHeader -> BlockHeader+headerWithVersion v (BlockHeader _ p m t b n) = BlockHeader v p m t b n++-- | Mine a chain of headers with the given timestamps and bits.+mineChain :: Network -> BlockHeader -> [(Timestamp, Word32)] -> [BlockHeader]+mineChain net parent = drop 1 . scanl (\p (time, bits) -> mineOn net p time bits) parent++-- | Put a chain of unvalidated headers in the store on top of the best block,+-- one per (timestamp, bits) pair. Heights, work and skip pointers are filled+-- in the way 'connectBlocks' does, but there is no proof of work and no rule+-- check, so difficulty tests can use any bits without mining. Returns the new+-- nodes, oldest first, and leaves the last one as the best block.+storeChain :: [(Timestamp, Word32)] -> State HeaderMemory [BlockNode]+storeChain specs = do+  start <- getBestBlockHeader+  go start specs+  where+    go _ [] = return []+    go par ((time, bits) : rest) = do+      let hdr = headerOn par.header time bits+          h = par.height + 1+      skipNode <- fromJust <$> getAncestor (skipHeight h) par+      let node =+            BlockNode+              { header = hdr,+                height = h,+                work = par.work + headerWork hdr,+                skip = headerHash skipNode.header+              }+      addBlockHeader node+      setBestBlockHeader node+      (node :) <$> go node rest++-- | Timestamps and bits of a chain of evenly spaced blocks after genesis.+evenlySpaced :: Network -> Word32 -> Int -> Word32 -> [(Timestamp, Word32)]+evenlySpaced net spacing n bits =+  [(net.genesisHeader.timestamp + spacing * fromIntegral i, bits) | i <- [1 .. n]]++-- | The work required for a block with the given timestamp on top of the+-- stored chain.+requiredAfter :: Network -> Timestamp -> State HeaderMemory Word32+requiredAfter net time = do+  par <- getBestBlockHeader+  nextWorkRequired net par (headerOn par.header time par.header.bits)++connectHeight :: Network -> Timestamp -> BlockHeader -> State HeaderMemory (Either String BlockHeight)+connectHeight net t h = fmap (.height) <$> connectBlock net t h++connectAll :: Network -> [BlockHeader] -> State HeaderMemory ()+connectAll net hs = connectBlocks net myTime hs >>= either error (const (return ()))++genesisHash :: BlockHash+genesisHash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"++-- Three distinct transaction hashes for Merkle tree tests.+txA, txB, txC :: TxHash+txA = TxHash (sha256 ("a" :: B.ByteString))+txB = TxHash (sha256 ("b" :: B.ByteString))+txC = TxHash (sha256 ("c" :: B.ByteString))++readBlock :: FilePath -> IO Block+readBlock path = do+  hex <- T.strip . cs <$> B.readFile path+  either error return $ runGetS deserialize $ fromJust $ decodeHex hex++displayTxHash :: Text -> TxHash+displayTxHash = fromJust . hexToTxHash++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  blockHashSpec+  blockHeaderSpec+  compactSpec+  serializationLimitSpec+  proofOfWorkSpec+  genesisSpec+  headerStoreSpec+  validationSpec+  medianTimeSpec+  bitcoinDifficultySpec+  cashDifficultySpec+  asertSpec+  checkpointSpec+  merkleSpec+  versionRuleSpec+  knownHeaderSpec+  merkleLimitSpec+  chainConstantsSpec+  activationSpec+  minimumWorkSpec+  coreProofSpec+  describe "block subsidy" $ do+    it "computes bitcoin block subsidy correctly" (testSubsidy btc)+    it "computes regtest block subsidy correctly" (testSubsidy btcRegTest)++blockHashSpec :: Spec+blockHashSpec = 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+  it "hashes the genesis header" $+    headerHash btc.genesisHeader `shouldBe` genesisHash+  it "shows a block hash in display order" $+    blockHashToHex genesisHash+      `shouldBe` "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"+  it "serializes a block hash in internal byte order" $+    encodeHex (runPutS (serialize genesisHash))+      `shouldBe` "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"+  it "encodes a block hash to JSON in display order" $+    encode genesisHash+      `shouldBe` "\"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f\""+  it "rejects hex that is not 32 bytes" $ do+    hexToBlockHash (T.replicate 31 "00") `shouldBe` Nothing+    hexToBlockHash (T.replicate 33 "00") `shouldBe` Nothing+    hexToBlockHash ("ff" <> T.replicate 32 "00") `shouldBe` Nothing+    hexToBlockHash (T.replicate 32 "zz") `shouldBe` Nothing+    hexToBlockHash "" `shouldBe` Nothing++blockHeaderSpec :: Spec+blockHeaderSpec = describe "block header" $ do+  it "serializes the genesis header" $+    encodeHex (runPutS (serialize btc.genesisHeader))+      `shouldBe` "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c"+  it "encodes the genesis header to JSON like the getblockheader RPC" $+    toJSON btc.genesisHeader `shouldBe` genesisHeaderJson+  it "encodes the same JSON with the streaming encoder" $+    decode (encode btc.genesisHeader) `shouldBe` Just genesisHeaderJson+  it "decodes the genesis header from JSON" $+    decode (encode genesisHeaderJson) `shouldBe` Just btc.genesisHeader+  it "rejects a merkle root that is not 32 bytes in JSON" $+    (decode (encode (object [("version", toJSON (1 :: Int)), ("prevblock", toJSON (T.replicate 64 "0")), ("merkleroot", toJSON (T.replicate 66 "0")), ("timestamp", toJSON (0 :: Int)), ("bits", toJSON (0 :: Int)), ("nonce", toJSON (0 :: Int))])) :: Maybe BlockHeader)+      `shouldBe` Nothing+  it "serializes a getheaders message" $+    -- version 70015 = 0x0001117f, one locator hash, stop hash of zeroes+    encodeHex (runPutS (serialize (GetHeaders 70015 [genesisHash] "0000000000000000000000000000000000000000000000000000000000000000")))+      `shouldBe` "7f110100016fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000000000000000000000000000000000000000000000000000000000"+  where+    genesisHeaderJson :: Value+    genesisHeaderJson =+      object+        [ "version" .= (1 :: Int),+          "prevblock" .= ("0000000000000000000000000000000000000000000000000000000000000000" :: Text),+          "merkleroot" .= ("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" :: Text),+          "timestamp" .= (1231006505 :: Int),+          "bits" .= (486604799 :: Int),+          "nonce" .= (2083236893 :: Int)+        ]++compactSpec :: Spec+compactSpec = describe "compact number" $ do+  it "compact number local vectors" testCompact+  it "compact number imported vectors" testCompactBitcoinCore+  it "refuses a number wider than 255 bytes" $+    evaluate (encodeCompact (2 ^ (2048 :: Int))) `shouldThrow` anyErrorCall+  it "refuses the largest 255-byte number" $+    -- 255 bytes of 0xff: the top byte has the sign bit set, so the mantissa+    -- shifts down one byte and the exponent becomes 256, which cannot be+    -- represented either+    evaluate (encodeCompact (2 ^ (2040 :: Int) - 1)) `shouldThrow` anyErrorCall++serializationLimitSpec :: Spec+serializationLimitSpec = describe "serialization limits" $ do+  it "refuses a block whose transaction count wraps around" $+    -- count 0xffffffffffffffff would become -1 as an Int and read as no transactions+    (runGetS deserialize (genesisHeaderBytes <> B.pack (0xff : replicate 8 0xff)) :: Either String Block)+      `shouldSatisfy` isLeft+  it "refuses a headers message that announces 2001 headers, before it reads them" $+    -- the count 2001 = 0x07d1 as a three-byte compact size, and no header.+    -- MAX_HEADERS_RESULTS is 2000 in Bitcoin Core and Bitcoin Cash Node.+    (runGetS deserialize (B.pack [0xfd, 0xd1, 0x07]) :: Either String Headers)+      `leftWith` "too many"+  it "accepts the count of a headers message of 2000 headers" $+    -- the count is accepted, and the parse then fails on the missing headers+    (runGetS deserialize (B.pack [0xfd, 0xd0, 0x07]) :: Either String Headers)+      `leftWith` "too few bytes"+  it "refuses a merkle block whose hash count wraps around" $+    (runGetS deserialize (genesisHeaderBytes <> B.pack [1, 0, 0, 0] <> B.pack (0xff : replicate 8 0xff)) :: Either String MerkleBlock)+      `shouldSatisfy` isLeft+  it "reads a block with no transactions when the count is zero" $+    (runGetS deserialize (genesisHeaderBytes <> B.pack [0]) :: Either String Block)+      `shouldBe` Right (Block btc.genesisHeader [])+  where+    genesisHeaderBytes = runPutS (serialize btc.genesisHeader)++proofOfWorkSpec :: Spec+proofOfWorkSpec = describe "proof of work" $ do+  it "accepts the genesis header" $+    isValidPOW btc btc.genesisHeader `shouldBe` True+  it "rejects a header whose hash is above its target" $+    -- a 32-bit nonce change leaves a hash with 32 leading zero bits with probability 2^-32+    isValidPOW btc (btc.genesisHeader :: BlockHeader) {nonce = 0} `shouldBe` False+  it "rejects a target above the proof of work limit" $+    -- exponent 0x1e makes the target 256 times the limit+    isValidPOW btc (btc.genesisHeader :: BlockHeader) {bits = 0x1e00ffff} `shouldBe` False+  it "rejects a negative target" $+    isValidPOW btc (btc.genesisHeader :: BlockHeader) {bits = 0x03800001} `shouldBe` False+  it "rejects an overflowing target" $+    isValidPOW btc (btc.genesisHeader :: BlockHeader) {bits = 0xff123456} `shouldBe` False+  it "rejects a zero target" $+    isValidPOW btc (btc.genesisHeader :: BlockHeader) {bits = 0} `shouldBe` False+  it "reads the hash as a number" $+    blockPOW genesisHash `shouldBe` 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+  it "counts the work of the genesis header" $+    -- 2^256 / (0xffff * 2^208 + 1) = 4295032833, Bitcoin Core's 0x0000000100010001+    headerWork btc.genesisHeader `shouldBe` 4295032833+  it "counts the work of a regtest header" $+    -- 2^256 / (0x7fffff * 2^232 + 1) = 2+    headerWork btcRegTest.genesisHeader `shouldBe` 2+  it "gives no work to a negative target" $+    headerWork (btc.genesisHeader :: BlockHeader) {bits = 0x03800001} `shouldBe` 0+  it "gives no work to an overflowing target" $+    headerWork (btc.genesisHeader :: BlockHeader) {bits = 0xff123456} `shouldBe` 0+  it "gives no work to a zero target" $+    headerWork (btc.genesisHeader :: BlockHeader) {bits = 0} `shouldBe` 0+  it "mines a regtest header by changing only the nonce" $ do+    let hdr = headerOn btcRegTest.genesisHeader (btcRegTest.genesisHeader.timestamp + 600) 0x207fffff+        mined = mineBlock btcRegTest 7 hdr+    isValidPOW btcRegTest mined `shouldBe` True+    (mined :: BlockHeader) {nonce = 0} `shouldBe` hdr+    mined.nonce `shouldSatisfy` (>= 7)+  it "appends mined blocks one target spacing apart" $ do+    let hs = appendBlocks btcRegTest 1 btcRegTest.genesisHeader 3+    map (.timestamp) hs `shouldBe` [1296689202, 1296689802, 1296690402]+    map (.prev) hs `shouldBe` headerHash btcRegTest.genesisHeader : map headerHash (take 2 hs)+    all (isValidPOW btcRegTest) hs `shouldBe` True++genesisSpec :: Spec+genesisSpec = describe "genesis blocks" $ do+  it "has the BIP94 genesis block" $+    headerHash btcTest4.genesisHeader+      `shouldBe` "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043"+  it "has the Bitcoin Cash testnet4 genesis block" $+    headerHash bchTest4.genesisHeader+      `shouldBe` "000000001dd410c49a788668ce26751718cc797474d3152a5fc073dd44fd9f7b"+  it "serializes the mainnet coinbase" $+    encodeHex (runPutS (serialize mainnetGenesisTx))+      `shouldBe` "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"+  it "serializes the testnet4 coinbase" $+    encodeHex (runPutS (serialize testnet4GenesisTx))+      `shouldBe` "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5504ffff001d01044c4c30332f4d61792f323032342030303030303030303030303030303030303030303165626435386332343439373062336161396437383362623030313031316662653865613865393865303065ffffffff0100f2052a010000002321000000000000000000000000000000000000000000000000000000000000000000ac00000000"+  it "hashes the testnet4 coinbase to its merkle root" $+    txHashToHex (txHash testnet4GenesisTx)+      `shouldBe` "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e"+  it "pairs every network's genesis header with its coinbase" $+    forM_ allNets $ \net ->+      (net.name, (txHash net.genesisTx).get) `shouldBe` (net.name, net.genesisHeader.merkle)+  it "builds the genesis block of every network" $+    forM_ allNets $ \net ->+      genesisBlock net `shouldBe` Block net.genesisHeader [net.genesisTx]+  it "builds the genesis node" $ do+    let node = genesisNode btc+    node.height `shouldBe` 0+    node.work `shouldBe` 4295032833+    node.skip `shouldBe` genesisHash+    isGenesis node `shouldBe` True++headerStoreSpec :: Spec+headerStoreSpec = describe "header store" $ do+  it "starts with genesis as the best block" $+    withChain btc getBestBlockHeader `shouldBe` genesisNode btc+  it "finds genesis by hash" $+    withChain btc (getBlockHeader genesisHash) `shouldBe` Just (genesisNode btc)+  it "finds nothing for an unknown hash" $+    withChain btc (getBlockHeader "0000000000000000000000000000000000000000000000000000000000000001") `shouldBe` Nothing+  it "finds nothing for a hash that only shares its first eight bytes with a block" $+    -- the last eight bytes of this display form are those of the genesis hash+    withChain btc (getBlockHeader "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa72b3f1b60a8ce26f") `shouldBe` Nothing+  it "reads a stored node whose chain work fits 256 bits, and no other" $ do+    -- chain work is a 256-bit unsigned number in Bitcoin Core+    let stored w = runPutS (serialize (genesisNode btc :: BlockNode) {work = w})+        readWork w = fmap (.work) (runGetS deserialize (stored w) :: Either String BlockNode)+    readWork (2 ^ (256 :: Int) - 1)+      `shouldBe` Right 115792089237316195423570985008687907853269984665640564039457584007913129639935+    readWork (2 ^ (256 :: Int)) `leftWith` "chain work out of range"+    readWork (-1) `leftWith` "chain work out of range"+  it "hashes two nodes with the same header to the same value, as they are equal" $ do+    let a = (genesisNode btc :: BlockNode) {work = 1}+        b = (genesisNode btc :: BlockNode) {work = 2}+    a == b `shouldBe` True+    hash a == hash b `shouldBe` True+  it "finds an added node by hash" $+    withChain btcRegTest (storeChain (evenlySpaced btcRegTest 600 3 0x207fffff) >>= \ns -> getBlockHeader (headerHash (last ns).header))+      `shouldSatisfy` maybe False ((== 3) . (.height))+  it "takes the short hash from the high-entropy end of the hash" $+    -- the first eight bytes of the internal order, as a big-endian number+    shortBlockHash genesisHash `shouldBe` 0x6fe28c0ab6f1b372+  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 "computes skip heights like Bitcoin Core" $+    map skipHeight [0 .. 12] `shouldBe` [0, 0, 0, 1, 0, 1, 4, 1, 0, 1, 8, 1, 8]+  it "gets ancestors at any height" $+    let heights =+          withChain btcRegTest $ do+            chain btcRegTest btcRegTest.genesisHeader 100+            bb <- getBestBlockHeader+            forM [0, 1, 37, 64, 99, 100] $ \h -> fmap (.height) <$> getAncestor h bb+     in heights `shouldBe` map Just [0, 1, 37, 64, 99, 100]+  it "has no ancestor above the block" $+    withChain btcRegTest (chain btcRegTest btcRegTest.genesisHeader 5 >> getBestBlockHeader >>= getAncestor 6)+      `shouldBe` Nothing+  it "finds the parent of a block" $+    let hdr = mineOn btcRegTest btcRegTest.genesisHeader (btcRegTest.genesisHeader.timestamp + 600) 0x207fffff+     in withChain btcRegTest (parentBlock hdr) `shouldBe` Just (genesisNode btcRegTest)+  it "lists parents from the nearest, stopping at genesis" $+    let heights = withChain btcRegTest $ do+          chain btcRegTest btcRegTest.genesisHeader 5+          bb <- getBestBlockHeader+          map (.height) <$> getParents 10 bb+     in heights `shouldBe` [4, 3, 2, 1, 0]+  it "lists the requested number of parents" $+    let heights = withChain btcRegTest $ do+          chain btcRegTest btcRegTest.genesisHeader 20+          bb <- getBestBlockHeader+          map (.height) <$> getParents 3 bb+     in heights `shouldBe` [19, 18, 17]+  it "builds a block locator like Bitcoin Core" $+    -- ten single steps below the tip, then steps of 2, 4, 8, ... and genesis+    locatorHeights 100 `shouldBe` [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 87, 83, 75, 59, 27, 0]+  it "ends the block locator at genesis once" $+    locatorHeights 12 `shouldBe` [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+  it "builds a block locator of a short chain" $+    locatorHeights 3 `shouldBe` [3, 2, 1, 0]+  it "builds a block locator of hashes" $+    let (hashes, nodes) = withChain btcRegTest $ do+          chain btcRegTest btcRegTest.genesisHeader 20+          bb <- getBestBlockHeader+          (,) <$> blockLocator bb <*> blockLocatorNodes bb+     in hashes `shouldBe` map (headerHash . (.header)) nodes+  it "follows split chains on bchRegTest" $+    let net = bchRegTest+        bb = withChain net $ splitChain net >> getBestBlockHeader+     in bb.height `shouldBe` 4035+  it "prefers the block with more work" $ do+    let node w h = (genesisNode btc :: BlockNode) {work = w, height = h}+    chooseBest (node 10 1) (node 20 1) `shouldBe` node 20 1+    chooseBest (node 20 1) (node 10 5) `shouldBe` node 20 1+  it "keeps the first block on equal work, whatever the heights" $ do+    -- CBlockIndexWorkComparator in Bitcoin Core compares the chain work and+    -- then the order of arrival. Height is not part of the rule.+    let node w h = (genesisNode btc :: BlockNode) {work = w, height = h}+    (chooseBest (node 10 1) (node 10 2)).height `shouldBe` 1+    (chooseBest (node 10 2) (node 10 1)).height `shouldBe` 2+  it "keeps the first block on equal work and height" $ do+    let node w h hdr = (genesisNode btc :: BlockNode) {work = w, height = h, header = hdr}+        first = node 10 1 btc.genesisHeader+        second = node 10 1 (btc.genesisHeader :: BlockHeader) {nonce = 1}+    chooseBest first second `shouldBe` first+    chooseBest second first `shouldBe` second+  it "orders nodes by height and then by header" $ do+    let node h hdr = (genesisNode btc :: BlockNode) {height = h, header = hdr}+        a = node 1 btc.genesisHeader+        b = node 1 (btc.genesisHeader :: BlockHeader) {nonce = 4294967295}+        c = node 2 btc.genesisHeader+    compare a b `shouldBe` LT+    compare a c `shouldBe` LT+    compare a a `shouldBe` EQ+  it "searches the chain for the first block at or above a height" $+    searchHeights `shouldBe` (Just 7, Just 7, Nothing, Just 20, Just 0, Nothing)+  where+    locatorHeights n = withChain btcRegTest $ do+      chain btcRegTest btcRegTest.genesisHeader n+      bb <- getBestBlockHeader+      map (.height) <$> blockLocatorNodes bb+    searchHeights = withChain btcRegTest $ do+      _ <- storeChain (evenlySpaced btcRegTest 600 20 0x207fffff)+      let byHeight :: BlockHeight -> BlockNode -> State HeaderMemory Ordering+          byHeight h bn = return (compare bn.height h)+          allBelow, allAbove :: BlockNode -> State HeaderMemory Ordering+          allBelow bn = return (compare bn.height 100)+          allAbove bn = return (compare (bn.height + 100) 50)+      a <- fmap (.height) <$> firstGreaterOrEqual btcRegTest (byHeight 7)+      b <- fmap (.height) <$> lastSmallerOrEqual btcRegTest (byHeight 7)+      c <- fmap (.height) <$> firstGreaterOrEqual btcRegTest allBelow+      d <- fmap (.height) <$> lastSmallerOrEqual btcRegTest allBelow+      e <- fmap (.height) <$> firstGreaterOrEqual btcRegTest allAbove+      f <- fmap (.height) <$> lastSmallerOrEqual btcRegTest allAbove+      return (a, b, c, d, e, f)++validationSpec :: Spec+validationSpec = describe "header validation" $ do+  let net = btcRegTest+      t0 = net.genesisHeader.timestamp+      b1 = mineOn net net.genesisHeader (t0 + 600) 0x207fffff+      b2 = mineOn net b1 (t0 + 1200) 0x207fffff+      b3 = mineOn net b2 (t0 + 1800) 0x207fffff+  it "connects a chain and returns the newest node first" $+    let heights = withChain net $ do+          r <- connectBlocks net myTime [b1, b2, b3]+          return (map (.height) <$> r)+     in heights `shouldBe` Right [3, 2, 1]+  it "connects headers one at a time" $+    withChain net (mapM (connectHeight net myTime) [b1, b2, b3]) `shouldBe` [Right 1, Right 2, Right 3]+  it "makes the connected chain the best block" $+    withChain net (connectAll net [b1, b2, b3] >> getBestBlockHeader) `shouldSatisfy` (== 3) . (.height)+  it "accepts a header it already has" $+    withChain net (connectAll net [b1] >> connectHeight net myTime b1) `shouldBe` Right 1+  it "rejects a header whose parent is unknown" $+    withChain net (connectHeight net myTime b2) `shouldBe` Left "Could not get parent block"+  it "rejects a list of headers that is not a chain" $+    withChain net (connectBlocks net myTime [b1, b3]) `shouldBe` Left "Blocks to connect do not form a chain"+  it "keeps the headers before a bad one and the best block among them" $+    let bad = mineOn net b2 (t0 + 1800) 0x1f7fffff+        (result, best, stored) = withChain net $ do+          r <- connectBlocks net myTime [b1, b2, bad]+          bb <- getBestBlockHeader+          s <- getBlockHeader (headerHash b2)+          return (map (.height) <$> r, bb.height, (.height) <$> s)+     in (result, best, stored)+          `shouldBe` (Left "Incorrect difficulty bits: 0x1f7fffff, expected 0x207fffff", 2, Just 2)+  it "rejects a header whose proof of work fails" $+    -- a nonce of zero on top of the mainnet genesis block has 32 leading zero bits with probability 2^-32+    withChain btc (connectBlock btc myTime (headerOn btc.genesisHeader (btc.genesisHeader.timestamp + 600) 0x1d00ffff))+      `shouldSatisfy` either ("Proof of work failed: " `isPrefixOf`) (const False)+  it "rejects a header whose bits differ from the required difficulty" $+    -- regtest requires 0x207fffff; the header is mined at a harder target+    withChain net (connectHeight net myTime (mineOn net net.genesisHeader (t0 + 600) 0x1f7fffff))+      `shouldBe` Left "Incorrect difficulty bits: 0x1f7fffff, expected 0x207fffff"+  it "rejects a timestamp more than two hours in the future" $+    -- current time 1296700000, header at 1296707201 is 7201 seconds ahead+    withChain net (connectHeight net 1296700000 (mineOn net net.genesisHeader 1296707201 0x207fffff))+      `shouldBe` Left "Invalid header timestamp: 1296707201"+  it "accepts a timestamp exactly two hours in the future" $+    withChain net (connectHeight net 1296700000 (mineOn net net.genesisHeader 1296707200 0x207fffff))+      `shouldBe` Right 1+  it "rejects a timestamp equal to the median time past" $+    -- the median of genesis (1296688602) and block 1 (1296689202) is 1296689202+    withChain net (connectAll net [b1] >> connectHeight net myTime (mineOn net b1 (t0 + 600) 0x207fffff))+      `shouldBe` Left "Block timestamp too early: 1296689202"+  it "accepts a timestamp one second above the median time past" $+    withChain net (connectAll net [b1] >> connectHeight net myTime (mineOn net b1 (t0 + 601) 0x207fffff))+      `shouldBe` Right 2+  it "rejects a block that does not match a checkpoint" $+    let cpNet = (net :: Network) {checkpoints = [(2, headerHash b3)]}+     in withChain cpNet (connectAll cpNet [b1] >> connectHeight cpNet myTime b2)+          `shouldBe` Left "Rejected checkpoint: 2"+  it "accepts a block that matches a checkpoint" $+    let cpNet = (net :: Network) {checkpoints = [(2, headerHash b2)]}+     in withChain cpNet (connectAll cpNet [b1] >> connectHeight cpNet myTime b2)+          `shouldBe` Right 2+  it "rejects a fork below the last checkpoint reached" $+    let cpNet = (net :: Network) {checkpoints = [(2, headerHash b2)]}+        fork2 = mineOn net b1 (t0 + 1201) 0x207fffff+     in withChain cpNet (connectAll cpNet [b1, b2, b3] >> connectHeight cpNet myTime fork2)+          `shouldBe` Left "Rewriting pre-checkpoint chain: 2"+  it "accepts a fork above the last checkpoint reached" $+    let cpNet = (net :: Network) {checkpoints = [(2, headerHash b2)]}+        fork3 = mineOn net b2 (t0 + 1801) 0x207fffff+     in withChain cpNet (connectAll cpNet [b1, b2, b3] >> connectHeight cpNet myTime fork3)+          `shouldBe` Right 3+  it "rejects the BIP34 activation block with the wrong hash" $+    let bipNet = (net :: Network) {bip34Height = 2, bip34Hash = Just (headerHash b3)}+     in withChain bipNet (connectAll bipNet [b1] >> connectHeight bipNet myTime b2)+          `shouldSatisfy` either ("Rejected BIP-34 block: " `isPrefixOf`) (const False)+  it "accepts the BIP34 activation block with the right hash" $+    let bipNet = (net :: Network) {bip34Height = 2, bip34Hash = Just (headerHash b2)}+     in withChain bipNet (connectAll bipNet [b1] >> connectHeight bipNet myTime b2)+          `shouldBe` Right 2+  it "accepts any block at the BIP34 height when no hash is pinned" $+    let bipNet = (net :: Network) {bip34Height = 2, bip34Hash = Nothing}+     in withChain bipNet (connectAll bipNet [b1] >> connectHeight bipNet myTime b2)+          `shouldBe` Right 2+  it "rejects version 1 once BIP34 is active" $+    let bipNet = (net :: Network) {bip34Height = 0, bip34Hash = Nothing}+        hdr = mineBlock net 0 (headerWithVersion 1 (headerOn net.genesisHeader (t0 + 600) 0x207fffff))+     in withChain bipNet (connectHeight bipNet myTime hdr) `shouldBe` Left "Invalid block version: 1"+  it "rejects a negative version once BIP34 is active" $+    let bipNet = (net :: Network) {bip34Height = 0, bip34Hash = Nothing}+        hdr = mineBlock net 0 (headerWithVersion 0xffffffff (headerOn net.genesisHeader (t0 + 600) 0x207fffff))+     in withChain bipNet (connectHeight bipNet myTime hdr) `shouldBe` Left "Invalid block version: 4294967295"+  it "rejects version 2 once BIP66 is active" $+    let bipNet = (net :: Network) {bip34Height = 0, bip34Hash = Nothing, bip66Height = 1}+        hdr = mineBlock net 0 (headerWithVersion 2 (headerOn net.genesisHeader (t0 + 600) 0x207fffff))+     in withChain bipNet (connectHeight bipNet myTime hdr) `shouldBe` Left "Invalid block version: 2"+  it "accepts version 4 everywhere" $+    let bipNet = (net :: Network) {bip34Height = 0, bip34Hash = Nothing, bip66Height = 1, bip65Height = 1}+        hdr = mineBlock net 0 (headerWithVersion 4 (headerOn net.genesisHeader (t0 + 600) 0x207fffff))+     in withChain bipNet (connectHeight bipNet myTime hdr) `shouldBe` Right 1+  it "keeps the first block seen on equal work and height" $+    let c1 = mineOn net net.genesisHeader (t0 + 601) 0x207fffff+        tips = withChain net $ do+          connectAll net [b1]+          _ <- connectBlock net myTime c1+          s1 <- getBestBlockHeader+          connectAll net [c1]+          s2 <- getBestBlockHeader+          connectAll net [b1]+          s3 <- getBestBlockHeader+          return [s1, s2, s3]+     in map (headerHash . (.header)) tips `shouldBe` replicate 3 (headerHash b1)+  it "moves the best block to the fork with more work" $+    let c1 = mineOn net net.genesisHeader (t0 + 601) 0x207fffff+        c2 = mineOn net c1 (t0 + 1201) 0x207fffff+        tips = withChain net $ do+          connectAll net [b1]+          connectAll net [c1, c2]+          s1 <- getBestBlockHeader+          connectAll net [b2, b3]+          s2 <- getBestBlockHeader+          return [s1, s2]+     in map (headerHash . (.header)) tips `shouldBe` [headerHash c2, headerHash b3]+  describe "BIP94" $ do+    it "rejects the first block of a retarget period when it is too early" $ do+      -- Blocks 1 to 8 are ten minutes apart; block 9 jumps two hours ahead.+      let t = bip94Net.genesisHeader.timestamp+          hs =+            mineChain bip94Net bip94Net.genesisHeader $+              [(t + 600 * i, 0x207fffff) | i <- [1 .. 8]] ++ [(t + 12000, 0x207fffff)]+          par = last hs+          b10 = mineOn bip94Net par (t + 11400) 0x207fffff+          connect net' chain' h = withChain net' $ do+            connectBlocks net' myTime chain' >>= either error (const (return ()))+            connectHeight net' myTime h+      -- Block 10 starts a retarget period, so it may be at most 600 seconds+      -- earlier than block 9.+      connect bip94Net hs (mineOn bip94Net par (t + 11399) 0x207fffff)+        `shouldBe` Left "Block timestamp too early for retarget: 1296700001"+      connect bip94Net hs b10 `shouldBe` Right 10+      -- Block 11 does not start a period.+      connect bip94Net (hs ++ [b10]) (mineOn bip94Net b10 (t + 10799) 0x207fffff)+        `shouldBe` Right 11+      connect noBip94Net hs (mineOn noBip94Net par (t + 11399) 0x207fffff)+        `shouldBe` Right 10+    it "allows the first block of a period 600 seconds before its parent" $ do+      validTimeWarp bip94Net 10 10000 9400 `shouldBe` True+      validTimeWarp bip94Net 10 10000 9399 `shouldBe` False+      validTimeWarp bip94Net 11 10000 0 `shouldBe` True+      validTimeWarp noBip94Net 10 10000 0 `shouldBe` True++medianTimeSpec :: Spec+medianTimeSpec = describe "median time past" $ do+  it "takes the middle of an odd count" $+    medianTime [5, 1, 3] `shouldBe` 3+  it "takes the upper middle of an even count" $+    medianTime [4, 1, 3, 2] `shouldBe` 3+  it "takes the only element" $+    medianTime [7] `shouldBe` 7+  it "is the genesis timestamp for genesis" $+    withChain btcRegTest (getBestBlockHeader >>= mtp) `shouldBe` 1296688602+  it "covers the block and its ten ancestors" $+    -- blocks 10 to 20 are 600 seconds apart, the median is block 15+    withChain btcRegTest (storeChain (evenlySpaced btcRegTest 600 20 0x207fffff) >>= mtp . last)+      `shouldBe` 1296688602 + 600 * 15+  it "covers fewer blocks near genesis" $+    -- genesis and blocks 1 to 3: the upper middle of four is block 2+    withChain btcRegTest (storeChain (evenlySpaced btcRegTest 600 3 0x207fffff) >>= mtp . last)+      `shouldBe` 1296688602 + 600 * 2++bitcoinDifficultySpec :: Spec+bitcoinDifficultySpec = describe "bitcoin difficulty" $ do+  let t0 = retargetNet.genesisHeader.timestamp+      mainnetHeader = (btc.genesisHeader :: BlockHeader) {bits = 0x1703a30c, timestamp = 20000000}+  it "has a retarget interval of 2016 blocks" $ do+    diffInterval btc `shouldBe` 2016+    diffInterval retargetNet `shouldBe` 10+  it "keeps the target when the period took the target timespan" $+    -- 1209600 seconds is two weeks+    calcNextWork btc mainnetHeader (20000000 - 1209600) `shouldBe` 0x1703a30c+  it "raises the target at most four times" $+    -- 0x03a30c * 4 = 0x0e8c30+    calcNextWork btc mainnetHeader (20000000 - 10 * 1209600) `shouldBe` 0x170e8c30+  it "lowers the target at most to a quarter" $+    -- 0x03a30c / 4 = 0x00e8c3+    calcNextWork btc mainnetHeader (20000000 - 1) `shouldBe` 0x1700e8c3+  it "treats a period that ends before it starts as a short one" $+    calcNextWork btc mainnetHeader (20000000 + 1) `shouldBe` 0x1700e8c3+  it "does not raise the target above the limit" $+    calcNextWork btc (btc.genesisHeader :: BlockHeader) {timestamp = 20000000} (20000000 - 2 * 1209600) `shouldBe` 0x1d00ffff+  it "keeps the bits when the network does not retarget" $+    calcNextWork btcRegTest mainnetHeader (20000000 - 10 * 1209600) `shouldBe` 0x1703a30c+  it "requires the parent's bits inside a period" $+    requiredOn retargetNet (evenlySpaced retargetNet 600 5 0x1f400000) (t0 + 600 * 6) `shouldBe` 0x1f400000+  it "keeps the target when the first period took the target timespan" $+    -- block 9 is 6000 seconds after genesis+    requiredOn retargetNet (nineBlocks (t0 + 6000)) (t0 + 6600) `shouldBe` 0x1f400000+  it "lowers the target in proportion to a fast period" $+    -- 5400 of 6000 seconds: 0x400000 * 0.9 = 0x399999.9+    requiredOn retargetNet (nineBlocks (t0 + 5400)) (t0 + 6000) `shouldBe` 0x1f399999+  it "doubles the target for a period that took twice as long" $+    -- 2^246 * 2 = 2^247 = 0x80 * 2^240, whose sign bit moves the mantissa down one byte+    requiredOn retargetNet (nineBlocks (t0 + 12000)) (t0 + 12600) `shouldBe` 0x20008000+  it "raises the target at most four times over a period" $+    -- 30000 of 6000 seconds is clamped to 24000: 2^246 * 4 = 2^248+    requiredOn retargetNet (nineBlocks (t0 + 30000)) (t0 + 30600) `shouldBe` 0x20010000+  it "lowers the target at most to a quarter over a period" $+    -- 600 of 6000 seconds is clamped to 1500: 2^246 / 4 = 2^244+    requiredOn retargetNet (nineBlocks (t0 + 600)) (t0 + 1200) `shouldBe` 0x1f100000+  it "treats a period that ends before it starts as a short one" $+    requiredOn retargetNet (nineBlocks (t0 - 100)) (t0 + 600) `shouldBe` 0x1f100000+  it "does not raise the target above the limit over a period" $+    requiredOn retargetNet (map (\(t, _) -> (t, 0x207fffff)) (nineBlocks (t0 + 12000))) (t0 + 12600) `shouldBe` 0x207fffff+  describe "min-difficulty rule" $ do+    let minNet = (retargetNet :: Network) {minDiffBlocks = True}+        -- blocks 1 to 5 at the normal target, block 6 at the minimum after a long gap+        withMinDiffBlock = evenlySpaced minNet 600 5 0x1f400000 ++ [(t0 + 600 * 5 + 1300, 0x207fffff)]+    it "allows the minimum difficulty after twice the target spacing" $+      requiredOn minNet (evenlySpaced minNet 600 5 0x1f400000) (t0 + 600 * 5 + 1201) `shouldBe` 0x207fffff+    it "requires the normal difficulty at exactly twice the target spacing" $+      requiredOn minNet (evenlySpaced minNet 600 5 0x1f400000) (t0 + 600 * 5 + 1200) `shouldBe` 0x1f400000+    it "requires the last normal difficulty after a minimum-difficulty block" $+      requiredOn minNet withMinDiffBlock (t0 + 600 * 5 + 1300 + 600) `shouldBe` 0x1f400000+    it "does not apply on networks without the rule" $+      requiredOn retargetNet (evenlySpaced retargetNet 600 5 0x1f400000) (t0 + 600 * 5 + 1201) `shouldBe` 0x1f400000+    it "finds the last block without minimum difficulty" $+      let heights = withChain minNet $ do+            nodes <- storeChain withMinDiffBlock+            forM [nodes !! 5, nodes !! 4, nodes !! 0] $ fmap (.height) . lastNoMinDiff minNet+       in heights `shouldBe` [5, 5, 1]+    it "stops at the first block of a period" $+      -- block 10 starts a period, so it is never a special block+      withChain minNet (storeChain (evenlySpaced minNet 600 10 0x207fffff) >>= fmap (.height) . lastNoMinDiff minNet . last)+        `shouldBe` 10+    it "stops at genesis" $+      withChain minNet (getBestBlockHeader >>= fmap (.height) . lastNoMinDiff minNet) `shouldBe` 0+  describe "BIP94 retarget" $ do+    let bip94MinNet = (retargetNet :: Network) {bip94 = True, minDiffBlocks = True}+        noBip94MinNet = (retargetNet :: Network) {minDiffBlocks = True}+        -- blocks 1 to 9 take exactly one target timespan; blocks 10 to 19 take+        -- four, and block 19 is a minimum-difficulty block+        twoPeriods =+          [(t0 + 600 * i, 0x1f400000) | i <- [1 .. 8]]+            ++ [(t0 + 6000, 0x1f400000)]+            ++ [(t0 + 6600 + 600 * i, 0x1f400000) | i <- [0 .. 8]]+            ++ [(t0 + 30600, 0x207fffff)]+    it "bases the retarget on the first block of the period" $+      -- four times the target of block 10: 2^246 * 4 = 2^248+      requiredOn bip94MinNet twoPeriods (t0 + 31200) `shouldBe` 0x20010000+    it "bases the retarget on the last block without BIP94" $+      -- four times the minimum difficulty is clamped to the limit+      requiredOn noBip94MinNet twoPeriods (t0 + 31200) `shouldBe` 0x207fffff+  where+    nineBlocks lastTime = evenlySpaced retargetNet 600 8 0x1f400000 ++ [(lastTime, 0x1f400000)]+    requiredOn net specs time = withChain net (storeChain specs >> requiredAfter net time)++cashDifficultySpec :: Spec+cashDifficultySpec = describe "bitcoin cash difficulty" $ do+  let t0 = cashNet.genesisHeader.timestamp+  describe "regtest" $+    it "never retargets" $+      -- a regtest chain at a harder target with an hour-long gap+      withChain bchRegTest (storeChain (evenlySpaced bchRegTest 600 5 0x1f400000) >> requiredAfter bchRegTest (t0 + 3000 + 3600))+        `shouldBe` 0x1f400000+  describe "emergency difficulty adjustment" $ do+    it "keeps the target when the last six blocks took less than twelve hours" $+      requiredOn edaNet (evenlySpaced edaNet 600 20 0x1f400000) (t0 + 600 * 21) `shouldBe` 0x1f400000+    it "keeps the target just below twelve hours" $+      -- 6 * 7199 = 43194 seconds between the two median times+      requiredOn edaNet (evenlySpaced edaNet 7199 20 0x1f400000) (t0 + 7199 * 21) `shouldBe` 0x1f400000+    it "raises the target by a quarter when the last six blocks took twelve hours" $+      -- 0x400000 + 0x100000 = 0x500000+      requiredOn edaNet (evenlySpaced edaNet 7200 20 0x1f400000) (t0 + 7200 * 21) `shouldBe` 0x1f500000+    it "does not raise the target above the limit" $+      requiredOn edaNet (evenlySpaced edaNet 7200 20 0x207fffff) (t0 + 7200 * 21) `shouldBe` 0x207fffff+    it "retargets normally at the end of a period" $+      -- 5400 of 6000 seconds: 0x400000 * 0.9 = 0x399999.9+      let shortNet = (edaNet :: Network) {targetTimespan = 10 * 600}+       in requiredOn shortNet (evenlySpaced shortNet 600 9 0x1f400000) (t0 + 6000) `shouldBe` 0x1f399999+    it "ignores the gap to the parent on mainnet" $+      requiredOn edaNet (evenlySpaced edaNet 600 20 0x1f400000) (t0 + 600 * 20 + 1201) `shouldBe` 0x1f400000+    describe "on test networks" $ do+      let testNet = (edaNet :: Network) {minDiffBlocks = True}+          withMinDiffBlock = evenlySpaced testNet 600 5 0x1f400000 ++ [(t0 + 600 * 5 + 1300, 0x207fffff)]+      it "allows the minimum difficulty after twice the target spacing" $+        requiredOn testNet (evenlySpaced testNet 600 20 0x1f400000) (t0 + 600 * 20 + 1201) `shouldBe` 0x207fffff+      it "requires the last normal difficulty after a minimum-difficulty block" $+        requiredOn testNet withMinDiffBlock (t0 + 600 * 5 + 1300 + 600) `shouldBe` 0x1f400000+      it "does not use the twelve-hour rule" $+        -- the last six blocks took twelve hours, the new block follows its parent closely+        requiredOn testNet (evenlySpaced testNet 7200 20 0x1f400000) (t0 + 7200 * 20 + 600) `shouldBe` 0x1f400000+  describe "difficulty adjustment algorithm" $ do+    -- The window runs from the suitable block before block 2056 to the one+    -- before block 2200: 144 blocks of work at 0x1f7fffff, which is 512 each.+    let window spacing = evenlySpaced daaNet spacing 2200 0x1f7fffff+    it "keeps the target when blocks come every ten minutes" $+      -- (2^256 - 512) / 512 = 2^247 - 1+      requiredOn daaNet (window 600) (t0 + 600 * 2201) `shouldBe` 0x1f7fffff+    it "halves the target when blocks come every five minutes" $+      -- (2^256 - 1024) / 1024 = 2^246 - 1+      requiredOn daaNet (window 300) (t0 + 300 * 2201) `shouldBe` 0x1f3fffff+    it "doubles the target when blocks come every twenty minutes" $+      -- (2^256 - 256) / 256 = 2^248 - 1, whose top byte has the sign bit set+      requiredOn daaNet (window 1200) (t0 + 1200 * 2201) `shouldBe` 0x2000ffff+    it "halves the target at most over a window" $+      -- 144 * 150 = 21600 seconds is clamped to 72 * 600 = 43200+      requiredOn daaNet (window 150) (t0 + 150 * 2201) `shouldBe` 0x1f3fffff+    it "doubles the target at most over a window" $+      -- 144 * 2400 = 345600 seconds is clamped to 288 * 600 = 172800+      requiredOn daaNet (window 2400) (t0 + 2400 * 2201) `shouldBe` 0x2000ffff+    it "ignores the gap to the parent on mainnet" $+      requiredOn daaNet (window 600) (t0 + 600 * 2200 + 1201) `shouldBe` 0x1f7fffff+    it "allows the minimum difficulty after twice the target spacing on test networks" $+      let testNet = (daaNet :: Network) {minDiffBlocks = True}+       in requiredOn testNet (window 600) (t0 + 600 * 2200 + 1201) `shouldBe` 0x207fffff+    it "computes the target from the work and time of a window" $ do+      -- 144 * 2^200 work over 144 * 600 seconds is 2^200 work per 600 seconds:+      -- (2^256 - 2^200) / 2^200 = 2^56 - 1+      computeTarget bch (node 0 1000000) (node (144 * 2 ^ (200 :: Int)) (1000000 + 86400)) `shouldBe` 72057594037927935+      -- one second is clamped to 43200: 2^201 work per 600 seconds gives 2^55 - 1+      computeTarget bch (node 0 1000000) (node (144 * 2 ^ (200 :: Int)) 1000001) `shouldBe` 36028797018963967+      -- a window that ends before it starts is clamped the same way+      computeTarget bch (node 0 1000000) (node (144 * 2 ^ (200 :: Int)) 999999) `shouldBe` 36028797018963967+      -- a million seconds is clamped to 172800: 2^199 work per 600 seconds gives 2^57 - 1+      computeTarget bch (node 0 1000000) (node (144 * 2 ^ (200 :: Int)) 2000000) `shouldBe` 144115188075855871+    it "picks the block with the median timestamp as suitable" $ do+      suitableHeight [600, 1200, 1800] `shouldBe` 2+      suitableHeight [600, 9000, 1200] `shouldBe` 3+      suitableHeight [9000, 600, 1200] `shouldBe` 3+    it "picks the same block as Bitcoin Cash Node on equal timestamps" $ do+      suitableHeight [600, 1200, 1200] `shouldBe` 2+      suitableHeight [1200, 600, 1200] `shouldBe` 1+      suitableHeight [1200, 1200, 600] `shouldBe` 2+      suitableHeight [600, 600, 600] `shouldBe` 2+  where+    requiredOn net specs time = withChain net (storeChain specs >> requiredAfter net time)+    node w t = (genesisNode bch :: BlockNode) {work = w, header = (bch.genesisHeader :: BlockHeader) {timestamp = t}}+    suitableHeight offsets = withChain cashNet $ do+      nodes <- storeChain [(cashNet.genesisHeader.timestamp + o, 0x207fffff) | o <- offsets]+      (.height) <$> getSuitableBlock (last nodes)++asertSpec :: Spec+asertSpec = describe "asert" $ do+  let t0 = asertNet.genesisHeader.timestamp+      halfLife = 2 * 24 * 60 * 60+      regtestLimit = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+      mainnetLimit = 0x00000000ffff0000000000000000000000000000000000000000000000000000+      -- thirty blocks ten minutes apart; block 15 is the first whose median+      -- time past reaches the activation time+      steady = evenlySpaced asertNet 600 30 0x1f400000+      requiredOn net specs time = withChain net (storeChain specs >> requiredAfter net time)+  it "finds the first block whose median time past reaches the activation time" $+    anchorHeights `shouldBe` [Nothing, Just 15, Just 15, Just 15]+  it "has no anchor without an activation time" $+    withChain cashNet (storeChain steady >>= asertAnchor cashNet . last) `shouldBe` Nothing+  it "keeps the target when the chain is on schedule" $+    requiredOn asertNet (take 15 steady) (t0 + 600 * 16) `shouldBe` 0x1f400000+  it "keeps the target later on the schedule" $+    requiredOn asertNet steady (t0 + 600 * 31) `shouldBe` 0x1f400000+  it "ignores the timestamp of the block itself" $ do+    -- The chain up to the parent is on schedule. A block that is a half-life+    -- late or early still carries the unchanged bits; only its children see+    -- the delay.+    requiredOn asertNet steady (t0 + 600 * 31 + halfLife) `shouldBe` 0x1f400000+    requiredOn asertNet steady (t0 + 600 * 31 - halfLife) `shouldBe` 0x1f400000+  it "connects a block that is not exactly ten minutes after its parent" $+    -- Block 31 comes 428 seconds after block 30, as mainnet block 975899+    -- came after its parent, with the unchanged bits of the chain.+    withChain asertNet (storeChain steady >>= connectLate) `shouldBe` Right 31+  it "doubles the target after a parent that is a half-life late" $+    -- Block 16 is 172800 seconds late. From the anchor's parent, block 14,+    -- to block 16 the chain took 1200 + 172800 seconds for two blocks, one+    -- half-life behind schedule. 2^246 * 2 = 2^247 = 0x80 * 2^240, whose sign+    -- bit moves the mantissa down one byte.+    let late = (t0 + 600 * 16 + halfLife, 0x1f400000)+     in requiredOn asertNet (take 15 steady ++ [late]) (t0 + 600 * 17 + halfLife) `shouldBe` 0x20008000+  it "halves the target after a chain that is a half-life ahead" $+    -- 300 blocks 24 seconds apart follow the anchor, block 15. From the+    -- anchor's parent to block 315 the chain took 600 + 300 * 24 = 7800+    -- seconds for 301 blocks instead of 301 * 600 = 180600, which is one+    -- half-life (172800 seconds) ahead of schedule.+    let fast = [(t0 + 600 * 15 + 24 * i, 0x1f400000) | i <- [1 .. 300]]+     in requiredOn asertNet (take 15 steady ++ fast) (t0 + 600 * 15 + 24 * 301) `shouldBe` 0x1f200000+  it "allows the minimum difficulty after twice the target spacing on test networks" $+    let testNet = (asertNet :: Network) {minDiffBlocks = True}+     in requiredOn testNet steady (t0 + 600 * 31 + halfLife) `shouldBe` 0x207fffff+  it "uses the previous algorithm before activation" $+    -- block 14 has not reached the activation time, so block 15 keeps its parent's bits+    requiredOn asertNet (take 14 steady) (t0 + 600 * 15 + halfLife) `shouldBe` 0x1f400000+  it "computes the formula on the anchor" $ do+    -- one block and 1200 seconds after the anchor's parent is on schedule;+    -- the half-life is 172800 seconds+    computeAsertBits regtestLimit 172800 0x1f400000 1200 1 `shouldBe` 0x1f400000+    computeAsertBits regtestLimit 172800 0x1f400000 (1200 + 172800) 1 `shouldBe` 0x20008000+    computeAsertBits regtestLimit 172800 0x1f400000 (1200 - 172800) 1 `shouldBe` 0x1f200000+  it "does not raise the target above the limit" $+    computeAsertBits (2 ^ (246 :: Int)) 172800 0x1f400000 (1200 + 172800) 1 `shouldBe` 0x1f400000+  it "does not lower the target below one" $+    -- a target of one halved ten times+    computeAsertBits regtestLimit 172800 0x01010000 (1200 - 10 * 172800) 1 `shouldBe` 0x01010000+  it "computes the bits of Bitcoin Cash mainnet block 975899" $+    -- The anchor is block 661647 with bits 0x1804dafe and its parent is dated+    -- 1605447844. Block 975898, the parent of 975899, is dated 1793800487:+    -- 1793800487 - 1605447844 = 188352643 seconds and 975898 - 661647 =+    -- 314251 blocks after the anchor.+    computeAsertBits mainnetLimit 172800 0x1804dafe 188352643 314251 `shouldBe` 0x1802308e+  describe "aserti3-2d test vectors" $+    mapM_+      ( \x ->+          asertTests $+            "test_vectors_aserti3-2d_run" ++ printf "%02d" x ++ ".txt"+      )+      [(1 :: Int) .. 12]+  where+    -- Mine block 31 with the chain's bits 428 seconds after block 30 and+    -- connect it with every rule checked.+    connectLate :: [BlockNode] -> State HeaderMemory (Either String BlockHeight)+    connectLate nodes =+      let parent = (last nodes).header+          t0 = asertNet.genesisHeader.timestamp+       in connectHeight asertNet myTime (mineOn asertNet parent (t0 + 600 * 30 + 428) 0x1f400000)+    anchorHeights = withChain asertNet $ do+      nodes <- storeChain (evenlySpaced asertNet 600 30 0x1f400000)+      forM [nodes !! 13, nodes !! 14, nodes !! 19, last nodes] $ \n ->+        fmap (.height) <$> asertAnchor asertNet n++checkpointSpec :: Spec+checkpointSpec = describe "checkpoints and versions" $ do+  it "accepts the checkpoint hash at its height" $+    validCP btc 11111 "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d" `shouldBe` True+  it "rejects another hash at a checkpoint height" $+    validCP btc 11111 genesisHash `shouldBe` False+  it "accepts any hash where there is no checkpoint" $+    validCP btc 11112 genesisHash `shouldBe` True+  it "requires new blocks above the last checkpoint reached" $ do+    -- the checkpoints at or below height 50000 are 11111 and 33333+    afterLastCP btc 50000 33334 `shouldBe` True+    afterLastCP btc 50000 33333 `shouldBe` False+    afterLastCP btc 50000 11112 `shouldBe` False+    afterLastCP btc 10000 1 `shouldBe` True+    afterLastCP btcRegTest 10000 1 `shouldBe` True+  it "checks the BIP34 activation block" $ do+    bip34 btc 227931 "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8" `shouldBe` True+    bip34 btc 227931 genesisHash `shouldBe` False+    bip34 btc 227932 genesisHash `shouldBe` True+    -- regtest pins no hash, so any block passes at its BIP34 height+    bip34 btcRegTest 1 genesisHash `shouldBe` True+    bip34 btcRegTest 5 genesisHash `shouldBe` True+    bip34 bchRegTest 100000000 genesisHash `shouldBe` True+  it "requires block versions by height" $ do+    validVersion btc 227930 1 `shouldBe` True+    validVersion btc 227931 1 `shouldBe` False+    validVersion btc 363724 2 `shouldBe` True+    validVersion btc 363725 2 `shouldBe` False+    validVersion btc 388380 3 `shouldBe` True+    validVersion btc 388381 3 `shouldBe` False+    validVersion btc 388381 4 `shouldBe` True+    validVersion btc 800000 0x20000000 `shouldBe` True+  it "treats a version with the top bit set as negative" $ do+    validVersion btc 800000 0xffffffff `shouldBe` False+    validVersion btc 800000 0x80000004 `shouldBe` False+    validVersion btc 0 0xffffffff `shouldBe` True++merkleSpec :: Spec+merkleSpec = 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+  it "computes tree heights" $+    map calcTreeHeight [0, 1, 2, 3, 4, 5, 8, 9, 1000] `shouldBe` [0, 0, 1, 2, 2, 3, 3, 4, 10]+  it "computes tree widths" $ do+    map (calcTreeWidth 5) [0, 1, 2, 3] `shouldBe` [5, 3, 2, 1]+    map (calcTreeWidth 4) [0, 1, 2] `shouldBe` [4, 2, 1]+  it "refuses to build a root without transactions" $+    buildMerkleRoot [] `shouldBe` Left "buildMerkleRoot: a block needs at least one transaction"+  it "refuses a mutated tree with duplicate siblings" $ do+    buildMerkleRoot [txA, txA] `shouldBe` Left "buildMerkleRoot: duplicate transaction hashes (mutated tree)"+    buildMerkleRoot [txA, txB, txC, txC] `shouldBe` Left "buildMerkleRoot: duplicate transaction hashes (mutated tree)"+  it "pairs the last odd transaction with itself without counting it as a duplicate" $+    buildMerkleRoot [txA, txB, txC] `shouldSatisfy` either (const False) (const True)+  it "uses the transaction hash as the root of a single transaction" $+    buildMerkleRoot [txA] `shouldBe` Right txA.get+  it "unpacks flag bits from the least significant bit" $ do+    decodeMerkleFlags [0xaa] `shouldBe` [False, True, False, True, False, True, False, True]+    decodeMerkleFlags [0x01, 0x80] `shouldBe` [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True]+    decodeMerkleFlags [] `shouldBe` []+  it "packs flag bits and pads the last byte" $ do+    encodeMerkleFlags [True, True, False, True, True] `shouldBe` [0x1b]+    encodeMerkleFlags (replicate 8 False ++ [True]) `shouldBe` [0x00, 0x01]+    encodeMerkleFlags [] `shouldBe` []+  it "splits a list in chunks" $+    splitIn 3 [1 .. 7 :: Int] `shouldBe` [[1, 2, 3], [4, 5, 6], [7]]+  it "gives the whole list as one chunk for a chunk length below one" $ do+    splitIn 0 [1, 2, 3 :: Int] `shouldBe` [[1, 2, 3]]+    splitIn (-1) [1, 2, 3 :: Int] `shouldBe` [[1, 2, 3]]+    splitIn 0 ([] :: [Int]) `shouldBe` []+  it "packs bools in a byte" $ do+    boolsToWord8 [] `shouldBe` 0+    boolsToWord8 [True] `shouldBe` 1+    boolsToWord8 [False, False, False, False, False, False, False, True] `shouldBe` 128+  describe "block 100000" $ do+    let txids =+          map+            displayTxHash+            [ "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",+              "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4",+              "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4",+              "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d"+            ]+        root = (displayTxHash "f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766").get+        rightNode = (displayTxHash "8e30899078ca1813be036a073bbf80b86cdddde1c96e9e9c99e9e3782df4ae49").get+    it "parses the block" $ do+      blk <- readBlock "data/block_100000.hex"+      headerHash blk.header `shouldBe` "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"+      map txHash blk.txs `shouldBe` txids+      blk.header.merkle `shouldBe` root+    it "builds its merkle root" $+      buildMerkleRoot txids `shouldBe` Right root+    it "hashes a pair of nodes" $+      hash2 (txids !! 2).get (txids !! 3).get `shouldBe` rightNode+    it "computes single nodes" $ do+      calcHash 0 2 txids `shouldBe` Just (txids !! 2).get+      calcHash 1 1 txids `shouldBe` Just rightNode+      calcHash 2 0 txids `shouldBe` Just root+    it "has no node outside the tree" $ do+      -- four transactions: levels of 4, 2 and 1 nodes+      calcHash 0 4 txids `shouldBe` Nothing+      calcHash 3 0 txids `shouldBe` Nothing+      calcHash (-1) 0 txids `shouldBe` Nothing+      calcHash 0 (-1) txids `shouldBe` Nothing+      calcHash 0 0 [] `shouldBe` Nothing+    it "gives the position of each match, as Bitcoin Core does" $ do+      extractIndexedMatches btc [True, True, False, True, False, False, False, False] [(txids !! 0).get, (txids !! 1).get, rightNode] 4+        `shouldBe` Right (root, [(1, txids !! 1)])+      -- every transaction matches: positions 0 to 3+      fmap (map fst . snd) (extractIndexedMatches btc (replicate 7 True ++ [False]) (map (.get) txids) 4)+        `shouldBe` Right [0, 1, 2, 3]+    it "builds a partial tree for the second transaction" $+      -- root and left node have a match below (1), the first transaction+      -- does not (0), the second does (1), the right node does not (0)+      buildPartialMerkle (zip txids [False, True, False, False])+        `shouldBe` ([True, True, False, True, False], [(txids !! 0).get, (txids !! 1).get, rightNode])+    it "extracts the second transaction from its partial tree" $+      extractMatches btc [True, True, False, True, False, False, False, False] [(txids !! 0).get, (txids !! 1).get, rightNode] 4+        `shouldBe` Right (root, [txids !! 1])+    it "builds a partial tree with no matches" $+      buildPartialMerkle (zip txids (repeat False)) `shouldBe` ([False], [root])+    it "builds a partial tree with every match" $+      buildPartialMerkle (zip txids (repeat True))+        `shouldBe` ([True, True, True, True, True, True, True], map (.get) txids)+  describe "merkle block" $ do+    -- Bitcoin Core's merkle_block_3_and_serialize test: a block with one+    -- transaction, which matches.+    let bytes = fromJust $ decodeHex "0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101"+        merkleBlock = either error id $ runGetS deserialize bytes+    it "parses Bitcoin Core's merkle block" $ do+      headerHash merkleBlock.header `shouldBe` "000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45"+      merkleBlock.txn `shouldBe` 1+      length merkleBlock.hashes `shouldBe` 1+      merkleBlock.flags `shouldBe` [True, False, False, False, False, False, False, False]+    it "serializes back to the same bytes" $+      runPutS (serialize merkleBlock) `shouldBe` bytes+    it "extracts the matching transaction" $+      merkleBlockTxs btc merkleBlock `shouldBe` Right [displayTxHash "63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"]+    it "accepts the merkle root" $+      testMerkleRoot btc merkleBlock `shouldBe` True+    it "rejects a changed merkle root" $+      merkleBlockTxs btc (MerkleBlock (merkleBlock.header :: BlockHeader) {merkle = txA.get} merkleBlock.txn merkleBlock.hashes merkleBlock.flags)+        `shouldBe` Left "merkleBlockTxs: Merkle root incorrect"+    it "rejects a changed transaction count" $+      testMerkleRoot btc (merkleBlock :: MerkleBlock) {txn = 2} `shouldBe` False+  describe "malformed partial trees" $ do+    it "rejects a block without transactions" $+      extractMatches btc [True] [txA.get] 0 `shouldBe` Left "extractMatches: number of transactions can not be 0"+    it "rejects too many transactions" $+      -- 1000000 bytes / 60 bytes per transaction is 16666+      extractMatches btc [True] [txA.get] 16667 `shouldBe` Left "extractMatches: number of transactions excessively high"+    it "rejects more hashes than transactions" $+      extractMatches btc [True] [txA.get, txB.get] 1 `shouldBe` Left "extractMatches: More hashes provided than the number of transactions"+    it "rejects fewer flags than hashes" $+      extractMatches btc [True] [txA.get, txB.get] 2 `shouldBe` Left "extractMatches: At least one bit per node and one bit per hash"+    it "rejects a tree that runs out of flags" $+      extractMatches btc [True, True] [txA.get, txB.get] 2 `shouldBe` Left "extractMatches: ran out of flag bits"+    it "rejects a tree that runs out of hashes" $+      extractMatches btc [True, True, True] [txA.get] 2 `shouldBe` Left "extractMatches: ran out of hashes"+    it "rejects identical left and right branches" $+      -- a mutated tree: the four leaves [a, b, c, c] have the root of [a, b, c]+      extractMatches btc [True, True, True, True, True, True, True, False] (map (.get) [txA, txB, txC, txC]) 4+        `shouldBe` Left "extractMatches: identical left and right branches (mutated tree)"+    it "rejects unused hashes" $+      extractMatches btc (False : replicate 7 False) [txA.get, txB.get] 2 `shouldBe` Left "extractMatches: All hashes were not consumed"+    it "rejects unused flags beyond the last byte" $+      extractMatches btc (True : replicate 8 False) [txA.get] 1 `shouldBe` Left "extractMatches: All bits were not consumed"+    it "accepts unused flags within the last byte" $+      extractMatches btc (True : replicate 7 False) [txA.get] 1 `shouldBe` Right (txA.get, [txA])++-- 0 → → 2015 → → → → → → → 4031+--       ↓+--       → → 2035 → → → → → → 4035*+--           ↓+--           → → 2185+splitChain :: Network -> State HeaderMemory ()+splitChain net = do+  start <- go 1 net.genesisHeader 2015+  e 2015 start+  tail1 <- go 2 start.header 2016+  e 4031 tail1+  tail2 <- go 3 start.header 20+  e 2035 tail2+  tail3 <- go 4 tail2.header 2000+  e 4035 tail3+  tail4 <- go 5 tail2.header 150+  e 2185 tail4+  sp1 <- splitPoint tail1 tail3+  unless (sp1 == start) $+    error $+      "Split point wrong between blocks 4031 and 4035: "+        ++ show sp1.height+  sp2 <- splitPoint tail4 tail3+  unless (sp2 == tail2) $+    error $+      "Split point wrong between blocks 2185 and 4035: "+        ++ show sp2.height+  where+    e n bn@BlockNode {} =+      unless (bn.height == n) $+        error $+          "Node height "+            ++ show bn.height+            ++ " of first chunk should be "+            ++ show n+    -- mine and connect n blocks on top of the start header, returning the tip+    go seed start n = do+      let bhs = appendBlocks net seed start n+      bnE <- connectBlocks net myTime bhs+      case bnE of+        Right (tip : _) -> return tip+        Right [] -> error "No blocks were connected"+        Left ex -> error ex++{- Merkle Trees -}++testTreeWidth :: Int -> Property+testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1++testBaseWidth :: Int -> Property+testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i++buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool+buildExtractTree net txs =+  Right r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)+  where+    (f, h) = buildPartialMerkle txs+    (r, m) =+      either error id $+        extractMatches net f h (length txs)++testCompact :: Assertion+testCompact = do+  assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)+  assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)+  assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)+  assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)+  assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))+  assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)++testCompactBitcoinCore :: Assertion+testCompactBitcoinCore = do+  assertEqual "zero" (0, False) (decodeCompact 0x00000000)+  assertEqual+    "zero (encode · decode)"+    0x00000000+    (encodeCompact . fst $ decodeCompact 0x00000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)+  assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)+  assertEqual+    "vector 1 (encode · decode)"+    0x01120000+    (encodeCompact . fst $ decodeCompact 0x01123456)+  assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)+  assertEqual+    "vector 2 (negative) (decode)"+    (-0x7e, False)+    (decodeCompact 0x01fedcba)+  assertEqual+    "vector 2 (negative) (encode · decode)"+    0x01fe0000+    (encodeCompact . fst $ decodeCompact 0x01fedcba)+  assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)+  assertEqual+    "vector 3 (encode · decode)"+    0x02123400+    (encodeCompact . fst $ decodeCompact 0x02123456)+  assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)+  assertEqual+    "vector 4 (encode · decode)"+    0x03123456+    (encodeCompact . fst $ decodeCompact 0x03123456)+  assertEqual+    "vector 5 (decode)"+    (0x12345600, False)+    (decodeCompact 0x04123456)+  assertEqual+    "vector 5 (encode · decode)"+    0x04123456+    (encodeCompact . fst $ decodeCompact 0x04123456)+  assertEqual+    "vector 6 (decode)"+    (-0x12345600, False)+    (decodeCompact 0x04923456)+  assertEqual+    "vector 6 (encode · decode)"+    0x04923456+    (encodeCompact . fst $ decodeCompact 0x04923456)+  assertEqual+    "vector 7 (decode)"+    (0x92340000, False)+    (decodeCompact 0x05009234)+  assertEqual+    "vector 7 (encode · decode)"+    0x05009234+    (encodeCompact . fst $ decodeCompact 0x05009234)+  assertEqual+    "vector 8 (decode)"+    ( 0x1234560000000000000000000000000000000000000000000000000000000000,+      False+    )+    (decodeCompact 0x20123456)+  assertEqual+    "vector 8 (encode · decode)"+    0x20123456+    (encodeCompact . fst $ decodeCompact 0x20123456)+  assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)+  assertBool+    "vector 9 (decode) (positive)"+    ((> 0) . fst $ decodeCompact 0xff123456)++runMerkleVector :: (Text, [Text]) -> Assertion+runMerkleVector (r, hs) =+  assertEqual "merkle vector" (Right (f r).get) (buildMerkleRoot (map f hs))+  where+    f = fromJust . hexToTxHash++merkleVectors :: [(Text, [Text])]+merkleVectors =+  -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1+  [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f",+      [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52",+        "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9",+        "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41",+        "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f",+        "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6",+        "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc",+        "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3",+        "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66",+        "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f",+        "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4",+        "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"+      ]+    ),+    -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4++    ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c",+      [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e",+        "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51",+        "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a",+        "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e",+        "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471",+        "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3",+        "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030",+        "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13",+        "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2",+        "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"+      ]+    ),+    -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048++    ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",+      ["0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"]+    ),+    -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c++    ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c",+      [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028",+        "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"+      ]+    ),+    -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e++    ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c",+      [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549",+        "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb",+        "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"+      ]+    ),+    -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021++    ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2",+      [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81",+        "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92",+        "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27",+        "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a",+        "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924",+        "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2",+        "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95",+        "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4",+        "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d",+        "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f",+        "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909",+        "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d",+        "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53",+        "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b",+        "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174",+        "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494",+        "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4",+        "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa",+        "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d",+        "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6",+        "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9",+        "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55",+        "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776",+        "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d",+        "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666",+        "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f",+        "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9",+        "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4",+        "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"+      ]+    )+  ]++testSubsidy :: Network -> Assertion+testSubsidy net = go (2 * 50 * 100 * 1000 * 1000) 0+  where+    go previous_subsidy halvings = do+      let height = halvings * net.halvingInterval+          subsidy = computeSubsidy net height+      if halvings >= 64+        then subsidy `shouldBe` 0+        else do+          subsidy `shouldBe` (previous_subsidy `div` 2)+          go subsidy (halvings + 1)++data AsertBlock = AsertBlock Int Integer Integer Word32++data AsertVector = AsertVector String Integer Integer Word32 [AsertBlock]++readAsertVector :: FilePath -> IO AsertVector+readAsertVector p = do+  (d : ah : apt : ab : _ : _ : _ : _ : xs) <- lines <$> readFile ("data/" ++ p)+  let desc = drop 16 d+      anchor_height = read (words ah !! 3)+      anchor_parent_time = read (words apt !! 4)+      anchor_nbits = read (words ab !! 3)+      blocks = map (f . words) (init xs)+  return $+    AsertVector+      desc+      anchor_height+      anchor_parent_time+      anchor_nbits+      blocks+  where+    f [i, h, t, g] = AsertBlock (read i) (read h) (read t) (read g)+    f _ = undefined++asertTests :: FilePath -> SpecWith ()+asertTests file = do+  v@(AsertVector d _ _ _ _) <- runIO $ readAsertVector file+  it d $ testAsertBits v++-- | The vectors use the mainnet proof-of-work limit and a two-day half-life.+testAsertBits :: AsertVector -> Assertion+testAsertBits (AsertVector _ anchor_height anchor_parent_time anchor_bits blocks) =+  forM_ blocks $ \(AsertBlock _ h t g) ->+    computeAsertBits+      0x00000000ffff0000000000000000000000000000000000000000000000000000+      (2 * 24 * 60 * 60)+      anchor_bits+      (t - anchor_parent_time)+      (h - anchor_height)+      `shouldBe` g++-- | The result must be a failure whose message holds the given words.+leftWith :: Either String a -> String -> Expectation+leftWith result msg =+  case result of+    Left e | msg `isInfixOf` e -> return ()+    Left e -> expectationFailure ("expected a failure mentioning " ++ show msg ++ ", got " ++ show e)+    Right _ -> expectationFailure ("expected a failure mentioning " ++ show msg ++ ", got a value")++versionRuleSpec :: Spec+versionRuleSpec = describe "block version rule, three independent conditions" $ do+  -- ContextualCheckBlockHeader in Bitcoin Core and Bitcoin Cash Node rejects+  -- when (version < 2 and height >= BIP34) or (version < 3 and height >=+  -- BIP66) or (version < 4 and height >= BIP65).+  it "refuses version 1 from the BIP66 height of bitcoin cash regtest, 1251" $ do+    -- BIP34 is at 100000000 there, far above BIP66 at 1251 and BIP65 at 1351+    validVersion bchRegTest 1250 1 `shouldBe` True+    validVersion bchRegTest 1251 1 `shouldBe` False+    validVersion bchRegTest 1251 2 `shouldBe` False+    validVersion bchRegTest 1251 3 `shouldBe` True+  it "refuses version 3 from the BIP65 height of bitcoin cash regtest, 1351" $ do+    validVersion bchRegTest 1350 3 `shouldBe` True+    validVersion bchRegTest 1351 3 `shouldBe` False+    validVersion bchRegTest 1351 4 `shouldBe` True+  it "refuses a negative version from the BIP66 height of bitcoin cash regtest" $+    validVersion bchRegTest 1251 0xffffffff `shouldBe` False+  it "refuses versions 2 and 3 at height 3 of bitcoin cash testnet4, where BIP65 comes before BIP66" $ do+    -- BIP34 at 2, BIP65 at 3, BIP66 at 4+    validVersion bchTest4 1 1 `shouldBe` True+    validVersion bchTest4 2 1 `shouldBe` False+    validVersion bchTest4 2 2 `shouldBe` True+    validVersion bchTest4 3 2 `shouldBe` False+    validVersion bchTest4 3 3 `shouldBe` False+    validVersion bchTest4 3 4 `shouldBe` True+  it "refuses a version 1 header at height 1252 of bitcoin cash regtest through connectBlock" $+    let net = bchRegTest+        result = withChain net $ do+          chain net net.genesisHeader 1251+          best <- getBestBlockHeader+          let hdr = mineBlock net 0 (headerWithVersion 1 (headerOn best.header (best.header.timestamp + 600) 0x207fffff))+          connectHeight net (best.header.timestamp + 1200) hdr+     in result `shouldBe` Left "Invalid block version: 1"++knownHeaderSpec :: Spec+knownHeaderSpec = describe "headers that are already in the store" $ do+  -- AcceptBlockHeader in Bitcoin Core returns the block index it already has+  -- for a known header, before any other check.+  let base = btcRegTest+      t0 = base.genesisHeader.timestamp+      b1 = mineOn base base.genesisHeader (t0 + 600) 0x207fffff+      b2 = mineOn base b1 (t0 + 1200) 0x207fffff+      b3 = mineOn base b2 (t0 + 1800) 0x207fffff+      -- regtest with a checkpoint on block 2+      net = (base :: Network) {checkpoints = [(2, headerHash b2)]}+  it "returns the stored node for a known header at or below a checkpoint that was reached" $+    withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime b1) `shouldBe` Right 1+  it "returns the stored nodes for a known batch, and keeps the best block" $+    let (heights, best) = withChain net $ do+          connectAll net [b1, b2, b3]+          r <- connectBlocks net myTime [b1, b2]+          bb <- getBestBlockHeader+          return (fmap (map (.height)) r, bb.height)+     in (heights, best) `shouldBe` (Right [2, 1], 3)+  it "still refuses another header at the height of a checkpoint that was reached" $+    let other = mineBlock base 1 (headerOn b1 (t0 + 1260) 0x207fffff)+     in withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime other)+          `shouldBe` Left "Rewriting pre-checkpoint chain: 2"++merkleLimitSpec :: Spec+merkleLimitSpec = describe "merkle block size limits" $ do+  -- A proof visits at most one tree node per flag bit. Every node it visits+  -- lies above a stored hash, so it uses at most (height + 1) bits per hash,+  -- and no more bits than the tree has nodes. CPartialMerkleTree in Bitcoin+  -- Core refuses more hashes than transactions and unused flag bytes.+  let header = runPutS (serialize btc.genesisHeader)+      hashA = runPutS (serialize txA.get)+      one = B.pack [1, 0, 0, 0]+      parseMerkle bytes = runGetS deserialize bytes :: Either String MerkleBlock+  it "refuses a merkle block of one transaction with 1000 flag bytes" $+    -- 0xfde803 is the count 1000+    parseMerkle (header <> one <> B.pack [1] <> hashA <> B.pack [0xfd, 0xe8, 0x03] <> B.replicate 1000 1)+      `leftWith` "flag"+  it "refuses a merkle block of one transaction with two flag bytes" $+    -- one transaction is one node, one flag bit, one byte+    parseMerkle (header <> one <> B.pack [1] <> hashA <> B.pack [2, 1, 0]) `leftWith` "flag"+  it "reads a merkle block of one transaction with one flag byte" $+    fmap (\m -> (m.txn, length m.hashes, length m.flags)) (parseMerkle (header <> one <> B.pack [1] <> hashA <> B.pack [1, 1]))+      `shouldBe` Right (1, 1, 8)+  it "refuses a merkle block with more hashes than transactions" $+    parseMerkle (header <> one <> B.pack [2] <> hashA <> hashA <> B.pack [1, 1]) `leftWith` "hashes"+  it "reads the proof of the second transaction of block 100000" $+    -- four transactions, three hashes, the five flag bits 1 1 0 1 0 in one byte: 0x0b+    let hashes =+          map+            (runPutS . serialize . (.get) . displayTxHash)+            [ "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",+              "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4",+              "8e30899078ca1813be036a073bbf80b86cdddde1c96e9e9c99e9e3782df4ae49"+            ]+        bytes = header <> B.pack [4, 0, 0, 0] <> B.pack [3] <> B.concat hashes <> B.pack [1, 0x0b]+     in fmap (\m -> extractMatches btc m.flags m.hashes (fromIntegral m.txn)) (parseMerkle bytes)+          `shouldBe` Right+            ( Right+                ( (displayTxHash "f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766").get,+                  [displayTxHash "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4"]+                )+            )+  it "refuses a proof of four transactions and three hashes with two flag bytes" $+    -- three hashes under a tree of height 2 use at most 3 * 3 = 9 bits, but+    -- the tree has only 7 nodes: one byte+    let bytes = header <> B.pack [4, 0, 0, 0] <> B.pack [3] <> B.concat (replicate 3 hashA) <> B.pack [2, 0x0b, 0]+     in parseMerkle bytes `leftWith` "flag"+  it "refuses a proof of one hash with three flag bytes under a tree of 1000 transactions" $+    -- 1000 transactions make a tree of height 10. One hash lies under at+    -- most 11 nodes, so it uses at most 11 flag bits: two bytes.+    let thousand = B.pack [0xe8, 0x03, 0, 0]+     in do+          parseMerkle (header <> thousand <> B.pack [1] <> hashA <> B.pack [3, 0, 0, 0]) `leftWith` "flag"+          fmap (\m -> length m.flags) (parseMerkle (header <> thousand <> B.pack [1] <> hashA <> B.pack [2, 0, 0]))+            `shouldBe` Right 16+  it "allows 33 flag bits per hash at most, and no more bits than the tree has nodes" $ do+    -- 7 transactions: widths 7, 4, 2, 1, so 14 nodes and a height of 3+    maxMerkleFlagBits 7 1 `shouldBe` 4+    maxMerkleFlagBits 7 3 `shouldBe` 12+    maxMerkleFlagBits 7 4 `shouldBe` 14+    maxMerkleFlagBits 7 7 `shouldBe` 14+    maxMerkleFlagBits 1 1 `shouldBe` 1+    maxMerkleFlagBits 0 0 `shouldBe` 0+    -- 2^32 - 1 transactions make a tree of height 32+    maxMerkleFlagBits 4294967295 2 `shouldBe` 66+  it "accepts a proof over 16666 transactions and refuses 16667 on bitcoin" $ do+    -- 1000000 bytes of block / 60 bytes per transaction = 16666+    extractMatches btc [False] [txA.get] 16666 `shouldBe` Right (txA.get, [])+    extractMatches btc [False] [txA.get] 16667 `shouldBe` Left "extractMatches: number of transactions excessively high"++chainConstantsSpec :: Spec+chainConstantsSpec = describe "chain parameters of the reference nodes" $ do+  it "retargets every 144 blocks on bitcoin regtest, where the timespan is one day" $ do+    -- kernel/chainparams.cpp of Bitcoin Core: nPowTargetTimespan = 24 * 60 * 60+    btcRegTest.targetTimespan `shouldBe` 86400+    diffInterval btcRegTest `shouldBe` 144+  it "retargets every 2016 blocks on the other networks" $+    map diffInterval [btc, btcTest, btcTest4, bch, bchTest, bchTest4, bchRegTest] `shouldBe` replicate 7 2016+  it "starts the EDA and the DAA one block after the heights of Bitcoin Cash Node" $ do+    -- chainparams.cpp of Bitcoin Cash Node: uahfHeight 478558, daaHeight+    -- 504031 on mainnet; 1155875 and 1188697 on testnet3; 6 and 3000 on testnet4+    (bch.edaHeight, bch.daaHeight) `shouldBe` (Just 478559, Just 504032)+    (bchTest.edaHeight, bchTest.daaHeight) `shouldBe` (Just 1155876, Just 1188698)+    (bchTest4.edaHeight, bchTest4.daaHeight) `shouldBe` (Just 7, Just 3001)+  it "activates ASERT at the time of Bitcoin Cash Node, with its half-lives" $ do+    -- axionActivationTime = 1605441600; nASERTHalfLife is two days on+    -- mainnet (172800 seconds) and one hour on the test networks (3600)+    map (.asertActivationTime) [bch, bchTest, bchTest4, bchRegTest] `shouldBe` replicate 4 (Just 1605441600)+    map (.asertHalfLife) [bch, bchTest, bchTest4, bchRegTest] `shouldBe` [172800, 3600, 3600, 172800]+  it "halves the subsidy every 210000 blocks, and every 150 on regtest" $ do+    map (.halvingInterval) [btc, btcTest, btcTest4, bch, bchTest, bchTest4] `shouldBe` replicate 6 210000+    map (.halvingInterval) [btcRegTest, bchRegTest] `shouldBe` [150, 150]+  it "pays 50 coins before block 210000 and 25 from it" $ do+    computeSubsidy btc 209999 `shouldBe` 5000000000+    computeSubsidy btc 210000 `shouldBe` 2500000000+    computeSubsidy btc 420000 `shouldBe` 1250000000+    computeSubsidy btcRegTest 149 `shouldBe` 5000000000+    computeSubsidy btcRegTest 150 `shouldBe` 2500000000+  it "pays one satoshi until block 6929999 and nothing from block 6930000" $ do+    -- 33 halvings: 5000000000 shifted right by 32 is 1, by 33 is 0+    computeSubsidy btc 6929999 `shouldBe` 1+    computeSubsidy btc 6930000 `shouldBe` 0+  it "has the checkpoint of block 33333 on bitcoin" $ do+    validCP btc 33333 "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6" `shouldBe` True+    validCP btc 33333 "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a7" `shouldBe` False+  it "enforces BIP94 on bitcoin testnet4 only" $ do+    map (.bip94) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [False, False, True, False, False, False, False, False]+    -- the first block of a period may be at most 600 seconds before its parent+    validTimeWarp btcTest4 2016 10000 9400 `shouldBe` True+    validTimeWarp btcTest4 2016 10000 9399 `shouldBe` False+    validTimeWarp btc 2016 10000 9399 `shouldBe` True+  it "computes the four retargets of the pow_tests of Bitcoin Core" $ do+    let next lastTime lastBits firstTime =+          calcNextWork btc (btc.genesisHeader :: BlockHeader) {timestamp = lastTime, bits = lastBits} firstTime+    -- blocks 30240 to 32255, the first difficulty change+    next 1262152739 0x1d00ffff 1261130161 `shouldBe` 0x1d00d86a+    -- blocks 0 to 2015, capped at the proof-of-work limit+    next 1233061996 0x1d00ffff 1231006505 `shouldBe` 0x1d00ffff+    -- blocks 66528 to 68543, limited to a quarter of the timespan+    next 1279297671 0x1c05a3f4 1279008237 `shouldBe` 0x1c0168fd+    -- limited to four times the timespan+    next 1269211443 0x1c387f6f 1263163443 `shouldBe` 0x1d00e1fd+  it "computes the retarget of bitcoin testnet4 block 157248" $+    -- first block of the period: bits 0x19037e43, time 1791203299. Last+    -- block: time 1792638187. 1434888 s of 1209600 s is a factor of 1.18625,+    -- and 0x037e43 * 1.18625 = 0x0424d1.+    calcNextWork btcTest4 (btcTest4.genesisHeader :: BlockHeader) {timestamp = 1792638187, bits = 0x19037e43} 1791203299+      `shouldBe` 0x190424d1+  it "counts 2^255 hashes of work for a target of one" $+    -- 2^256 / (1 + 1)+    headerWork (btc.genesisHeader :: BlockHeader) {bits = 0x01010000}+      `shouldBe` 57896044618658097711785492504343953926634992332820282019728792003956564819968++activationSpec :: Spec+activationSpec = describe "first block of each Bitcoin Cash difficulty rule" $ do+  -- Expected bits come from a Python port of GetNextEDAWorkRequired and+  -- GetNextCashWorkRequired of Bitcoin Cash Node.+  it "uses the DAA for the block at the DAA height, and not for the block before" $ do+    -- DAA from block 150. Blocks every 300 seconds at bits 0x1f00ffff.+    -- Block 149 keeps the bits of its parent. Block 150 gets the DAA: 144+    -- blocks of work in 43200 seconds, twice the rate, so half the target.+    -- The DAA needs a parent at or above the retarget interval, as in+    -- Bitcoin Cash Node, so the interval is 144 blocks here.+    let net = (cashNet :: Network) {daaHeight = Just 150, targetTimespan = 144 * 600}+        required n = withChain net (storeChain (evenlySpaced net 300 n 0x1f00ffff) >> requiredAfter net (net.genesisHeader.timestamp + 300 * (fromIntegral n + 1)))+    required 148 `shouldBe` 0x1f00ffff+    required 149 `shouldBe` 0x1e7fff80+  it "uses the EDA for the block at the EDA height, and not for the block before" $ do+    -- EDA from block 20. Blocks every three hours at bits 0x1f00ffff, so the+    -- median time past moves 18 hours in six blocks, more than 12. Block 19+    -- keeps the bits of its parent. Block 20 gets a target a quarter higher:+    -- 0x00ffff + 0x003fff = 0x013ffe.+    let net = (cashNet :: Network) {edaHeight = Just 20}+        required n = withChain net (storeChain (evenlySpaced net 10800 n 0x1f00ffff) >> requiredAfter net (net.genesisHeader.timestamp + 10800 * (fromIntegral n + 1)))+    required 18 `shouldBe` 0x1f00ffff+    required 19 `shouldBe` 0x1f013ffe++minimumWorkSpec :: Spec+minimumWorkSpec = describe "minimum chain work" $ do+  -- A regtest header at bits 0x207fffff has a target of 0x7fffff * 2^232,+  -- so its work is 2^256 / (target + 1) = 2. Genesis has 2, block 1 has 4,+  -- block 2 has 6, block 3 has 8 and so on.+  let base = btcRegTest+      t0 = base.genesisHeader.timestamp+      b1 = mineOn base base.genesisHeader (t0 + 600) 0x207fffff+      b2 = mineOn base b1 (t0 + 1200) 0x207fffff+      b3 = mineOn base b2 (t0 + 1800) 0x207fffff+      b4 = mineOn base b3 (t0 + 2400) 0x207fffff+      -- other blocks on top of genesis, block 1 and block 2+      fork0 = mineBlock base 1 (headerOn base.genesisHeader (t0 + 660) 0x207fffff)+      fork1 = mineBlock base 1 (headerOn b1 (t0 + 1260) 0x207fffff)+      fork2 = mineBlock base 1 (headerOn b2 (t0 + 1860) 0x207fffff)+      -- the chain is known to have a work of 8, which block 3 reaches+      net = (base :: Network) {minimumChainWork = 8}+  it "has the work the test assumes" $+    map headerWork [base.genesisHeader, b1] `shouldBe` [2, 2]+  it "accepts a fork below the minimum chain work while the best block is below it too" $+    withChain net (connectAll net [b1, b2] >> connectHeight net myTime fork0) `shouldBe` Right 1+  it "refuses a fork whose chain has less than the minimum chain work once the best block has it" $ do+    -- best block 3 with a work of 8. A second block 1 would have 4, a second block 2 would have 6.+    withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime fork0)+      `shouldBe` Left "Header chain has less work than the minimum chain work: 1"+    withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime fork1)+      `shouldBe` Left "Header chain has less work than the minimum chain work: 2"+  it "accepts a fork whose chain has exactly the minimum chain work" $+    -- a second block 3 has a work of 8+    withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime fork2) `shouldBe` Right 3+  it "accepts the next block of the best chain" $+    withChain net (connectAll net [b1, b2, b3] >> connectHeight net myTime b4) `shouldBe` Right 4+  it "applies no minimum on regtest, where it is zero" $+    withChain base (connectAll base [b1, b2, b3] >> connectHeight base myTime fork0) `shouldBe` Right 1+  it "has the minimum chain work of Bitcoin Core 32.1 and Bitcoin Cash Node on each network" $ do+    -- nMinimumChainWork in kernel/chainparams.cpp of Bitcoin Core v32.1+    btc.minimumChainWork `shouldBe` 0x0000000000000000000000000000000000000000145ec036acc5ba740052af1a0+    btcTest.minimumChainWork `shouldBe` 0x0000000000000000000000000000000000000000000017f49f702147f10c0eb6+    btcTest4.minimumChainWork `shouldBe` 0x000000000000000000000000000000000000000000000e346a558455ade8eca9+    btcRegTest.minimumChainWork `shouldBe` 0+    -- chainparamsconstants.h of Bitcoin Cash Node at 3d13ce8+    bch.minimumChainWork `shouldBe` 0x0000000000000000000000000000000000000000030fd678912166474fe93f14+    bchTest.minimumChainWork `shouldBe` 0x00000000000000000000000000000000000000000000011fdbbe582349040762+    bchTest4.minimumChainWork `shouldBe` 0x000000000000000000000000000000000000000000000000bd5a1b5bda45b5da+    bchRegTest.minimumChainWork `shouldBe` 0++coreProofSpec :: Spec+coreProofSpec = describe "merkle proofs made by Bitcoin Core 32.1" $ do+  -- Output of gettxoutproof on a regtest block of seven transactions,+  -- 307105bce8534c8829f50dbee4a0f5d5f702327443d7078853d0003b19099718. The+  -- result of that call is a serialized merkle block.+  let proofOfOne = fromJust $ decodeHex "00000020890b601aa1dcb84e3647ac08bba63d1b2f4c20f493fa83c81c96e113775e525996ec6f2e21c4e47055a7977fefa3567adcf9618591645e1aa004a366a7bd60d5ace5ec6affff7f2000000000070000000431094d2f5d072ffc7cee499023fb27b7910b7c0579f43d6ef46ae4a5ee78d21bacda1c3873e5bc3343400027c94c3ee281134ada869cbc15533593ededf6bd85ccac70b301ce3c55ba6c315c05dd255396d7c76add7ce5fd0780c741353d02c1d444e4de84cf6931a28901c210ad7d81a63e8e9c66ab239e00a0dceb06cb2fa3012b"+      proofOfTwo = fromJust $ decodeHex "00000020890b601aa1dcb84e3647ac08bba63d1b2f4c20f493fa83c81c96e113775e525996ec6f2e21c4e47055a7977fefa3567adcf9618591645e1aa004a366a7bd60d5ace5ec6affff7f2000000000070000000631094d2f5d072ffc7cee499023fb27b7910b7c0579f43d6ef46ae4a5ee78d21bacda1c3873e5bc3343400027c94c3ee281134ada869cbc15533593ededf6bd85ccac70b301ce3c55ba6c315c05dd255396d7c76add7ce5fd0780c741353d02c1406b3b9a3292f7e0efcd4d4a63197c97d4ab4a7109e8e8ece3dfbd25db25fcece9cc6977b0609eb4c3a5bb98dc6e113a87136a2c2d75320d367ddaa32a20a0d680edfad428b72d1713a241614633c4f0ec6109b7c57cd8a42e6b4c6bd0d736cc02eb02"+      tx3 = displayTxHash "c1023d3541c78007fde57cdd6ac7d7965325dd055c316cba553cce01b370accc"+      tx5 = displayTxHash "d6a0202aa3da7d360d32752d2c6a13873a116edc98bba5c3b49e60b07769cce9"+      parseMerkle bytes = runGetS deserialize bytes :: Either String MerkleBlock+  it "reads the proof of the transaction at position 3" $ do+    fmap (\m -> (headerHash m.header, m.txn)) (parseMerkle proofOfOne)+      `shouldBe` Right ("307105bce8534c8829f50dbee4a0f5d5f702327443d7078853d0003b19099718", 7)+    fmap (merkleBlockIndexedTxs btcRegTest) (parseMerkle proofOfOne) `shouldBe` Right (Right [(3, tx3)])+  it "reads the proof of the transactions at positions 3 and 5" $+    fmap (merkleBlockIndexedTxs btcRegTest) (parseMerkle proofOfTwo) `shouldBe` Right (Right [(3, tx3), (5, tx5)])+  it "writes both proofs back to the same bytes" $ do+    fmap (runPutS . serialize) (parseMerkle proofOfOne) `shouldBe` Right proofOfOne+    fmap (runPutS . serialize) (parseMerkle proofOfTwo) `shouldBe` Right proofOfTwo
test/Haskoin/Crypto/HashSpec.hs view
@@ -1,76 +1,140 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}-module Haskoin.Crypto.HashSpec (spec) where+{-# LANGUAGE NoFieldSelectors #-} -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.Maybe              (fromJust)-import           Data.Serialize          as S-import           Data.String             (fromString)-import           Data.String.Conversions-import           Data.Text               (Text)-import           Data.Word-import           Haskoin.Block-import           Haskoin.Crypto-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit-import           Test.QuickCheck+module Haskoin.Crypto.HashSpec (spec) where -serialVals :: [SerialBox]-serialVals =-    [ SerialBox arbitraryBS-    , SerialBox arbitraryBSS-    , SerialBox arbitraryHash160-    , SerialBox arbitraryHash256-    , SerialBox arbitraryHash512-    ]+import Control.Exception (evaluate)+import Crypto.Hash (hashFinalize, hashUpdate)+import Data.Bits+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString.Builder+import Data.ByteString.Char8 qualified as Char8+import Data.ByteString.Lazy qualified as Lazy+import Data.ByteString.Short qualified as Short+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Maybe (fromJust)+import Data.String (fromString)+import Data.String.Conversions+import Data.Text (Text)+import Data.Word+import Haskoin.Block+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Read (readMaybe) -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 $ encode h) == h-            prop "from string Hash256" $-                forAll arbitraryHash256 $ \h ->-                    fromString (cs $ encodeHex $ encode h) == h-            prop "from string Hash160" $-                forAll arbitraryHash160 $ \h ->-                    fromString (cs $ encodeHex $ encode 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+    describe "Composite hashes" $ do+      it "double SHA-256 hashes the SHA-256 of the input again" $ do+        -- sha256(sha256("")) and sha256(sha256("abc"))+        doubleSHA256 ("" :: ByteString)+          `shouldBe` "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"+        doubleSHA256 ("abc" :: ByteString)+          `shouldBe` "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358"+      it "address hash is the RIPEMD-160 of the SHA-256 of the input" $ do+        addressHash ("" :: ByteString)+          `shouldBe` "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb"+        addressHash ("abc" :: ByteString)+          `shouldBe` "bb1be98c142444d7a56aa3981c3942a978e4dc33"+      it "32-bit checksum is the first four bytes of the double SHA-256" $+        -- double SHA-256 of "" starts with 5d f6 e0 e2+        (checkSum32 ("" :: ByteString)).get `shouldBe` 0x5df6e0e2+      it "tagged hash is SHA-256 of the hashed tag twice and the message" $ do+        -- sha256(sha256("TapLeaf") || sha256("TapLeaf") || msg), as in BIP340+        taggedHex "TapLeaf" ""+          `shouldBe` "5212c288a377d1f8164962a5a13429f9ba6a7b84e59776a52c6637df2106facb"+        taggedHex "TapLeaf" "abc"+          `shouldBe` "83a56308a9c56f467e8df293da5ae5fdbc85b871952a83c4bf0575ee948ec230"+      it "split512 gives the two halves of a 512-bit hash" $+        -- SHA-512 of "" is cf83e135…d36ce9ce followed by 47d0d13c…f927da3e+        split512 (sha512 ("" :: ByteString))+          `shouldBe` ( "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce",+                       "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"+                     )+      it "join512 puts the two halves back together" $+        join512+          ( "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce",+            "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"+          )+          `shouldBe` "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\+                     \47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"+    describe "Hash text forms" $ do+      it "reads a hash from its hex string" $+        readMaybe "\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\""+          `shouldBe` Just ("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" :: Hash256)+      it "refuses to read a hash of the wrong length" $ do+        readMaybe "\"abcd\"" `shouldBe` (Nothing :: Maybe Hash160)+        readMaybe "\"abcd\"" `shouldBe` (Nothing :: Maybe Hash256)+        readMaybe "\"abcd\"" `shouldBe` (Nothing :: Maybe Hash512)+      it "refuses to read a hash that is not hex" $+        readMaybe "\"zz\"" `shouldBe` (Nothing :: Maybe Hash256)+      it "refuses a hash literal of the wrong length" $ do+        evaluate ("abcd" :: Hash160) `shouldThrow` anyErrorCall+        evaluate ("abcd" :: Hash256) `shouldThrow` anyErrorCall+        evaluate ("abcd" :: Hash512) `shouldThrow` anyErrorCall +-- | Hex of the BIP340 tagged hash of a message.+taggedHex :: ByteString -> ByteString -> Text+taggedHex tag msg =+  encodeHex (BA.convert (hashFinalize (hashUpdate (initTaggedHash tag) msg)))+ joinSplit512 :: Hash256 -> Hash256 -> Bool joinSplit512 a b = split512 (join512 (a, b)) == (a, b) @@ -78,264 +142,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+    go (i : is) =+      let i1 = fromIntegral $! i+          i2 = fromIntegral $! i `shiftR` 4+          i3 = fromIntegral $! i `shiftR` 8+          i4 = fromIntegral $! i `shiftR` 12+          i5 = fromIntegral $! i `shiftR` 16+       in word8 i1 <> word8 i2 <> word8 i3 <> word8 i4 <> word8 i5 <> go is  ripemd160Vectors :: [(ByteString, Text)] ripemd160Vectors =-    [ ("", "9c1185a5c5e9fc54612808977ee8f548b2258d31")-    , ("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")-    , ("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36")-    , ("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9")-    , ( "RIPEMD160 is considered to be safe"-      , "a7d78608c7af8a8e728778e81576870734122b66")-    , ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"-      , "12a053384a9c0c88e405a06c27dcf49ada62eb2b")-    , ( "For this sample, this 63-byte string will be used as input data"-      , "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11")-    , ( "This is exactly 64 bytes long, not counting the terminating byte"-      , "eda31d51d3a623b81e19eb02e24ff65d27d67b37")-    , (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.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\-        \654a6566654a6566654a6566654a6566654a6566654a6566654a6566"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6")-      -- Test case with key length 64 bytes.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\-        \654a6566654a6566654a6566654a6566654a6566654a6566654a656665"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd")-      -- Test case with key length 65 bytes.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\-        \654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483")-    ]+  [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",+      "4869205468657265",+      "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"+    ),+    ( "4a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+      \dddddddddddddddddddddddddddddd",+      "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"+    ),+    ( "0102030405060708090a0b0c0d0e0f10111213141516171819",+      "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+      \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",+      "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b\+      \6579202d2048617368204b6579204669727374",+      "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "5468697320697320612074657374207573696e672061206c6172676572207468616e20\+      \626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c\+      \6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520\+      \686173686564206265666f7265206265696e6720757365642062792074686520484d41\+      \4320616c676f726974686d2e",+      "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"+    ),+    -- Test case with key length 63 bytes. +    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a6566",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6"+    ),+    -- Test case with key length 64 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"+    ),+    -- Test case with key length 65 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483"+    )+  ]+ -- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 hmacSha512Vectors :: [(Text, Text, Text)] hmacSha512Vectors =-    [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"-      , "4869205468657265"-      , "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\-        \daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854")-    , ( "4a656665"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\-        \9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737")-    , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"-      , "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\-        \dddddddddddddddddddddddddddddddddddd"-      , "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\-        \bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb")-    , ( "0102030405060708090a0b0c0d0e0f10111213141516171819"-      , "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\-        \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"-      , "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\-        \a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd")-    , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaa"-      , "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a\-        \65204b6579202d2048617368204b6579204669727374"-      , "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\-        \6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598")-    , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\-        \aaaaaa"-      , "5468697320697320612074657374207573696e672061206c6172676572207468\-        \616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074\-        \68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565\-        \647320746f20626520686173686564206265666f7265206265696e6720757365\-        \642062792074686520484d414320616c676f726974686d2e"-      , "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\-        \b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")-      -- Test case with key length 127 bytes.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a\-        \c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e")-      -- Test case with key length 128 bytes.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\-        \2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258")-      -- Test case with key length 129 bytes.-    , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\-        \4a"-      , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"-      , "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\-        \fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c")-    ]+  [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",+      "4869205468657265",+      "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\+      \daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"+    ),+    ( "4a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\+      \9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+      \dddddddddddddddddddddddddddddddddddd",+      "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\+      \bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"+    ),+    ( "0102030405060708090a0b0c0d0e0f10111213141516171819",+      "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+      \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",+      "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\+      \a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaa",+      "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a\+      \65204b6579202d2048617368204b6579204669727374",+      "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\+      \6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaa",+      "5468697320697320612074657374207573696e672061206c6172676572207468\+      \616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074\+      \68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565\+      \647320746f20626520686173686564206265666f7265206265696e6720757365\+      \642062792074686520484d414320616c676f726974686d2e",+      "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\+      \b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"+    ),+    -- Test case with key length 127 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a\+      \c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e"+    ),+    -- Test case with key length 128 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\+      \2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258"+    ),+    -- Test case with key length 129 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\+      \fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c"+    )+  ]
+ test/Haskoin/Crypto/Keys/ExtendedSpec.hs view
@@ -0,0 +1,1085 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}++module Haskoin.Crypto.Keys.ExtendedSpec (spec) where++import Control.Exception (evaluate)+import Control.Monad (forM_)+import Data.Aeson as A+import Data.Bits ((.&.))+import Data.ByteString qualified as B+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.List (isInfixOf)+import Data.Maybe (fromJust, isJust, isNothing)+import Data.Ord (comparing)+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word32)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Script+import Haskoin.Util+import Haskoin.Util.Arbitrary+import System.Timeout (timeout)+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck hiding ((.&.))+import Text.Read (readMaybe)++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ 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 test vector 4: private keys with leading zeros" $+    forM_ vector4 $ \(path, xpubText, xprvText) ->+      it ("chain " <> path) $ do+        let m = makeXPrvKey (fromJust (decodeHex m4))+            k = derivePath ctx ((.get) (fromJust (parsePath path))) m+        xPrvExport btc k `shouldBe` xprvText+        xPubExport btc ctx (deriveXPubKey ctx k) `shouldBe` xpubText+  describe "bip32 test vector 5: invalid extended keys" $+    forM_ vector5 $ \(key, reason) ->+      it ("refuses " <> reason) $ do+        xPrvImport btc key `shouldBe` Nothing+        xPubImport btc ctx key `shouldBe` Nothing+  describe "Extended key import" $ do+    it "refuses an xpub with trailing bytes" $+      -- the 78 bytes of the vector 1 master xpub followed by de ad be ef+      xPubImport btc ctx "7JJikZQ2NUXjSAnAF2v9rxutS1j84JbPJspH9roCjFfNvxFfhmgTczN53frJ4RaYoKmwYCj5r1qRsGbLz2fiUE4Aw7LqCR9NYxR72w6AThhWJsB43ZrGA"+        `shouldBe` Nothing+    it "refuses an xprv with trailing bytes" $+      xPrvImport btc "7JJRGiiVuW9DCAkyM2PaXqVx51tHCM3s2Wbq9SnmstZD4r8Ryf4i7Dbe2YQyz9mUF9oZEcdkyzU96dsBwJz3Uvu6PzVTC5k53mBrVHWxZMPdSBtmunEjn"+        `shouldBe` Nothing+    it "refuses an xpub carrying an uncompressed public key" $+      -- 110 bytes: the vector 1 master xpub with its key in the 65-byte form+      xPubImport btc ctx "HarqLWPftSVzTwufEPMMMdZS33daVnF1pMQCFXEQoWwsd7tK1oCwB34reM1xTwhKvY11Pfgite2BcpvEDscYTtuZYL3pZAwQzAoZRLA1xCjK2F28jNBa3muZtLmsqfWHzwSGk2RRAd1e52SvdFecN97hmfA"+        `shouldBe` Nothing+    it "refuses a string longer than any extended key" $ do+      xPrvImport btc (T.replicate 113 "z") `shouldBe` Nothing+      xPubImport btc ctx (T.replicate 113 "z") `shouldBe` Nothing+    it "refuses a string of a million characters at once, without decoding it" $ do+      -- Decoding that much Base58 takes minutes. The length check comes first.+      timeout 1000000 (evaluate (xPrvImport btc (T.replicate 1000000 "z")))+        `shouldReturn` Just Nothing+      timeout 1000000 (evaluate (xPubImport btc ctx (T.replicate 1000000 "z")))+        `shouldReturn` Just Nothing+    it "refuses an extended key of another network" $ do+      xPrvImport btcTest xprv1Text `shouldBe` Nothing+      xPrvImport btc tprv1Text `shouldBe` Nothing+      xPubImport btcTest ctx xpub1Text `shouldBe` Nothing+      xPubImport btc ctx tpub1Text `shouldBe` Nothing+    it "imports the testnet form of a key on testnet" $ do+      fmap (.key) (xPrvImport btcTest tprv1Text) `shouldBe` Just (xprv1 ctx).key+      fmap (.key) (xPubImport btcTest ctx tpub1Text) `shouldBe` Just (xpub1 ctx).key+    it "writes a key with the version bytes of each of the eight networks" $ do+      -- xprv and xpub (0488ade4, 0488b21e) on the two main networks, tprv and+      -- tpub (04358394, 043587cf) on the six others: chainparams.cpp of+      -- Bitcoin Core and of Bitcoin Cash Node. The key is the master key of+      -- BIP32 test vector 1.+      forM_ [btc, bch] $ \net -> do+        xPrvExport net (xprv1 ctx) `shouldBe` xprv1Text+        xPubExport net ctx (xpub1 ctx) `shouldBe` xpub1Text+      forM_ [btcTest, btcTest4, btcRegTest, bchTest, bchTest4, bchRegTest] $ \net -> do+        xPrvExport net (xprv1 ctx) `shouldBe` tprv1Text+        xPubExport net ctx (xpub1 ctx) `shouldBe` tpub1Text+    it "refuses an xpub given to the private key importer, and the reverse" $ do+      xPrvImport btc xpub1Text `shouldBe` Nothing+      xPubImport btc ctx xprv1Text `shouldBe` Nothing+    it "fails the JSON parse of an invalid extended key" $+      -- BIP32 test vector 5: private key 0+      (unmarshalJSON btc "\"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx\"" :: Maybe XPrvKey)+        `shouldBe` Nothing+  describe "Binary extended keys" $ do+    -- The 78 bytes of the master keys of BIP32 test vector 1: version, depth+    -- 0, parent 00000000, index 0, the chain code, then the key+    let header = "000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"+        xpubBytes = hex ("0488b21e" <> header <> "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2")+        xprvBytes = hex ("0488ade4" <> header <> "00e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")+    it "reads the 78 bytes of an extended public key" $+      fmap (xPubExport btc ctx) (unmarshal (btc, ctx) xpubBytes) `shouldBe` Right xpub1Text+    it "reads the 78 bytes of an extended private key" $+      fmap (xPrvExport btc) (unmarshal btc xprvBytes) `shouldBe` Right xprv1Text+    it "refuses an extended public key whose key is in the 65-byte form" $+      -- the same key with its y coordinate, 110 bytes in all+      ( unmarshal+          (btc, ctx)+          ( hex+              ( "0488b21e"+                  <> header+                  <> "0439a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2\+                     \3cbe7ded0e7ce6a594896b8f62888fdbc5c8821305e2ea42bf01e37300116281"+              )+          ) ::+          Either String XPubKey+      )+        `shouldSatisfy` either ("compressed public key" `isInfixOf`) (const False)+    it "refuses bytes after the 78 bytes of an extended key" $ do+      (unmarshal (btc, ctx) (xpubBytes <> "\x00") :: Either String XPubKey)+        `shouldSatisfy` either ("bytes left over" `isInfixOf`) (const False)+      (unmarshal btc (xprvBytes <> "\x00") :: Either String XPrvKey)+        `shouldSatisfy` either ("bytes left over" `isInfixOf`) (const False)+  describe "Master key" $ do+    it "has depth 0, no parent and index 0" $ do+      let m = makeXPrvKey (fromJust (decodeHex m1))+      m.depth `shouldBe` 0+      m.parent `shouldBe` "00000000"+      m.index `shouldBe` 0+    -- BIP32: the seed is "between 128 and 512 bits", that is 16 to 64 bytes+    let badSeed = DerivationException "Invalid seed: its length must be between 16 and 64 bytes"+    it "is made from the 16-byte seed of BIP32 test vector 1" $+      xPrvExport btc (makeXPrvKey (hex "000102030405060708090a0b0c0d0e0f")) `shouldBe` xprv1Text+    it "is made from the 64-byte seed of BIP32 test vector 2" $+      xPrvExport+        btc+        ( makeXPrvKey+            ( hex+                "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a2\+                \9f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"+            )+        )+        `shouldBe` "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"+    it "is not made from a seed of 15 bytes" $+      evaluate (makeXPrvKey (B.replicate 15 1)) `shouldThrow` (== badSeed)+    it "is not made from a seed of 65 bytes" $+      evaluate (makeXPrvKey (B.replicate 65 1)) `shouldThrow` (== badSeed)+    it "is not made from an empty seed" $+      evaluate (makeXPrvKey B.empty) `shouldThrow` (== badSeed)+  describe "Showing an extended private key" $+    -- The master key of vector 1 has fingerprint 3442193e; its hardened+    -- child 7 has index 2^31 + 7 = 2147483655+    it "shows the depth, parent and index but not the chain code or the key" $ do+      let m = makeXPrvKey (fromJust (decodeHex m1))+      show m `shouldBe` "XPrvKey {depth = 0, parent = \"00000000\", index = 0, chain = <redacted>, key = <redacted>}"+      show (hardSubKey ctx m 7) `shouldBe` "XPrvKey {depth = 1, parent = \"3442193e\", index = 2147483655, chain = <redacted>, key = <redacted>}"+      show (Just m) `shouldBe` "Just (XPrvKey {depth = 0, parent = \"00000000\", index = 0, chain = <redacted>, key = <redacted>})"+  describe "Derivation depth" $ do+    let m = makeXPrvKey (fromJust (decodeHex m1))+        atDepth255 = XPrvKey 255 m.parent m.index m.chain m.key+        tooDeep = DerivationException "Maximum derivation depth exceeded"+        hardIndex = DerivationException "Invalid child derivation index"+    it "a child is one level deeper than its parent" $ do+      (prvSubKey ctx m 0).depth `shouldBe` 1+      (hardSubKey ctx m 0).depth `shouldBe` 1+      (pubSubKey ctx (deriveXPubKey ctx m) 0).depth `shouldBe` 1+      (derivePath ctx (Deriv :| 0 :/ 1 :/ 2 :: DerivPath) m).depth `shouldBe` 3+    it "refuses to derive a child of a key at depth 255" $ do+      evaluate (prvSubKey ctx atDepth255 0) `shouldThrow` (== tooDeep)+      evaluate (hardSubKey ctx atDepth255 0) `shouldThrow` (== tooDeep)+      evaluate (pubSubKey ctx (deriveXPubKey ctx atDepth255) 0) `shouldThrow` (== tooDeep)+    it "refuses a child index with the hard bit set with a DerivationException" $ do+      evaluate (prvSubKey ctx m 0x80000000) `shouldThrow` (== hardIndex)+      evaluate (hardSubKey ctx m 0x80000000) `shouldThrow` (== hardIndex)+      evaluate (pubSubKey ctx (deriveXPubKey ctx m) 0x80000000) `shouldThrow` (== hardIndex)+    -- The depth is one byte, so a master key has descendants down to depth+    -- 255 and no further+    let steps n = concat (replicate n "/0")+        depthOf (XPrv k _) = k.depth+        depthOf (XPub k _) = k.depth+    it "applyPath derives a path of 255 steps from a master key" $ do+      fmap depthOf (applyPath ctx (fromString ("m" <> steps 255)) (XPrv m btc)) `shouldBe` Right 255+      fmap depthOf (applyPath ctx (fromString ("M" <> steps 255)) (XPub (deriveXPubKey ctx m) btc)) `shouldBe` Right 255+    it "applyPath gives an error value for a path of 256 steps from a master key" $ do+      applyPath ctx (fromString ("m" <> steps 256)) (XPrv m btc)+        `shouldBe` Left "applyPath: Maximum derivation depth exceeded"+      applyPath ctx (fromString ("M" <> steps 256)) (XPrv m btc)+        `shouldBe` Left "applyPath: Maximum derivation depth exceeded"+      applyPath ctx (fromString ("M" <> steps 256)) (XPub (deriveXPubKey ctx m) btc)+        `shouldBe` Left "applyPath: Maximum derivation depth exceeded"+    it "applyPath gives an error value for one step from a key at depth 255" $+      applyPath ctx "m/0" (XPrv atDepth255 btc)+        `shouldBe` Left "applyPath: Maximum derivation depth exceeded"+    it "tryDerivePath derives what derivePath derives" $+      -- BIP32 test vector 1, chain m/0'/1+      fmap (xPrvExport btc) (tryDerivePath ctx (Deriv :| 0 :/ 1 :: DerivPath) m)+        `shouldBe` Right "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"+    it "tryDerivePath gives an error value for a path of 256 steps from a master key" $+      tryDerivePath ctx (listToPath (replicate 256 0)) m+        `shouldBe` Left "Maximum derivation depth exceeded"+    it "tryDerivePubPath derives what derivePubPath derives" $+      -- BIP32 test vector 2, chain m/0, from the master public key+      fmap (xPubExport btc ctx) (tryDerivePubPath ctx (Deriv :/ 0) (xpub2 ctx))+        `shouldBe` Right "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"+    it "tryDerivePubPath gives an error value for a path of 256 steps from a master key" $+      tryDerivePubPath ctx (foldl (:/) Deriv (replicate 256 0)) (deriveXPubKey ctx m)+        `shouldBe` Left "Maximum derivation depth exceeded"+    it "derivePath throws a DerivationException for a path of 256 steps from a master key" $+      evaluate (derivePath ctx (listToPath (replicate 256 0)) m) `shouldThrow` (== tooDeep)+    it "the path constructors refuse an index of 2^31 or more" $ do+      evaluate (Deriv :/ 0x80000000 :: DerivPath) `shouldThrow` (== hardIndex)+      evaluate (Deriv :| 0x80000000 :: DerivPath) `shouldThrow` (== hardIndex)+      evaluate (Deriv :| 0xffffffff :: HardPath) `shouldThrow` (== hardIndex)+      evaluate (Deriv :/ 0xffffffff :: SoftPath) `shouldThrow` (== hardIndex)+    it "the path constructors take the largest index, 2^31 - 1" $ do+      pathToStr (Deriv :/ 0x7fffffff :: DerivPath) `shouldBe` "/2147483647"+      pathToStr (Deriv :| 0x7fffffff :: DerivPath) `shouldBe` "/2147483647'"+      pathToList (Deriv :| 0x7fffffff :: DerivPath) `shouldBe` [0xffffffff]+  describe "Child index" $ do+    let m = makeXPrvKey (fromJust (decodeHex m1))+    it "a hardened child stores its index with the hard bit set" $ do+      let k = hardSubKey ctx m 7+      k.index `shouldBe` 0x80000007+      xPrvChild k `shouldBe` 7+      xPrvIsHard k `shouldBe` True+      xPubChild (deriveXPubKey ctx k) `shouldBe` 7+      xPubIsHard (deriveXPubKey ctx k) `shouldBe` True+    it "a soft child stores its index as given" $ do+      let k = prvSubKey ctx m 7+      k.index `shouldBe` 7+      xPrvChild k `shouldBe` 7+      xPrvIsHard k `shouldBe` False+      xPubChild (deriveXPubKey ctx k) `shouldBe` 7+      xPubIsHard (deriveXPubKey ctx k) `shouldBe` False+  describe "Addresses of an extended public key" $ do+    -- BIP32 vector 1 master public key 0339a360…85c2 has HASH160 3442193e…f811+    it "pay to public key hash" $+      addrToText btc (xPubAddr ctx (xpub1 ctx)) `shouldBe` Just "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"+    it "pay to witness public key hash" $+      addrToText btc (xPubWitnessAddr ctx (xpub1 ctx)) `shouldBe` Just "bc1qx3ppj0smkuy3d6g525sh9n2w9k7fm7q3x30rtg"+    it "pay to script hash wrapping a witness public key hash" $+      addrToText btc (xPubCompatWitnessAddr ctx (xpub1 ctx)) `shouldBe` Just "3PpgpssV7mcAGpZRWiCWhodUTnjpoSZg7a"+  describe "Address derivation helpers" $ do+    -- BIP32 vector 2: child 0 of the master key has public key 02fc9e5a…12ea+    it "deriveAddr gives the address and the public key of the child" $ do+      let (addr, key) = deriveAddr ctx (xpub2 ctx) 0+      addrToText btc addr `shouldBe` Just "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"+      encodeHex (exportPubKey ctx True key)+        `shouldBe` "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"+    it "deriveWitnessAddr gives the witness address of the child" $+      addrToText btc (fst (deriveWitnessAddr ctx (xpub2 ctx) 0))+        `shouldBe` Just "bc1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qykp3q3we"+    it "deriveCompatWitnessAddr gives the wrapped witness address of the child" $+      addrToText btc (fst (deriveCompatWitnessAddr ctx (xpub2 ctx) 0))+        `shouldBe` Just "39cFMGtVaEw6AfksMcaLpu8frZxmqngh8c"+    it "deriveAddrs lists children from the start index and wraps around" $ do+      map lst3 (take 3 (deriveAddrs ctx (xpub2 ctx) 0x7ffffffe))+        `shouldBe` [0x7ffffffe, 0x7fffffff, 0]+      map addressAndIndex (take 1 (deriveAddrs ctx (xpub2 ctx) 5))+        `shouldBe` [(Just "16KmCWXw8kPgr9nnvETXoqzmsYcvscXfGL", 5)]+    it "deriveWitnessAddrs lists witness addresses with their index" $+      map addressAndIndex (take 1 (deriveWitnessAddrs ctx (xpub2 ctx) 5))+        `shouldBe` [(Just "bc1q8fjs9hakl9489q0fem0gya4m8yqr4gl32dhjr9", 5)]+    it "deriveCompatWitnessAddrs lists wrapped witness addresses with their index" $+      map addressAndIndex (take 1 (deriveCompatWitnessAddrs ctx (xpub2 ctx) 5))+        `shouldBe` [(Just "3FzrsDTC8KiEsBeBTrhDnJoJYvkDpWaWR6", 5)]+    it "prvSubKeys, pubSubKeys and hardSubKeys list children with their index" $ do+      let m = makeXPrvKey (fromJust (decodeHex m1))+      map snd (take 3 (prvSubKeys ctx m 0x7ffffffe)) `shouldBe` [0x7ffffffe, 0x7fffffff, 0]+      map snd (take 2 (hardSubKeys ctx m 0)) `shouldBe` [0, 1]+      map ((.index) . fst) (take 1 (hardSubKeys ctx m 1)) `shouldBe` [0x80000001]+      map (\(k, i) -> (addrToText btc (xPubAddr ctx k), i)) (take 1 (pubSubKeys ctx (xpub2 ctx) 0))+        `shouldBe` [(Just "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ", 0)]+    it "cycleIndex runs through the soft indices from the start and wraps around" $ do+      take 3 (cycleIndex 0x7ffffffe) `shouldBe` [0x7ffffffe, 0x7fffffff, 0]+      take 2 (cycleIndex 0) `shouldBe` [0, 1]+      evaluate (cycleIndex 0x80000000) `shouldThrow` (== DerivationException "cycleIndex: invalid index 2147483648")+  describe "Multisig derivation" $ do+    -- 2-of-2 of child 0 of vectors 1 and 2; sorted keys 027c4b09… then 02fc9e5a…+    it "deriveMSAddr gives the P2SH address of the sorted multisig script" $ do+      let Right (addr, rdm) = deriveMSAddr ctx [xpub2 ctx, xpub1 ctx] 2 0+      addrToText btc addr `shouldBe` Just "3ARSZ47cqQBb9y6QhTk6UoWHMwpruUryZp"+      encodeHex (marshal ctx rdm)+        `shouldBe` "5221027c4b09ffb985c298afe7e5813266cbfcb7780b480ac294b0b43dc21f2be3d13c\+                   \2102fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea52ae"+    it "deriveMSAddrs lists multisig addresses with their index" $ do+      (map lst3 . take 2 <$> deriveMSAddrs ctx [xpub1 ctx, xpub2 ctx] 2 0) `shouldBe` Right [0, 1]+      -- 2-of-2 of child 1 of vectors 1 and 2+      (map addressAndIndex . take 1 <$> deriveMSAddrs ctx [xpub1 ctx, xpub2 ctx] 2 1)+        `shouldBe` Right [(Just "3Pp8RkaJc8eNreFuTTTJ92QfXECQMXDEoE", 1)]+    it "deriveMSAddr gives the P2SH address of 15 keys, the most that fit" $+      -- 1-of-15 of child 0 of m/0 to m/14 of vector 1. The redeem script is+      -- 3 + 15 × 34 = 513 bytes. The address is the one that createmultisig+      -- of Bitcoin Core 32.1 gives for these keys on regtest.+      (addrToText btcRegTest . fst <$> deriveMSAddr ctx (accountKeys ctx 15) 1 0)+        `shouldBe` Right (Just "2NFJxQqeqnzhLx9a1UBhpdgYVGxcd95Ka1b")+    it "deriveMSAddr has no address for 16 keys, whose redeem script cannot be spent" $+      -- 3 + 16 × 34 = 547 bytes, and a push holds at most 520. Bitcoin Core+      -- answers "redeemScript exceeds size limit: 547 > 520".+      (fst <$> deriveMSAddr ctx (accountKeys ctx 16) 1 0)+        `shouldBe` Left "The redeem script is 547 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "deriveMSAddr has no address for 20 keys" $+      -- 3 + 20 × 34 + 1 = 684 bytes+      (fst <$> deriveMSAddr ctx (accountKeys ctx 20) 1 0)+        `shouldBe` Left "The redeem script is 684 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "deriveMSAddrs and the path forms fail for 16 keys as deriveMSAddr does" $ do+      let tooLong = Left "The redeem script is 547 bytes long, and a pay-to-script-hash redeem script can have at most 520"+      (length . take 1 <$> deriveMSAddrs ctx (accountKeys ctx 16) 1 0) `shouldBe` tooLong+      (fst <$> derivePathMSAddr ctx (accountKeys ctx 16) (Deriv :/ 1) 1 0) `shouldBe` tooLong+      (length . take 1 <$> derivePathMSAddrs ctx (accountKeys ctx 16) (Deriv :/ 1) 1 0) `shouldBe` tooLong+    it "deriveMSAddr gives an error value for a signature count the keys cannot meet" $ do+      (fst <$> deriveMSAddr ctx [xpub1 ctx, xpub2 ctx] 3 0)+        `shouldBe` Left "encodeOutput: multisig requires more signatures than it has keys"+      (fst <$> deriveMSAddr ctx [xpub1 ctx, xpub2 ctx] 0 0)+        `shouldBe` Left "encodeOutput: multisig must require at least one signature"+      (fst <$> deriveMSAddr ctx [xpub1 ctx, xpub2 ctx] (-1) 0)+        `shouldBe` Left "encodeOutput: multisig must require at least one signature"+      (fst <$> deriveMSAddr ctx [] 1 0)+        `shouldBe` Left "encodeOutput: multisig requires more signatures than it has keys"+      (length . take 1 <$> deriveMSAddrs ctx [xpub1 ctx, xpub2 ctx] 3 0)+        `shouldBe` Left "encodeOutput: multisig requires more signatures than it has keys"+  describe "Address derivation below a path" $ do+    -- vector 2: m/0/0 has public key 0205c889…ef8c+    it "derivePathAddr derives the address of a child below a soft path" $ do+      let (addr, key) = derivePathAddr ctx (xpub2 ctx) (Deriv :/ 0) 0+      addrToText btc addr `shouldBe` Just "1NyMg76BQxDvV6vRsQugNS4ED2hpZCJtwK"+      encodeHex (exportPubKey ctx True key)+        `shouldBe` "0205c8897fd0ff5644adba4545a84020cd6aa94d90e1e0a56bb4b8eb7522e3ef8c"+    it "derivePathAddrs lists children below the path with their index" $+      map addressAndIndex (take 1 (derivePathAddrs ctx (xpub2 ctx) (Deriv :/ 0) 0))+        `shouldBe` [(Just "1NyMg76BQxDvV6vRsQugNS4ED2hpZCJtwK", 0)]+    it "derivePathMSAddr derives a multisig address below a soft path" $ do+      -- 2-of-2 of m/1/0 of vectors 1 and 2+      let Right (addr, _) = derivePathMSAddr ctx [xpub1 ctx, xpub2 ctx] (Deriv :/ 1) 2 0+      addrToText btc addr `shouldBe` Just "39HHTbxsnt5SrM4h2BtUgVRFzGYcavJ8M4"+    it "derivePathMSAddrs lists multisig addresses below the path with their index" $+      (map addressAndIndex . take 1 <$> derivePathMSAddrs ctx [xpub1 ctx, xpub2 ctx] (Deriv :/ 1) 2 0)+        `shouldBe` Right [(Just "39HHTbxsnt5SrM4h2BtUgVRFzGYcavJ8M4", 0)]+  describe "Path operations" $ do+    it "++/ appends two paths into a mixed path" $ do+      let p = (Deriv :| 44 :| 0 :: HardPath) ++/ (Deriv :/ 0 :/ 5 :: SoftPath)+      p `shouldBe` (Deriv :| 44 :| 0 :/ 0 :/ 5)+      pathToStr p `shouldBe` "/44'/0'/0/5"+    it "toHard keeps a path only when every segment is hardened" $ do+      toHard (Deriv :| 1 :| 2 :: DerivPath) `shouldBe` Just (Deriv :| 1 :| 2)+      toHard (Deriv :| 1 :/ 2 :: DerivPath) `shouldBe` Nothing+    it "toSoft keeps a path only when no segment is hardened" $ do+      toSoft (Deriv :/ 1 :/ 2 :: DerivPath) `shouldBe` Just (Deriv :/ 1 :/ 2)+      toSoft (Deriv :| 1 :/ 2 :: DerivPath) `shouldBe` Nothing+    it "toGeneric keeps every segment" $ do+      toGeneric (Deriv :| 1 :| 2 :: HardPath) `shouldBe` (Deriv :| 1 :| 2)+      toGeneric (Deriv :/ 1 :/ 2 :: SoftPath) `shouldBe` (Deriv :/ 1 :/ 2)+    it "writes hardened segments with an apostrophe" $ do+      pathToStr (Deriv :| 44 :/ 0 :: DerivPath) `shouldBe` "/44'/0"+      show (ParsedPrv (Deriv :| 44 :/ 0)) `shouldBe` "ParsedPath \"m/44'/0\""+      show (ParsedPub (Deriv :| 44 :/ 0)) `shouldBe` "ParsedPath \"M/44'/0\""+    it "pathToList sets the hard bit on hardened segments" $+      pathToList (Deriv :| 1 :/ 2 :: DerivPath) `shouldBe` [0x80000001, 2]+    it "listToPath reads the hard bit back" $+      listToPath [0x80000001, 2] `shouldBe` (Deriv :| 1 :/ 2)+    it "serializes a path as a count and its indices with the hard bit" $+      runPutS (serialize (Deriv :| 1 :/ 2 :: DerivPath))+        `shouldBe` hex "0000000000000002" <> hex "80000001" <> hex "00000002"+    it "binary hard path refuses a soft segment" $+      (runGetS deserialize (hex "0000000000000001" <> hex "00000001") :: Either String HardPath)+        `shouldSatisfy` isLeft+    it "binary soft path refuses a hardened segment" $+      (runGetS deserialize (hex "0000000000000001" <> hex "80000001") :: Either String SoftPath)+        `shouldSatisfy` isLeft+    it "sorts a shorter path first, then by segment, soft before hard" $ do+      (Deriv :/ 9 :: DerivPath) < (Deriv :/ 0 :/ 1) `shouldBe` True+      (Deriv :/ 5 :: DerivPath) < (Deriv :| 0) `shouldBe` True+      (Deriv :/ 1 :: DerivPath) < (Deriv :/ 2) `shouldBe` True+    prop "ordering agrees with the shortlex order of the index lists" $+      forAll arbitraryDerivPath $ \a ->+        forAll arbitraryDerivPath $ \b ->+          compare a b == comparing (\p -> (length (pathToList p), pathToList p)) a b+  describe "Path parsing" $ do+    -- BIP32 writes a hardened index with a subscript H, as in m/0H/1. Bitcoin+    -- Core and BIP380 write an apostrophe or a small h.+    it "accepts the apostrophe, h and H as hardened markers" $ do+      parsePath "m/44h/0H/1'" `shouldBe` Just (ParsedPrv (Deriv :| 44 :| 0 :| 1))+      parseHard "m/44h/0H" `shouldBe` Just (Deriv :| 44 :| 0)+      (fromString "m/44h/0H/1" :: DerivPath) `shouldBe` (Deriv :| 44 :| 0 :/ 1)+    it "accepts the largest index, 2^31 - 1" $+      parsePath "m/2147483647'" `shouldBe` Just (ParsedPrv (Deriv :| 2147483647))+    it "refuses an index that does not fit in 31 bits instead of wrapping it" $ do+      parsePath "m/2147483648" `shouldBe` Nothing+      parsePath "m/4294967296" `shouldBe` Nothing -- 2^32, would wrap to 0+      parsePath "m/4294967297'" `shouldBe` Nothing -- 2^32 + 1, would wrap to 1+      parsePath "m/-4294967295" `shouldBe` Nothing -- would wrap to 1+      parsePath "m/18446744073709551617" `shouldBe` Nothing -- 2^64 + 1+    it "refuses anything but decimal digits in an index" $ do+      parsePath "m/0x10" `shouldBe` Nothing+      parsePath "m/(7)" `shouldBe` Nothing+      parsePath "m/+1" `shouldBe` Nothing+      parsePath "m/1e3" `shouldBe` Nothing+      parsePath "m/ 1" `shouldBe` Nothing+      parsePath "m/1 " `shouldBe` Nothing+      parsePath "m/1''" `shouldBe` Nothing+      parsePath "m/'" `shouldBe` Nothing+    it "refuses empty segments" $ do+      parsePath "m/" `shouldBe` Nothing+      parsePath "m//1" `shouldBe` Nothing+      parsePath "m/1/" `shouldBe` Nothing+    it "fails the JSON parse of an out-of-range index" $+      (A.decode "[\"m/4294967296\"]" :: Maybe [DerivPath]) `shouldBe` Nothing+    it "refuses to read a path index that does not fit in 31 bits" $+      readMaybe "Bip32SoftIndex 4294967296" `shouldBe` (Nothing :: Maybe Bip32PathIndex)+  describe "Fingerprint text" $ do+    -- the identifier of the vector 1 master key starts with 3442193e+    it "writes eight hex digits" $+      fingerprintToText (xPubFP ctx (xpub1 ctx)) `shouldBe` "3442193e"+    it "reads eight hex digits" $+      textToFingerprint "3442193e" `shouldBe` Right (xPubFP ctx (xpub1 ctx))+    it "refuses more or fewer than four bytes" $ do+      textToFingerprint "0011223344" `shouldSatisfy` isLeft+      textToFingerprint "001122" `shouldSatisfy` isLeft+      textToFingerprint "" `shouldSatisfy` isLeft+    it "refuses text that is not hex" $+      textToFingerprint "zzzzzzzz" `shouldSatisfy` isLeft+    it "parses from JSON and refuses the wrong length" $ do+      A.decode "\"3442193e\"" `shouldBe` Just (xPubFP ctx (xpub1 ctx))+      (A.decode "\"0011223344\"" :: Maybe Fingerprint) `shouldBe` Nothing+    it "refuses to read the wrong length" $+      readMaybe "\"0011223344\"" `shouldBe` (Nothing :: Maybe Fingerprint)+    it "refuses a literal of the wrong length" $+      evaluate ("0011223344" :: Fingerprint) `shouldThrow` anyErrorCall+  describe "bip32 subkey derivation using string path" $ do+    it "applyPath derives private and public keys along a path" $ testApplyPath ctx+    it "applyPath refuses private derivations on a public key and hard public derivations" $ testBadApplyPath ctx+    it "applyPath gives the keys of BIP32 test vector 1, chain m/0'/1" $ do+      let exported (XPrv k n) = xPrvExport n k+          exported (XPub k n) = xPubExport n ctx k+      fmap exported (applyPath ctx "m/0'/1" (XPrv (xprv1 ctx) btc))+        `shouldBe` Right "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"+      fmap exported (applyPath ctx "M/0'/1" (XPrv (xprv1 ctx) btc))+        `shouldBe` Right "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"+    it "public 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" $+      -- a key at depth 255 cannot have children+      forAll (arbitraryXPrvKey `suchThat` ((< 255) . (.depth))) $+        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++hex :: Text -> B.ByteString+hex = fromJust . decodeHex++-- | The address text and the index of an entry of a derivation list.+addressAndIndex :: (Address, a, KeyIndex) -> (Maybe Text, KeyIndex)+addressAndIndex (addr, _, i) = (addrToText btc addr, i)++-- | Master keys of BIP32 test vectors 1 and 2, and the testnet form of the+-- first.+xpub1Text, xprv1Text, tpub1Text, tprv1Text, xpub2Text :: Text+xpub1Text = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"+xprv1Text = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"+tpub1Text = "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp"+tprv1Text = "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m"+xpub2Text = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"++xpub1, xpub2 :: Ctx -> XPubKey+xpub1 ctx = fromJust (xPubImport btc ctx xpub1Text)+xpub2 ctx = fromJust (xPubImport btc ctx xpub2Text)++-- | The extended public keys m/0 to m/(n-1) of BIP 32 test vector 1.+accountKeys :: Ctx -> Int -> [XPubKey]+accountKeys ctx n =+  [ deriveXPubKey ctx (prvSubKey ctx master (fromIntegral i))+    | i <- [0 .. n - 1]+  ]+  where+    master = makeXPrvKey (hex "000102030405060708090a0b0c0d0e0f")++xprv1 :: Ctx -> XPrvKey+xprv1 _ = fromJust (xPrvImport btc xprv1Text)++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"++-- Test vector 4 checks that private keys with leading zero bytes are kept at+-- 32 bytes. Each entry is the path, the extended public key and the extended+-- private key.+m4 :: TestKey+m4 = "3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678"++vector4 :: [(String, Text, Text)]+vector4 =+  [ ( "m",+      "xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa",+      "xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv"+    ),+    ( "m/0'",+      "xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m",+      "xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G"+    ),+    ( "m/0'/1'",+      "xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt",+      "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1"+    )+  ]++-- Test vector 5 lists extended keys that must be refused, with the reason.+vector5 :: [(Text, String)]+vector5 =+  [ ("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm", "pubkey version / prvkey mismatch"),+    ("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH", "prvkey version / pubkey mismatch"),+    ("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn", "invalid pubkey prefix 04"),+    ("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ", "invalid prvkey prefix 04"),+    ("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4", "invalid pubkey prefix 01"),+    ("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J", "invalid prvkey prefix 01"),+    ("xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv", "zero depth with non-zero parent fingerprint (xprv)"),+    ("xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ", "zero depth with non-zero parent fingerprint (xpub)"),+    ("xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN", "zero depth with non-zero index (xprv)"),+    ("xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8", "zero depth with non-zero index (xpub)"),+    ("DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4", "unknown extended key version (first)"),+    ("DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9", "unknown extended key version (second)"),+    ("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx", "private key 0 not in 1..n-1"),+    ("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G", "private key n not in 1..n-1"),+    ("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY", "invalid pubkey 020000000000000000000000000000000000000000000000000000000000000007"),+    ("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL", "invalid checksum")+  ]++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,818 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Crypto.Keys.MnemonicSpec (spec) where++import Control.DeepSeq (NFData, force)+import Control.Exception (evaluate)+import Control.Monad (forM_, zipWithM_)+import Crypto.Hash (SHA256 (..), hashWith)+import Data.Bits (shiftR, (.&.))+import Data.ByteString qualified as BS+import Data.Either (fromRight)+import Data.List (isPrefixOf)+import Data.Map.Strict qualified as M+import Data.Maybe (fromJust)+import Data.Serialize (Serialize, encode)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Text.IO qualified as TIO+import Data.Vector qualified as V+import Data.Word (Word32, Word64)+import Haskoin (btc)+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import System.Timeout (timeout)+import Test.HUnit+import Test.Hspec+import Test.QuickCheck hiding ((.&.))++spec :: Spec+spec =+  describe "mnemonic" $ do+    -- data/bip39_wordlist.txt is bip-0039/english.txt of the BIPs repository,+    -- and data/bip39_vectors.json the vectors.json of python-mnemonic, which+    -- the BIP points to; its English vectors are also written out below as+    -- trezorVectors+    wordListFile <- runIO (TIO.readFile "data/bip39_wordlist.txt")+    vectorsFile <- runIO (readTestFile "bip39_vectors.json" :: IO (M.Map Text [(Text, Mnemonic, Text, Text)]))+    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 15 words" $ property toMnemonic160+    it "generate 24 words" $ property toMnemonic256+    it "refuses 64 bytes of entropy" $ property toMnemonic512+    it "generate any allowed number of words" toMnemonicVar+    it "refuses entropy below 16 bytes" toMnemonicTooShort+    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 n-bit entropy" 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 n-bit mnemonic to seed" mnemonicToSeedVar+    it "get bits" $ property getBitsByteCount+    it "get end bits" $ property getBitsEndBits+    it "passes the Trezor vectors" trezorVectorTests+    it "refuses entropy of 4 bytes" $+      toMnemonic (BS.replicate 4 0)+        `shouldBe` Left "toMnemonic: minimum entropy is 16 bytes (128 bits)"+    it "refuses entropy of 64 bytes" $+      toMnemonic (BS.replicate 64 0)+        `shouldBe` Left "toMnemonic: maximum entropy is 32 bytes (256 bits)"+    it "refuses entropy that is not a multiple of 4 bytes" $+      toMnemonic (BS.replicate 17 0)+        `shouldBe` Left "toMnemonic: entropy must be multiples of 4 bytes"+    it "gives 12 to 24 words for 16 to 32 bytes of entropy" $+      -- (bytes * 8 + bytes * 8 / 32) / 11 words: 16 bytes give 132 / 11 = 12+      forM_ [(16, 12), (20, 15), (24, 18), (28, 21), (32, 24)] $ \(bytes, count) ->+        fmap (length . T.words) (toMnemonic (BS.replicate bytes 0)) `shouldBe` Right count+    it "refuses a sentence of 3 words" $+      fromMnemonic "abandon abandon ability"+        `shouldBe` Left "fromMnemonic: wrong number of words: 3"+    it "refuses a sentence of 48 words" $+      fromMnemonic (T.unwords (replicate 48 "abandon"))+        `shouldBe` Left "fromMnemonic: wrong number of words: 48"+    it "refuses a sentence of only whitespace" $+      fromMnemonic "   " `shouldBe` Left "fromMnemonic: wrong number of words: 0"+    it "refuses a word that is not in the list, naming its position and not the word" $+      -- the list is lower case, so "Abandon" is not in it+      fromMnemonic "Abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"+        `shouldBe` Left "fromMnemonic: word 1 is not in the list"+    it "names the positions of two unknown words" $+      fromMnemonic "legal winner thankk year wave sausage wort useful legal winner thank yellow"+        `shouldBe` Left "fromMnemonic: words 3 and 7 are not in the list"+    it "keeps every word of an upper-case sentence out of the error text" $ do+      -- A sentence typed in capitals is a real secret. It used to be+      -- echoed whole.+      let capitals = "LEGAL WINNER THANK YEAR WAVE SAUSAGE WORTH USEFUL LEGAL WINNER THANK YELLOW"+          refusal = Left "fromMnemonic: words 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 and 12 are not in the list"+      fromMnemonic capitals `shouldBe` refusal+      mnemonicToSeed "" capitals `shouldBe` refusal+    it "reports a failed checksum without its values" $+      -- "zoo" in place of "yellow": every word is in the list+      fromMnemonic "legal winner thank year wave sausage worth useful legal winner thank zoo"+        `shouldBe` Left "fromMnemonic: checksum failed"+    it "matches words after NFKD normalisation" $+      -- the first word in fullwidth letters, which NFKD maps back to ASCII+      fmap encodeHex (fromMnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")+        `shouldBe` Right "00000000000000000000000000000000"+    it "normalises the passphrase to NFKD before deriving the seed" $ do+      -- PBKDF2-HMAC-SHA512, 2048 rounds, of the sentence with the salt+      -- "mnemonic" ++ NFKD("café"), as python-mnemonic computes it. The+      -- precomposed and the decomposed spelling must give the same seed.+      fmap encodeHex (mnemonicToSeed "caf\x00e9" abandon12) `shouldBe` Right cafeSeed+      fmap encodeHex (mnemonicToSeed "cafe\x0301" abandon12) `shouldBe` Right cafeSeed+    it "has the 2048 BIP39 words in order" $ do+      V.length wordList `shouldBe` 2048+      wordList V.! 0 `shouldBe` "abandon"+      wordList V.! 2047 `shouldBe` "zoo"+      M.size wordListMap `shouldBe` 2048+      M.lookup "abandon" wordListMap `shouldBe` Just 0+      M.lookup "zoo" wordListMap `shouldBe` Just 2047+    it "has the published English word list, by its SHA-256" $+      -- sha256sum of bip-0039/english.txt in the BIPs repository, one word+      -- per line with a newline after each+      show (hashWith SHA256 (E.encodeUtf8 (T.unlines (V.toList wordList))))+        `shouldBe` "2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"+    it "has the word list of the data directory, word for word" $+      V.toList wordList `shouldBe` T.lines wordListFile+    it "has the 24 English vectors of the published file, as written out here" $ do+      M.lookup "english" vectorsFile `shouldBe` Just trezorVectors+      length trezorVectors `shouldBe` 24+    it "accepts the 24-word sentence of Trezor's firmware tests" $+      -- 22 times "abstract", then "artefact electric"; the entropy is the+      -- 11-bit index of each word in a row, 6 for abstract, 36 and 572+      fmap encodeHex (fromMnemonic (T.unwords (replicate 22 "abstract" ++ ["artefact", "electric"])))+        `shouldBe` Right "00e01c0380700e01c0380700e01c0380700e01c0380700e01c0380700e01c33a"+    it "refuses each old vector sentence with a word put in front, as Trezor's firmware does" $+      -- 13, 19 or 25 words+      forM_ mss $ \m ->+        fromMnemonic ("above " <> m) `shouldBe` Left ("fromMnemonic: wrong number of words: " <> show (length (T.words m) + 1))+    it "refuses each old vector sentence without its first word, as Trezor's firmware does" $+      -- 11, 17 or 23 words+      forM_ mss $ \m ->+        fromMnemonic (T.unwords (drop 1 (T.words m))) `shouldBe` Left ("fromMnemonic: wrong number of words: " <> show (length (T.words m) - 1))+    it "refuses the wrong-checksum sentence of python-mnemonic's tests" $+      fromMnemonic "bless cloud wheel regular tiny venue bird web grief security dignity zoo"+        `shouldBe` Left "fromMnemonic: checksum failed"+    it "refuses the sentences of the JavaScript library's custom word list, naming their positions" $+      -- bitcoinjs/bip39 tests them against a word list of its own+      fromMnemonic "aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n ab0ut"+        `shouldBe` Left "fromMnemonic: words 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 and 12 are not in the list"+    it "has words of 3 to 8 letters, so a longer word is never looked up" $+      -- the lengths of bip-0039/english.txt+      (minimum (fmap T.length wordList), maximum (fmap T.length wordList)) `shouldBe` (3, 8)+    it "refuses 36 bytes of entropy, the first size above the limit" $+      toMnemonic (BS.replicate 36 0)+        `shouldBe` Left "toMnemonic: maximum entropy is 32 bytes (256 bits)"+    it "refuses 12 bytes of entropy, the last size below the limit" $+      toMnemonic (BS.replicate 12 0)+        `shouldBe` Left "toMnemonic: minimum entropy is 16 bytes (128 bits)"+    it "refuses a sentence of 9 words and one of 27, the sizes next to the allowed ones" $ do+      fromMnemonic (T.unwords (replicate 9 "abandon"))+        `shouldBe` Left "fromMnemonic: wrong number of words: 9"+      fromMnemonic (T.unwords (replicate 27 "abandon"))+        `shouldBe` Left "fromMnemonic: wrong number of words: 27"+    it "gives no seed for a sentence with a wrong checksum" $+      -- twelve times "abandon": the checksum of 16 zero bytes is "about"+      mnemonicToSeed "" (T.unwords (replicate 12 "abandon"))+        `shouldBe` Left "fromMnemonic: checksum failed"+    it "gives no seed for a sentence of 11 words" $+      mnemonicToSeed "" (T.unwords (replicate 11 "abandon"))+        `shouldBe` Left "fromMnemonic: wrong number of words: 11"+    it "gives the same seed whatever the white space between the words" $+      -- the second Trezor vector, with two spaces, a tab and a line break+      fmap encodeHex (mnemonicToSeed "TREZOR" "  legal  winner thank\tyear wave sausage worth useful\nlegal winner thank yellow ")+        `shouldBe` Right "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"+    it "takes an ASCII passphrase as it is" $+      -- lower-case letters and spaces: the seed python-mnemonic 0.21 gives+      fmap encodeHex (mnemonicToSeed "correct horse battery staple" abandon12)+        `shouldBe` Right "af1c9b2fb5ae8365dc09b9abfd57c2319bf328aee47fad36e1b06c5c115f20285dd3b29aa5a84bbc8167237869e80a0556581cecfab3f2363f844f082bc87837"+    it "refuses a sentence of one word of 10,001 characters within a second" $+      -- "a" followed by 10,000 combining acute accents. Normalising it took+      -- about ten seconds, and the time grew with the square of the length.+      withinOneSecond (fromMnemonic ("a" <> T.replicate 10000 "\x0301"))+        `shouldReturn` Just (Left "fromMnemonic: wrong number of words: 1")+    it "refuses a word of 10,001 characters in a sentence of 12 words within a second" $+      withinOneSecond (fromMnemonic (T.unwords (("a" <> T.replicate 10000 "\x0301") : replicate 11 "abandon")))+        `shouldReturn` Just (Left "fromMnemonic: word 1 is not in the list")+    it "refuses a passphrase with 10,000 combining characters in a row within a second" $+      withinOneSecond (mnemonicToSeed ("a" <> T.replicate 10000 "\x0301") abandon12)+        `shouldReturn` Just (Left "mnemonicToSeed: the passphrase has more than 30 combining characters in a row")+    it "accepts a passphrase with 30 combining characters in a row, and refuses 31" $ do+      -- The limit of the Stream-Safe Text Format of Unicode (UAX #15). The+      -- seed is the one python-mnemonic 0.21 gives.+      fmap encodeHex (mnemonicToSeed ("a" <> T.replicate 30 "\x0301") abandon12)+        `shouldBe` Right "dcd74c9cf73d2153256e7eb6634c8e3210d81067c00eb522dae1e5b69b6e9068fc422798c5933be591f8828f1f516bbfe4add8f46c8ac854ff17315ee90df46d"+      mnemonicToSeed ("a" <> T.replicate 31 "\x0301") abandon12+        `shouldBe` Left "mnemonicToSeed: the passphrase has more than 30 combining characters in a row"+    it "counts the combining characters of the normalised passphrase, not of its spelling" $ do+      -- U+FF9E, the halfwidth voiced sound mark, is a letter that NFKD turns+      -- into the combining character U+3099. The seed is python-mnemonic's.+      fmap encodeHex (mnemonicToSeed ("\xff76" <> T.replicate 30 "\xff9e") abandon12)+        `shouldBe` Right "ff2b0cc184bad26986cd04e499def27a5c50a2d35b95489d66ee2a04de568e639d70b3e3ea553956d55fe65629c030f446d63407472d6982270221a26417b88b"+      mnemonicToSeed ("\xff76" <> T.replicate 31 "\xff9e") abandon12+        `shouldBe` Left "mnemonicToSeed: the passphrase has more than 30 combining characters in a row"+    it "accepts a long passphrase of accented letters, in either spelling" $ do+      -- 1,000 times "é", decomposed and precomposed: the seed of+      -- python-mnemonic 0.21 for both+      let seed = "62654f25fab756984c5220d458717da768c5e31db01773f14a6e4fbde575a03d0d6edca6fa48ad7c71b4ebb9822fbb25f0ef95d3246261016e2aee21361b247e"+      fmap encodeHex (mnemonicToSeed (T.replicate 1000 "e\x0301") abandon12) `shouldBe` Right seed+      fmap encodeHex (mnemonicToSeed (T.replicate 1000 "\x00e9") abandon12) `shouldBe` Right seed+    it "gives 15 and 21 words for 20 and 28 bytes of entropy, with their seeds and keys" $+      -- The published vectors have no 15- or 21-word case. These were+      -- computed with an independent Python implementation of BIP39 from+      -- hashlib: the checksum is the first 5 or 7 bits of the SHA-256 of+      -- the entropy, the seed is PBKDF2-HMAC-SHA512 of the sentence with+      -- 2048 rounds and the salt "mnemonic" ++ "TREZOR", and the key is+      -- the BIP32 master key of the seed.+      forM_ fifteenAndTwentyOneWordVectors $ \(entTxt, mnem, seedTxt, xprvTxt) -> do+        let ent = fromJust (decodeHex entTxt)+        toMnemonic ent `shouldBe` Right mnem+        fmap encodeHex (mnemonicToSeed "TREZOR" mnem) `shouldBe` Right seedTxt+        fmap (xPrvExport btc . makeXPrvKey) (mnemonicToSeed "TREZOR" mnem) `shouldBe` Right xprvTxt++-- | The value in normal form, or nothing when computing it takes more than+-- a second.+withinOneSecond :: (NFData a) => a -> IO (Maybe a)+withinOneSecond = timeout 1000000 . evaluate . force++-- | The first Trezor sentence, for 16 zero bytes of entropy.+abandon12 :: Mnemonic+abandon12 =+  "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"++-- | Seed of 'abandon12' with the passphrase "café".+cafeSeed :: Text+cafeSeed =+  "af8bbd2566df7b69d926f2b09dfdbd75db6c994a3399b2cc65f928d63e3fd4e6\+  \1218ee0d15f8c810be4d45e66d47b43c15a5cc753976b1666912377ff7ae9818"++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 -> err == "fromMnemonic: checksum failed"++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++-- The next three lists are the English vectors of an earlier vectors.json+-- of python-mnemonic, whose random cases (from the 13th on) were replaced+-- later; Trezor's firmware still tests the sentences as valid.+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"+  ]++-- | Entropy, sentence, seed with the passphrase "TREZOR" and master key for+-- 20 and 28 bytes of entropy, which the published vectors do not cover.+fifteenAndTwentyOneWordVectors :: [(Text, Mnemonic, Text, Text)]+fifteenAndTwentyOneWordVectors =+  [ ( "0000000000000000000000000000000000000000",+      "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address",+      "fa08713f46bf5cb48728ceb70e3aae1bc53c5cb7b4e29c5610261d1cbb7be3bed4d805256fec515754d2be35974fc5da678168e9d9bb0cb70948026923b0def3",+      "xprv9s21ZrQH143K2nE8x21M7hF4msPN7WC7b6yCn1usVKBA9myCGie4yQCqsw479qmbjXeeRZgMjnNwvWweSCbBeUSPADo2aZbfpkTvJ8de1Qi"+    ),+    ( "ffffffffffffffffffffffffffffffffffffffff",+      "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrist",+      "bfee6f9d2bcfa1331bd6482a24abca521e5f7e769498b9a0146672194c7356e4e409be22bc379c8b64fee2aa24b54d3ec20d10a083eaa5d1d6b4b365941ad37c",+      "xprv9s21ZrQH143K4YQcGfMEpbEoWtj4dJ8aSkUPCXEbg3kWsR8kcdcmhGK3y4swwdjUpyGBfWT9bpizzQJrznSRuFx9K4FKeoNEYT45rLY11Hc"+    ),+    ( "00000000000000000000000000000000000000000000000000000000",+      "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit",+      "e7dadc189d2e8d07ac278d9ec98a1d2d327e4a6b7df494c00cbf2cbf2d3543dac7000fc72d4ada8d9997dc8db388ff22c6d79f604a7455f2df5534a28eee04c6",+      "xprv9s21ZrQH143K3CXWs34yKD87zsm1AVExnEEUnH8rKWqWVguXRprSz8bFFZDbKY7oai19nPTZ68HnW8xX62HFr8WxsNL4CEYmGLcTxAyKNHd"+    ),+    ( "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+      "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo veteran",+      "4aa0af4ca02ef1d9fa675cd02aa06d318425564e7fadd3d51b6165cc56d77398f28d8522073cd036c2a4a24a83e919211c84500d96cb120084e613ff5fcd96c1",+      "xprv9s21ZrQH143K4DvgrFvRv4J7m2qehW4MCdmLfRrpAJQFzgVFGi8L9QaapqJPQLyX9SAeGWATu6vXNYmo542LR8yMf4W7DMF1Xgsp96RYWDy"+    )+  ]++-- 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++-- 64 bytes of entropy are above the 32 bytes BIP39 allows.+toMnemonic512 ::+  ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+toMnemonic512 ((a, b, c, d), (e, f, g, h)) =+  toMnemonic bs == Left "toMnemonic: maximum entropy is 32 bytes (256 bits)"+  where+    bs =+      BS.concat+        [ encode a,+          encode b,+          encode c,+          encode d,+          encode e,+          encode f,+          encode g,+          encode h+        ]++-- Fewer than 4 words of 32 bits are below the 16 bytes BIP39 allows.+toMnemonicTooShort :: Property+toMnemonicTooShort =+  forAll (choose (0, 3) >>= \n -> vectorOf n (arbitrary :: Gen Word32)) $ \ls ->+    toMnemonic (binWordsToBS ls)+      == Left "toMnemonic: minimum entropy is 16 bytes (128 bits)"++-- | Entropy of 16 to 32 bytes in steps of 4 bytes, the sizes BIP39 allows.+allowedEntropy :: Gen BS.ByteString+allowedEntropy = do+  n <- choose (4, 8)+  binWordsToBS <$> vectorOf n (arbitrary :: Gen Word32)++-- Every 32 bits of entropy add one checksum bit, and every 11 bits give a word.+toMnemonicVar :: Property+toMnemonicVar = forAll allowedEntropy $ \bs ->+  let bl = BS.length bs+      wc = (bl * 8 + bl `div` 4) `div` 11+      l =+        length+          . T.words+          . fromRight (error "Could not decode mnemonic sentence")+          $ toMnemonic bs+   in l == wc++{- 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)++fromToMnemonicVar :: Property+fromToMnemonicVar = forAll allowedEntropy $ \bs ->+  fromRight+    (error "Could not decode mnemonic entropy")+    (fromMnemonic =<< toMnemonic bs)+    == 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++mnemonicToSeedVar :: Property+mnemonicToSeedVar = forAll allowedEntropy $ \bs ->+  BS.length+    ( fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    )+    == 64++{- 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/Keys/Slip39Spec.hs view
@@ -0,0 +1,762 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- The vectors of the reference implementation live in+-- data/slip39_vectors.json, copied from+-- https://github.com/trezor/python-shamir-mnemonic/blob/master/vectors.json:+-- a description, the mnemonics, the master secret they give with the+-- passphrase "TREZOR" (empty when they must be refused) and the BIP-32 key+-- of that secret. The split cases in data/slip39_split.json were made by+-- test/Haskoin/Crypto/Keys/slip39_verify.py, an implementation of the+-- specification independent of this one: a scheme, the parameters and the+-- random values of a split, and the mnemonics they must give. The word list+-- in data/slip39_wordlist.txt is the file of the specification.+module Haskoin.Crypto.Keys.Slip39Spec (spec) where++import Control.DeepSeq (force)+import Control.Exception (evaluate)+import Control.Monad (forM_)+import Control.Monad.State.Strict (State, modify, runState)+import Crypto.Hash (SHA256 (..), hashWith)+import Crypto.Random (drgNewTest, getRandomBytes, withDRG)+import Data.Aeson (FromJSON (..), withObject, (.:))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Char (ord)+import Data.List (nub, sort, subsequences)+import Data.Map.Strict qualified as M+import Data.Maybe (fromJust)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Text.IO qualified as TIO+import Data.Vector qualified as V+import Haskoin (btc, makeXPrvKey, xPrvExport)+import Haskoin.Crypto.Keys.Slip39+import Haskoin.Util (decodeHex, encodeHex, readTestFile)+import System.Timeout (timeout)+import Test.Hspec+import Test.QuickCheck++-- | A case of data/slip39_split.json.+data SplitCase = SplitCase+  { caseDescription :: Text,+    caseScheme :: Scheme,+    caseIdentifier :: Int,+    caseExtendable :: Bool,+    caseIterationExponent :: Int,+    casePassphrase :: Text,+    caseMasterSecret :: ByteString,+    caseRandom :: [SplitRandom],+    caseMnemonics :: [[Text]]+  }++instance FromJSON SplitCase where+  parseJSON = withObject "SplitCase" $ \o ->+    SplitCase+      <$> o .: "description"+      <*> (o .: "scheme" >>= withObject "Scheme" (\s -> Scheme <$> s .: "groupThreshold" <*> s .: "groups"))+      <*> o .: "identifier"+      <*> o .: "extendable"+      <*> o .: "iterationExponent"+      <*> o .: "passphrase"+      <*> (hex <$> o .: "masterSecret")+      <*> (o .: "random" >>= mapM (withObject "SplitRandom" (\r -> SplitRandom <$> (map hex <$> r .: "shares") <*> (hex <$> r .: "digest"))))+      <*> o .: "mnemonics"++hex :: Text -> ByteString+hex = fromJust . decodeHex++spec :: Spec+spec = do+  vectors <- runIO (readTestFile "slip39_vectors.json" :: IO [(Text, [Text], Text, Text)])+  splitCases <- runIO (readTestFile "slip39_split.json" :: IO [SplitCase])+  wordListFile <- runIO (TIO.readFile "data/slip39_wordlist.txt")+  describe "the word list" $ do+    it "has the 1024 words of the specification, academic to zero" $ do+      V.length slip39WordList `shouldBe` 1024+      V.head slip39WordList `shouldBe` "academic"+      V.last slip39WordList `shouldBe` "zero"+    it "is the published file, by its SHA-256" $+      -- sha256sum of slip-0039/wordlist.txt in the slips repository, one+      -- word per line with a newline after each+      show (hashWith SHA256 (E.encodeUtf8 (T.unlines (V.toList slip39WordList))))+        `shouldBe` "bcc4555340332d169718aed8bf31dd9d5248cb7da6e5d355140ef4f1e601eec3"+    it "is the file in the data directory, word for word" $+      V.toList slip39WordList `shouldBe` T.lines wordListFile+    it "is sorted" $+      V.toList slip39WordList `shouldBe` sort (V.toList slip39WordList)+    it "has words of 4 to 8 letters" $+      forM_ slip39WordList $ \w -> T.length w `shouldSatisfy` (\n -> n >= 4 && n <= 8)+    it "has distinct first four letters" $+      length (nub (map (T.take 4) (V.toList slip39WordList))) `shouldBe` 1024+    it "gives each word its index" $ do+      M.lookup "academic" slip39WordListMap `shouldBe` Just 0+      M.lookup "satoshi" slip39WordListMap `shouldBe` Just 781+      M.lookup "zero" slip39WordListMap `shouldBe` Just 1023+      M.size slip39WordListMap `shouldBe` 1024+    it "reads the words of a mnemonic in any case" $+      mnemonicToIndices "Academic ZERO satoshi" `shouldBe` Right [0, 1023, 781]+    it "names the position of a word that is not in the list, and not the word" $+      -- a share is a secret, and a mistyped word is most of the right one+      mnemonicToIndices "academic abandon" `shouldBe` Left "Word 2 is not in the SLIP-39 word list"+    it "reads ASCII letters only: a look-alike of another script is not a letter of the list" $+      -- U+212A KELVIN SIGN lower-cases to "k" under the Unicode rules+      mnemonicToIndices "\x212A\&ernel kidney" `shouldBe` Left "Word 1 is not in the SLIP-39 word list"+    it "writes indices as words" $+      indicesToMnemonic [0, 781, 1023] `shouldBe` Right "academic satoshi zero"+    it "refuses an index outside the list instead of raising an error" $ do+      indicesToMnemonic [0, 1024] `shouldBe` Left "The word index must be between 0 and 1023, not 1024"+      indicesToMnemonic [-1] `shouldBe` Left "The word index must be between 0 and 1023, not -1"++  describe "the field GF(256)" $ do+    it "multiplies 0x57 by 0x83 to 0xc1, the example of FIPS 197" $+      gfMul 0x57 0x83 `shouldBe` 0xc1+    it "multiplies 0x57 by 0x13 to 0xfe, the other example of FIPS 197" $+      gfMul 0x57 0x13 `shouldBe` 0xfe+    it "multiplies 2 by 0x80 to 0x1b: 0x100 reduced by x^8 + x^4 + x^3 + x + 1" $+      gfMul 2 0x80 `shouldBe` 0x1b+    it "multiplies by zero to zero and by one to the byte itself" $ property $ \a ->+      (gfMul a 0, gfMul 0 a, gfMul a 1, gfMul 1 a) === (0, 0, a, a)+    it "commutes" $ property $ \a b ->+      gfMul a b === gfMul b a+    it "inverts 0x53 to 0xca, the example of the AES S-box" $ do+      gfInv 0x53 `shouldBe` Just 0xca+      gfMul 0x53 0xca `shouldBe` 1+    it "inverts 3, the generator, to 0xf6" $+      gfInv 3 `shouldBe` Just 0xf6+    it "has no inverse of zero" $ do+      gfInv 0 `shouldBe` Nothing+      gfDiv 1 0 `shouldBe` Nothing+    it "multiplies every non-zero byte by its inverse to one" $+      forM_ [1 .. 255] $ \a -> (gfMul a <$> gfInv a) `shouldBe` Just 1+    it "divides 0xc1 by 0x83 to 0x57" $+      gfDiv 0xc1 0x83 `shouldBe` Just 0x57++  describe "interpolation" $ do+    it "takes the line through (0, 0x00) and (1, 0x80) to (2, 0x1b)" $+      -- f(x) = 0x80 x, and 2 times 0x80 is 0x1b in the field+      interpolate 2 [(0, "\x00"), (1, "\x80")] `shouldBe` Right "\x1b"+    it "takes that line to (3, 0x9b)" $+      -- 3 times 0x80 is 0x180, reduced by 0x11b to 0x9b+      interpolate 3 [(0, "\x00"), (1, "\x80")] `shouldBe` Right "\x9b"+    it "takes the line through (1, 0x01) and (2, 0x03) to (0, 0xf6) and (3, 0xf4)" $ do+      -- the slope is (3 xor 1) / (2 xor 1) = 2 / 3 = 2 times 0xf6 = 0xf7, so+      -- f(0) = 1 xor 0xf7 = 0xf6 and f(3) = 0xf6 xor 3 times 0xf7 = 0xf6 xor 2+      interpolate 0 [(1, "\x01"), (2, "\x03")] `shouldBe` Right "\xf6"+      interpolate 3 [(1, "\x01"), (2, "\x03")] `shouldBe` Right "\xf4"+    it "interpolates each byte on its own" $+      -- the first byte as the line through 0x00 and 0x80, the second+      -- through 0x00 and 0x01: f(2) = 2+      interpolate 2 [(0, "\x00\x00"), (1, "\x80\x01")] `shouldBe` Right "\x1b\x02"+    it "gives the constant of a constant polynomial anywhere" $+      interpolate 200 [(5, B.pack [0 .. 15]), (9, B.pack [0 .. 15])] `shouldBe` Right (B.pack [0 .. 15])+    it "gives a point its own value" $+      interpolate 1 [(0, "\x00"), (1, "\x80")] `shouldBe` Right "\x80"+    it "refuses no points" $+      interpolate 1 [] `shouldBe` Left "There are no shares to interpolate"+    it "refuses two points at one index" $+      interpolate 2 [(1, "\x01"), (1, "\x03")] `shouldBe` Left "The shares do not have distinct indices"+    it "refuses values of different lengths" $+      interpolate 2 [(0, "\x01"), (1, "\x03\x04")] `shouldBe` Left "The shares do not have the same length"++  describe "the checksum" $ do+    -- The first vector: 17 data words then "critical decision keyboard"+    let vector1Words = [248, 288, 0, 0, 17, 753, 521, 840, 372, 497, 155, 670, 192, 448, 297, 249, 23]+    it "makes the three words critical decision keyboard for the first vector" $ do+      rs1024Checksum (customizationString False) vector1Words `shouldBe` [173, 196, 496]+      indicesToMnemonic [173, 196, 496] `shouldBe` Right "critical decision keyboard"+    it "verifies the first vector" $+      rs1024Verify (customizationString False) (vector1Words ++ [173, 196, 496]) `shouldBe` True+    it "fails when the last word changes to kidney" $+      rs1024Verify (customizationString False) (vector1Words ++ [173, 196, 497]) `shouldBe` False+    it "has a polymod of 1 for a valid mnemonic after its customization string" $+      rs1024Polymod (customizationString False ++ vector1Words ++ [173, 196, 496]) `shouldBe` 1+    it "feeds shamir or shamir_extendable to the polymod as ASCII" $ do+      customizationString False `shouldBe` map ord "shamir"+      customizationString True `shouldBe` map ord "shamir_extendable"+    it "verifies an extendable share under shamir_extendable only" $ do+      -- Vector 42, an extendable share+      let indices = indicesOf (mnemonicOf vectors 42)+      rs1024Verify (customizationString True) indices `shouldBe` True+      rs1024Verify (customizationString False) indices `shouldBe` False+    it "verifies every mnemonic of the valid vectors" $+      forM_ [(d, m) | (d, ms, s, _) <- vectors, s /= "", m <- ms] $ \(d, m) -> do+        let indices = indicesOf m+            -- the extendable flag is bit 24 of the 40-bit header, bit 4 of the second word+            ext = odd ((indices !! 1) `div` 16)+        (d, rs1024Verify (customizationString ext) indices) `shouldBe` (d, True)++  describe "share mnemonics" $ do+    it "decodes the share of the first vector" $+      decodeShare (mnemonicOf vectors 1) `shouldBe` Right vector1+    it "encodes it back to its 20 words" $+      encodeShare vector1 `shouldBe` Right (mnemonicOf vectors 1)+    it "decodes an extendable share with an iteration exponent of 3" $+      -- Vector 42+      decodeShare (mnemonicOf vectors 42)+        `shouldBe` Right (singleShare 29019 True 3 (hex "9e8773c7313b11d3bfe219291976433b"))+    it "decodes a share of 33 words as 32 bytes of value" $+      -- Vector 20+      decodeShare (mnemonicOf vectors 20)+        `shouldBe` Right (singleShare 29172 False 0 (hex "d772fee46424e100bec16d165f1fcc346d1e8d909da580f9f9f04ea5c788d212"))+    it "encodes a 32-byte value as 33 words and a 16-byte one as 20" $ do+      fmap (length . T.words) (encodeShare vector1) `shouldBe` Right 20+      fmap (length . T.words) (encodeShare vector1 {value = B.replicate 32 0}) `shouldBe` Right 33+    it "encodes a 20-byte value as 23 words, with no padding" $+      -- 160 bits is 16 words exactly+      fmap (length . T.words) (encodeShare vector1 {value = B.replicate 20 0}) `shouldBe` Right 23+    it "reads words in upper case" $+      decodeShare (T.toUpper (mnemonicOf vectors 1)) `shouldBe` Right vector1+    it "refuses 19 words" $+      -- Vector 39+      decodeShare (mnemonicOf vectors 39) `shouldBe` Left "The mnemonic has 19 words, but a share has at least 20"+    it "refuses 21 words, which would leave 12 bits of padding" $+      -- Vector 40+      decodeShare (mnemonicOf vectors 40) `shouldBe` Left "The mnemonic has 21 words, which is not a valid share length"+    it "refuses 24 words, which would leave 10 bits of padding" $+      -- 17 words of value are 170 bits, and 170 modulo 16 is 10. The+      -- checksum words are those of the reference implementation, so the+      -- length is the only thing wrong.+      decodeShare (T.unwords (["duckling", "enlarge"] ++ replicate 19 "academic" ++ ["diminish", "hearing", "undergo"]))+        `shouldBe` Left "The mnemonic has 24 words, which is not a valid share length"+    it "refuses 1025 words, the first share length above the 1023 words the checksum covers" $+      -- A Reed-Solomon code over GF(1024) has at most 1023 symbols. This is+      -- the share of 1272 zero bytes as the reference implementation writes+      -- it, with its checksum, so the length is the only thing wrong. 1024+      -- words are no share length: they would leave 10 bits of padding.+      decodeShare (T.unwords (["duckling", "enlarge"] ++ replicate 1020 "academic" ++ ["sack", "sugar", "course"]))+        `shouldBe` Left "The mnemonic has 1025 words, but a share has at most 1023"+    it "refuses a share of 256,007 words with a valid checksum within a second" $ do+      -- The header of the first vector, then 256,000 words of value with+      -- every bit set, which leave no padding, then their checksum. Such a+      -- share used to have its words turned into one number, in time that+      -- grew with the square of the length: 1.8 seconds for half as many+      -- words. The checksum is computed here because the share is the+      -- input of the test, not its expectation.+      let dataWords = [248, 288, 0, 0] ++ replicate 256000 1023+          checksum = rs1024Checksum (customizationString False) dataWords+          mnemonic = T.unwords (map (slip39WordList V.!) (dataWords ++ checksum))+      _ <- evaluate (T.length mnemonic)+      -- the length of the value forces its bytes+      timeout 1000000 (evaluate (force (fmap (\s -> B.length s.value) (decodeShare mnemonic))))+        `shouldReturn` Just (Left "The mnemonic has 256007 words, but a share has at most 1023")+    it "encodes a value of 1270 bytes as 1023 words, the longest share, and reads it back" $ do+      -- (1023 - 7) words of 10 bits are 10160 bits, 1270 bytes. The three+      -- checksum words are those of the reference implementation.+      let longest = vector1 {value = B.replicate 1270 0}+          mnemonic = encodeShare longest+      fmap (length . T.words) mnemonic `shouldBe` Right 1023+      fmap (drop 1020 . T.words) mnemonic `shouldBe` Right ["academic", "shaped", "dictate"]+      (mnemonic >>= decodeShare) `shouldBe` Right longest+    it "refuses to encode a value of 1272 bytes, which would take 1025 words" $+      encodeShare vector1 {value = B.replicate 1272 0}+        `shouldBe` Left "The share value must have at most 1270 bytes, not 1272"+    it "refuses a wrong checksum" $+      -- Vector 2: the first vector with its last word changed+      decodeShare (mnemonicOf vectors 2) `shouldBe` Left "The checksum of the mnemonic is invalid"+    it "refuses padding bits that are not zero" $+      -- Vector 3+      decodeShare (mnemonicOf vectors 3) `shouldBe` Left "The padding bits of the share value are not zero"+    it "refuses a group threshold above the group count" $+      -- Vector 10, first share+      decodeShare (mnemonicOf vectors 10) `shouldBe` Left "The group threshold is greater than the group count"+    it "refuses a word that is not in the list" $+      decodeShare "duckling enlarge academic academic agency result length solution fridge kidney coal piece deal husband erode duke ajar critical decision abandon"+        `shouldBe` Left "Word 20 is not in the SLIP-39 word list"+    it "refuses to encode an identifier of 16 bits" $+      encodeShare (singleShare 32768 False 0 vector1.value) `shouldBe` Left "The identifier must be between 0 and 32767, not 32768"+    it "refuses to encode an iteration exponent of 16" $+      encodeShare (singleShare 7945 False 16 vector1.value) `shouldBe` Left "The iteration exponent must be between 0 and 15, not 16"+    it "refuses to encode a group index of 16" $+      encodeShare vector1 {groupIndex = 16} `shouldBe` Left "The group index must be between 0 and 15, not 16"+    it "refuses to encode a group threshold of 0" $+      encodeShare (groupShare 0 1 vector1.value) `shouldBe` Left "The group threshold must be between 1 and 16, not 0"+    it "refuses to encode a group count of 17" $+      encodeShare vector1 {groupCount = 17} `shouldBe` Left "The group count must be between 1 and 16, not 17"+    it "refuses to encode a group threshold above the group count" $+      encodeShare (groupShare 2 1 vector1.value) `shouldBe` Left "The group threshold is greater than the group count"+    it "refuses to encode a member index of 16" $+      encodeShare vector1 {memberIndex = 16} `shouldBe` Left "The member index must be between 0 and 15, not 16"+    it "refuses to encode a member threshold of 17" $+      encodeShare vector1 {memberThreshold = 17} `shouldBe` Left "The member threshold must be between 1 and 16, not 17"+    it "refuses to encode an identifier of -1" $+      encodeShare (singleShare (-1) False 0 vector1.value) `shouldBe` Left "The identifier must be between 0 and 32767, not -1"+    it "refuses to encode an iteration exponent of -1" $+      encodeShare (singleShare 7945 False (-1) vector1.value) `shouldBe` Left "The iteration exponent must be between 0 and 15, not -1"+    it "refuses to encode a group index of -1" $+      encodeShare vector1 {groupIndex = -1} `shouldBe` Left "The group index must be between 0 and 15, not -1"+    it "refuses to encode a group count of 0" $+      encodeShare (groupShare 1 0 vector1.value) `shouldBe` Left "The group count must be between 1 and 16, not 0"+    it "refuses to encode a member index of -1" $+      encodeShare vector1 {memberIndex = -1} `shouldBe` Left "The member index must be between 0 and 15, not -1"+    it "refuses to encode a member threshold of 0" $+      encodeShare vector1 {memberThreshold = 0} `shouldBe` Left "The member threshold must be between 1 and 16, not 0"+    it "refuses to encode a value of 15 bytes" $+      encodeShare vector1 {value = B.replicate 15 0} `shouldBe` Left "The share value must have at least 16 bytes, not 15"+    it "refuses to encode a value of 17 bytes" $+      encodeShare vector1 {value = B.replicate 17 0} `shouldBe` Left "The share value must have an even number of bytes, not 17"+    it "shows a share without its value" $+      show vector1+        `shouldBe` "Share {identifier = 7945, extendable = False, iterationExponent = 0, groupIndex = 0, groupThreshold = 1, groupCount = 1, memberIndex = 0, memberThreshold = 1, value = <redacted>}"++  describe "splitting and recovering a secret" $ do+    let secret = "ABCDEFGHIJKLMNOP"+        digestRandom = B.pack [0 .. 11]+        noRandom = SplitRandom [] B.empty+        -- 2-of-3 of the secret with that random part: the verifier's values+        share0 = (0, hex "8459b115d432d65461876350dc3ade25")+        share1 = (1, hex "2b463c939175931f2cc82e1b997d9b7e")+        share2 = (2, hex "c167b0025ebc5cc2fb19f9c656b45493")+        twoOfThree = [share0, share1, share2]+    it "gives the secret itself as every share at a threshold of 1" $+      splitSecret 1 3 secret noRandom `shouldBe` Right [(0, secret), (1, secret), (2, secret)]+    it "splits 2-of-3 as the verifier does, from the random part of the digest share" $+      splitSecret 2 3 secret (SplitRandom [] digestRandom) `shouldBe` Right twoOfThree+    it "recovers the secret from any two of the three" $+      forM_ [[share0, share1], [share1, share2], [share0, share2]] $ \two ->+        recoverSecret 2 two `shouldBe` Right secret+    it "recovers it from all three as well" $+      recoverSecret 2 twoOfThree `shouldBe` Right secret+    it "splits 3-of-4 as the verifier does, from a random share and the random part" $+      -- The random share takes index 0 as it is+      splitSecret 3 4 secret (SplitRandom [B.pack [0x10 .. 0x1f]] (B.pack [0x20 .. 0x2b]))+        `shouldBe` Right+          [ (0, hex "101112131415161718191a1b1c1d1e1f"),+            (1, hex "7d29eb177172737c75767770797a7b64"),+            (2, hex "009af86a90059252089d0aac980d9a96"),+            (3, hex "6da2016ef562f73965f267c7fd6affed")+          ]+    it "refuses the digest when a share of another secret is mixed in" $+      -- the share at index 1 of the 2-of-3 of "abcdefghijklmnop" with the same random part+      recoverSecret 2 [share0, (1, hex "b086c5a043a741cdfe1afcc94baf49ac")]+        `shouldBe` Left "The digest of the secret does not match"+    it "refuses the digest when a share is corrupted" $+      recoverSecret 2 [share0, (1, hex "2b463c939175931f2cc82e1b997d9b7f")]+        `shouldBe` Left "The digest of the secret does not match"+    it "recovers the first share at a threshold of 1" $+      recoverSecret 1 [(4, secret), (5, "ponmlkjihgfedcba")] `shouldBe` Right secret+    it "refuses no shares" $+      recoverSecret 2 [] `shouldBe` Left "There are no shares to recover the secret from"+    it "refuses a threshold of 0" $+      splitSecret 0 3 secret noRandom `shouldBe` Left "The threshold must be at least 1"+    it "refuses a threshold above the share count" $+      splitSecret 4 3 secret noRandom `shouldBe` Left "The threshold is greater than the number of shares"+    it "refuses 17 shares" $+      splitSecret 2 17 secret noRandom `shouldBe` Left "The number of shares is greater than 16"+    it "refuses a secret of 15 bytes" $+      splitSecret 1 1 (B.take 15 secret) noRandom `shouldBe` Left "The secret must have at least 16 bytes, not 15"+    it "refuses a secret of 17 bytes" $+      splitSecret 1 1 (secret <> "Q") noRandom `shouldBe` Left "The secret must have an even number of bytes, not 17"+    it "refuses random shares at a threshold of 1" $+      splitSecret 1 1 secret (SplitRandom [secret] B.empty) `shouldBe` Left "A split with a threshold of 1 takes 0 random shares"+    it "refuses a random part at a threshold of 1" $+      splitSecret 1 1 secret (SplitRandom [] digestRandom) `shouldBe` Left "The random part of the digest share must have 0 bytes"+    it "refuses too few random shares" $+      splitSecret 3 4 secret (SplitRandom [] digestRandom) `shouldBe` Left "A split with a threshold of 3 takes 1 random shares"+    it "refuses a random share of another length" $+      splitSecret 3 4 secret (SplitRandom [B.take 15 secret] digestRandom) `shouldBe` Left "A random share must be as long as the secret"+    it "refuses a random part of another length" $+      splitSecret 2 3 secret (SplitRandom [] (B.take 11 digestRandom)) `shouldBe` Left "The random part of the digest share must have 12 bytes"+    it "shows the random values of a split without their bytes" $+      -- the random shares are real shares, and the random part is most of+      -- the digest share+      show (SplitRandom [B.replicate 16 0x41] (B.replicate 12 0x42))+        `shouldBe` "SplitRandom {randomShares = <redacted>, digestRandom = <redacted>}"+    it "sizes the random values: none at threshold 1, T - 2 shares and n - 4 bytes otherwise" $ do+      splitRandomSizes 1 16 `shouldBe` (0, 16, 0)+      splitRandomSizes 2 16 `shouldBe` (0, 16, 12)+      splitRandomSizes 5 32 `shouldBe` (3, 32, 28)+    it "recovers any random secret from any threshold of its shares" $+      property $ forAll ((,) <$> bytesOf 16 <*> bytesOf 12) $ \(s, r) ->+        case splitSecret 2 3 s (SplitRandom [] r) of+          Right shares -> all ((== Right s) . recoverSecret 2) [two | two <- subsequences shares, length two == 2]+          Left _ -> False++  describe "the passphrase" $ do+    let secret = "ABCDEFGHIJKLMNOP"+        ems = EncryptedMasterSecret {identifier = 7945, extendable = False, iterationExponent = 0, ciphertext = hex "11bc609d21747c49ba78c0701293e417"}+    it "decrypts the encrypted master secret of the first vector with TREZOR" $+      decryptMasterSecret "TREZOR" ems `shouldBe` Right (hex "bb54aac4b89dc868ba37d9cc21b2cece")+    it "encrypts that master secret back" $+      encryptMasterSecret 7945 False 0 "TREZOR" (hex "bb54aac4b89dc868ba37d9cc21b2cece") `shouldBe` Right ems+    it "encrypts ABCDEFGHIJKLMNOP under an empty passphrase as the verifier does" $+      fmap (.ciphertext) (encryptMasterSecret 1 True 0 "" secret) `shouldBe` Right (hex "7b78e5f690282c25aa183fa39a23dd02")+    it "ignores the identifier when the backup is extendable" $+      fmap (.ciphertext) (encryptMasterSecret 2 True 0 "" secret) `shouldBe` Right (hex "7b78e5f690282c25aa183fa39a23dd02")+    it "salts with the identifier when the backup is not extendable" $ do+      fmap (.ciphertext) (encryptMasterSecret 1 False 0 "" secret) `shouldBe` Right (hex "3162b7863d3710ef05f35feaa08fbc54")+      fmap (.ciphertext) (encryptMasterSecret 2 False 0 "" secret) `shouldBe` Right (hex "90330cd4eef5857777c6a09793195a23")+    it "gives another ciphertext under another passphrase" $+      fmap (.ciphertext) (encryptMasterSecret 1 True 0 "TREZOR" secret) `shouldBe` Right (hex "c5462e7421d4c690de1668d6a3bfd337")+    it "gives another ciphertext under another iteration exponent" $+      fmap (.ciphertext) (encryptMasterSecret 1 True 1 "" secret) `shouldBe` Right (hex "d51a2d68cb0adf27958873071f5253ac")+    it "decrypts to another master secret under a wrong passphrase, without an error" $+      decryptMasterSecret "trezor" ems `shouldSatisfy` \r -> r /= Right (hex "bb54aac4b89dc868ba37d9cc21b2cece") && either (const False) (const True) r+    it "accepts the first and the last printable ASCII characters" $+      fmap (const ()) (encryptMasterSecret 1 True 0 " ~" secret) `shouldBe` Right ()+    it "refuses a passphrase outside printable ASCII" $+      encryptMasterSecret 1 True 0 "café" secret+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+    it "refuses a control character in the passphrase" $+      encryptMasterSecret 1 True 0 "a\tb" secret+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+    it "refuses the characters next to printable ASCII: U+001F and U+007F" $ do+      encryptMasterSecret 1 True 0 "a\x1f" secret+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+      encryptMasterSecret 1 True 0 "a\x7f" secret+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+    it "refuses a passphrase outside printable ASCII on decryption too" $ do+      -- "the passphrase MUST be a string containing only printable ASCII+      -- characters" (SLIP-39). Two spellings of one visible passphrase+      -- used to open two wallets.+      decryptMasterSecret "caf\x00e9" cafeEms+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+      decryptMasterSecret "cafe\x0301" cafeEms+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+      combineMnemonics "caf\x00e9" [cafeMnemonic]+        `shouldBe` Left "The passphrase must contain only printable ASCII characters, code points 32 to 126"+    it "decrypts with the bytes of a passphrase of other software, which it does not check" $ do+      -- The share was made by version 0.3.0 of the reference implementation+      -- with "café" as UTF-8, 63 61 66 c3 a9. The second line is what the+      -- reference gives for "e" followed by U+0301: another secret.+      decryptMasterSecretBytes (B.pack [0x63, 0x61, 0x66, 0xc3, 0xa9]) cafeEms `shouldBe` Right secret+      fmap encodeHex (decryptMasterSecretBytes (B.pack [0x63, 0x61, 0x66, 0x65, 0xcc, 0x81]) cafeEms)+        `shouldBe` Right "7a1965e4e0f2c5efee3e7efe53222345"+    it "rebuilds the encrypted master secret of that share, whatever its passphrase was" $+      (decodeShare cafeMnemonic >>= \s -> combineShares [s]) `shouldBe` Right cafeEms+    it "refuses to decrypt under an iteration exponent of 16" $+      decryptMasterSecret "" EncryptedMasterSecret {identifier = 7945, extendable = False, iterationExponent = 16, ciphertext = hex "11bc609d21747c49ba78c0701293e417"}+        `shouldBe` Left "The iteration exponent must be between 0 and 15, not 16"+    it "refuses to decrypt under an identifier of 16 bits" $+      decryptMasterSecret "" EncryptedMasterSecret {identifier = 32768, extendable = False, iterationExponent = 0, ciphertext = hex "11bc609d21747c49ba78c0701293e417"}+        `shouldBe` Left "The identifier must be between 0 and 32767, not 32768"+    it "refuses a master secret of 15 bytes" $+      encryptMasterSecret 1 True 0 "" (B.take 15 secret) `shouldBe` Left "The master secret must have at least 16 bytes, not 15"+    it "refuses a master secret of 17 bytes" $+      encryptMasterSecret 1 True 0 "" (secret <> "Q") `shouldBe` Left "The master secret must have an even number of bytes, not 17"+    it "refuses an identifier of 16 bits" $+      encryptMasterSecret 32768 True 0 "" secret `shouldBe` Left "The identifier must be between 0 and 32767, not 32768"+    it "refuses an iteration exponent of 16" $+      encryptMasterSecret 1 True 16 "" secret `shouldBe` Left "The iteration exponent must be between 0 and 15, not 16"+    it "refuses to decrypt a ciphertext of 15 bytes" $+      decryptMasterSecret "" ems {ciphertext = B.take 15 ems.ciphertext} `shouldBe` Left "The encrypted master secret must have at least 16 bytes, not 15"+    it "shows an encrypted master secret without its ciphertext" $+      show ems `shouldBe` "EncryptedMasterSecret {identifier = 7945, extendable = False, iterationExponent = 0, ciphertext = <redacted>}"+    it "decrypts what it encrypts, for any secret and passphrase" $+      property $ forAll ((,) <$> bytesOf 16 <*> listOf (elements [' ' .. '~'])) $ \(s, p) ->+        (encryptMasterSecret 5 True 0 (T.pack p) s >>= decryptMasterSecret (T.pack p)) === Right s++  describe "generating shares" $ do+    forM_ splitCases $ \c -> do+      it ("splits " <> T.unpack c.caseDescription <> " as the verifier does") $+        (generateSharesWith c.caseScheme c.caseIdentifier c.caseExtendable c.caseIterationExponent c.casePassphrase c.caseMasterSecret c.caseRandom >>= traverse (traverse encodeShare))+          `shouldBe` Right c.caseMnemonics+      it ("combines any threshold of the shares of " <> T.unpack c.caseDescription) $+        forM_ (thresholdSubsets c) $ \subset ->+          combineMnemonics c.casePassphrase subset `shouldBe` Right c.caseMasterSecret+    it "gives the 2-of-3 case these three mnemonics" $+      -- The second split case, written out: the first three words are the+      -- identifier 42, the flag and the exponent 1; the fourth differs by+      -- the member index+      generateSharesWith (Scheme 1 [(2, 3)]) 42 True 1 "TREZOR" "ABCDEFGHIJKLMNOP" [SplitRandom [] B.empty, SplitRandom [] (hex "698c2ccb37ff81b7f5603f9b")]+        `shouldBe` Right+          [ [ share 42 True 1 0 1 1 0 2 "acid fawn academic acid club member living lawsuit sunlight pancake market lend peasant sunlight glad simple infant organize grant infant",+              share 42 True 1 0 1 1 1 2 "acid fawn academic agency bracelet transfer march argue wits exact dough parcel animal traveler dictate river military salt satisfy payment",+              share 42 True 1 0 1 1 2 2 "acid fawn academic always advocate either wine oasis solution goat toxic injury radar romantic shrimp check warmth fangs ending mineral"+            ]+          ]+    it "refuses a scheme without groups" $+      generateSharesWith (Scheme 1 []) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "A scheme has at least one group"+    it "refuses a scheme of 17 groups" $+      generateSharesWith (Scheme 1 (replicate 17 (1, 1))) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "A scheme has at most 16 groups"+    it "refuses a group threshold of 0" $+      generateSharesWith (Scheme 0 [(1, 1)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "The group threshold must be between 1 and 1, not 0"+    it "refuses a group threshold above the number of groups" $+      generateSharesWith (Scheme 3 [(1, 1), (1, 1)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "The group threshold must be between 1 and 2, not 3"+    it "refuses a member count of 17" $+      generateSharesWith (Scheme 1 [(2, 17)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "The member count of group 1 must be between 1 and 16, not 17"+    it "refuses a member threshold of 0" $+      generateSharesWith (Scheme 1 [(0, 3)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "The member threshold of group 1 must be between 1 and 3, not 0"+    it "refuses a member threshold above the member count" $+      generateSharesWith (Scheme 1 [(1, 1), (4, 3)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "The member threshold of group 2 must be between 1 and 3, not 4"+    it "refuses a group of several members with a member threshold of 1" $+      generateSharesWith (Scheme 1 [(1, 3)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [] `shouldBe` Left "A group with a member threshold of 1 has one member, but group 1 has 3"+    it "refuses random values for the wrong number of splits" $+      generateSharesWith (Scheme 1 [(1, 1)]) 1 True 0 "" "ABCDEFGHIJKLMNOP" [SplitRandom [] B.empty]+        `shouldBe` Left "A scheme of 1 groups takes 2 sets of random values: one for the split among the groups, then one per group"+    it "asks its source for nothing when the master secret has 15 bytes" $+      runState (generateSharesFrom recordedZeros (Scheme 1 [(2, 3)]) True 0 "" "ABCDEFGHIJKLMNO") []+        `shouldBe` (Left "The master secret must have at least 16 bytes, not 15", [])+    it "asks its source for nothing when the scheme, the exponent or the passphrase is refused" $ do+      runState (generateSharesFrom recordedZeros (Scheme 1 [(1, 3)]) True 0 "" "ABCDEFGHIJKLMNOP") []+        `shouldBe` (Left "A group with a member threshold of 1 has one member, but group 1 has 3", [])+      runState (generateSharesFrom recordedZeros (Scheme 1 [(2, 3)]) True 16 "" "ABCDEFGHIJKLMNOP") []+        `shouldBe` (Left "The iteration exponent must be between 0 and 15, not 16", [])+      runState (generateSharesFrom recordedZeros (Scheme 1 [(2, 3)]) True 0 "caf\x00e9" "ABCDEFGHIJKLMNOP") []+        `shouldBe` (Left "The passphrase must contain only printable ASCII characters, code points 32 to 126", [])+    it "asks its source for 2 and 12 bytes to split 16 bytes 2-of-3" $+      -- 2 for the identifier, nothing for the split among one group, and+      -- 16 - 4 = 12 for the random part of the digest share+      snd (runState (generateSharesFrom recordedZeros (Scheme 1 [(2, 3)]) True 0 "" "ABCDEFGHIJKLMNOP") [])+        `shouldBe` [2, 12]+    it "asks its source for 2, 32 and 28 bytes to split 32 bytes 3-of-5" $+      -- 2 for the identifier, 32 for the one random share, and 32 - 4 = 28+      -- for the random part of the digest share+      snd (runState (generateSharesFrom recordedZeros (Scheme 1 [(3, 5)]) True 0 "" "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345") [])+        `shouldBe` [2, 32, 28]+    it "refuses a source that gives fewer bytes than asked" $+      fst (runState (generateSharesFrom (\_ -> return "x") (Scheme 1 [(2, 3)]) True 0 "" "ABCDEFGHIJKLMNOP") ())+        `shouldBe` Left "The source of random bytes gave 1 bytes where 2 were asked for"+    it "draws a 15-bit identifier and shares of which any two of three rebuild the secret" $ do+      let secret = "ABCDEFGHIJKLMNOP"+          drawn = fst (withDRG (drgNewTest (1, 2, 3, 4, 5)) (generateSharesFrom getRandomBytes (Scheme 1 [(2, 3)]) True 0 "" secret))+      case drawn of+        Right [shares] -> do+          length shares `shouldBe` 3+          length (nub (map (.identifier) shares)) `shouldBe` 1+          forM_ shares $ \s -> do+            s.identifier `shouldSatisfy` (\i -> i >= 0 && i <= 32767)+            (s.extendable, s.iterationExponent, s.groupIndex, s.groupThreshold, s.groupCount, s.memberThreshold) `shouldBe` (True, 0, 0, 1, 1, 2)+          map (.memberIndex) shares `shouldBe` [0, 1, 2]+          forM_ [two | two <- subsequences shares, length two == 2] $ \two ->+            (combineShares two >>= decryptMasterSecret "") `shouldBe` Right secret+        other -> expectationFailure ("expected one group of three shares, got " <> show other)+    it "draws the same shares from the same random state and others from another" $ do+      let draw seed = fst (withDRG (drgNewTest seed) (generateSharesFrom getRandomBytes (Scheme 1 [(2, 3)]) True 0 "" "ABCDEFGHIJKLMNOP"))+      draw (1, 2, 3, 4, 5) `shouldBe` draw (1, 2, 3, 4, 5)+      draw (1, 2, 3, 4, 5) `shouldNotBe` draw (6, 7, 8, 9, 10)+    it "rebuilds a secret of 16 to 64 bytes from a threshold of the shares of any small scheme" $+      property $ forAll splitOfAnyScheme $ \(scheme, ext, passphrase, secret, drawn) ->+        case drawn of+          Left err -> counterexample err False+          Right groups ->+            -- the first groups and the first members that meet the+            -- thresholds, then the last ones+            let firstOnes = concat [take t members | ((t, _), members) <- take scheme.groupThreshold (zip scheme.groups groups)]+                lastOnes = concat [lastN t members | ((t, _), members) <- lastN scheme.groupThreshold (zip scheme.groups groups)]+                rebuilt shares = combineShares shares >>= decryptMasterSecret passphrase+             in (rebuilt firstOnes, rebuilt lastOnes) === (Right secret, Right secret)+    it "refuses a share with one word of its value or checksum changed" $+      -- the checksum catches any change of up to three words+      property $ forAll changedWord $ \(position, word) ->+        let ws = T.words (mnemonicOf vectors 1)+            changed = T.unwords (take position ws ++ [word] ++ drop (position + 1) ws)+         in ws !! position /= word ==> decodeShare changed === Left "The checksum of the mnemonic is invalid"+    it "draws shares with the system's randomness that rebuild a two-level scheme" $ do+      let secret = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"+      drawn <- generateMnemonics (Scheme 2 [(1, 1), (2, 3), (3, 5)]) True 0 "pass" secret+      case drawn of+        Right [[a], bs, cs] -> do+          combineMnemonics "pass" (a : take 2 bs) `shouldBe` Right secret+          combineMnemonics "pass" (take 2 bs ++ take 3 cs) `shouldBe` Right secret+          combineMnemonics "pass" (a : drop 2 cs) `shouldBe` Right secret+          combineMnemonics "pass" (a : take 1 bs) `shouldBe` Left "Group 1 has 1 share, but its member threshold is 2"+        other -> expectationFailure ("expected groups of 1, 3 and 5 shares, got " <> show other)++  describe "combining shares" $ do+    it "rebuilds the encrypted master secret of the first vector from its one share" $+      combineShares [vector1]+        `shouldBe` Right EncryptedMasterSecret {identifier = 7945, extendable = False, iterationExponent = 0, ciphertext = hex "11bc609d21747c49ba78c0701293e417"}+    it "refuses no shares" $+      combineShares [] `shouldBe` Left "There are no shares to combine"+    it "refuses no mnemonics" $+      combineMnemonics "" [] `shouldBe` Left "There are no shares to combine"+    it "refuses shares that differ on the extendable flag" $+      combineShares [vector1, (singleShare 7945 True 0 vector1.value) {memberIndex = 1}] `shouldBe` Left "The shares do not have the same extendable flag"+    it "refuses shares whose values differ in length" $+      combineShares [vector1, vector1 {memberIndex = 1, value = B.replicate 32 0}] `shouldBe` Left "The shares do not have the same length of their values"+    it "refuses a share with a field out of range" $+      combineShares [vector1 {memberIndex = 16}] `shouldBe` Left "The member index must be between 0 and 15, not 16"+    it "refuses more groups than the group threshold" $+      combineShares [groupShare 1 2 vector1.value, (groupShare 1 2 vector1.value) {groupIndex = 1}]+        `shouldBe` Left "The shares come from 2 groups, but the group threshold is 1"+    it "refuses more shares than the member threshold of a group" $+      combineShares [vector1, vector1 {memberIndex = 1}] `shouldBe` Left "Group 0 has 2 shares, but its member threshold is 1"++  describe "the shares of another implementation" $ do+    -- From the tests of Sparrow's library: eight groups of one member, five+    -- of them needed, and the value of the fifth share starts with a zero+    -- byte, which a conversion through a number would drop+    let sparrow =+          [ "budget surprise agency romp clay quiet spew favorite purchase thumb observe sympathy acrobat presence unfold focus already tricycle diagnose muscle",+            "budget surprise birthday romp breathe scroll intend humidity aspect plains isolate crystal blimp dragon garbage both kind dance human craft",+            "budget surprise cleanup romp early remove uncover hearing lamp firefly ounce species submit snake phrase genuine tactics mustang desire artist",+            "budget surprise desert romp deny easy senior arena smirk average single smart fatal lecture threaten eyebrow lunar carve indicate romantic",+            "budget surprise email romp academic anxiety sister nylon fake makeup spine breathe window true recall desktop shelter divorce luck screw"+          ]+    it "keeps the zero byte at the head of a share value" $+      fmap (\s -> (s.groupIndex, s.groupThreshold, s.groupCount, s.memberThreshold, encodeHex s.value)) (decodeShare (last sparrow))+        `shouldBe` Right (4, 5, 8, 1, "000bf319a1478ab5418bedeb2de34f26")+    it "rebuilds the master secret from five of the eight groups" $+      fmap encodeHex (combineMnemonics "" sparrow) `shouldBe` Right "609d9cf21c065a88e9f5b97c0e6928c6"++  describe "the vectors of the reference implementation" $ do+    it "has 45 vectors, 15 of them valid" $ do+      length vectors `shouldBe` 45+      length [() | (_, _, s, _) <- vectors, s /= ""] `shouldBe` 15+    forM_ (zip [1 :: Int ..] vectors) $ \(n, (d, ms, secret, xprv)) ->+      if secret /= ""+        then it (T.unpack d <> ": gives the master secret and its BIP-32 key") $ do+          combineMnemonics "TREZOR" ms `shouldBe` Right (hex secret)+          xPrvExport btc (makeXPrvKey (hex secret)) `shouldBe` xprv+        else it (T.unpack d <> ": is refused") $+          combineMnemonics "TREZOR" ms `shouldBe` Left (refusalOf n)++-- | A share made by version 0.3.0 of the reference implementation with the+-- passphrase "café", from its tests. Its master secret is ABCDEFGHIJKLMNOP.+cafeMnemonic :: Text+cafeMnemonic = "acid fawn academic academic company fridge picture cage pecan glance privacy carpet should public pharmacy eclipse percent python together flavor"++-- | The encrypted master secret of 'cafeMnemonic', as the reference+-- implementation reads it: a 1-of-1 share is the encrypted secret itself.+cafeEms :: EncryptedMasterSecret+cafeEms = EncryptedMasterSecret {identifier = 42, extendable = True, iterationExponent = 1, ciphertext = hex "9f5d29d1ca9062eb91fb29b0e9840a93"}++-- | The share of the first vector.+vector1 :: Share+vector1 = singleShare 7945 False 0 (hex "11bc609d21747c49ba78c0701293e417")++-- | A share of a 1-of-1 split with the identifier, the flag, the exponent+-- and the value given.+singleShare :: Int -> Bool -> Int -> ByteString -> Share+singleShare i ext e v =+  Share+    { identifier = i,+      extendable = ext,+      iterationExponent = e,+      groupIndex = 0,+      groupThreshold = 1,+      groupCount = 1,+      memberIndex = 0,+      memberThreshold = 1,+      value = v+    }++-- | The first vector's share with the group threshold and count given.+groupShare :: Int -> Int -> ByteString -> Share+groupShare threshold count v =+  Share+    { identifier = 7945,+      extendable = False,+      iterationExponent = 0,+      groupIndex = 0,+      groupThreshold = threshold,+      groupCount = count,+      memberIndex = 0,+      memberThreshold = 1,+      value = v+    }++-- | The indices of a mnemonic's words, which must all be in the list.+indicesOf :: Text -> [Int]+indicesOf = either error id . mnemonicToIndices++-- | The one mnemonic, or the first, of a vector by its number.+mnemonicOf :: [(Text, [Text], Text, Text)] -> Int -> Text+mnemonicOf vectors n = case vectors !! (n - 1) of+  (_, m : _, _, _) -> m+  (d, [], _, _) -> error ("vector without mnemonics: " <> T.unpack d)++-- | Why each invalid vector is refused, by its number.+refusalOf :: Int -> String+refusalOf n = case n of+  2 -> checksum+  3 -> padding+  5 -> oneShareOfTwo 0+  6 -> differ "identifier"+  7 -> differ "iteration exponent"+  8 -> differ "group threshold"+  9 -> differ "group count"+  10 -> thresholdAboveCount+  11 -> duplicate+  12 -> memberThresholds+  13 -> digest+  14 -> oneGroupOfTwo+  15 -> oneGroupOfTwo+  16 -> oneShareOfTwo 3+  21 -> checksum+  22 -> padding+  24 -> oneShareOfTwo 0+  25 -> differ "identifier"+  26 -> differ "iteration exponent"+  27 -> differ "group threshold"+  28 -> differ "group count"+  29 -> thresholdAboveCount+  30 -> duplicate+  31 -> memberThresholds+  32 -> digest+  33 -> oneGroupOfTwo+  34 -> oneGroupOfTwo+  35 -> oneShareOfTwo 3+  39 -> "The mnemonic has 19 words, but a share has at least 20"+  40 -> "The mnemonic has 21 words, which is not a valid share length"+  _ -> error ("vector " <> show n <> " is valid")+  where+    checksum = "The checksum of the mnemonic is invalid"+    padding = "The padding bits of the share value are not zero"+    differ what = "The shares do not have the same " <> what+    thresholdAboveCount = "The group threshold is greater than the group count"+    duplicate = "Group 0 has two shares with the member index 2"+    memberThresholds = "The shares of group 0 do not have the same member threshold"+    digest = "The digest of the secret does not match"+    oneGroupOfTwo = "The shares come from 1 group, but the group threshold is 2"+    oneShareOfTwo g = "Group " <> show (g :: Int) <> " has 1 share, but its member threshold is 2"++-- | Every set of mnemonics of a split case that meets its scheme: enough+-- groups, and enough members of each.+thresholdSubsets :: SplitCase -> [[Text]]+thresholdSubsets c =+  [ concat members+    | chosen <- subsequences (zip (map fst c.caseScheme.groups) c.caseMnemonics),+      length chosen == c.caseScheme.groupThreshold,+      members <- mapM (\(threshold, ms) -> [ms' | ms' <- subsequences ms, length ms' == threshold]) chosen+  ]++-- | A share of the given fields, decoded from its mnemonic for its value.+share :: Int -> Bool -> Int -> Int -> Int -> Int -> Int -> Int -> Text -> Share+share i ext e gi gt gc mi mt mnemonic =+  case decodeShare mnemonic of+    Right s+      | (s.identifier, s.extendable, s.iterationExponent, s.groupIndex, s.groupThreshold, s.groupCount, s.memberIndex, s.memberThreshold) == (i, ext, e, gi, gt, gc, mi, mt) -> s+      | otherwise -> error ("the mnemonic does not carry the fields given: " <> show s)+    Left err -> error err++bytesOf :: Int -> Gen ByteString+bytesOf n = B.pack <$> vectorOf n arbitrary++-- | A source of random bytes that gives zeros and records how many bytes+-- each request asked for, in order.+recordedZeros :: Int -> State [Int] ByteString+recordedZeros n = modify (++ [n]) >> return (B.replicate n 0)++-- | The last n elements of a list.+lastN :: Int -> [a] -> [a]+lastN n xs = drop (length xs - n) xs++-- | A scheme of one to three groups of one to four members, a secret of 16+-- to 64 bytes, a printable ASCII passphrase, and the shares of the secret+-- drawn from the generator.+splitOfAnyScheme :: Gen (Scheme, Bool, Text, ByteString, Either String [[Share]])+splitOfAnyScheme = do+  groupCount <- choose (1, 3)+  groups <- vectorOf groupCount $ do+    members <- choose (1, 4)+    -- a group with a member threshold of 1 has one member+    threshold <- if members == 1 then return 1 else choose (2, members)+    return (threshold, members)+  groupThreshold <- choose (1, groupCount)+  let scheme = Scheme groupThreshold groups+  ext <- arbitrary+  passphrase <- T.pack <$> listOf (elements [' ' .. '~'])+  secret <- choose (8, 32) >>= \half -> bytesOf (2 * half)+  drawn <- generateSharesFrom bytesOf scheme ext 0 passphrase secret+  return (scheme, ext, passphrase, secret, drawn)++-- | A position in the 20 words of the first vector after the two words+-- that hold the extendable flag, and a word of the list to put there.+changedWord :: Gen (Int, Text)+changedWord = (,) <$> choose (2, 19) <*> elements (V.toList slip39WordList)
+ test/Haskoin/Crypto/KeysSpec.hs view
@@ -0,0 +1,440 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Crypto.KeysSpec (spec) where++import Control.Exception (evaluate)+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.Char (isAsciiLower, isAsciiUpper, toLower, toUpper)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either (isLeft)+import Data.List (isInfixOf)+import Data.Maybe+import Data.Serialize qualified as S+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data (Network (..))+import Haskoin.Script+import Haskoin.Util+import Haskoin.Util.Arbitrary+import System.Timeout (timeout)+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 (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 "a compressed key is written in 33 bytes starting with 02 or 03, an uncompressed key in 65 bytes starting with 04" $+      forAll (arbitraryKeyPair ctx) $ \(_, pub) ->+        let bytes = marshal ctx pub+         in if pub.compress+              then B.length bytes == 33 && B.head bytes `elem` [0x02, 0x03]+              else B.length bytes == 65 && B.head bytes == 0x04+    prop "Public key show . read identity" $+      forAll (arbitraryKeyPair ctx) $ \(_, pub) ->+        (read . show) pub == pub+    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+  describe "MiniKey vectors" $+    it "Passes MiniKey decoding tests" testMiniKey+  describe "Secret key range" $ do+    it "accepts the smallest secret key, 1" $+      decodeSecKey (B.replicate 31 0 <> B.singleton 1) `shouldBe` Just secKey1+    it "accepts the largest secret key, n - 1" $+      decodeSecKey (hex "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140")+        `shouldSatisfy` isJust+    it "refuses the zero key" $+      decodeSecKey (B.replicate 32 0) `shouldBe` Nothing+    it "refuses the curve order n" $+      decodeSecKey (hex "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")+        `shouldBe` Nothing+    it "refuses a key that is not 32 bytes" $ do+      decodeSecKey (B.replicate 31 1) `shouldBe` Nothing+      decodeSecKey (B.replicate 33 1) `shouldBe` Nothing+  describe "WIF" $ do+    it "decodes the compressed WIF of key 1" $+      fromWif btc "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"+        `shouldBe` Just (wrapSecKey True secKey1)+    it "decodes the uncompressed WIF of key 1" $+      fromWif btc "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf"+        `shouldBe` Just (wrapSecKey False secKey1)+    it "refuses the WIF of key 0" $ do+      fromWif btc "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73Nd2Mcv1" `shouldBe` Nothing+      fromWif btc "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAbuatmU" `shouldBe` Nothing+    it "refuses the WIF of the curve order n" $ do+      fromWif btc "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFqRzExJJ" `shouldBe` Nothing+      fromWif btc "5Km2kuu7vtFDPpxywn4u3NLpbr5jKpTB3jsuDU2KYEqetwr388P" `shouldBe` Nothing+    it "refuses a compressed WIF whose flag byte is not 0x01" $+      -- key 1 followed by the flag byte 0x02+      fromWif btc "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sfZr2ym" `shouldBe` Nothing+    it "refuses the WIF of another network" $ do+      -- key 1 with the testnet prefix 0xef+      fromWif btc "cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA" `shouldBe` Nothing+      fromWif btcTest "cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA"+        `shouldBe` Just (wrapSecKey True secKey1)+    it "refuses a Base58Check string with no payload" $+      -- "3QJmnh" is the Base58Check encoding of the empty string+      fromWif btc "3QJmnh" `shouldBe` Nothing+    it "refuses a string longer than any WIF" $+      fromWif btc (T.replicate 53 "z") `shouldBe` Nothing+    it "refuses a string of a million characters at once, without decoding it" $+      -- Decoding that much Base58 takes minutes. The length check comes first.+      timeout 1000000 (evaluate (fromWif btc (T.replicate 1000000 "z")))+        `shouldReturn` Just Nothing+    it "fails the JSON parse of an invalid WIF instead of throwing" $ do+      (unmarshalJSON btc "\"3QJmnh\"" :: Maybe PrivateKey) `shouldBe` Nothing+      (unmarshalJSON btc "\"KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73Nd2Mcv1\"" :: Maybe PrivateKey)+        `shouldBe` Nothing+  describe "Mini keys" $ do+    it "decodes a 22-character mini key" $+      -- the example key of the Bitcoin wiki+      fromMiniKey "S4b3N3oGqDqR5jNuxEvDwf"+        `shouldBe` Just (wrapSecKey False "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d")+    it "refuses a mini key whose check byte is not zero" $+      -- SHA-256 of "S4b3N3oGqDqR5jNuxEvDwg?" starts with 0x8f+      fromMiniKey "S4b3N3oGqDqR5jNuxEvDwg" `shouldBe` Nothing+    it "decodes a 30-character mini key" $+      -- the sample key of the Bitcoin wiki+      fromMiniKey "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy"+        `shouldBe` Just (wrapSecKey False "4c7a9640c72dc2099f23715d0c8a0d8a35f8906e3cab61dd3f78b67bf887c9ab")+    it "refuses a mini key of 21 or 23 characters, even with a zero check byte" $ do+      -- SHA-256 of each string followed by "?" starts with 0x00+      fromMiniKey "S4b3N3oGqDqR5jNuxE1Be" `shouldBe` Nothing+      fromMiniKey "S4b3N3oGqDqR5jNuxEvD13Z" `shouldBe` Nothing+    it "refuses a string that does not start with S" $+      -- 22 Base58 characters with a zero check byte+      fromMiniKey "T4b3N3oGqDqR5jNuxEv13r" `shouldBe` Nothing+    it "refuses a character outside the Base58 alphabet" $ do+      -- 22 characters with a zero check byte: one holds the digit 0, the+      -- other the letters O, I and l and underscores+      fromMiniKey "S4b3N3oGqDqR5jNux0v1B7" `shouldBe` Nothing+      fromMiniKey "0OIl_______________607" `shouldBe` Nothing+  describe "Key tweaks" $ do+    it "adding a zero tweak leaves the secret key unchanged" $+      tweakSecKey ctx secKey1 zeroHash `shouldBe` Just secKey1+    it "adding one to secret key 1 gives secret key 2" $+      tweakSecKey ctx secKey1 oneHash+        `shouldBe` Just "0000000000000000000000000000000000000000000000000000000000000002"+    it "refuses a tweak that brings the secret key to zero" $+      -- 1 + (n - 1) = n, which is 0 modulo n+      tweakSecKey ctx secKey1 orderMinusOneHash `shouldBe` Nothing+    it "refuses a tweak at or above the curve order" $+      tweakSecKey ctx secKey1 orderHash `shouldBe` Nothing+    it "adding one to the public key of 1 gives the public key of 2" $+      tweakPubKey ctx (pubKey1 ctx) oneHash `shouldBe` Just (pubKey2 ctx)+    it "refuses a tweak that brings the public key to infinity" $+      tweakPubKey ctx (pubKey1 ctx) orderMinusOneHash `shouldBe` Nothing+    prop "tweaking the public key matches tweaking the secret key" $+      forAll arbitraryPrivateKey $ \k ->+        forAll arbitraryHash256 $ \t ->+          (derivePubKey ctx <$> tweakSecKey ctx k.key t)+            == tweakPubKey ctx (derivePubKey ctx k.key) t+  describe "Public key text forms" $ do+    it "parses a compressed public key from JSON" $+      unmarshalJSON ctx "\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\""+        `shouldBe` Just (wrapPubKey True (pubKey1 ctx))+    it "parses an uncompressed public key from JSON" $+      unmarshalJSON+        ctx+        "\"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\+        \483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8\""+        `shouldBe` Just (wrapPubKey False (pubKey1 ctx))+    it "refuses a public key with trailing bytes" $+      (unmarshalJSON ctx "\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798deadbeef\"" :: Maybe PublicKey)+        `shouldBe` Nothing+    it "refuses a public key with an unknown prefix byte" $+      (unmarshalJSON ctx "\"0579be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"" :: Maybe PublicKey)+        `shouldBe` Nothing+    it "refuses a hybrid public key" $+      -- 06, then x and the even y of the public key of 1. Consensus takes+      -- this form, the relay rules of Bitcoin Core do not, and this library+      -- reads keys in the two standard forms only.+      ( unmarshalJSON+          ctx+          "\"0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\+          \483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8\"" ::+          Maybe PublicKey+      )+        `shouldBe` Nothing+    it "refuses a point that is not on the curve" $+      -- the invalid public key of BIP32 test vector 5+      (unmarshalJSON ctx "\"020000000000000000000000000000000000000000000000000000000000000007\"" :: Maybe PublicKey)+        `shouldBe` Nothing+  describe "Binary private key" $ do+    it "is the 32 bytes of the key, then 01 for a compressed public key or 00" $ do+      runPutS (serialize (wrapSecKey True secKey1))+        `shouldBe` hex "000000000000000000000000000000000000000000000000000000000000000101"+      runPutS (serialize (wrapSecKey False secKey1))+        `shouldBe` hex "000000000000000000000000000000000000000000000000000000000000000100"+      runGetS deserialize (hex "000000000000000000000000000000000000000000000000000000000000000101")+        `shouldBe` Right (wrapSecKey True secKey1)+    it "refuses a serialized zero key" $+      (runGetS deserialize (B.replicate 33 0) :: Either String PrivateKey) `shouldSatisfy` isLeft+    it "refuses the curve order n as a serialized key" $+      (runGetS deserialize (hex "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414101") :: Either String PrivateKey)+        `shouldSatisfy` either ("curve order" `isInfixOf`) (const False)+  describe "Showing a private key" $ do+    -- The secret of key 1 must not appear in the text+    it "does not reveal the secret" $+      show (PrivateKey "0000000000000000000000000000000000000000000000000000000000000001" True)+        `shouldBe` "PrivateKey {key = <redacted>, compress = True}"+    it "nests in parentheses like a derived instance" $+      show (Just (PrivateKey "0000000000000000000000000000000000000000000000000000000000000001" False))+        `shouldBe` "Just (PrivateKey {key = <redacted>, compress = False})"+  describe "key_io vectors of Bitcoin Core 32.1" $ do+    valid <- runIO (readTestFile "key_io_valid.json" :: IO [(Text, Text, A.Value)])+    invalid <- runIO (readTestFile "key_io_invalid.json" :: IO [[Text]])+    it "the files hold 70 valid and 70 invalid vectors" $ do+      length valid `shouldBe` 70+      length invalid `shouldBe` 70+    it "reads and writes every valid private key and address" $+      mapM_ (testKeyIOValidVector ctx) valid+    it "refuses every invalid string as an address and as a private key, on every network" $+      mapM_ testKeyIOInvalidVector invalid++hex :: Text -> B.ByteString+hex = fromJust . decodeHex++-- | The secret key 1.+secKey1 :: SecKey+secKey1 = "0000000000000000000000000000000000000000000000000000000000000001"++-- | Public keys of the secret keys 1 and 2, that is G and 2G.+pubKey1, pubKey2 :: Ctx -> PubKey+pubKey1 ctx = fromJust $ importPubKey ctx $ hex "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"+pubKey2 ctx = fromJust $ importPubKey ctx $ hex "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"++-- | Tweaks of 0, 1, n - 1 and n, where n is the curve order.+zeroHash, oneHash, orderMinusOneHash, orderHash :: Hash256+zeroHash = "0000000000000000000000000000000000000000000000000000000000000000"+oneHash = "0000000000000000000000000000000000000000000000000000000000000001"+orderMinusOneHash = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+orderHash = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"++testMiniKey :: Assertion+testMiniKey =+  assertEqual "fromMiniKey" (Just res) (go "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy")+  where+    go = fmap (encodeHex . (.key.get)) . fromMiniKey+    res = "4c7a9640c72dc2099f23715d0c8a0d8a35f8906e3cab61dd3f78b67bf887c9ab"++-- Test vectors from src/test/data/key_io_valid.json and key_io_invalid.json of+-- Bitcoin Core 32.1. The checks are those of src/test/key_io_tests.cpp.++-- | One valid vector: a string, the bytes it stands for and its chain. For a+-- private key the bytes are the secret, for an address the output script.+testKeyIOValidVector :: Ctx -> (Text, Text, A.Value) -> Assertion+testKeyIOValidVector ctx (string, payload, metadata)+  | isPrivkey = do+      let compressed = metadata ^?! key "isCompressed" . _Bool+          secret = fromMaybe (error "bad secret in the vector file") (secKey (hex payload))+      assertEqual+        ("the WIF " <> cs string <> " holds the key and its compression flag")+        (Just (payload, compressed))+        ((\k -> (encodeHex k.key.get, k.compress)) <$> fromWif net string)+      assertEqual+        ("the key is written as the WIF " <> cs string)+        string+        (toWif net (wrapSecKey compressed secret))+      assertEqual+        ("the WIF " <> cs string <> " is not an address")+        Nothing+        (textToAddr net string)+  | otherwise = do+      assertEqual+        ("the address " <> cs string <> " pays to the script")+        (Just payload)+        (encodeHex <$> (addressToScriptBS ctx =<< textToAddr net string))+      assertEqual+        ("the script is written as the address " <> cs string)+        (Just string)+        (addrToText net =<< eitherToMaybe (scriptToAddressBS net ctx (hex payload)))+      -- With the case of every letter flipped, a Bech32 address reads the+      -- same and a Base58 address does not read+      assertEqual+        ("the address " <> cs string <> " with its case flipped")+        (if tryCaseFlip then Just payload else Nothing)+        (encodeHex <$> (addressToScriptBS ctx =<< textToAddr net (flipCase string)))+      assertEqual+        ("the address " <> cs string <> " is not a private key")+        Nothing+        (fromWif net string)+  where+    isPrivkey = metadata ^?! key "isPrivkey" . _Bool+    tryCaseFlip = fromMaybe False (metadata ^? key "tryCaseFlip" . _Bool)+    net =+      case metadata ^?! key "chain" . _String of+        "main" -> btc+        "testnet4" -> btcTest4+        "regtest" -> btcRegTest+        -- This library has no signet. Signet writes keys and addresses as+        -- testnet3 does: the same version bytes and the prefix "tb"+        "signet" -> btcTest+        chain -> error ("Unknown chain in key_io_valid.json: " <> cs chain)++-- | Upper-case letters become lower-case and the reverse.+flipCase :: Text -> Text+flipCase = T.map flipLetter+  where+    flipLetter c+      | isAsciiLower c = toUpper c+      | isAsciiUpper c = toLower c+      | otherwise = c++-- | One invalid vector: a string that is no address and no private key, on+-- any network.+testKeyIOInvalidVector :: [Text] -> Assertion+testKeyIOInvalidVector [string] =+  forM_ [btc, btcTest, btcTest4, btcRegTest] $ \net -> do+    assertEqual+      (show string <> " is not an address on " <> net.name)+      Nothing+      (textToAddr net string)+    assertEqual+      (show string <> " is not a private key on " <> net.name)+      Nothing+      (fromWif net string)+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++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,104 +1,251 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+ module Haskoin.Crypto.SignatureSpec (spec) where -import           Control.Monad-import           Data.Bits               (testBit)-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.Map.Strict         (Map)-import qualified Data.Map.Strict         as Map-import           Data.Maybe-import           Data.Serialize          as S-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import           Haskoin.Address-import           Haskoin.Transaction-import           Haskoin.Keys-import           Haskoin.Crypto-import           Haskoin.Script-import           Haskoin.Constants-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Haskoin.UtilSpec        (readTestFile)-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit-import           Test.QuickCheck+import Control.Monad+import Data.Aeson qualified as A+import Data.Bits (testBit)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Aeson.Types qualified as A (parseEither)+import Data.Either (isLeft)+import Data.List (isInfixOf)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe+import Data.Serialize as S+import Data.String.Conversions (cs)+import Data.Text (Text)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck  spec :: Spec-spec = 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+  cashSchnorrSpec ctx+  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+  describe "Strict signature decoding" $ do+    it "accepts the smallest signature, r = 1 and s = 1" $+      -- DER: 30 06 | 02 01 01 | 02 01 01+      fmap (exportSig ctx) (decodeStrictSig ctx (hex "3006020101020101"))+        `shouldBe` Just (hex "3006020101020101")+    it "refuses a signature whose r is zero" $+      decodeStrictSig ctx (hex "3006020100020101") `shouldBe` Nothing+    it "refuses a signature whose s is zero" $+      decodeStrictSig ctx (hex "3006020101020100") `shouldBe` Nothing+    -- BIP 66 is about the encoding only. Low S is a relay rule of Bitcoin+    -- Core (SCRIPT_VERIFY_LOW_S), not a consensus rule.+    it "accepts a signature whose s is n - 1, above half the curve order" $ do+      -- DER: 30 26 | 02 01 01 | 02 21 00 <n - 1>, 3 + 2 + 33 = 38 = 0x26 bytes+      let der = hex "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+      fmap (exportSig ctx) (decodeStrictSig ctx der) `shouldBe` Just der+      fmap (isCanonicalHalfOrder ctx) (decodeStrictSig ctx der) `shouldBe` Just False+    prop "accepts the high-S form of any signature" $+      forAll (arbitrarySignature ctx) $ \(_, _, s) ->+        decodeStrictSig ctx (exportSig ctx (highSVariant ctx s)) == Just (highSVariant ctx s)+    it "accepts an r that needs a leading zero byte" $+      -- r = 0x80 is written 00 80, since 80 alone would be negative+      fmap (exportSig ctx) (decodeStrictSig ctx (hex "300702020080020101"))+        `shouldBe` Just (hex "300702020080020101")+    it "refuses a negative r or s" $ do+      decodeStrictSig ctx (hex "3006020181020101") `shouldBe` Nothing+      decodeStrictSig ctx (hex "3006020101020181") `shouldBe` Nothing+    it "refuses an r or s with a leading zero byte that is not needed" $ do+      decodeStrictSig ctx (hex "300702020001020101") `shouldBe` Nothing+      decodeStrictSig ctx (hex "300702010102020001") `shouldBe` Nothing+    it "refuses an r or s of no bytes" $ do+      decodeStrictSig ctx (hex "30050200020101") `shouldBe` Nothing+      decodeStrictSig ctx (hex "30050201010200") `shouldBe` Nothing+    it "refuses an r or s that is not tagged as an integer" $ do+      decodeStrictSig ctx (hex "3006030101020101") `shouldBe` Nothing+      decodeStrictSig ctx (hex "3006020101030101") `shouldBe` Nothing+    it "refuses a sequence whose length is not that of its content" $ do+      decodeStrictSig ctx (hex "3005020101020101") `shouldBe` Nothing+      decodeStrictSig ctx (hex "3007020101020101") `shouldBe` Nothing+    it "refuses a byte after the sequence" $+      decodeStrictSig ctx (hex "300602010102010100") `shouldBe` Nothing+    it "refuses a length written in the long form" $+      decodeStrictSig ctx (hex "308106020101020101") `shouldBe` Nothing+    it "refuses no bytes at all" $+      decodeStrictSig ctx "" `shouldBe` Nothing+    it "refuses an r at the curve order, which BIP 66 allows but which can never verify" $+      -- DER: 30 26 | 02 21 00 <n> | 02 01 01+      decodeStrictSig ctx (hex "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101")+        `shouldBe` Nothing+  describe "Binary signature decoding" $ do+    it "decodes the smallest signature" $+      fmap (exportSig ctx) (unmarshal ctx (hex "3006020101020101"))+        `shouldBe` Right (hex "3006020101020101")+    it "refuses an identifier byte other than 0x30" $+      (unmarshal ctx (hex "3106020101020101") :: Either String Sig) `shouldSatisfy` isLeft+    it "refuses the indefinite length form" $+      (unmarshal ctx (hex "3000020101020101") :: Either String Sig) `shouldSatisfy` isLeft+    it "refuses a length of more than one byte" $+      (unmarshal ctx (hex "308106020101020101") :: Either String Sig) `shouldSatisfy` isLeft+    it "refuses a signature cut short" $+      (unmarshal ctx (hex "3006020101") :: Either String Sig) `shouldSatisfy` isLeft+    it "refuses bytes after the signature" $+      (unmarshal ctx (hex "300602010102010100ffff") :: Either String Sig)+        `shouldSatisfy` either ("bytes left over" `isInfixOf`) (const False)+    it "decodes a signature whose s is n - 1" $+      fmap (exportSig ctx) (unmarshal ctx (hex "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"))+        `shouldBe` Right (hex "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140")+  describe "Signature JSON" $ do+    prop "reads a high-S signature, like the binary form" $+      forAll (arbitrarySignature ctx) $ \(_, _, s) ->+        (unmarshalJSON ctx (A.encode (encodeHex (exportSig ctx (highSVariant ctx s)))) :: Maybe Sig)+          == Just (highSVariant ctx s)+    it "refuses a signature whose r is zero, like the binary form" $+      (unmarshalJSON ctx "\"3006020100020101\"" :: Maybe Sig) `shouldBe` Nothing+    it "refuses a signature that is not hex" $+      (unmarshalJSON ctx "\"zz\"" :: Maybe Sig) `shouldBe` Nothing+    it "does not copy the text it refuses into its error message" $+      (A.parseEither (unmarshalValue ctx) (A.String "zz-attacker-text") :: Either String Sig)+        `shouldBe` Left "Error in $: Could not decode signature"+  describe "High-S signatures" $ do+    prop "are not in canonical half order" $+      forAll (arbitrarySignature ctx) $ \(_, _, s) ->+        not (isCanonicalHalfOrder ctx (highSVariant ctx s))+    prop "still verify after normalisation, as in Bitcoin Core" $+      forAll (arbitrarySignature ctx) $ \(m, key', s) ->+        verifyHashSig ctx m (highSVariant ctx s) (derivePubKey ctx key')+    prop "a signature does not verify for another message" $+      forAll (arbitrarySignature ctx) $ \(m, key', s) ->+        forAll arbitraryHash256 $ \other ->+          other /= m ==> not (verifyHashSig ctx other s (derivePubKey ctx key'))+  describe "Bitcoin Core deterministic signature vectors" $+    it "signs 'Very deterministic message' as Bitcoin Core does" $+      case getImpl ctx of+        Just ImplCore -> do+          let h = doubleSHA256 ("Very deterministic message" :: ByteString)+          encodeHex (exportSig ctx (signHash ctx coreKey1 h))+            `shouldBe` "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d\+                       \022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"+          encodeHex (exportSig ctx (signHash ctx coreKey2 h))+            `shouldBe` "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5\+                       \022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"+        _ -> pendingWith "the vector is for the nonce function of Bitcoin Core" +hex :: Text -> ByteString+hex = fromJust . decodeHex++-- | The keys strSecret1 and strSecret2 of key_tests.cpp in Bitcoin Core.+coreKey1, coreKey2 :: SecKey+coreKey1 = (fromJust (fromWif btc "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj")).key+coreKey2 = (fromJust (fromWif btc "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3")).key++-- | The same signature with s replaced by n - s. It verifies as well, but it+-- is not in the low-S form that BIP62 requires.+highSVariant :: Ctx -> Sig -> Sig+highSVariant ctx s =+  fromJust $ importCompactSig ctx $ fromJust $ compactSig (r <> highS)+  where+    (r, lowS) = BS.splitAt 32 (exportCompactSig ctx s).get+    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141+    highS = padTo32 (integerToBS (n - bsToInteger lowS))+    padTo32 bs = BS.replicate (32 - BS.length bs) 0 <> bs+ -- github.com/bitcoin/bitcoin/blob/master/src/script.cpp -- from function IsCanonicalSignature-testIsCanonical :: Sig -> Bool-testIsCanonical sig = not $+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)-    )+    (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)@@ -109,45 +256,46 @@ -- between implementations. We check the output of signMsg 1 0  data ValidImpl-    = ImplCore-    | ImplABC+  = ImplCore+  | ImplCash -implSig :: Text-implSig =-    encodeHex $-    exportSig $-    signMsg+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@@ -155,272 +303,382 @@ 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+    Just h = addressHash160 =<< 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+    Just h = addressHash160 =<< 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+        (either error id $ 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 (either error id $ 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++-- | The test vectors of the Schnorr module of Bitcoin Cash Node+-- (@src\/secp256k1\/src\/modules\/schnorr\/tests_impl.h@, version 29.0.0):+-- a name, the public key, the digest, the signature, and whether it+-- verifies.+cashSchnorrVectors :: [(String, Text, Text, Text, Bool)]+cashSchnorrVectors =+  [+    ( "1",+      "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",+      "0000000000000000000000000000000000000000000000000000000000000000",+      "787a848e71043d280c50470e8e1532b2dd5d20ee912a45dbdd2bd1dfbf187ef6\+      \7031a98831859dc34dffeedda86831842ccd0079e1f92af177f7f22cc1dced05",+      True+    ),+    ( "2",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd",+      True+    ),+    ( "3",+      "03fac2114c2fbb091527eb7c64ecb11f8021cb45e8e7809d3c0938e4b8c0e5f84b",+      "5e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c",+      "00da9b08172a9b6f0466a2defd817f2d7ab437e0d253cb5395a963866b3574be\+      \00880371d01766935b92d2ab4cd5c8a2a5837ec57fed7660773a05f0de142380",+      True+    ),+    ( "4",+      "03defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34",+      "4df3c3f68fcc83b27e9d42c90431a72499f17875c81a599b566c9889b9696703",+      "00000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63\+      \02a8dc32e64e86a333f20ef56eac9ba30b7246d6d25e22adb8c6be1aeb08d49d",+      True+    ),+    ( "4b",+      "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",+      "0000000000000000000000000000000000000000000000000000000000000000",+      "52818579aca59767e3291d91b76b637bef062083284992f2d95f564ca6cb4e35\+      \30b1da849c8e8304adc0cfe870660334b3cfc18e825ef1db34cfae3dfc5d8187",+      True+    ),+    ( "6: R.y is not a quadratic residue",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \fa16aee06609280a19b67a24e1977e4697712b5fd2943914ecd5f730901b4ab7",+      False+    ),+    ( "7: negated message hash",+      "03fac2114c2fbb091527eb7c64ecb11f8021cb45e8e7809d3c0938e4b8c0e5f84b",+      "5e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c",+      "00da9b08172a9b6f0466a2defd817f2d7ab437e0d253cb5395a963866b3574be\+      \d092f9d860f1776a1f7412ad8a1eb50daccc222bc8c0e26b2056df2f273efdec",+      False+    ),+    ( "8: negated s",+      "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",+      "0000000000000000000000000000000000000000000000000000000000000000",+      "787a848e71043d280c50470e8e1532b2dd5d20ee912a45dbdd2bd1dfbf187ef6\+      \8fce5677ce7a623cb20011225797ce7a8de1dc6ccd4f754a47da6c600e59543c",+      False+    ),+    ( "9: negated P",+      "03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd",+      False+    ),+    ( "10: s * G = e * P, R at infinity",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \8c3428869a663ed1e954705b020cbb3e7bb6ac31965b9ea4c73e227b17c5af5a",+      False+    ),+    ( "11: r not on the curve",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "4a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd",+      False+    ),+    ( "12: r = p",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2f\+      \1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd",+      False+    ),+    ( "13: s = n",+      "02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",+      "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",+      "2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d\+      \fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",+      False+    )+  ]++cashSchnorrSpec :: Ctx -> Spec+cashSchnorrSpec ctx =+  describe "Schnorr signatures of Bitcoin Cash" $+    forM_ cashSchnorrVectors $ \(name, pk, digest, sig, valid) ->+      it ("vector " <> name) $ do+        let bytes = fromJust . decodeHex+            pub = fromJust (importPubKey ctx (bytes pk))+            h = either error id (S.decode (bytes digest)) :: Hash256+        verifyCashSchnorr ctx pub (bytes sig) h `shouldBe` valid
− test/Haskoin/Keys/ExtendedSpec.hs
@@ -1,578 +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.Either                (isLeft)-import           Data.Maybe                 (fromJust, isJust, isNothing)-import           Data.Serialize             as S-import           Data.String                (fromString)-import           Data.String.Conversions    (cs)-import           Data.Text                  (Text)-import           Data.Word                  (Word32)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Haskoin.UtilSpec           (customCerealID)-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit                 (Assertion, assertBool, assertEqual)-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 (S.encode $ xPrvID m) == v !! 0-    assertBool "xPrvFP" $ encodeHex (S.encode $ 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 (S.encode $ xPrvChain m) == v !! 7-    assertBool "Hex PubKey" $-        encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m) == v !! 8-    assertBool "Hex PrvKey" $ encodeHex (runPut (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 (S.encode $ xPrvID m))-    , ("xPrvFP", encodeHex (S.encode $ 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 (S.encode $ xPrvChain m))-    , ("Hex PubKey", encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m))-    , ("Hex PrvKey", encodeHex (runPut (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,490 +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.Hspec-import           Test.HUnit-import           Test.QuickCheck        hiding ((.&.))--spec :: Spec-spec =-    describe "mnemonic" $ do-        it "entropy to mnemonic sentence" toMnemonicTest-        it "mnemonic sentence to entropy" fromMnemonicTest-        it "mnemonic sentence to seed" mnemonicToSeedTest-        it "mnemonic sentence with invalid checksum" fromMnemonicInvalidTest-        it "empty mnemonic sentence is invalid" $ sequence_ [emptyMnemonicTest]-        it "generate 12 words" $ property toMnemonic128-        it "generate 18 words" $ property toMnemonic160-        it "generate 24 words" $ property toMnemonic256-        it "generate 48 words" $ property toMnemonic512-        it "generate any number of words" $ property toMnemonicVar-        it "encode and decode 128-bit entropy" $ property fromToMnemonic128-        it "encode and decode 160-bit entropy" $ property fromToMnemonic160-        it "encode and decode 256-bit entropy" $ property fromToMnemonic256-        it "encode and decode 512-bit entropy" $ property fromToMnemonic512-        it "encode and decode n-bit entropy" $ property fromToMnemonicVar-        it "convert 128-bit mnemonic to seed" $ property mnemonicToSeed128-        it "convert 160-bit mnemonic to seed" $ property mnemonicToSeed160-        it "convert 256-bit mnemonic to seed" $ property mnemonicToSeed256-        it "convert 512-bit mnemonic to seed" $ property mnemonicToSeed512-        it "convert n-bit mnemonic to seed" $ property mnemonicToSeedVar-        it "get bits" $ property getBitsByteCount-        it "get end bits" $ property getBitsEndBits--toMnemonicTest :: Assertion-toMnemonicTest = zipWithM_ f ents mss-  where-    f e m = assertEqual "" m . h $ e-    h =-        fromRight (error "Could not decode mnemonic sentence") .-        toMnemonic . fromJust . decodeHex--fromMnemonicTest :: Assertion-fromMnemonicTest = zipWithM_ f ents mss-  where-    f e = assertEqual "" e . h-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic sentence") . fromMnemonic--mnemonicToSeedTest :: Assertion-mnemonicToSeedTest = zipWithM_ f mss seeds-  where-    f m s = assertEqual "" s . h $ m-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic seed") .-        mnemonicToSeed "TREZOR"--fromMnemonicInvalidTest :: Assertion-fromMnemonicInvalidTest = mapM_ f invalidMss-  where-    f = assertBool "" . h-    h m = case fromMnemonic m of-            Right _  -> False-            Left err -> "fromMnemonic: checksum failed:" `isPrefixOf` err--emptyMnemonicTest :: Assertion-emptyMnemonicTest =-    assertBool "" $-    case fromMnemonic "" of-        Right _  -> False-        Left err -> "fromMnemonic: empty mnemonic" `isPrefixOf` err--ents :: [Text]-ents =-    [ "00000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "80808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffff"-    , "000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffff"-    , "0000000000000000000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "8080808080808080808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"-    , "77c2b00716cec7213839159e404db50d"-    , "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b"-    , "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982"-    , "0460ef47585604c5660618db2e6a7e7f"-    , "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f"-    , "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416"-    , "eaebabb2383351fd31d703840b32e9e2"-    , "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78"-    , "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef"-    , "18ab19a9f54a9274f03e5209a2ac8a91"-    , "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4"-    , "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419"-    ]--mss :: [Mnemonic]-mss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon about"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ yellow"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage above"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon agent"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal will"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter always"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ when"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon art"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth title"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic bless"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo vote"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood bridge"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge swap"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion exotic"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ yellow"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter left"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion taste"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ shell"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price segment"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing top"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ cat"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum chief"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ coconut"-    ]--seeds :: [Text]-seeds =-    [ "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a69\-      \87599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"-    , "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1\-      \296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"-    , "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f1\-      \2eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8"-    , "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a133325729\-      \17f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069"-    , "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9\-      \a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa"-    , "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c3\-      \92d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd"-    , "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913f\-      \fb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65"-    , "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43\-      \348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528"-    , "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4\-      \d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8"-    , "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146a\-      \d717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87"-    , "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61a\-      \f0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f"-    , "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0\-      \a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad"-    , "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f6\-      \72a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff"-    , "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941\-      \c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5"-    , "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da2\-      \0af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67"-    , "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41\-      \f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4"-    , "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349db\-      \e2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba"-    , "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f\-      \86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449"-    , "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c4\-      \04f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c"-    , "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079\-      \403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79"-    , "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b\-      \14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c"-    , "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe0\-      \05831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8"-    , "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf0\-      \7c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9"-    , "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb\-      \86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"-    ]--invalidMss :: [Mnemonic]-invalidMss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ thank"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage sausage"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter abandon"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic letter"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo zoo"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood zoo"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge zoo"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion zoo"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ zoo"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter zoo"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion zoo"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ zoo"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price zoo"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing zoo"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ zoo"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum zoo"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ zoo"-    ]--binWordsToBS :: Serialize a => [a] -> BS.ByteString-binWordsToBS = foldr f BS.empty-  where-    f b a = a `BS.append` encode b--{- Encode mnemonic -}--toMnemonic128 :: (Word64, Word64) -> Bool-toMnemonic128 (a, b) = l == 12-  where-    bs = encode a `BS.append` encode b-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic senttence") $-        toMnemonic bs--toMnemonic160 :: (Word32, Word64, Word64) -> Bool-toMnemonic160 (a, b, c) = l == 15-  where-    bs = BS.concat [encode a, encode b, encode c]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-toMnemonic256 (a, b, c, d) = l == 24-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-toMnemonic512 ((a, b, c, d), (e, f, g, h)) = l == 48-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonicVar :: [Word32] -> Property-toMnemonicVar ls = not (null ls) && length ls <= 8 ==> l == wc-  where-    bs = binWordsToBS ls-    bl = BS.length bs-    cb = bl `div` 4-    wc = (cb + bl * 8) `div` 11-    l =-        length . T.words .-        fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--{- Encode/Decode -}--fromToMnemonic128 :: (Word64, Word64) -> Bool-fromToMnemonic128 (a, b) = bs == bs'-  where-    bs = encode a `BS.append` encode b-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic160 :: (Word32, Word64, Word64) -> Bool-fromToMnemonic160 (a, b, c) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-fromToMnemonic256 (a, b, c, d) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-fromToMnemonic512 ((a, b, c, d), (e, f, g, h)) = bs == bs'-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonicVar :: [Word32] -> Property-fromToMnemonicVar ls = not (null ls) && length ls <= 8 ==> bs == bs'-  where-    bs = binWordsToBS ls-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--{- Mnemonic to seed -}--mnemonicToSeed128 :: (Word64, Word64) -> Bool-mnemonicToSeed128 (a, b) = l == 64-  where-    bs = encode a `BS.append` encode b-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed160 :: (Word32, Word64, Word64) -> Bool-mnemonicToSeed160 (a, b, c) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed256 :: (Word64, Word64, Word64, Word64) -> Bool-mnemonicToSeed256 (a, b, c, d) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-mnemonicToSeed512 ((a, b, c, d), (e, f, g, h)) = l == 64-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeedVar :: [Word32] -> Property-mnemonicToSeedVar ls = not (null ls) && length ls <= 16 ==> l == 64-  where-    bs = binWordsToBS ls-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--{- Get bits from ByteString -}--data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show--instance Arbitrary ByteCountGen where-    arbitrary = do-        bs <- arbitraryBS-        i <- choose (0, BS.length bs * 8)-        return $ ByteCountGen bs i--getBitsByteCount :: ByteCountGen -> Bool-getBitsByteCount (ByteCountGen bs i) = BS.length bits == l-  where-    (q, r) = i `quotRem` 8-    bits = getBits i bs-    l = if r == 0 then q else q + 1--getBitsEndBits :: ByteCountGen -> Bool-getBitsEndBits (ByteCountGen bs i) =-    (r == 0) || (BS.last bits .&. (0xff `shiftR` r) == 0x00)-  where-    r = i `mod` 8-    bits = getBits i bs
− test/Haskoin/KeysSpec.hs
@@ -1,257 +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.Maybe-import           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.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit-import           Test.QuickCheck--serialVals :: [SerialBox]-serialVals =-    [ SerialBox (arbitrary :: Gen SecKey)-    , 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 $ S.encode 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 = S.encode p--testMiniKey :: Assertion-testMiniKey =-    assertEqual "fromMiniKey" (Just res) (go "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy")-  where-    go = fmap (encodeHex . S.encode . 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 . S.encode . 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 . S.decode =<< 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 . S.decode =<< 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 . S.decode <=< 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,170 +1,1509 @@-{-# LANGUAGE OverloadedStrings #-}-module Haskoin.NetworkSpec (spec) where--import           Data.Maybe             (fromJust)-import           Data.Serialize         as S-import           Data.Text              (Text)-import           Data.Word              (Word32)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys-import           Haskoin.Network-import           Haskoin.Transaction-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Haskoin.UtilSpec       (customCerealID)-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit             (Assertion, assertBool, assertEqual)-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-    ]--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--bloomFilter :: Word32 -> Text -> Assertion-bloomFilter n x = do-    assertBool "Bloom filter doesn't contain vector 1" $ bloomContains f1 v1-    assertBool "Bloom filter contains something it should not" $-        not $ bloomContains f1 v2-    assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3-    assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f4 == bs-  where-    f0 = bloomCreate 3 0.01 n BloomUpdateAll-    f1 = bloomInsert f0 v1-    f3 = bloomInsert f1 v3-    f4 = bloomInsert f3 v4-    v1 = fromJust $ decodeHex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v2 = fromJust $ decodeHex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v3 = fromJust $ decodeHex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"-    v4 = fromJust $ decodeHex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"-    bs = fromJust $ decodeHex x--bloomFilter1 :: Assertion-bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"--bloomFilter2 :: Assertion-bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"--bloomFilter3 :: Assertion-bloomFilter3 =-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f2 == bs-  where-    f0 = bloomCreate 2 0.001 0 BloomUpdateAll-    f1 = bloomInsert f0 $ S.encode p-    f2 = bloomInsert f1 $ S.encode $ getAddrHash160 $ pubKeyAddr p-    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"-    p = derivePubKeyI k-    bs = fromJust $ decodeHex "038fc16b080000000000000001"--relevantOutputUpdated :: Assertion-relevantOutputUpdated = assertBool "Bloom filter output updated" $-    any (bloomContains bf2) spendTxInput-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx-        spendTxInput = encode .prevOutput <$> txIn spendRelevantTx--irrelevantOutputNotUpdated :: Assertion-irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = bloomRelevantUpdate bf1 unrelatedTx---- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)-relevantTx :: Tx-relevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1-            , scriptInput = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 100000000, scriptOutput = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac" }-            , TxOut { outValue = 107980000, scriptOutput = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)-spendRelevantTx :: Tx-spendRelevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0-            , scriptInput = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88"-            , txInSequence = 4294967295-            }, TxIn-            { prevOutput = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0-            , scriptInput = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 3851100, scriptOutput = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac" }-            , TxOut { outValue = 111000000, scriptOutput = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- This random transaction is unrelated to the others-unrelatedTx :: Tx-unrelatedTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20-            , scriptInput = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1"-            , txInSequence = 4294967295-            }]-        , txOut =-            [ TxOut { outValue = 12600000, scriptOutput = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.NetworkSpec (spec) where++import Control.Exception (evaluate)+import Control.Monad (forM_)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.List (isInfixOf, nub)+import Data.Maybe (fromJust)+import Data.Sequence qualified as S+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word32)+import Haskoin.Address+import Haskoin.Block+import Haskoin.Crypto+import Haskoin.Network+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Network.Socket (SockAddr (..), tupleToHostAddress)+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++-- Helpers -------------------------------------------------------------------++-- | Bytes of a hex string.+hex :: Text -> ByteString+hex = fromJust . decodeHex++-- | Hex of the serialization of a value.+hexOf :: (Serial a) => a -> Text+hexOf = encodeHex . runPutS . serialize++-- | Parse bytes as a value.+parse :: (Serial a) => ByteString -> Either String a+parse = runGetS deserialize++-- | The parse must fail and the error must mention the given words.+failsWith :: (Show a) => Either String a -> String -> Expectation+failsWith result msg =+  case result of+    Left e | msg `isInfixOf` e -> return ()+    _ ->+      expectationFailure $+        "expected a failure mentioning " ++ show msg ++ ", got " ++ show result++-- Hashes whose hex is the same read in either direction, so their bytes on+-- the wire are the hex below whether or not the type reverses them.+h1, h2, h3 :: Hash256+h1 = fromString' (T.replicate 32 "11")+h2 = fromString' (T.replicate 32 "22")+h3 = fromString' (T.replicate 32 "33")++fromString' :: Text -> Hash256+fromString' = either error id . runGetS deserialize . hex++h1Hex, h2Hex, h3Hex :: Text+h1Hex = T.replicate 32 "11"+h2Hex = T.replicate 32 "22"+h3Hex = T.replicate 32 "33"++bh1 :: BlockHash+bh1 = "1111111111111111111111111111111111111111111111111111111111111111"++bh1Hex :: Text+bh1Hex = T.replicate 32 "11"++-- | 10.0.0.1 port 8333 as an IPv4-mapped IPv6 address.+ipv4Host :: HostAddress+ipv4Host = sockToHostAddress (SockAddrInet 8333 (tupleToHostAddress (10, 0, 0, 1)))++-- | Ten zero bytes, two 0xff bytes, the four address bytes, then the port in+-- network byte order.+ipv4Bytes :: Text+ipv4Bytes = "00000000000000000000ffff0a000001208d"++-- | 2001:db8::1 port 8333.+ipv6Host :: HostAddress+ipv6Host = sockToHostAddress (SockAddrInet6 8333 0 (0x20010db8, 0, 0, 1) 0)++ipv6Bytes :: Text+ipv6Bytes = "20010db8000000000000000000000001208d"++-- | 0.0.0.0 port 0, the address of the documentation's version message.+zeroHost :: HostAddress+zeroHost = sockToHostAddress (SockAddrInet 0 (tupleToHostAddress (0, 0, 0, 0)))++-- | The sample version message of the Bitcoin wiki protocol documentation,+-- sent with protocol 60002, before the relay flag existed.+versionPayloadHex :: Text+versionPayloadHex =+  "62ea0000" -- protocol 60002+    <> "0100000000000000" -- services NODE_NETWORK+    <> "11b2d05000000000" -- timestamp 1355854353+    <> "010000000000000000000000000000000000ffff000000000000" -- addr_recv+    <> "010000000000000000000000000000000000ffff000000000000" -- addr_from+    <> "3b2eb35d8ce61765" -- nonce+    <> "0f2f5361746f7368693a302e372e322f" -- "/Satoshi:0.7.2/"+    <> "c03e0300" -- start height 212672++versionMessageHex :: Text+versionMessageHex =+  "f9beb4d9" -- mainnet magic+    <> "76657273696f6e0000000000" -- "version"+    <> "64000000" -- 100 payload bytes+    <> "3b648d5a" -- checksum+    <> versionPayloadHex++wikiVersion :: Version+wikiVersion =+  Version+    { version = 60002,+      services = 1,+      timestamp = 1355854353,+      addrRecv = NetworkAddress 1 zeroHost,+      addrSend = NetworkAddress 1 zeroHost,+      nonce = 7284544412836900411, -- 0x6517e68c5db32e3b+      userAgent = VarString "/Satoshi:0.7.2/",+      startHeight = 212672,+      relay = True+    }++-- | The sample addr message of the Bitcoin wiki protocol documentation:+-- one entry, seen at 1292899810, services NODE_NETWORK, 10.0.0.1:8333.+addrMessageHex :: Text+addrMessageHex =+  "f9beb4d9"+    <> "61646472" <> "0000000000000000" -- "addr"+    <> "1f000000" -- 31 payload bytes+    <> "ed52399b" -- checksum+    <> "01" -- one entry+    <> "e215104d" -- time 1292899810+    <> "0100000000000000" -- services+    <> ipv4Bytes++-- | A ping with nonce 1. The checksum is the first four bytes of the double+-- SHA-256 of the eight payload bytes.+pingMessageHex :: Text+pingMessageHex =+  "f9beb4d9"+    <> "70696e670000000000000000" -- "ping"+    <> "08000000"+    <> "08533f6b"+    <> "0100000000000000"++-- | A verack. The checksum of an empty payload is 5df6e0e2.+verackMessageHex :: Text+verackMessageHex = "f9beb4d9" <> "76657261636b000000000000" <> "00000000" <> "5df6e0e2"++-- | Pay-to-pubkey-hash output script for the hash+-- 03f47604ea2736334151081e13265b4fe38e6fa8.+p2pkhHash :: ByteString+p2pkhHash = hex "03f47604ea2736334151081e13265b4fe38e6fa8"++p2pkhScript :: ByteString+p2pkhScript = hex "76a914" <> p2pkhHash <> hex "88ac"++-- | A transaction with one input and the given output scripts, each paying+-- 1000 satoshi.+txPaying :: [ByteString] -> Tx+txPaying scripts =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1,+              script = B.empty,+              sequence = 0xffffffff+            }+        ],+      outputs = [TxOut {value = 1000, script = s} | s <- scripts],+      witness = [],+      locktime = 0+    }++-- | The bytes of the outpoint of an output of a transaction, as inserted in+-- a bloom filter.+outpointBytes :: Tx -> Word32 -> ByteString+outpointBytes tx i = runPutS $ serialize $ OutPoint (txHash tx) i+++-- | 'txPaying' [p2pkhScript] serialized: version, one input (outpoint hash+-- in internal byte order, index 1, empty script, sequence), one output+-- (1000 satoshi, 25-byte script), lock time.+oneInputTxHex :: Text+oneInputTxHex =+  "01000000"+    <> "01"+    <> "0e065dc971ac296141cb9db1cd520caf2fc06aa06fb5202959afe3b71790fe35"+    <> "01000000"+    <> "00"+    <> "ffffffff"+    <> "01"+    <> "e803000000000000"+    <> "19"+    <> "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac"+    <> "00000000"++genesisHeaderHex :: Text+genesisHeaderHex =+  "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c"++knownCommands :: [MessageCommand]+knownCommands =+  [ MCVersion,+    MCVerAck,+    MCAddr,+    MCAddrV2,+    MCSendAddrV2,+    MCInv,+    MCGetData,+    MCNotFound,+    MCGetBlocks,+    MCGetHeaders,+    MCTx,+    MCBlock,+    MCMerkleBlock,+    MCHeaders,+    MCGetAddr,+    MCFilterLoad,+    MCFilterAdd,+    MCFilterClear,+    MCPing,+    MCPong,+    MCAlert,+    MCMempool,+    MCReject,+    MCSendHeaders,+    MCFeeFilter,+    MCSendCmpct,+    MCCmpctBlock,+    MCGetBlockTxn,+    MCBlockTxn,+    MCGetCFilters,+    MCCFilter,+    MCGetCFHeaders,+    MCCFHeaders,+    MCGetCFCheckpt,+    MCCFCheckpt,+    MCWtxidRelay,+    MCSendTxRcncl+  ]++-- Identity tests --------------------------------------------------------------++identityTests :: IdentityTests+identityTests =+  def+    { readTests =+        [ ReadBox arbitraryVarInt,+          ReadBox arbitraryVarString,+          ReadBox arbitraryAddressNetwork,+          ReadBox arbitraryNetworkAddressV2,+          ReadBox arbitraryInvType,+          ReadBox arbitraryAlert,+          ReadBox arbitraryFeeFilter,+          ReadBox arbitrarySendTxRcncl,+          ReadBox arbitraryReject,+          ReadBox arbitraryRejectCode,+          ReadBox arbitraryPing,+          ReadBox arbitraryPong,+          ReadBox arbitraryMessageCommand,+          ReadBox arbitraryBloomFlags,+          ReadBox arbitraryBloomFilter,+          ReadBox arbitraryFilterLoad,+          ReadBox arbitraryFilterAdd,+          ReadBox arbitrarySendCompact+        ],+      serialTests =+        [ SerialBox arbitraryVarInt,+          SerialBox arbitraryVarString,+          SerialBox arbitraryNetworkAddress,+          SerialBox arbitraryNetworkAddressV2,+          SerialBox arbitraryAddrV2,+          SerialBox arbitraryInvType,+          SerialBox arbitraryInvVector,+          SerialBox arbitraryInv1,+          SerialBox arbitraryVersion,+          SerialBox arbitraryAddr1,+          SerialBox arbitraryAlert,+          SerialBox arbitraryFeeFilter,+          SerialBox arbitrarySendTxRcncl,+          SerialBox arbitraryReject,+          SerialBox arbitraryRejectCode,+          SerialBox arbitraryGetData,+          SerialBox arbitraryNotFound,+          SerialBox arbitraryPing,+          SerialBox arbitraryPong,+          SerialBox arbitraryMessageCommand,+          SerialBox arbitraryMessageHeader,+          SerialBox arbitraryBloomFlags,+          SerialBox arbitraryBloomFilter,+          SerialBox arbitraryFilterLoad,+          SerialBox arbitraryFilterAdd,+          SerialBox arbitrarySendCompact,+          SerialBox arbitraryGetBlockTxn,+          SerialBox arbitraryGetCFilters,+          SerialBox arbitraryCFilter,+          SerialBox arbitraryGetCFHeaders,+          SerialBox arbitraryCFHeaders,+          SerialBox arbitraryGetCFCheckpt,+          SerialBox arbitraryCFCheckpt+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity identityTests+  describe "Custom identity tests" $ do+    prop "Data.Serialize Encoding for type Message" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryMessage net ctx) $+          customCerealID (getMessage net) (putMessage net)+    prop "Binary encoding/decoding identity for CompactBlock" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryCompactBlock net ctx) $ \c ->+          parse (runPutS (serialize c)) == Right c+    prop "Binary encoding/decoding identity for BlockTxn" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryBlockTxn net ctx) $ \b ->+          parse (runPutS (serialize b)) == Right b+    prop "msgType is the command a message is sent with" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryMessage net ctx) $ \m ->+          fmap (.cmd) (parse (runPutS (putMessage net m)) :: Either String MessageHeader)+            == Right (msgType m)+  varIntSpec+  listSpec+  commandSpec+  inventorySpec+  addressSpec+  addrV2Spec+  versionSpec+  smallMessagesSpec+  rejectSpec+  framingSpec+  bloomSpec ctx+  compactBlockSpec+  blockFilterSpec+  constantsSpec+  sizeLimitSpec+  itemLimitSpec+  compactPositionSpec+  witnessFormatSpec+  bloomMatchSpec ctx+  boundarySpec++-- VarInt ----------------------------------------------------------------------++varIntSpec :: Spec+varIntSpec = describe "VarInt" $ do+  it "writes the shortest encoding at every boundary" $ do+    hexOf (VarInt 0) `shouldBe` "00"+    hexOf (VarInt 0xfc) `shouldBe` "fc"+    hexOf (VarInt 0xfd) `shouldBe` "fdfd00"+    hexOf (VarInt 0xffff) `shouldBe` "fdffff"+    hexOf (VarInt 0x10000) `shouldBe` "fe00000100"+    hexOf (VarInt 0xffffffff) `shouldBe` "feffffffff"+    hexOf (VarInt 0x100000000) `shouldBe` "ff0000000001000000"+    hexOf (VarInt 0xffffffffffffffff) `shouldBe` "ffffffffffffffffff"+  it "reads each encoding back" $ do+    parse (hex "00") `shouldBe` Right (VarInt 0)+    parse (hex "fc") `shouldBe` Right (VarInt 0xfc)+    parse (hex "fdfd00") `shouldBe` Right (VarInt 0xfd)+    parse (hex "fdffff") `shouldBe` Right (VarInt 0xffff)+    parse (hex "fe00000100") `shouldBe` Right (VarInt 0x10000)+    parse (hex "feffffffff") `shouldBe` Right (VarInt 0xffffffff)+    parse (hex "ff0000000001000000") `shouldBe` Right (VarInt 0x100000000)+    parse (hex "ffffffffffffffffff") `shouldBe` Right (VarInt 0xffffffffffffffff)+  it "refuses a three-byte encoding of a value below 253" $+    (parse (hex "fd0100") :: Either String VarInt) `failsWith` "non-canonical"+  it "refuses a five-byte encoding of a value below 0x10000" $+    (parse (hex "fe01000000") :: Either String VarInt) `failsWith` "non-canonical"+  it "refuses a nine-byte encoding of a value below 0x100000000" $+    (parse (hex "ff0100000000000000") :: Either String VarInt) `failsWith` "non-canonical"+  it "writes an Int with putVarInt like a VarInt" $+    encodeHex (runPutS (putVarInt (300 :: Int))) `shouldBe` "fd2c01"++-- Lists and byte strings -----------------------------------------------------++listSpec :: Spec+listSpec = describe "lists and byte strings" $ do+  it "limits counts to the MAX_SIZE of Bitcoin Core" $+    maxListSize `shouldBe` 33554432+  it "writes a byte string with its length in front" $ do+    hexOf (VarString "abc") `shouldBe` "03616263"+    hexOf (VarString "") `shouldBe` "00"+  it "reads a byte string" $+    parse (hex "03616263") `shouldBe` Right (VarString "abc")+  it "refuses a byte count above the limit" $+    -- 0x02000001 is one above the limit+    (parse (hex "fe01000002") :: Either String VarString) `failsWith` "Byte count too large"+  it "writes bytes with their count" $+    encodeHex (runPutS (putVarIntBytes "abc")) `shouldBe` "03616263"+  it "reads a list with its count" $+    runGetS (getVarIntList getWord8) (hex "020102") `shouldBe` Right [1, 2]+  it "writes a list with its count" $+    encodeHex (runPutS (putVarIntList putWord8 [1, 2])) `shouldBe` "020102"+  it "writes the count of a list of 253 elements in three bytes" $+    -- 253 = 0xfd is the first count that does not fit one byte+    B.take 4 (runPutS (putVarIntList putWord8 (replicate 253 7))) `shouldBe` hex "fdfd0007"+  it "reads a list of 253 elements" $+    fmap length (runGetS (getVarIntList getWord8) (hex "fdfd00" <> B.replicate 253 7)) `shouldBe` Right 253+  it "refuses a list above a given limit as soon as the count is read" $ do+    runGetS (getVarIntListMax "test" 2 getWord8) (hex "03") `failsWith` "test: too many entries: 3, the limit is 2"+    runGetS (getVarIntListMax "test" 2 getWord8) (hex "020102") `shouldBe` Right [1, 2]+  it "knows the item limits of the protocol" $+    -- MAX_INV_SZ, MAX_ADDR_TO_SEND, MAX_HEADERS_RESULTS, MAX_LOCATOR_SZ, and BIP 157+    [maxInvSize, maxAddrSize, maxHeadersSize, maxLocatorSize, maxFilterHashes] `shouldBe` [50000, 1000, 2000, 101, 2000]+  it "refuses a list count above the limit" $+    runGetS (getVarIntList getWord8) (hex "fe01000002") `failsWith` "List count too large"++-- Message commands -----------------------------------------------------------++commandSpec :: Spec+commandSpec = describe "message commands" $ do+  it "names every known command as Bitcoin Core does" $+    map commandToString knownCommands+      `shouldBe` [ "version",+                   "verack",+                   "addr",+                   "addrv2",+                   "sendaddrv2",+                   "inv",+                   "getdata",+                   "notfound",+                   "getblocks",+                   "getheaders",+                   "tx",+                   "block",+                   "merkleblock",+                   "headers",+                   "getaddr",+                   "filterload",+                   "filteradd",+                   "filterclear",+                   "ping",+                   "pong",+                   "alert",+                   "mempool",+                   "reject",+                   "sendheaders",+                   "feefilter",+                   "sendcmpct",+                   "cmpctblock",+                   "getblocktxn",+                   "blocktxn",+                   "getcfilters",+                   "cfilter",+                   "getcfheaders",+                   "cfheaders",+                   "getcfcheckpt",+                   "cfcheckpt",+                   "wtxidrelay",+                   "sendtxrcncl"+                 ]+  it "reads every known name back to its command" $+    forM_ knownCommands $ \c ->+      stringToCommand (commandToString c) `shouldBe` c+  it "reads an unknown name as an unknown command" $+    stringToCommand "foo" `shouldBe` MCOther "foo"+  it "pads a name with NUL bytes to twelve" $ do+    hexOf MCVersion `shouldBe` "76657273696f6e0000000000"+    hexOf MCPing `shouldBe` "70696e670000000000000000"+    hexOf MCSendTxRcncl `shouldBe` "73656e64747872636e636c00"+    hexOf (MCOther "foo") `shouldBe` "666f6f000000000000000000"+  it "reads a padded name" $ do+    parse (hex "76657273696f6e0000000000") `shouldBe` Right MCVersion+    parse (hex "666f6f000000000000000000") `shouldBe` Right (MCOther "foo")+  it "reads twelve NUL bytes as an empty unknown command" $+    parse (hex "000000000000000000000000") `shouldBe` Right (MCOther "")+  it "refuses a byte after the NUL terminator" $+    (parse "ping\0garbage" :: Either String MessageCommand) `failsWith` "after its NUL terminator"+  it "refuses a name with a byte outside printable ASCII" $+    -- "ping" followed by a line feed+    (parse (hex "70696e670a00000000000000") :: Either String MessageCommand) `failsWith` "printable ASCII"+  it "refuses to serialize a name longer than twelve bytes" $+    evaluate (runPutS (serialize (MCOther "averyverylongcommand"))) `shouldThrow` anyErrorCall+  it "refuses to serialize a name with a byte outside printable ASCII" $+    evaluate (runPutS (serialize (MCOther "ping\n"))) `shouldThrow` anyErrorCall+  it "shows and reads a command as its name" $ do+    show MCPing `shouldBe` "\"ping\""+    read "\"ping\"" `shouldBe` MCPing+    read "\"foo\"" `shouldBe` MCOther "foo"+    ("ping" :: MessageCommand) `shouldBe` MCPing++-- Inventory ------------------------------------------------------------------++inventorySpec :: Spec+inventorySpec = describe "inventory" $ do+  it "numbers every inventory type as Bitcoin Core does" $ do+    hexOf InvError `shouldBe` "00000000"+    hexOf InvTx `shouldBe` "01000000"+    hexOf InvBlock `shouldBe` "02000000"+    hexOf InvMerkleBlock `shouldBe` "03000000"+    hexOf InvCompactBlock `shouldBe` "04000000"+    hexOf InvWTx `shouldBe` "05000000"+    -- MSG_WITNESS_FLAG is 1 << 30, so 0x40000001 little-endian+    hexOf InvWitnessTx `shouldBe` "01000040"+    hexOf InvWitnessBlock `shouldBe` "02000040"+    hexOf InvWitnessMerkleBlock `shouldBe` "03000040"+    hexOf (InvType 7) `shouldBe` "07000000"+  it "reads every inventory type" $ do+    parse (hex "01000000") `shouldBe` Right InvTx+    parse (hex "05000000") `shouldBe` Right InvWTx+    parse (hex "02000040") `shouldBe` Right InvWitnessBlock+  it "reads an unknown inventory type as a number" $+    parse (hex "06000000") `shouldBe` Right (InvType 6)+  it "writes an inventory vector as its type then its hash" $+    hexOf (InvVector InvTx h1) `shouldBe` "01000000" <> h1Hex+  it "writes inv, getdata and notfound as a counted list of vectors" $ do+    hexOf (Inv [InvVector InvTx h1, InvVector InvBlock h2])+      `shouldBe` "02" <> "01000000" <> h1Hex <> "02000000" <> h2Hex+    hexOf (GetData [InvVector InvWitnessTx h1]) `shouldBe` "01" <> "01000040" <> h1Hex+    hexOf (NotFound [InvVector InvTx h1]) `shouldBe` "01" <> "01000000" <> h1Hex+  it "reads inv, getdata and notfound" $ do+    parse (hex ("01" <> "01000000" <> h1Hex)) `shouldBe` Right (Inv [InvVector InvTx h1])+    parse (hex ("01" <> "02000000" <> h1Hex)) `shouldBe` Right (GetData [InvVector InvBlock h1])+    parse (hex ("00")) `shouldBe` Right (NotFound [])++-- Network addresses ----------------------------------------------------------++addressSpec :: Spec+addressSpec = describe "network addresses" $ do+  it "maps an IPv4 address into IPv6 with the port in network byte order" $+    hexOf ipv4Host `shouldBe` ipv4Bytes+  it "reads an IPv4-mapped address back as an IPv4 socket address" $+    fmap hostToSockAddr (parse (hex ipv4Bytes))+      `shouldBe` Right (SockAddrInet 8333 (tupleToHostAddress (10, 0, 0, 1)))+  it "writes an IPv6 address as sixteen bytes then the port" $+    hexOf ipv6Host `shouldBe` ipv6Bytes+  it "reads an IPv6 address back" $+    fmap hostToSockAddr (parse (hex ipv6Bytes))+      `shouldBe` Right (SockAddrInet6 8333 0 (0x20010db8, 0, 0, 1) 0)+  it "refuses an address shorter than 18 bytes" $+    (parse (hex "00000000000000000000ffff0a000001") :: Either String HostAddress)+      `failsWith` "too few bytes"+  it "writes the services before the address" $+    hexOf (NetworkAddress 1 ipv4Host) `shouldBe` "0100000000000000" <> ipv4Bytes+  it "writes an addr entry with its time first" $+    hexOf (Addr [(1292899810, NetworkAddress 1 ipv4Host)])+      `shouldBe` "01" <> "e215104d" <> "0100000000000000" <> ipv4Bytes+  it "reads an addr message" $+    parse (hex ("01" <> "e215104d" <> "0100000000000000" <> ipv4Bytes))+      `shouldBe` Right (Addr [(1292899810, NetworkAddress 1 ipv4Host)])++-- addrv2 ----------------------------------------------------------------------++addrV2Spec :: Spec+addrV2Spec = describe "addrv2" $ do+  let ipv4Entry = NetworkAddressV2 1292899810 1 AddrIPv4 (hex "0a000001") 8333+      ipv4EntryHex = "e215104d" <> "01" <> "01" <> "04" <> "0a000001" <> "208d"+  it "numbers the networks as BIP 155 does" $+    map addressNetworkId [AddrIPv4, AddrIPv6, AddrTorV2, AddrTorV3, AddrI2P, AddrCJDNS, AddrYggdrasil, AddrUnknown 9]+      `shouldBe` [1, 2, 3, 4, 5, 6, 7, 9]+  it "knows the address length of every network" $+    map addressNetworkLength [AddrIPv4, AddrIPv6, AddrTorV2, AddrTorV3, AddrI2P, AddrCJDNS, AddrYggdrasil, AddrUnknown 9]+      `shouldBe` [Just 4, Just 16, Just 10, Just 32, Just 32, Just 16, Just 16, Nothing]+  it "limits an address to 512 bytes" $+    maxAddrV2Size `shouldBe` 512+  it "writes an IPv4 entry with time, services as a VarInt, network, address and port" $+    hexOf ipv4Entry `shouldBe` ipv4EntryHex+  it "writes services of more than one byte as a VarInt" $+    -- 1024 is NODE_NETWORK_LIMITED+    hexOf (NetworkAddressV2 0 1024 AddrIPv6 (B.replicate 16 0) 0)+      `shouldBe` "00000000" <> "fd0004" <> "02" <> "10" <> T.replicate 32 "0" <> "0000"+  it "writes a Tor v3 entry with 32 address bytes and no port" $+    hexOf (NetworkAddressV2 0 0 AddrTorV3 (B.replicate 32 0xab) 0)+      `shouldBe` "00000000" <> "00" <> "04" <> "20" <> T.replicate 32 "ab" <> "0000"+  it "reads an entry" $+    parse (hex ipv4EntryHex) `shouldBe` Right ipv4Entry+  it "refuses an IPv4 entry whose address is not four bytes" $+    (parse (hex ("00000000" <> "00" <> "01" <> "05" <> "0a00000102" <> "208d")) :: Either String NetworkAddressV2)+      `failsWith` "should be 4"+  it "keeps an entry of an unknown network with its bytes" $+    parse (hex ("00000000" <> "00" <> "c8" <> "03" <> "aabbcc" <> "0000"))+      `shouldBe` Right (NetworkAddressV2 0 0 (AddrUnknown 200) (hex "aabbcc") 0)+  it "accepts an address of 512 bytes" $+    parse (hex ("00000000" <> "00" <> "c8" <> "fd0002") <> B.replicate 512 0x11 <> hex "0000")+      `shouldBe` Right (NetworkAddressV2 0 0 (AddrUnknown 200) (B.replicate 512 0x11) 0)+  it "refuses an address of 513 bytes" $+    (parse (hex ("00000000" <> "00" <> "c8" <> "fd0102") <> B.replicate 513 0x11 <> hex "0000") :: Either String NetworkAddressV2)+      `failsWith` "too long"+  it "writes an addrv2 message as a counted list of entries" $+    hexOf (AddrV2 [ipv4Entry, ipv4Entry]) `shouldBe` "02" <> ipv4EntryHex <> ipv4EntryHex+  it "turns an addr entry into an addrv2 entry" $+    toNetworkAddressV2 (1292899810, NetworkAddress 1 ipv4Host) `shouldBe` ipv4Entry+  it "keeps an IPv6 entry as sixteen bytes" $+    toNetworkAddressV2 (0, NetworkAddress 0 ipv6Host)+      `shouldBe` NetworkAddressV2 0 0 AddrIPv6 (hex "20010db8000000000000000000000001") 8333+  it "turns an addrv2 entry back into an addr entry" $ do+    fromNetworkAddressV2 ipv4Entry `shouldBe` Just (1292899810, NetworkAddress 1 ipv4Host)+    fromNetworkAddressV2 (NetworkAddressV2 0 0 AddrIPv6 (hex "20010db8000000000000000000000001") 8333)+      `shouldBe` Just (0, NetworkAddress 0 ipv6Host)+  it "has no addr form for a Tor address" $+    fromNetworkAddressV2 (NetworkAddressV2 0 0 AddrTorV3 (B.replicate 32 0) 0) `shouldBe` Nothing++-- Version ---------------------------------------------------------------------++versionSpec :: Spec+versionSpec = describe "version" $ do+  it "reads the version message of the protocol documentation" $+    (parse (hex versionPayloadHex) :: Either String Version) `shouldBe` Right wikiVersion+  it "reads the relay flag" $ do+    parse (hex (versionPayloadHex <> "00")) `shouldBe` Right wikiVersion {relay = False}+    parse (hex (versionPayloadHex <> "01")) `shouldBe` Right wikiVersion+  it "treats any other relay byte as true, as Bitcoin Core does" $+    parse (hex (versionPayloadHex <> "07")) `shouldBe` Right wikiVersion+  it "writes the relay flag after the start height" $+    hexOf wikiVersion `shouldBe` versionPayloadHex <> "01"+  it "limits the user agent to 256 bytes" $+    maxUserAgentLength `shouldBe` 256+  it "accepts a user agent of 256 bytes" $+    -- the 80 bytes before the user agent, then a 256-byte agent of "A"s+    let ua = hex "fd0001" <> B.replicate 256 0x41+        payload = hex (T.take 160 versionPayloadHex) <> ua <> hex "c03e030001"+     in fmap (\v -> B.length v.userAgent.get) (parse payload :: Either String Version)+          `shouldBe` Right 256+  it "refuses a user agent of 257 bytes" $+    let ua = hex "fd0101" <> B.replicate 257 0x41+        payload = hex (T.take 160 versionPayloadHex) <> ua <> hex "c03e030001"+     in (parse payload :: Either String Version) `failsWith` "too long"++-- Ping, pong, feefilter, sendtxrcncl, alert ----------------------------------++smallMessagesSpec :: Spec+smallMessagesSpec = describe "small messages" $ do+  it "writes ping and pong as a little-endian nonce" $ do+    hexOf (Ping 1) `shouldBe` "0100000000000000"+    hexOf (Pong 0x0102030405060708) `shouldBe` "0807060504030201"+  it "reads ping and pong" $ do+    parse (hex "0100000000000000") `shouldBe` Right (Ping 1)+    parse (hex "0807060504030201") `shouldBe` Right (Pong 0x0102030405060708)+  it "writes feefilter as a little-endian fee rate" $+    hexOf (FeeFilter 1000) `shouldBe` "e803000000000000"+  it "reads feefilter" $+    parse (hex "e803000000000000") `shouldBe` Right (FeeFilter 1000)+  it "writes sendtxrcncl as version then salt" $+    hexOf (SendTxRcncl 1 0x0102030405060708) `shouldBe` "01000000" <> "0807060504030201"+  it "reads sendtxrcncl" $+    parse (hex ("01000000" <> "0807060504030201")) `shouldBe` Right (SendTxRcncl 1 0x0102030405060708)+  it "writes an alert as two byte strings" $+    hexOf (Alert (VarString "ab") (VarString "c")) `shouldBe` "026162" <> "0163"+  it "reads an alert" $+    parse (hex ("026162" <> "0163")) `shouldBe` Right (Alert (VarString "ab") (VarString "c"))++-- Reject ----------------------------------------------------------------------++rejectSpec :: Spec+rejectSpec = describe "reject" $ do+  it "uses the code bytes of BIP 61" $+    map hexOf [RejectMalformed, RejectInvalid, RejectObsolete, RejectDuplicate, RejectNonStandard, RejectDust, RejectInsufficientFee, RejectCheckpoint]+      `shouldBe` ["01", "10", "11", "12", "40", "41", "42", "43"]+  it "reads every code" $+    forM_ [RejectMalformed, RejectInvalid, RejectObsolete, RejectDuplicate, RejectNonStandard, RejectDust, RejectInsufficientFee, RejectCheckpoint] $ \c ->+      parse (runPutS (serialize c)) `shouldBe` Right c+  it "refuses an unknown code" $+    (parse (hex "02") :: Either String RejectCode) `failsWith` "Invalid code"+  it "builds a reject without a hash" $+    (reject MCTx RejectDust "dust").extra `shouldBe` Nothing+  it "writes the command, code and reason, and no hash when there is none" $+    hexOf (reject MCTx RejectInvalid "bad") `shouldBe` "027478" <> "10" <> "03626164"+  it "writes the hash after the reason" $+    hexOf (Reject MCBlock RejectCheckpoint (VarString "") (Just h1))+      `shouldBe` "05626c6f636b" <> "43" <> "00" <> h1Hex+  it "reads a reject without a hash" $+    parse (hex ("027478" <> "10" <> "03626164")) `shouldBe` Right (reject MCTx RejectInvalid "bad")+  it "reads a reject with a hash" $+    parse (hex ("05626c6f636b" <> "43" <> "00" <> h1Hex))+      `shouldBe` Right (Reject MCBlock RejectCheckpoint (VarString "") (Just h1))+  it "refuses extra data that is not a hash" $+    (parse (hex ("027478" <> "10" <> "00" <> "0102030405")) :: Either String Reject) `failsWith` "too few bytes"++-- Message framing -------------------------------------------------------------++framingSpec :: Spec+framingSpec = describe "message framing" $ do+  it "writes a header as big-endian magic, padded command, little-endian length and checksum" $+    encodeHex (runPutS (putMessage btc MVerAck)) `shouldBe` verackMessageHex+  it "writes the payload after the header" $+    encodeHex (runPutS (putMessage btc (MPing (Ping 1)))) `shouldBe` pingMessageHex+  it "uses the magic of the network" $+    T.take 8 (encodeHex (runPutS (putMessage bch MVerAck))) `shouldBe` "e3e1f3e8"+  it "reads a header" $+    fmap (\h -> (h.magic, h.cmd, h.size)) (parse (hex verackMessageHex) :: Either String MessageHeader)+      `shouldBe` Right (0xf9beb4d9, MCVerAck, 0)+  it "writes a header back as it was" $+    fmap hexOf (parse (hex verackMessageHex) :: Either String MessageHeader) `shouldBe` Right verackMessageHex+  it "reads the verack message of the protocol documentation" $+    runGetS (getMessage btc) (hex verackMessageHex) `shouldBe` Right MVerAck+  it "reads the version message of the protocol documentation" $+    runGetS (getMessage btc) (hex versionMessageHex) `shouldBe` Right (MVersion wikiVersion)+  it "reads the addr message of the protocol documentation" $+    runGetS (getMessage btc) (hex addrMessageHex)+      `shouldBe` Right (MAddr (Addr [(1292899810, NetworkAddress 1 ipv4Host)]))+  it "refuses a message of another network" $+    runGetS (getMessage btcTest) (hex pingMessageHex) `failsWith` "Invalid network magic"+  it "refuses a message whose checksum does not match its payload" $+    runGetS (getMessage btc) (B.take 24 (hex pingMessageHex) <> hex "0200000000000000")+      `failsWith` "Invalid message checksum"+  it "refuses a length above maxMessageSize before reading the payload" $+    -- 4000001 is one above the limit of btc, written little-endian+    runGetS (getMessage btc) (hex ("f9beb4d9" <> "70696e670000000000000000" <> "01093d00" <> "5df6e0e2"))+      `failsWith` "larger than the limit"+  it "reads one message and leaves the next one in the stream" $+    runGetS ((,) <$> getMessage btc <*> getMessage btc) (hex (pingMessageHex <> verackMessageHex))+      `shouldBe` Right (MPing (Ping 1), MVerAck)+  it "hands the payload parser only the declared bytes" $+    -- a ping declared as 4 bytes, followed by 4 bytes that belong to nothing+    let payload = hex "01000000"+        msg = hex ("f9beb4d9" <> "70696e670000000000000000" <> "04000000") <> runPutS (serialize (checkSum32 payload)) <> payload <> hex "deadbeef"+     in runGetS (getMessage btc) msg `failsWith` "too few bytes"+  it "accepts a payload on a message type that carries none" $+    let payload = hex "00"+        msg = hex ("f9beb4d9" <> "76657261636b000000000000" <> "01000000") <> runPutS (serialize (checkSum32 payload)) <> payload+     in runGetS (getMessage btc) msg `shouldBe` Right MVerAck+  it "refuses a message type that needs a payload without one" $+    runGetS (getMessage btc) (hex ("f9beb4d9" <> "70696e670000000000000000" <> "00000000" <> "5df6e0e2"))+      `failsWith` "too few bytes"+  it "keeps the command and payload of an unknown message" $+    let payload = hex "616263"+        msg = hex ("f9beb4d9" <> "666f6f000000000000000000" <> "03000000") <> runPutS (serialize (checkSum32 payload)) <> payload+     in do+          runGetS (getMessage btc) msg `shouldBe` Right (MOther "foo" "abc")+          runPutS (putMessage btc (MOther "foo" "abc")) `shouldBe` msg+  it "gives each message its command" $ do+    msgType MVerAck `shouldBe` MCVerAck+    msgType (MPing (Ping 1)) `shouldBe` MCPing+    msgType MWtxidRelay `shouldBe` MCWtxidRelay+    msgType MSendAddrV2 `shouldBe` MCSendAddrV2+    msgType (MOther "foo" "") `shouldBe` MCOther "foo"++-- Bloom filters ---------------------------------------------------------------++bloomSpec :: Ctx -> Spec+bloomSpec ctx = describe "bloom filters" $ do+  describe "test vectors of Bitcoin Core" $ do+    it "bloom filter vector 1" bloomFilter1+    it "bloom filter vector 2" bloomFilter2+    it "bloom filter vector 3" $ bloomFilter3 ctx+  describe "bloomCreate" $ do+    it "sizes the filter for the elements and false positive rate" $ do+      -- 3 elements at 1%: 3 bytes and 5 hash functions, as in the vectors+      let f = bloomCreate 3 0.01 0 BloomUpdateAll+      S.length f.array `shouldBe` 3+      f.functions `shouldBe` 5+      -- 20000 elements at 0.1%: 20000 * 6.9078 / 0.48045 = 287552 bits,+      -- 35943 bytes; 35943 * 8 / 20000 * 0.6931 = 9.97, so 9 functions+      let g = bloomCreate 20000 0.001 0 BloomUpdateAll+      S.length g.array `shouldBe` 35943+      g.functions `shouldBe` 9+    it "caps the size at 36000 bytes and the hash functions at 50" $ do+      let f = bloomCreate 100000 0.01 0 BloomUpdateAll+      S.length f.array `shouldBe` 36000+      -- 36000 * 8 / 100000 * 0.6931 = 1.99, so 1 function+      f.functions `shouldBe` 1+      -- 1 element at 1e-20: 46.05 / 0.48045 = 95.85 bits, 11 bytes;+      -- 11 * 8 * 0.6931 = 60.99, capped at 50 functions+      let g = bloomCreate 1 1e-20 0 BloomUpdateAll+      S.length g.array `shouldBe` 11+      g.functions `shouldBe` 50+    it "gives zero hash functions when the elements outnumber the capped size" $+      -- 36000 * 8 / 1000000 * 0.6931 = 0.19+      (bloomCreate 1000000 0.01 0 BloomUpdateAll).functions `shouldBe` 0+    it "makes an empty filter for zero elements" $ do+      let f = bloomCreate 0 0.01 0 BloomUpdateAll+      S.length f.array `shouldBe` 0+      f.functions `shouldBe` 0+    it "keeps the tweak and flags" $ do+      let f = bloomCreate 3 0.01 7 BloomUpdateP2PubKeyOnly+      f.tweak `shouldBe` 7+      f.flags `shouldBe` BloomUpdateP2PubKeyOnly+  describe "bloomContains and bloomInsert" $ do+    it "does not match data that was not inserted" $+      bloomContains (bloomCreate 3 0.01 0 BloomUpdateAll) "abc" `shouldBe` False+    it "matches everything with no hash functions" $+      bloomContains (BloomFilter (S.replicate 10 0xaa) 0 0 BloomUpdateAll) "abc" `shouldBe` True+    it "changes nothing when inserting with no hash functions" $+      bloomInsert (BloomFilter (S.replicate 2 0) 0 0 BloomUpdateAll) "abc"+        `shouldBe` BloomFilter (S.replicate 2 0) 0 0 BloomUpdateAll+    it "matches everything with an empty filter" $+      bloomContains (BloomFilter S.empty 5 0 BloomUpdateAll) "abc" `shouldBe` True+    it "changes nothing when inserting into an empty filter" $+      bloomInsert (BloomFilter S.empty 5 0 BloomUpdateAll) "abc"+        `shouldBe` BloomFilter S.empty 5 0 BloomUpdateAll+    it "matches everything when every bit is set" $+      bloomContains (BloomFilter (S.replicate 3 0xff) 5 0 BloomUpdateAll) "abc" `shouldBe` True+  describe "serialization" $ do+    it "writes the bytes with their count, then functions, tweak and flags" $+      -- the first vector of Bitcoin Core after inserting its three elements+      hexOf (BloomFilter (S.fromList [0x61, 0x4e, 0x9b]) 5 0 BloomUpdateAll)+        `shouldBe` "03614e9b" <> "05000000" <> "00000000" <> "01"+    it "reads a filter" $+      parse (hex ("03614e9b" <> "05000000" <> "00000000" <> "01"))+        `shouldBe` Right (BloomFilter (S.fromList [0x61, 0x4e, 0x9b]) 5 0 BloomUpdateAll)+    it "writes the flags as one byte" $+      map hexOf [BloomUpdateNone, BloomUpdateAll, BloomUpdateP2PubKeyOnly] `shouldBe` ["00", "01", "02"]+    it "reads only the two low bits of the flags, as Bitcoin Core does" $ do+      parse (hex "03") `shouldBe` Right BloomUpdateNone+      parse (hex "81") `shouldBe` Right BloomUpdateAll+      parse (hex "82") `shouldBe` Right BloomUpdateP2PubKeyOnly+    it "accepts the largest filter a peer may load" $+      -- 36000 bytes and 50 hash functions. Only sizes are compared: showing+      -- two filters of this size in a failure report needs gigabytes.+      let f = BloomFilter (S.replicate 36000 0x55) 50 0 BloomUpdateAll+          result = parse (runPutS (serialize f)) :: Either String BloomFilter+       in do+            fmap (\g -> (S.length g.array, g.functions, g.tweak, g.flags)) result+              `shouldBe` Right (36000, 50, 0, BloomUpdateAll)+            fmap (== f) result `shouldBe` Right True+    it "refuses a filter of 36001 bytes" $+      (parse (runPutS (serialize (BloomFilter (S.replicate 36001 0) 1 0 BloomUpdateAll))) :: Either String BloomFilter)+        `failsWith` "too large"+    it "refuses a filter with 51 hash functions" $+      (parse (runPutS (serialize (BloomFilter (S.replicate 1 0) 51 0 BloomUpdateAll))) :: Either String BloomFilter)+        `failsWith` "too many"+    it "refuses a filter that announces 2^64 - 1 bytes" $+      -- the count is above the 36000 bytes a filter may have+      (parse (hex ("ffffffffffffffffff" <> "01000000" <> "00000000" <> "01")) :: Either String BloomFilter)+        `failsWith` "too large"+    it "writes filterload as the filter" $+      hexOf (FilterLoad (BloomFilter (S.fromList [0x61]) 1 0 BloomUpdateNone)) `shouldBe` "0161" <> "01000000" <> "00000000" <> "00"+    it "writes filteradd as the element with its length" $+      hexOf (FilterAdd "abc") `shouldBe` "03616263"+    it "accepts a filteradd element of 520 bytes" $+      parse (hex "fd0802" <> B.replicate 520 0x11) `shouldBe` Right (FilterAdd (B.replicate 520 0x11))+    it "refuses a filteradd element of 521 bytes" $+      (parse (hex "fd0902" <> B.replicate 521 0x11) :: Either String FilterAdd) `failsWith` "too long"+  describe "limits" $ do+    it "knows the limits of BIP 37" $ do+      maxBloomSize `shouldBe` 36000+      maxHashFuncs `shouldBe` 50+      maxFilterAddSize `shouldBe` 520+    it "isBloomValid accepts the limits and refuses one more" $ do+      isBloomValid (BloomFilter (S.replicate 36000 0) 50 0 BloomUpdateAll) `shouldBe` True+      isBloomValid (BloomFilter (S.replicate 36001 0) 50 0 BloomUpdateAll) `shouldBe` False+      isBloomValid (BloomFilter (S.replicate 36000 0) 51 0 BloomUpdateAll) `shouldBe` False+    it "isBloomEmpty is true for all-zero bytes" $ do+      isBloomEmpty (BloomFilter (S.fromList [0, 0]) 1 0 BloomUpdateAll) `shouldBe` True+      isBloomEmpty (BloomFilter (S.fromList [0, 1]) 1 0 BloomUpdateAll) `shouldBe` False+    it "isBloomFull is true for all-0xff bytes" $ do+      isBloomFull (BloomFilter (S.fromList [0xff, 0xff]) 1 0 BloomUpdateAll) `shouldBe` True+      isBloomFull (BloomFilter (S.fromList [0xff, 0xfe]) 1 0 BloomUpdateAll) `shouldBe` False+    it "acceptsFilters looks at the NODE_BLOOM bit" $ do+      acceptsFilters 4 `shouldBe` True+      acceptsFilters 5 `shouldBe` True+      acceptsFilters 1 `shouldBe` False+      acceptsFilters 0 `shouldBe` False+  describe "scriptPushData" $ do+    it "lists the data pushed by a pay-to-pubkey-hash script" $+      scriptPushData p2pkhScript `shouldBe` [p2pkhHash]+    it "lists nothing for a script without pushes" $+      scriptPushData (hex "51") `shouldBe` []+    it "lists every push of an OP_RETURN script" $+      scriptPushData (hex "6a" <> hex "0111" <> hex "022233") `shouldBe` [hex "11", hex "2233"]+    it "leaves out empty pushes" $+      scriptPushData (hex "00" <> hex "0111") `shouldBe` [hex "11"]+    it "keeps the pushes before a truncated one" $+      -- a two-byte push, then OP_PUSHDATA1 with no length byte+      scriptPushData (hex "02aabb" <> hex "4c") `shouldBe` [hex "aabb"]+  describe "bloomRelevantUpdate" $ do+    it "inserts the outpoint of a matching output under BloomUpdateAll" relevantOutputUpdated+    it "gives Nothing for a transaction with no matching output" irrelevantOutputNotUpdated+    it "leaves the filter as it is for a matching pay-to-pubkey-hash output under BloomUpdateP2PubKeyOnly" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateP2PubKeyOnly) p2pkhHash+      bloomRelevantUpdate ctx bf (txPaying [p2pkhScript]) `shouldBe` Just bf+    it "inserts the outpoint of a matching pay-to-pubkey output under BloomUpdateP2PubKeyOnly" $ do+      let k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"+          p = derivePublicKey ctx k+          bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateP2PubKeyOnly) (marshal ctx p)+          tx = txPaying [marshal ctx (PayPK p)]+      fmap (`bloomContains` outpointBytes tx 0) (bloomRelevantUpdate ctx bf tx) `shouldBe` Just True+    it "leaves the filter as it is under BloomUpdateNone" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateNone) p2pkhHash+      bloomRelevantUpdate ctx bf (txPaying [p2pkhScript]) `shouldBe` Just bf+    it "scans a non-standard output script without failing" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateAll) p2pkhHash+      bloomRelevantUpdate ctx bf (txPaying [hex "51"]) `shouldBe` Nothing+    it "matches a push inside a non-standard output script" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateAll) p2pkhHash+          -- push the hash, drop it, then OP_TRUE+          tx = txPaying [hex "14" <> p2pkhHash <> hex "7551"]+      fmap (`bloomContains` outpointBytes tx 0) (bloomRelevantUpdate ctx bf tx) `shouldBe` Just True+    it "still finds the matching output next to a non-standard one" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateAll) p2pkhHash+          tx = txPaying [hex "51", p2pkhScript]+      fmap (`bloomContains` outpointBytes tx 1) (bloomRelevantUpdate ctx bf tx) `shouldBe` Just True+    it "matches every output with an empty filter and changes nothing" $ do+      let bf = BloomFilter S.empty 0 0 BloomUpdateAll+      bloomRelevantUpdate ctx bf (txPaying [hex "51"]) `shouldBe` Just bf+    it "does not insert the outpoint of an output that does not match" $ do+      let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateAll) p2pkhHash+          tx = txPaying [p2pkhScript, hex "76a914" <> B.replicate 20 0x42 <> hex "88ac"]+      fmap (`bloomContains` outpointBytes tx 1) (bloomRelevantUpdate ctx bf tx) `shouldBe` Just False++bloomFilter :: Word32 -> Text -> Expectation+bloomFilter n x = do+  bloomContains f1 v1 `shouldBe` True+  bloomContains f1 v2 `shouldBe` False+  bloomContains f3 v3 `shouldBe` True+  bloomContains f4 v4 `shouldBe` True+  runPutS (serialize f4) `shouldBe` bs+  where+    f0 = bloomCreate 3 0.01 n BloomUpdateAll+    f1 = bloomInsert f0 v1+    f3 = bloomInsert f1 v3+    f4 = bloomInsert f3 v4+    v1 = hex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v2 = hex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v3 = hex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"+    v4 = hex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"+    bs = hex x++bloomFilter1 :: Expectation+bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"++bloomFilter2 :: Expectation+bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"++bloomFilter3 :: Ctx -> Expectation+bloomFilter3 ctx =+  runPutS (serialize f2) `shouldBe` bs+  where+    f0 = bloomCreate 2 0.001 0 BloomUpdateAll+    f1 = bloomInsert f0 $ marshal ctx p+    f2 = bloomInsert f1 $ runPutS $ serialize $ fromJust $ addressHash160 $ pubKeyAddr ctx p+    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"+    p = derivePublicKey ctx k+    bs = hex "038fc16b080000000000000001"++-- | The first output of 'relevantTx' pays the hash in the filter. Its+-- outpoint, which 'spendRelevantTx' spends with its first input, must be in+-- the updated filter. The second output pays somewhere else and its outpoint+-- must not be.+relevantOutputUpdated :: Expectation+relevantOutputUpdated = do+  bloomContains bf2 (outpoint <> hex "00000000") `shouldBe` True+  bloomContains bf2 (outpoint <> hex "01000000") `shouldBe` False+  where+    bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+    bf1 = bloomInsert bf0 p2pkhHash+    bf2 = fromJust $ bloomRelevantUpdate undefined' bf1 relevantTx+    -- txid 57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9+    -- in internal byte order+    outpoint = hex "e925f2a4505198733a27dd4c78df09ad91879e46dd21b3b7aa4dad324f90dc57"+    undefined' = error "bloomRelevantUpdate must not need the context for pay-to-pubkey-hash under BloomUpdateAll"++irrelevantOutputNotUpdated :: Expectation+irrelevantOutputNotUpdated = bloomRelevantUpdate undefined' bf1 unrelatedTx `shouldBe` Nothing+  where+    bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+    bf1 = bloomInsert bf0 p2pkhHash+    undefined' = error "bloomRelevantUpdate must not need the context when nothing matches"++-- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)+relevantTx :: Tx+relevantTx =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1,+              script = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6",+              sequence = 4294967295+            }+        ],+      outputs =+        [ TxOut {value = 100000000, script = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac"},+          TxOut {value = 107980000, script = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac"}+        ],+      witness = [],+      locktime = 0+    }++-- This random transaction is unrelated to the others+unrelatedTx :: Tx+unrelatedTx =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20,+              script = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1",+              sequence = 4294967295+            }+        ],+      outputs =+        [ TxOut {value = 12600000, script = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac"}+        ],+      witness = [],+      locktime = 0+    }++-- Compact blocks --------------------------------------------------------------++compactBlockSpec :: Spec+compactBlockSpec = describe "compact blocks" $ do+  it "writes sendcmpct as a flag byte and a little-endian version" $+    hexOf (SendCompact True 2) `shouldBe` "01" <> "0200000000000000"+  it "reads sendcmpct" $+    parse (hex ("00" <> "0100000000000000")) `shouldBe` Right (SendCompact False 1)+  it "limits short ids to six bytes" $+    maxShortId `shouldBe` 0xffffffffffff+  it "writes getblocktxn positions as the distance from the previous one minus one" $+    hexOf (GetBlockTxn bh1 [0, 1, 5]) `shouldBe` bh1Hex <> "03" <> "00" <> "00" <> "03"+  it "reads getblocktxn positions" $+    parse (hex (bh1Hex <> "03" <> "00" <> "00" <> "03")) `shouldBe` Right (GetBlockTxn bh1 [0, 1, 5])+  it "reads a first position that is not zero" $+    parse (hex (bh1Hex <> "02" <> "07" <> "00")) `shouldBe` Right (GetBlockTxn bh1 [7, 8])+  it "refuses positions that do not increase" $+    evaluate (runPutS (serialize (GetBlockTxn bh1 [1, 1]))) `shouldThrow` anyErrorCall+  it "refuses a position beyond 32 bits" $+    -- 0 then a distance of 0xffffffff, which lands on 2^32+    (parse (hex (bh1Hex <> "02" <> "00" <> "feffffffff")) :: Either String GetBlockTxn)+      `failsWith` "does not fit 32 bits"+  it "writes a compact block as header, nonce, short ids and prefilled transactions" $+    hexOf (CompactBlock btc.genesisHeader 0x0102030405060708 [1, maxShortId] [PrefilledTx 0 (txPaying [p2pkhScript])])+      `shouldBe` genesisHeaderHex+        <> "0807060504030201"+        <> "02"+        <> "010000000000"+        <> "ffffffffffff"+        <> "01"+        <> "00"+        <> oneInputTxHex+  it "reads a compact block" $+    parse (hex (genesisHeaderHex <> "0807060504030201" <> "02" <> "010000000000" <> "ffffffffffff" <> "01" <> "00" <> oneInputTxHex))+      `shouldBe` Right (CompactBlock btc.genesisHeader 0x0102030405060708 [1, maxShortId] [PrefilledTx 0 (txPaying [p2pkhScript])])+  it "writes prefilled positions as distances too" $+    let tx = txPaying [p2pkhScript]+     in hexOf (CompactBlock btc.genesisHeader 0 [] [PrefilledTx 0 tx, PrefilledTx 2 tx])+          `shouldBe` genesisHeaderHex <> "0000000000000000" <> "00" <> "02" <> "00" <> oneInputTxHex <> "01" <> oneInputTxHex+  it "refuses a short id wider than six bytes" $+    evaluate (runPutS (serialize (CompactBlock btc.genesisHeader 0 [maxShortId + 1] []))) `shouldThrow` anyErrorCall+  it "writes blocktxn as the block hash and the transactions" $+    hexOf (BlockTxn bh1 [txPaying [p2pkhScript]]) `shouldBe` bh1Hex <> "01" <> oneInputTxHex+  it "reads blocktxn" $+    parse (hex (bh1Hex <> "01" <> oneInputTxHex)) `shouldBe` Right (BlockTxn bh1 [txPaying [p2pkhScript]])++-- Block filters ---------------------------------------------------------------++blockFilterSpec :: Spec+blockFilterSpec = describe "block filters" $ do+  it "numbers the basic filter type zero" $+    basicFilterType `shouldBe` 0+  it "writes getcfilters as type, little-endian start height and stop hash" $+    hexOf (GetCFilters 0 1000 bh1) `shouldBe` "00" <> "e8030000" <> bh1Hex+  it "reads getcfilters" $+    parse (hex ("00" <> "e8030000" <> bh1Hex)) `shouldBe` Right (GetCFilters 0 1000 bh1)+  it "writes cfilter as type, block hash and the filter with its length" $+    hexOf (CFilter 0 bh1 (hex "abcd")) `shouldBe` "00" <> bh1Hex <> "02abcd"+  it "reads cfilter" $+    parse (hex ("00" <> bh1Hex <> "02abcd")) `shouldBe` Right (CFilter 0 bh1 (hex "abcd"))+  it "writes getcfheaders like getcfilters" $+    hexOf (GetCFHeaders basicFilterType 2000 bh1) `shouldBe` "00" <> "d0070000" <> bh1Hex+  it "reads getcfheaders" $+    parse (hex ("00" <> "d0070000" <> bh1Hex)) `shouldBe` Right (GetCFHeaders 0 2000 bh1)+  it "writes cfheaders as type, stop hash, previous header and the counted hashes" $+    hexOf (CFHeaders 0 bh1 h1 [h2, h3]) `shouldBe` "00" <> bh1Hex <> h1Hex <> "02" <> h2Hex <> h3Hex+  it "reads cfheaders" $+    parse (hex ("00" <> bh1Hex <> h1Hex <> "02" <> h2Hex <> h3Hex)) `shouldBe` Right (CFHeaders 0 bh1 h1 [h2, h3])+  it "writes getcfcheckpt as type and stop hash" $+    hexOf (GetCFCheckpt 0 bh1) `shouldBe` "00" <> bh1Hex+  it "reads getcfcheckpt" $+    parse (hex ("00" <> bh1Hex)) `shouldBe` Right (GetCFCheckpt 0 bh1)+  it "writes cfcheckpt as type, stop hash and the counted headers" $+    hexOf (CFCheckpt 0 bh1 [h1]) `shouldBe` "00" <> bh1Hex <> "01" <> h1Hex+  it "reads cfcheckpt" $+    parse (hex ("00" <> bh1Hex <> "01" <> h1Hex)) `shouldBe` Right (CFCheckpt 0 bh1 [h1])++-- Constants -------------------------------------------------------------------++constantsSpec :: Spec+constantsSpec = describe "network constants" $ do+  it "lists eight networks with distinct names and magics" $ do+    map (.name) allNets `shouldBe` ["btc", "bch", "btctest", "btctest4", "bchtest4", "bchtest", "btcreg", "bchreg"]+    length (nub (map (.magic) allNets)) `shouldBe` 8+  it "finds every network by name" $+    forM_ allNets $ \n -> netByName n.name `shouldBe` Just n+  it "finds nothing for an unknown name" $+    netByName "ltc" `shouldBe` Nothing+  it "uses the magic bytes of each network" $+    map (.magic) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [0xf9beb4d9, 0x0b110907, 0x1c163f28, 0xfabfb5da, 0xe3e1f3e8, 0xf4e5f3f4, 0xe2b7daaf, 0xdab5bffa]+  it "uses the default port of each network" $+    map (.defaultPort) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [8333, 18333, 48333, 18444, 8333, 18333, 28333, 18444]+  it "has the genesis block of each network" $+    map (headerHash . (.genesisHeader)) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [ "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",+                   "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",+                   "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043",+                   "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",+                   "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",+                   "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",+                   "000000001dd410c49a788668ce26751718cc797474d3152a5fc073dd44fd9f7b",+                   "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"+                 ]+  it "limits messages to 4 MB on bitcoin and twice the block size on bitcoin cash" $+    map (.maxMessageSize) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [4000000, 4000000, 4000000, 4000000, 64000000, 64000000, 4000000, 64000000]+  it "knows the block size of each network" $+    map (.maxBlockSize) [btc, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [1000000, 32000000, 32000000, 2000000, 32000000]+  it "activates BIP34, BIP65 and BIP66 at height 1 on bitcoin regtest, as Bitcoin Core does" $ do+    btcRegTest.bip34Height `shouldBe` 1+    btcRegTest.bip34Hash `shouldBe` Nothing+    btcRegTest.bip65Height `shouldBe` 1+    btcRegTest.bip66Height `shouldBe` 1+  it "keeps the activation heights of Bitcoin Cash Node on bitcoin cash regtest" $ do+    bchRegTest.bip34Height `shouldBe` 100000000+    bchRegTest.bip34Hash `shouldBe` Nothing+    bchRegTest.bip65Height `shouldBe` 1351+    bchRegTest.bip66Height `shouldBe` 1251+  it "pins the BIP34 block on mainnet" $ do+    btc.bip34Height `shouldBe` 227931+    btc.bip34Hash `shouldBe` Just "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"+    btc.bip65Height `shouldBe` 388381+    btc.bip66Height `shouldBe` 363725+  it "pins no BIP34 block on testnet4, as Bitcoin Core does" $ do+    -- kernel/chainparams.cpp, CTestNet4Params: BIP34Height = 1, BIP34Hash = uint256{}+    btcTest4.bip34Height `shouldBe` 1+    btcTest4.bip34Hash `shouldBe` Nothing+  it "uses the BIP44 coin type of each network" $+    map (.bip44Coin) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [0, 1, 1, 1, 145, 1, 1, 1]+  it "lists the Bitcoin Forks seed for bitcoin cash" $+    bch.seeds `shouldSatisfy` elem "seed-bch.bitcoinforks.org"+  it "numbers the service bits as Bitcoin Core does" $ do+    nodeNone `shouldBe` 0+    nodeNetwork `shouldBe` 1+    nodeGetUTXO `shouldBe` 2+    nodeBloom `shouldBe` 4+    nodeWitness `shouldBe` 8+    nodeXThin `shouldBe` 16+    nodeCompactFilters `shouldBe` 64+    nodeNetworkLimited `shouldBe` 1024+    nodeP2PV2 `shouldBe` 2048++-- Message sizes per command -----------------------------------------------------++-- | A P2P message as it is on the wire: magic, command padded with NUL+-- bytes, payload length, checksum and payload.+frame :: Network -> ByteString -> ByteString -> ByteString+frame net cmd payload =+  runPutS $ do+    putWord32be net.magic+    putByteString (cmd <> B.replicate (12 - B.length cmd) 0)+    putWord32le (fromIntegral (B.length payload))+    serialize (checkSum32 payload)+    putByteString payload++-- | A message header that announces a payload of the given length, with no+-- payload after it. Reading it fails either on the length or, when the+-- length is allowed, on the missing payload.+headerOnly :: Network -> ByteString -> Word32 -> ByteString+headerOnly net cmd len =+  runPutS $ do+    putWord32be net.magic+    putByteString (cmd <> B.replicate (12 - B.length cmd) 0)+    putWord32le len+    putWord32be 0++sizeLimitSpec :: Spec+sizeLimitSpec = describe "message size per command" $ do+  -- Bitcoin Cash Node, CMessageHeader::IsOversized: a message above+  -- MAX_PROTOCOL_MESSAGE_LENGTH (2 * 1024 * 1024 = 2097152) is oversized+  -- unless it is block, cmpctblock or blocktxn. Those three may be twice the+  -- block size: 2 * 32000000 = 64000000.+  it "refuses an inv of 2097153 bytes on bitcoin cash before reading the payload" $+    runGetS (getMessage bch) (headerOnly bch "inv" 2097153) `failsWith` "larger than the limit"+  it "accepts the length of an inv of 2097152 bytes on bitcoin cash" $+    runGetS (getMessage bch) (headerOnly bch "inv" 2097152) `failsWith` "too few bytes"+  it "refuses a getblocktxn, a merkleblock and a tx of 2097153 bytes on bitcoin cash" $+    forM_ ["getblocktxn", "merkleblock", "tx", "headers", "addr", "foo"] $ \cmd ->+      runGetS (getMessage bch) (headerOnly bch cmd 2097153) `failsWith` "larger than the limit"+  it "accepts the length of a block, a cmpctblock and a blocktxn of 64000000 bytes on bitcoin cash" $+    forM_ ["block", "cmpctblock", "blocktxn"] $ \cmd ->+      runGetS (getMessage bch) (headerOnly bch cmd 64000000) `failsWith` "too few bytes"+  it "refuses a block of 64000001 bytes on bitcoin cash" $+    forM_ ["block", "cmpctblock", "blocktxn"] $ \cmd ->+      runGetS (getMessage bch) (headerOnly bch cmd 64000001) `failsWith` "larger than the limit"+  it "accepts the length of any message of 4000000 bytes on bitcoin" $+    forM_ ["inv", "block", "tx", "foo"] $ \cmd ->+      runGetS (getMessage btc) (headerOnly btc cmd 4000000) `failsWith` "too few bytes"+  it "refuses any message of 4000001 bytes on bitcoin" $+    forM_ ["inv", "block", "tx", "foo"] $ \cmd ->+      runGetS (getMessage btc) (headerOnly btc cmd 4000001) `failsWith` "larger than the limit"+  it "reads a message whose payload is exactly the limit" $+    -- an unknown command with 4000000 payload bytes on bitcoin+    let result = runGetS (getMessage btc) (frame btc "foo" (B.replicate 4000000 0x61))+     in fmap (\m -> case m of MOther c p -> (c, B.length p); _ -> ("", 0)) result+          `shouldBe` Right ("foo", 4000000)+  it "gives the limit of each command on bitcoin cash" $ do+    map (maxPayloadSize bch) [MCBlock, MCCmpctBlock, MCBlockTxn] `shouldBe` [64000000, 64000000, 64000000]+    map (maxPayloadSize bch) [MCInv, MCTx, MCMerkleBlock, MCGetBlockTxn, MCHeaders, MCOther "foo"]+      `shouldBe` replicate 6 2097152+  it "gives 4000000 for every command on bitcoin" $+    map (maxPayloadSize btc) [MCBlock, MCCmpctBlock, MCBlockTxn, MCInv, MCTx, MCOther "foo"]+      `shouldBe` replicate 6 4000000+  it "gives twice the 2 MB block size for a block on bitcoin cash testnet4" $ do+    maxPayloadSize bchTest4 MCBlock `shouldBe` 4000000+    maxPayloadSize bchTest4 MCInv `shouldBe` 2097152+  it "limits messages without block content to 4 MB on bitcoin and 2 MiB on bitcoin cash" $+    -- MAX_PROTOCOL_MESSAGE_LENGTH: 4 * 1000 * 1000 in Bitcoin Core, 2 * 1024 * 1024 in Bitcoin Cash Node+    map (.maxProtocolMessageSize) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [4000000, 4000000, 4000000, 4000000, 2097152, 2097152, 2097152, 2097152]+  it "checks the magic before the command name" $+    -- magic 00000000 and the command "ping" followed by a line feed+    runGetS (getMessage btc) (hex ("00000000" <> "70696e670a00000000000000" <> "00000000" <> "5df6e0e2"))+      `failsWith` "Invalid network magic"++-- Item counts ---------------------------------------------------------------------++itemLimitSpec :: Spec+itemLimitSpec = describe "item counts" $ do+  -- Bitcoin Core net_processing.cpp and Bitcoin Cash Node net.h:+  -- MAX_INV_SZ = 50000, MAX_ADDR_TO_SEND = 1000, MAX_HEADERS_RESULTS = 2000,+  -- MAX_LOCATOR_SZ = 101. BIP 157: at most 2000 filter hashes.+  let invEntry = hex ("01000000" <> h1Hex)+      addrEntry = hex ("e215104d" <> "0100000000000000" <> ipv4Bytes)+      addrV2Entry = hex ("e215104d" <> "01" <> "01" <> "04" <> "0a000001" <> "208d")+      headerEntry = hex (genesisHeaderHex <> "00")+      -- counts as three-byte compact sizes, little-endian+      -- 50000 = 0xc350, 50001 = 0xc351, 1000 = 0x03e8, 1001 = 0x03e9,+      -- 2000 = 0x07d0, 2001 = 0x07d1+      many :: Text -> Int -> ByteString -> ByteString+      many count n entry = hex count <> B.concat (replicate n entry)+  it "accepts an inv of 50000 entries" $+    fmap (\(Inv xs) -> length xs) (parse (many "fd50c3" 50000 invEntry)) `shouldBe` Right 50000+  it "refuses an inv of 50001 entries" $+    fmap (\(Inv xs) -> length xs) (parse (many "fd51c3" 50001 invEntry)) `failsWith` "too many"+  it "accepts a getdata of 50000 entries and refuses 50001" $ do+    fmap (\(GetData xs) -> length xs) (parse (many "fd50c3" 50000 invEntry)) `shouldBe` Right 50000+    fmap (\(GetData xs) -> length xs) (parse (many "fd51c3" 50001 invEntry)) `failsWith` "too many"+  it "accepts an addr of 1000 entries and refuses 1001" $ do+    fmap (\(Addr xs) -> length xs) (parse (many "fde803" 1000 addrEntry)) `shouldBe` Right 1000+    fmap (\(Addr xs) -> length xs) (parse (many "fde903" 1001 addrEntry)) `failsWith` "too many"+  it "accepts an addrv2 of 1000 entries and refuses 1001" $ do+    fmap (\(AddrV2 xs) -> length xs) (parse (many "fde803" 1000 addrV2Entry)) `shouldBe` Right 1000+    fmap (\(AddrV2 xs) -> length xs) (parse (many "fde903" 1001 addrV2Entry)) `failsWith` "too many"+  it "accepts a headers message of 2000 entries and refuses 2001" $ do+    fmap (\(Headers xs) -> length xs) (parse (many "fdd007" 2000 headerEntry)) `shouldBe` Right 2000+    fmap (\(Headers xs) -> length xs) (parse (many "fdd107" 2001 headerEntry)) `failsWith` "too many"+  it "accepts a getheaders locator of 101 hashes and refuses 102" $ do+    -- version 70001 = 0x00011171, then the locator, then the stop hash+    fmap (\g -> length g.locator) (parse (hex "71110100" <> many "65" 101 (hex h1Hex) <> hex h2Hex) :: Either String GetHeaders)+      `shouldBe` Right 101+    fmap (\g -> length g.locator) (parse (hex "71110100" <> many "66" 102 (hex h1Hex) <> hex h2Hex) :: Either String GetHeaders)+      `failsWith` "too many"+  it "accepts a getblocks locator of 101 hashes and refuses 102" $ do+    fmap (\g -> length g.locator) (parse (hex "71110100" <> many "65" 101 (hex h1Hex) <> hex h2Hex) :: Either String GetBlocks)+      `shouldBe` Right 101+    fmap (\g -> length g.locator) (parse (hex "71110100" <> many "66" 102 (hex h1Hex) <> hex h2Hex) :: Either String GetBlocks)+      `failsWith` "too many"+  it "accepts a cfheaders of 2000 filter hashes and refuses 2001" $ do+    fmap (\c -> length c.filterHashes) (parse (hex ("00" <> bh1Hex <> h1Hex) <> many "fdd007" 2000 (hex h2Hex)) :: Either String CFHeaders)+      `shouldBe` Right 2000+    fmap (\c -> length c.filterHashes) (parse (hex ("00" <> bh1Hex <> h1Hex) <> many "fdd107" 2001 (hex h2Hex)) :: Either String CFHeaders)+      `failsWith` "too many"++-- Compact block positions per network -----------------------------------------------++compactPositionSpec :: Spec+compactPositionSpec = describe "compact block positions" $ do+  -- BIP 152 and Bitcoin Core (blockencodings.h) use 16-bit positions:+  -- BlockTransactionsRequest::indexes is std::vector<uint16_t>, and a+  -- cmpctblock with more than 65535 transactions fails with "indexes+  -- overflowed 16 bits". Bitcoin Cash Node uses uint32_t in both places.+  let requestFrame positions = frame btc "getblocktxn" (hex (bh1Hex <> positions))+      indexes = fmap (\m -> case m of MGetBlockTxn g -> g.indexes; _ -> [])+      tx = hex oneInputTxHex+      compact n prefilled =+        hex (genesisHeaderHex <> "0000000000000000")+          <> runPutS (putVarInt (n :: Int))+          <> B.concat (replicate n (hex "010000000000"))+          <> prefilled+      counts = fmap (\m -> case m of MCompactBlock c -> (length c.shortIds, map (.index) c.prefilled); _ -> (0, []))+  it "reads the position 65535 in a getblocktxn on bitcoin" $+    indexes (runGetS (getMessage btc) (requestFrame "01fdffff")) `shouldBe` Right [65535]+  it "refuses the position 65536 in a getblocktxn on bitcoin" $+    indexes (runGetS (getMessage btc) (requestFrame "01fe00000100")) `failsWith` "16 bits"+  it "refuses a getblocktxn position reached by adding distances beyond 65535 on bitcoin" $+    -- 65535, then a distance of 0, which is position 65536+    indexes (runGetS (getMessage btc) (requestFrame "02fdffff00")) `failsWith` "16 bits"+  it "reads the position 65536 in a getblocktxn on bitcoin cash" $+    indexes (runGetS (getMessage bch) (frame bch "getblocktxn" (hex (bh1Hex <> "01fe00000100"))))+      `shouldBe` Right [65536]+  it "refuses a prefilled transaction at position 65536 on bitcoin" $+    counts (runGetS (getMessage btc) (frame btc "cmpctblock" (compact 0 (hex "01fe00000100" <> tx))))+      `failsWith` "16 bits"+  it "reads a compact block of 65535 transactions on bitcoin" $+    -- 65534 short ids and one prefilled transaction+    counts (runGetS (getMessage btc) (frame btc "cmpctblock" (compact 65534 (hex "0100" <> tx))))+      `shouldBe` Right (65534, [0])+  it "refuses a compact block of 65536 transactions on bitcoin" $+    -- 65535 short ids and one prefilled transaction+    counts (runGetS (getMessage btc) (frame btc "cmpctblock" (compact 65535 (hex "0100" <> tx))))+      `failsWith` "16 bits"+  it "reads a compact block of 65536 transactions on bitcoin cash" $+    counts (runGetS (getMessage bch) (frame bch "cmpctblock" (compact 65535 (hex "0100" <> tx))))+      `shouldBe` Right (65535, [0])+  it "allows positions of 16 bits on bitcoin and of 32 bits on bitcoin cash" $+    map (.maxCompactBlockIndex) [btc, btcTest, btcTest4, btcRegTest, bch, bchTest, bchTest4, bchRegTest]+      `shouldBe` [65535, 65535, 65535, 65535, 4294967295, 4294967295, 4294967295, 4294967295]+  it "keeps 32-bit positions in the form without a network" $ do+    parse (hex (bh1Hex <> "01feffffffff")) `shouldBe` Right (GetBlockTxn bh1 [4294967295])+    hexOf (GetBlockTxn bh1 [4294967295]) `shouldBe` bh1Hex <> "01feffffffff"+  it "refuses a getblocktxn count above the list limit" $+    -- 0x02000001 positions announced+    (parse (hex (bh1Hex <> "fe01000002")) :: Either String GetBlockTxn) `failsWith` "List count too large"+  it "reads any non-zero announce byte of sendcmpct as true" $+    parse (hex ("02" <> "0100000000000000")) `shouldBe` Right (SendCompact True 1)++-- SegWit format on networks without SegWit -------------------------------------------++witnessFormatSpec :: Spec+witnessFormatSpec = describe "transaction format per network" $ do+  -- A transaction in the extended format of BIP 144: version, marker 00,+  -- flag 01, one input, one output, one witness stack with the item ab, lock+  -- time. The outpoint hash is 32 bytes of 0x11.+  let witnessTx =+        hex $+          "01000000"+            <> "0001"+            <> "01"+            <> h1Hex+            <> "00000000"+            <> "00"+            <> "ffffffff"+            <> "01"+            <> "e803000000000000"+            <> "0151"+            <> "0101ab"+            <> "00000000"+      shape = fmap (\m -> case m of MTx t -> (length t.inputs, length t.outputs, t.witness); _ -> (-1, -1, []))+  it "reads the witness of a tx message on bitcoin" $+    shape (runGetS (getMessage btc) (frame btc "tx" witnessTx)) `shouldBe` Right (1, 1, [[hex "ab"]])+  it "does not read the extended format in a tx message on bitcoin cash" $+    -- Bitcoin Cash Node has no witness format. It reads the marker as an+    -- input count of zero and the flag as an output count of one.+    shape (runGetS (getMessage bch) (frame bch "tx" witnessTx)) `shouldBe` Right (0, 1, [])+  it "does not read the extended format in a block message on bitcoin cash" $+    let block = hex genesisHeaderHex <> hex "01" <> witnessTx+        txShape = fmap (\m -> case m of MBlock b -> map (\t -> (length t.inputs, t.witness)) b.txs; _ -> [])+     in txShape (runGetS (getMessage bch) (frame bch "block" block)) `shouldBe` Right [(0, [])]+  it "reads a transaction in the old format only on a network without SegWit" $ do+    fmap (\t -> (length t.inputs, t.witness)) (runGetS (getNetworkTx btc) witnessTx) `shouldBe` Right (1, [[hex "ab"]])+    fmap (\t -> (length t.inputs, t.witness)) (runGetS (getNetworkTx bch) witnessTx) `shouldBe` Right (0, [])+  it "does not read the extended format in a blocktxn or a cmpctblock message on bitcoin cash" $ do+    let blockTxn = hex bh1Hex <> hex "01" <> witnessTx+        compact = hex (genesisHeaderHex <> "0000000000000000" <> "00" <> "0100") <> witnessTx+    fmap (\m -> case m of MBlockTxn b -> map (\t -> length t.inputs) b.transactions; _ -> [])+      (runGetS (getMessage bch) (frame bch "blocktxn" blockTxn))+      `shouldBe` Right [0]+    fmap (\m -> case m of MCompactBlock c -> map (\x -> length x.tx.inputs) c.prefilled; _ -> [])+      (runGetS (getMessage bch) (frame bch "cmpctblock" compact))+      `shouldBe` Right [0]+  it "reads an empty witness item as an empty byte string" $+    -- one input with a witness stack of three empty items+    let tx3 =+          hex $+            "01000000" <> "0001" <> "01" <> h1Hex <> "00000000" <> "00" <> "ffffffff"+              <> "01" <> "e803000000000000" <> "0151" <> "03000000" <> "00000000"+     in fmap (.witness) (parse tx3 :: Either String Tx) `shouldBe` Right [["", "", ""]]++-- Bloom filter matching -----------------------------------------------------------++-- | The transaction of the bloom_match case of Bitcoin Core+-- (src/test/bloom_tests.cpp),+-- b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b.+bloomMatchTx :: Tx+bloomMatchTx =+  either error id . parse . hex $+    "01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"++-- | The transaction that spends output 0 of 'bloomMatchTx' in the same case,+-- e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436.+bloomSpendingTx :: Tx+bloomSpendingTx =+  either error id . parse . hex $+    "01000000016bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4000000008c493046022100da0dc6aecefe1e06efdf05773757deb168820930e3b0d03f46f5fcf150bf990c022100d25b5c87040076e4f253f8262e763e2dd51e7ff0be157727c4bc42807f17bd39014104e6c26ef67dc610d2cd192484789a6cf9aea9930b944b7e2db5342b9d9e5b9ff79aff9a2ee1978dd7fd01dfc522ee02283d3b06a9d03acf8096968d7dbb0f9178ffffffff028ba7940e000000001976a914badeecfdef0507247fc8f74241d73bc039972d7b88ac4094a802000000001976a914c10932483fec93ed51f5fe95e72559f2cc7043f988ac00000000"++bloomMatchSpec :: Ctx -> Spec+bloomMatchSpec ctx = describe "bloom filter matching, the bloom_match case of Bitcoin Core" $ do+  let fresh = bloomCreate 10 0.000001 0 BloomUpdateAll+      matches element = bloomRelevantUpdate ctx (bloomInsert fresh element) bloomMatchTx /= Nothing+      -- the outpoint 90c122d7...260b:n that the transaction spends+      spent n = hex "0b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190" <> hex n+  it "matches the transaction hash" $+    -- the hash in internal byte order+    matches (hex "6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4") `shouldBe` True+  it "matches the signature of an input" $+    matches (hex "30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01") `shouldBe` True+  it "matches the public key of an input" $+    matches (hex "046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339") `shouldBe` True+  it "matches the address of each output" $ do+    matches (hex "04943fdd508053c75000106d3bc6e2754dbcff19") `shouldBe` True+    matches (hex "a266436d2965547608b9e15d9032a7b9d64fa431") `shouldBe` True+  it "matches the outpoint that an input spends" $+    matches (spent "00000000") `shouldBe` True+  it "does not match another transaction hash" $+    matches (hex "36041132b83ff15b9143736458a67be0985bf4d4639784ef622ff384e7090000") `shouldBe` False+  it "does not match another address" $+    matches (hex "0000006d2965547608b9e15d9032a7b9d64fa431") `shouldBe` False+  it "does not match another output of the spent transaction" $+    matches (spent "01000000") `shouldBe` False+  it "matches the transaction that spends a matched output" $+    -- "Simple Bloom filter didn't add output" in Bitcoin Core+    let updated = bloomRelevantUpdate ctx (bloomInsert fresh (hex "04943fdd508053c75000106d3bc6e2754dbcff19")) bloomMatchTx+     in fmap (\f -> bloomRelevantUpdate ctx f bloomSpendingTx /= Nothing) updated `shouldBe` Just True+  it "leaves the filter as it is when only the transaction hash matches" $+    let f = bloomInsert fresh (hex "6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4")+     in bloomRelevantUpdate ctx f bloomMatchTx `shouldBe` Just f+  describe "pay-to-pubkey test of BloomUpdateP2PubKeyOnly, as Solver in Bitcoin Core" $ do+    -- Solver checks the size and the first byte of a key, not the curve.+    let hybridKey = hex "06" <> B.replicate 64 0x11+        offCurveKey = hex "02" <> B.replicate 32 0xff+        g = hex "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"+        inserted key script =+          let bf = bloomInsert (bloomCreate 10 0.000001 0 BloomUpdateP2PubKeyOnly) key+              tx = txPaying [script]+           in fmap (`bloomContains` outpointBytes tx 0) (bloomRelevantUpdate ctx bf tx)+    it "inserts the outpoint of a pay-to-pubkey output with a hybrid key" $+      inserted hybridKey (hex "41" <> hybridKey <> hex "ac") `shouldBe` Just True+    it "inserts the outpoint of a pay-to-pubkey output whose key is not on the curve" $+      inserted offCurveKey (hex "21" <> offCurveKey <> hex "ac") `shouldBe` Just True+    it "inserts the outpoint of a bare multisig output with a key that is not on the curve" $+      inserted g (hex "51" <> hex "21" <> g <> hex "21" <> offCurveKey <> hex "52ae") `shouldBe` Just True+    it "inserts the outpoint of a bare 1-of-2 multisig output" $+      let k2 = hex "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"+       in inserted g (hex "51" <> hex "21" <> g <> hex "21" <> k2 <> hex "52ae") `shouldBe` Just True+    it "does not insert the outpoint of a pay-to-pubkey-hash output" $+      inserted p2pkhHash p2pkhScript `shouldBe` Just False+    it "does not insert the outpoint of a script that only starts like pay-to-pubkey" $+      -- a 33-byte push and OP_CHECKSIG followed by OP_NOP+      inserted g (hex "21" <> g <> hex "ac61") `shouldBe` Just False+  it "reads the flags byte 05 as update-all, since only the two low bits count" $+    (parse (hex "05") :: Either String BloomFlags) `shouldBe` Right BloomUpdateAll+  it "picks the number of hash functions with the integer division of Bitcoin Core" $+    -- 5 elements at a rate of 0.08: -1 / ln(2)^2 * 5 * ln(0.08) = 26.2 bits,+    -- 3 bytes. Then 3 * 8 / 5 = 4 in integers, and 4 * ln(2) = 2.77, so 2.+    let f = bloomCreate 5 0.08 0 BloomUpdateNone+     in (S.length f.array, f.functions) `shouldBe` (3, 2)++-- Boundaries --------------------------------------------------------------------------++boundarySpec :: Spec+boundarySpec = describe "boundaries" $ do+  it "refuses the largest value that a three-byte encoding does not need" $+    (parse (hex "fdfc00") :: Either String VarInt) `failsWith` "non-canonical"+  it "refuses the largest value that a five-byte encoding does not need" $+    (parse (hex "feffff0000") :: Either String VarInt) `failsWith` "non-canonical"+  it "refuses the largest value that a nine-byte encoding does not need" $+    (parse (hex "ffffffffff00000000") :: Either String VarInt) `failsWith` "non-canonical"+  it "accepts a list count equal to the limit and then fails on the missing elements" $+    -- 0x02000000 elements announced, none present+    runGetS (getVarIntList getWord8) (hex "fe00000002") `failsWith` "too few bytes"+  it "accepts a byte count equal to the limit and then fails on the missing bytes" $+    (parse (hex "fe00000002") :: Either String VarString) `failsWith` "too few bytes"+  it "accepts a space and a tilde in a command name" $ do+    -- 0x20 and 0x7e are the first and the last printable ASCII characters+    parse (hex "612062000000000000000000") `shouldBe` Right (MCOther "a b")+    parse (hex "617e62000000000000000000") `shouldBe` Right (MCOther "a~b")+  it "refuses the bytes 0x1f and 0x7f in a command name" $ do+    (parse (hex "611f62000000000000000000") :: Either String MessageCommand) `failsWith` "printable ASCII"+    (parse (hex "617f62000000000000000000") :: Either String MessageCommand) `failsWith` "printable ASCII"+  it "reads a version message with negative fields as negative numbers" $+    -- Bitcoin Core reads int nVersion, int64_t nTime and int starting_height+    let payload =+          hex "ffffffff" <> hex "0100000000000000" <> hex "ffffffffffffffff"+            <> B.drop 20 (hex (T.take 160 versionPayloadHex))+            <> hex "00"+            <> hex "ffffffff"+     in fmap (\v -> (toInteger v.version, toInteger v.timestamp, toInteger v.startHeight)) (parse payload :: Either String Version)+          `shouldBe` Right (-1, -1, -1)+  it "reads back the shown form of a command name with a byte above 0x7f" $+    read (show (MCOther "\200")) `shouldBe` MCOther "\200"+  it "reads back the shown form of a reject that names such a command" $+    -- command of one byte c8, code 0x10, empty reason+    let r = either error id (parse (hex "01c8" <> hex "10" <> hex "00")) :: Reject+     in read (show r) `shouldBe` r+  describe "addrv2" $ do+    it "keeps a Tor v2 entry whatever its length, since BIP 155 says to ignore it" $+      parse (hex ("00000000" <> "00" <> "03" <> "05" <> "0102030405" <> "0000"))+        `shouldBe` Right (NetworkAddressV2 0 0 AddrTorV2 (hex "0102030405") 0)+    it "reads the entries after a Tor v2 entry of the wrong length" $+      fmap (\(AddrV2 xs) -> map (.network) xs)+        (parse (hex ("02" <> "00000000" <> "00" <> "03" <> "05" <> "0102030405" <> "0000" <> "e215104d" <> "01" <> "01" <> "04" <> "0a000001" <> "208d")))+        `shouldBe` Right [AddrTorV2, AddrIPv4]+    it "has no addr form for an IPv6 entry that holds an IPv4-mapped address" $+      fromNetworkAddressV2 (NetworkAddressV2 0 0 AddrIPv6 (hex "00000000000000000000ffff0a000001") 8333) `shouldBe` Nothing+    it "has no addr form for an IPv6 entry that holds an OnionCat address" $+      fromNetworkAddressV2 (NetworkAddressV2 0 0 AddrIPv6 (hex "fd87d87eeb4301020304050607080910") 8333) `shouldBe` Nothing+    it "turns an OnionCat addr entry into a Tor v2 entry of ten bytes" $+      toNetworkAddressV2 (0, NetworkAddress 0 (either error id (parse (hex "fd87d87eeb4301020304050607080910" <> hex "208d"))))+        `shouldBe` NetworkAddressV2 0 0 AddrTorV2 (hex "01020304050607080910") 8333
test/Haskoin/ScriptSpec.hs view
@@ -1,439 +1,1346 @@-{-# LANGUAGE OverloadedStrings #-}-module Haskoin.ScriptSpec (spec) where--import           Control.Monad-import           Data.Aeson              as A-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as B-import           Data.Either-import           Data.List-import           Data.Maybe-import           Data.Serialize          as S-import           Data.String-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import           Data.Word-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys-import           Haskoin.Script-import           Haskoin.Transaction-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Haskoin.UtilSpec        (readTestFile)-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit              as HUnit-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)-    ]--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--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 S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs ->-                xs == sort xs-    it "can decode inputs with empty signatures" $ do-        decodeInput net (Script [OP_0]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        decodeInput net (Script [opPushData ""]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        let pk =-                derivePubKeyI $-                wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1-        decodeInput net (Script [OP_0, opPushData $ S.encode pk]) `shouldBe`-            Right (RegularInput (SpendPKHash TxSignatureEmpty pk))-        decodeInput net (Script [OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig [TxSignatureEmpty]))-        decodeInput net (Script [OP_0, OP_0, OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty))--scriptSpec :: Network -> Spec-scriptSpec net =-    when (getNetworkName net == "btc") $-    it "can verify standard scripts from script_tests.json file" $ do-        xs <- readTestFile "script_tests.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 =-                        "DERSIG" `isInfixOf` flags ||-                        "STRICTENC" `isInfixOf` flags ||-                        "NULLDUMMY" `isInfixOf` flags-                    scriptSig     = parseScript siStr-                    scriptPubKey  = parseScript soStr-                    decodedOutput = decodeOutputBS scriptPubKey-                    ver = either (const False) $ \so ->-                        verifyStdInput-                            net-                            (spendTx scriptPubKey 0 scriptSig)-                            0-                            so-                            (val * 100000000)-                case res of-                    "OK" -> assertBool desc $ ver decodedOutput-                    _    -> assertBool desc (not $ ver decodedOutput)--forkIdScriptSpec :: Network -> Spec-forkIdScriptSpec net =-    when (isJust (getSigHashForkId net)) $-    it "can verify scripts from forkid_script_tests.json file" $ do-        xs <- readTestFile "forkid_script_tests.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--creditTx :: ByteString -> Word64 -> Tx-creditTx scriptPubKey val =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = scriptPubKey}-    txI =-        TxIn-        { prevOutput = nullOutPoint-        , scriptInput = S.encode $ Script [OP_0, OP_0]-        , txInSequence = maxBound-        }--spendTx :: ByteString -> Word64 -> ByteString -> Tx-spendTx scriptPubKey val scriptSig =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = B.empty}-    txI =-        TxIn-        { prevOutput = OutPoint (txHash $ creditTx scriptPubKey val) 0-        , scriptInput = scriptSig-        , txInSequence = maxBound-        }--parseScript :: String -> ByteString-parseScript str =-    B.concat $ fromMaybe err $ mapM f $ words str-  where-    f = decodeHex . cs . dropHex . replaceToken-    dropHex ('0':'x':xs) = xs-    dropHex xs           = xs-    err = error $ "Could not decode script: " <> str--replaceToken :: String -> String-replaceToken str = case readMaybe $ "OP_" <> str of-    Just opcode -> "0x" <> cs (encodeHex $ S.encode (opcode :: ScriptOp))-    _           -> str--strictSigSpec :: Network -> Spec-strictSigSpec net =-    when (getNetworkName net == "btc") $ do-        it "can decode strict signatures" $ do-            xs <- readTestFile "sig_strict.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--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 . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res =-                    eitherToMaybe . S.decode . B.reverse =<<-                    decodeHex (cs resStr)-            Just (txSigHash net tx s 0 i sh) `shouldBe` res--txSigHashForkIdSpec :: Network -> Spec-txSigHashForkIdSpec net =-    when (getNetworkName net == "btc") $-    it "can produce valid sighashes from forkid_sighash.json test vectors" $ do-        xs <- readTestFile "forkid_sighash.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 . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res = eitherToMaybe . S.decode =<< decodeHex (cs resStr)-            Just (txSigHashForkId net tx s val i sh) `shouldBe` res--sigHashSpec :: Network -> Spec-sigHashSpec net = do-    it "can correctly show" $ do-        show (0x00 :: SigHash) `shouldBe` "SigHash " <> show (0x00 :: Word32)-        show (0x01 :: SigHash) `shouldBe` "SigHash " <> show (0x01 :: Word32)-        show (0xff :: SigHash) `shouldBe` "SigHash " <> show (0xff :: Word32)-        show (0xabac3344 :: SigHash) `shouldBe` "SigHash " <>-            show (0xabac3344 :: Word32)-    it "can add a forkid" $ do-        0x00 `sigHashAddForkId` 0x00 `shouldBe` 0x00-        0xff `sigHashAddForkId` 0x00ffffff `shouldBe` 0xffffffff-        0xffff `sigHashAddForkId` 0x00aaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0xaaaaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0x00004444 `shouldBe` 0x004444ff-        0xff01 `sigHashAddForkId` 0x44440000 `shouldBe` 0x44000001-        0xff03 `sigHashAddForkId` 0x00550000 `shouldBe` 0x55000003-    it "can extract a forkid" $ do-        sigHashGetForkId 0x00000000 `shouldBe` 0x00000000-        sigHashGetForkId 0x80000000 `shouldBe` 0x00800000-        sigHashGetForkId 0xffffffff `shouldBe` 0x00ffffff-        sigHashGetForkId 0xabac3403 `shouldBe` 0x00abac34-    it "can build some vectors" $ do-        sigHashAll `shouldBe` 0x01-        sigHashNone `shouldBe` 0x02-        sigHashSingle `shouldBe` 0x03-        setForkIdFlag sigHashAll `shouldBe` 0x41-        setAnyoneCanPayFlag sigHashAll `shouldBe` 0x81-        setAnyoneCanPayFlag (setForkIdFlag sigHashAll) `shouldBe` 0xc1-    it "can test flags" $ do-        hasForkIdFlag sigHashAll `shouldBe` False-        hasForkIdFlag (setForkIdFlag sigHashAll) `shouldBe` True-        hasAnyoneCanPayFlag sigHashAll `shouldBe` False-        hasAnyoneCanPayFlag (setAnyoneCanPayFlag sigHashAll) `shouldBe` True-        isSigHashAll sigHashNone `shouldBe` False-        isSigHashAll sigHashAll `shouldBe` True-        isSigHashNone sigHashSingle `shouldBe` False-        isSigHashNone sigHashNone `shouldBe` True-        isSigHashSingle sigHashAll `shouldBe` False-        isSigHashSingle sigHashSingle `shouldBe` True-        isSigHashUnknown sigHashAll `shouldBe` False-        isSigHashUnknown sigHashNone `shouldBe` False-        isSigHashUnknown sigHashSingle `shouldBe` False-        isSigHashUnknown 0x00 `shouldBe` True-        isSigHashUnknown 0x04 `shouldBe` True-    it "can decodeTxSig . encode a TxSignature" $-        property $-        forAll (arbitraryTxSignature net) $ \(_, _, ts) ->-            decodeTxSig net (encodeTxSig ts) `shouldBe` Right ts-    it "can produce the sighash one" $-        property $-        forAll (arbitraryTx net) $ forAll arbitraryScript . testSigHashOne net--testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property-testSigHashOne net tx s val acp =-    not (null $ txIn tx) ==>-    if length (txIn tx) > length (txOut tx)-        then res `shouldBe` one-        else res `shouldNotBe` one-  where-    res = txSigHash net tx s val (length (txIn tx) - 1) (f sigHashSingle)-    one = "0100000000000000000000000000000000000000000000000000000000000000"-    f =-        if acp-            then setAnyoneCanPayFlag-            else id--{- Parse tests from bitcoin-qt repository -}--mapMulSigVector :: ((Text, Text), Int) -> Spec-mapMulSigVector (v, i) =-    it name $ runMulSigVector v-  where-    name = "check multisig vector " <> show i--runMulSigVector :: (Text, Text) -> Assertion-runMulSigVector (a, ops) = assertBool "multisig vector" $ Just a == b-  where-    s = do-        s' <- decodeHex ops-        eitherToMaybe $ S.decode s'-    b = do-        o <- s-        d <- eitherToMaybe $ decodeOutput o-        addrToText btc $ payToScriptAddress d--sigDecodeMap :: Network -> (Text, Int) -> Spec-sigDecodeMap net (_, i) =-    it ("check signature " ++ show i) func-  where-    func = testSigDecode net $ scriptSigSignatures !! i--testSigDecode :: Network -> Text -> Assertion-testSigDecode net str =-    let bs = fromJust $ decodeHex str-        eitherSig = decodeTxSig net bs-    in assertBool-           (unwords-                [ "Decode failed:"-                , fromLeft (error "Decode did not fail") eitherSig-                ]) $-       isRight eitherSig--mulSigVectors :: [(Text, Text)]-mulSigVectors =-    [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"-      , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb\-        \129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8\-        \334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253\-        \e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccf\-        \fef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f997\-        \4ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f\-        \322a1863d4621353ae"-      )-    ]--scriptSigSignatures :: [Text]-scriptSigSignatures =-     -- Signature in input of txid-     -- 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053-    [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f333\-      \57119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a29\-      \41a2b1e401"-      -- Signature in input of txid-      -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.-      -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only-      -- decode strict signatures.-      -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378d\-      -- \b4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22\-      -- \e18fe1e7d1510db501"-    ]--encodeScriptVector :: Assertion-encodeScriptVector =-    assertEqual "Encode script" res (encodeHex $ S.encode s)-  where-    res =-        "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\-        \bfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d\-        \348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2f\-        \cfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b\-        \25c15342af52ae"-    s =-        Script-            [ OP_1-            , opPushData $ d-              "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef5\-              \8bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d\-              \11fcdd0d348ac4"-            , opPushData $ d-              "0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcf\-              \deb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39\-              \f58b25c15342af"-            , OP_2-            , OP_CHECKMULTISIG-            ]-    d = fromJust . decodeHex-+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.ScriptSpec (spec) where++import Control.Exception (evaluate)+import Control.Monad+import Crypto.Secp256k1 (Sig)+import Data.Aeson as A+import Data.Aeson.Types as A (parseMaybe)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either+import Data.List+import Data.Maybe+import Data.String+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit as HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Read++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox arbitrarySigHash,+          ReadBox arbitrarySigHashFlag,+          ReadBox arbitraryScript,+          ReadBox arbitraryPushDataType,+          ReadBox arbitraryScriptOp,+          ReadBox ((`arbitraryScriptOutput` ctx) =<< arbitraryNetwork),+          ReadBox ((`arbitraryScriptInput` ctx) =<< arbitraryNetwork)+        ],+      jsonTests =+        [ JsonBox arbitraryScript,+          JsonBox arbitraryOutPoint,+          JsonBox arbitrarySigHash+        ],+      marshalJsonTests =+        [ MarshalJsonBox $ do+            n <- arbitraryNetwork+            (_, _, ts) <- arbitraryTxSignature n ctx+            return ((n, ctx), ts),+          MarshalJsonBox $ do+            n <- arbitraryNetwork+            o <- arbitraryScriptOutput n ctx+            return (ctx, o),+          MarshalJsonBox $ do+            n <- arbitraryNetwork+            (i, _) <- arbitrarySigInput n ctx+            return (ctx, i)+        ],+      serialTests =+        [ SerialBox arbitraryScriptOp,+          SerialBox arbitraryScript+        ],+      marshalTests =+        [ MarshalBox $ do+            n <- arbitraryNetwork+            (_, _, ts) <- arbitraryTxSignature n ctx+            return ((n, ctx), ts),+          MarshalBox $ do+            n <- arbitraryNetwork+            o <- arbitraryScriptOutput n ctx+            return (ctx, o),+          MarshalBox $ do+            n <- arbitraryNetwork+            i <- arbitraryScriptInput n ctx+            return ((n, ctx), i)+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "opcodes" opcodeSpec+  describe "push operations" pushSpec+  describe "small integer opcodes" smallIntSpec+  describe "standard outputs" $ outputSpec ctx+  describe "standard inputs" $ inputSpec ctx+  describe "sighash types" sigHashTypeSpec+  describe "sighash digests" $ digestSpec ctx+  describe "transaction signatures" $ txSignatureSpec 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 "Script vectors" $+    it "Can encode script vectors" encodeScriptVector++{- Fixtures -}++hex :: Text -> ByteString+hex = fromJust . decodeHex++-- | The generator point of secp256k1 as a compressed public key. Its hash160+-- is 751e76e8199196d454941c45d1b3a323f1433bd6, the witness program of the+-- BIP 173 example address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4.+gKeyHex :: Text+gKeyHex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"++-- | The x and y coordinates of the same key. Its y is even, so its hybrid+-- form starts with 0x06.+gUncompressedXY :: Text+gUncompressedXY =+  "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\+  \483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"++gKey :: Ctx -> PublicKey+gKey ctx = either error id $ unmarshal ctx $ hex gKeyHex++gHash :: Hash160+gHash = "751e76e8199196d454941c45d1b3a323f1433bd6"++-- | The two keys of the first BIP 67 test vector, in the order the vector+-- lists them before sorting.+bip67KeyA, bip67KeyB :: Text+bip67KeyA = "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"+bip67KeyB = "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f"++bip67Key :: Ctx -> Text -> PublicKey+bip67Key ctx = either error id . unmarshal ctx . hex++-- | The 2-of-2 script of the first BIP 67 test vector, keys sorted: 71 bytes.+bip67ScriptHex :: Text+bip67ScriptHex = "5221" <> bip67KeyB <> "21" <> bip67KeyA <> "52ae"++-- | A strict DER signature from the input of transaction+-- 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053, without+-- its hash type byte. It is 70 bytes, so with the hash type byte a push of it+-- starts with 0x47.+sigDerHex :: Text+sigDerHex =+  "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d\+  \02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e4"++sigDer :: Ctx -> Sig+sigDer ctx = fromJust $ decodeStrictSig ctx $ hex sigDerHex++-- | The signature with SIGHASH_ALL, pushed in a script.+sigAllPushHex :: Text+sigAllPushHex = "47" <> sigDerHex <> "01"++-- | The unsigned transaction of the native P2WPKH example of BIP 143.+bip143Tx :: Tx+bip143Tx =+  "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f\+  \0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57\+  \b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85\+  \c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2\+  \f0167faa815988ac11000000"++-- | The scriptCode of the second input of that transaction: the P2PKH script+-- of the key that the P2WPKH output pays to.+bip143ScriptCode :: Script+bip143ScriptCode =+  either error id $+    runGetS deserialize $+      hex "76a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac"++-- | The value of the output that the second input spends: 6 BTC.+bip143Value :: Word64+bip143Value = 600000000++-- | The digest BIP 143 gives for that input with SIGHASH_ALL.+bip143Digest :: Hash256+bip143Digest = "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670"++-- | Opcodes by their byte, from the script specification.+namedOpcodes :: [(Word8, ScriptOp)]+namedOpcodes =+  [ (0x4f, OP_1NEGATE),+    (0x50, OP_RESERVED),+    (0x51, OP_1),+    (0x60, OP_16),+    (0x61, OP_NOP),+    (0x62, OP_VER),+    (0x63, OP_IF),+    (0x64, OP_NOTIF),+    (0x67, OP_ELSE),+    (0x68, OP_ENDIF),+    (0x69, OP_VERIFY),+    (0x6a, OP_RETURN),+    (0x6b, OP_TOALTSTACK),+    (0x6c, OP_FROMALTSTACK),+    (0x73, OP_IFDUP),+    (0x74, OP_DEPTH),+    (0x75, OP_DROP),+    (0x76, OP_DUP),+    (0x7c, OP_SWAP),+    (0x7e, OP_CAT),+    (0x82, OP_SIZE),+    (0x87, OP_EQUAL),+    (0x88, OP_EQUALVERIFY),+    (0x8b, OP_1ADD),+    (0x93, OP_ADD),+    (0x94, OP_SUB),+    (0x9c, OP_NUMEQUAL),+    (0xa5, OP_WITHIN),+    (0xa6, OP_RIPEMD160),+    (0xa7, OP_SHA1),+    (0xa8, OP_SHA256),+    (0xa9, OP_HASH160),+    (0xaa, OP_HASH256),+    (0xab, OP_CODESEPARATOR),+    (0xac, OP_CHECKSIG),+    (0xad, OP_CHECKSIGVERIFY),+    (0xae, OP_CHECKMULTISIG),+    (0xaf, OP_CHECKMULTISIGVERIFY),+    (0xb0, OP_NOP1),+    (0xb1, OP_CHECKLOCKTIMEVERIFY),+    (0xb2, OP_CHECKSEQUENCEVERIFY),+    (0xb3, OP_NOP4),+    (0xb9, OP_NOP10),+    (0xba, OP_CHECKDATASIG),+    (0xbb, OP_CHECKDATASIGVERIFY),+    (0xbc, OP_REVERSEBYTES),+    (0xbd, OP_INVALIDOPCODE 0xbd),+    (0xfc, OP_INVALIDOPCODE 0xfc),+    (0xfd, OP_PUBKEYHASH),+    (0xfe, OP_PUBKEY),+    (0xff, OP_INVALIDOPCODE 0xff)+  ]++{- Opcodes and pushes -}++opcodeSpec :: Spec+opcodeSpec = do+  it "reads every byte from 0x4f to 0xff as one opcode that writes back to that byte" $+    forM_ [0x4f .. 0xff] $ \b ->+      (b, fmap (runPutS . serialize) (runGetS deserialize (B.singleton b) :: Either String ScriptOp))+        `shouldBe` (b, Right (B.singleton b))+  it "names the opcodes of the specification" $+    forM_ namedOpcodes $ \(b, op) -> do+      (b, runGetS deserialize (B.singleton b)) `shouldBe` (b, Right op)+      (b, runPutS (serialize op)) `shouldBe` (b, B.singleton b)+  it "reads the byte 0x00 as OP_0" $+    runGetS deserialize (B.singleton 0x00) `shouldBe` Right OP_0+  it "calls opcode 0xba OP_CHECKSIGADD too, as Tapscript does" $ do+    OP_CHECKSIGADD `shouldBe` OP_CHECKDATASIG+    runPutS (serialize OP_CHECKSIGADD) `shouldBe` B.singleton 0xba+    (case OP_CHECKDATASIG of OP_CHECKSIGADD -> True; _ -> False) `shouldBe` True+  it "shows and reads opcode names" $ do+    show OP_CHECKLOCKTIMEVERIFY `shouldBe` "OP_CHECKLOCKTIMEVERIFY"+    readMaybe "OP_DUP" `shouldBe` Just OP_DUP+    readMaybe "OP_FOO" `shouldBe` (Nothing :: Maybe ScriptOp)+  it "writes a script as the bytes of its opcodes and reads them back" $ do+    runPutS (serialize (Script [OP_1, OP_2, OP_ADD])) `shouldBe` hex "515293"+    runGetS deserialize (hex "515293") `shouldBe` Right (Script [OP_1, OP_2, OP_ADD])+    runGetS deserialize B.empty `shouldBe` Right (Script [])+  it "serializes a script to JSON as its hex and reads it back" $ do+    A.encode (Script [OP_1, OP_2, OP_ADD]) `shouldBe` "\"515293\""+    A.decode "\"515293\"" `shouldBe` Just (Script [OP_1, OP_2, OP_ADD])+    A.decode "\"zz\"" `shouldBe` (Nothing :: Maybe Script)+    A.decode "\"4c\"" `shouldBe` (Nothing :: Maybe Script)+    A.decode "515293" `shouldBe` (Nothing :: Maybe Script)+  describe "push data wire format" $ do+    it "writes data of 1 to 75 bytes after the byte of its length" $ do+      runPutS (serialize (OP_PUSHDATA (hex "aa") OPCODE)) `shouldBe` hex "01aa"+      runPutS (serialize (OP_PUSHDATA (B.replicate 75 0xcc) OPCODE))+        `shouldBe` B.singleton 0x4b <> B.replicate 75 0xcc+    it "writes data of 76 to 255 bytes after 0x4c and one length byte" $ do+      runPutS (serialize (OP_PUSHDATA (B.replicate 76 0xcc) OPDATA1))+        `shouldBe` hex "4c4c" <> B.replicate 76 0xcc+      runPutS (serialize (OP_PUSHDATA (B.replicate 255 0xcc) OPDATA1))+        `shouldBe` hex "4cff" <> B.replicate 255 0xcc+    it "writes data of 256 to 65535 bytes after 0x4d and two length bytes, little endian" $ do+      runPutS (serialize (OP_PUSHDATA (B.replicate 256 0xcc) OPDATA2))+        `shouldBe` hex "4d0001" <> B.replicate 256 0xcc+      runPutS (serialize (OP_PUSHDATA (B.replicate 65535 0xcc) OPDATA2))+        `shouldBe` hex "4dffff" <> B.replicate 65535 0xcc+    it "writes data of 65536 bytes or more after 0x4e and four length bytes, little endian" $+      runPutS (serialize (OP_PUSHDATA (B.replicate 65536 0xcc) OPDATA4))+        `shouldBe` hex "4e00000100" <> B.replicate 65536 0xcc+    it "reads each push type back with its payload" $ do+      runGetS deserialize (hex "01aa") `shouldBe` Right (OP_PUSHDATA (hex "aa") OPCODE)+      runGetS deserialize (hex "4c4c" <> B.replicate 76 0xcc)+        `shouldBe` Right (OP_PUSHDATA (B.replicate 76 0xcc) OPDATA1)+      runGetS deserialize (hex "4d0001" <> B.replicate 256 0xcc)+        `shouldBe` Right (OP_PUSHDATA (B.replicate 256 0xcc) OPDATA2)+      runGetS deserialize (hex "4e00000100" <> B.replicate 65536 0xcc)+        `shouldBe` Right (OP_PUSHDATA (B.replicate 65536 0xcc) OPDATA4)+    it "keeps a push that uses a longer type than its data needs" $ do+      runGetS deserialize (hex "4c01aa") `shouldBe` Right (OP_PUSHDATA (hex "aa") OPDATA1)+      runGetS deserialize (hex "4d0100aa") `shouldBe` Right (OP_PUSHDATA (hex "aa") OPDATA2)+      runGetS deserialize (hex "4e01000000aa") `shouldBe` Right (OP_PUSHDATA (hex "aa") OPDATA4)+      runGetS deserialize (hex "4c00") `shouldBe` Right (OP_PUSHDATA "" OPDATA1)+      runPutS (serialize (OP_PUSHDATA (hex "aa") OPDATA1)) `shouldBe` hex "4c01aa"+      runPutS (serialize (OP_PUSHDATA (hex "aa") OPDATA2)) `shouldBe` hex "4d0100aa"+      runPutS (serialize (OP_PUSHDATA (hex "aa") OPDATA4)) `shouldBe` hex "4e01000000aa"+    it "fails to read a push whose data is missing" $ do+      runGetS deserialize (hex "4c") `shouldSatisfy` (isLeft :: Either String ScriptOp -> Bool)+      runGetS deserialize (hex "02aa") `shouldSatisfy` (isLeft :: Either String ScriptOp -> Bool)+      runGetS deserialize (hex "4d0500aa") `shouldSatisfy` (isLeft :: Either String ScriptOp -> Bool)+      runGetS deserialize (hex "4e01000000") `shouldSatisfy` (isLeft :: Either String ScriptOp -> Bool)+      runGetS deserialize (hex "514c") `shouldSatisfy` (isLeft :: Either String Script -> Bool)+    it "fails to write a push whose payload does not fit its type" $ do+      evaluate (runPutS (serialize (OP_PUSHDATA (B.replicate 76 0) OPCODE))) `shouldThrow` anyErrorCall+      evaluate (runPutS (serialize (OP_PUSHDATA (B.replicate 256 0) OPDATA1))) `shouldThrow` anyErrorCall+      evaluate (runPutS (serialize (OP_PUSHDATA (B.replicate 65536 0) OPDATA2))) `shouldThrow` anyErrorCall++pushSpec :: Spec+pushSpec = do+  describe "opPushData" $ do+    it "uses OP_0 for the empty string" $+      opPushData "" `shouldBe` OP_0+    it "uses OP_1 to OP_16 for one byte from 1 to 16" $ do+      opPushData "\x01" `shouldBe` OP_1+      opPushData "\x10" `shouldBe` OP_16+    it "uses OP_1NEGATE for the byte 0x81" $+      opPushData "\x81" `shouldBe` OP_1NEGATE+    it "pushes any other single byte as data" $ do+      opPushData "\x00" `shouldBe` OP_PUSHDATA "\x00" OPCODE+      opPushData "\x11" `shouldBe` OP_PUSHDATA "\x11" OPCODE+      opPushData "\x80" `shouldBe` OP_PUSHDATA "\x80" OPCODE+    it "picks the smallest push type for the length of the data" $ do+      opPushData (B.replicate 75 0) `shouldBe` OP_PUSHDATA (B.replicate 75 0) OPCODE+      opPushData (B.replicate 76 0) `shouldBe` OP_PUSHDATA (B.replicate 76 0) OPDATA1+      opPushData (B.replicate 255 0) `shouldBe` OP_PUSHDATA (B.replicate 255 0) OPDATA1+      opPushData (B.replicate 256 0) `shouldBe` OP_PUSHDATA (B.replicate 256 0) OPDATA2+      opPushData (B.replicate 65535 0) `shouldBe` OP_PUSHDATA (B.replicate 65535 0) OPDATA2+      opPushData (B.replicate 65536 0) `shouldBe` OP_PUSHDATA (B.replicate 65536 0) OPDATA4+    prop "makes a minimal push that pushes the data" $+      forAll arbitraryBS $ \bs ->+        let op = opPushData bs+         in (isMinimalPush op, pushOpData op) `shouldBe` (True, Just bs)+  describe "pushOpData" $ do+    it "gives the data that a push opcode puts on the stack" $ do+      pushOpData OP_0 `shouldBe` Just ""+      pushOpData OP_1 `shouldBe` Just "\x01"+      pushOpData OP_16 `shouldBe` Just "\x10"+      pushOpData OP_1NEGATE `shouldBe` Just "\x81"+      pushOpData (OP_PUSHDATA "\xaa\xbb" OPDATA2) `shouldBe` Just "\xaa\xbb"+    it "gives nothing for an opcode that pushes nothing" $ do+      pushOpData OP_RESERVED `shouldBe` Nothing+      pushOpData OP_DUP `shouldBe` Nothing+      pushOpData OP_PUBKEY `shouldBe` Nothing+      pushOpData (OP_INVALIDOPCODE 0xff) `shouldBe` Nothing+  describe "isPushOp" $ do+    it "is true for the opcodes that push data" $+      forM_ [OP_0, OP_1NEGATE, OP_1, OP_16, OP_PUSHDATA "" OPDATA1, OP_PUSHDATA "\xaa" OPCODE] $ \op ->+        (op, isPushOp op) `shouldBe` (op, True)+    it "is false for every other opcode" $+      forM_ [OP_RESERVED, OP_NOP, OP_DUP, OP_CHECKSIG, OP_PUBKEYHASH, OP_INVALIDOPCODE 0xff] $ \op ->+        (op, isPushOp op) `shouldBe` (op, False)+  describe "isMinimalPush" $ do+    it "is true for the shortest encoding of the data" $+      forM_+        [ OP_0,+          OP_1,+          OP_16,+          OP_1NEGATE,+          OP_PUSHDATA "\x11" OPCODE,+          OP_PUSHDATA "\x00" OPCODE,+          OP_PUSHDATA (B.replicate 75 0) OPCODE,+          OP_PUSHDATA (B.replicate 76 0) OPDATA1,+          OP_PUSHDATA (B.replicate 256 0) OPDATA2,+          OP_PUSHDATA (B.replicate 65536 0) OPDATA4+        ]+        $ \op -> (B.length (runPutS (serialize op)), isMinimalPush op) `shouldSatisfy` snd+    it "is false for a longer encoding of the same data" $+      forM_+        [ OP_PUSHDATA "" OPCODE,+          OP_PUSHDATA "\x01" OPCODE,+          OP_PUSHDATA "\x81" OPCODE,+          OP_PUSHDATA (B.replicate 75 0) OPDATA1,+          OP_PUSHDATA (B.replicate 255 0) OPDATA2,+          OP_PUSHDATA (B.replicate 256 0) OPDATA4+        ]+        $ \op -> (B.length (runPutS (serialize op)), isMinimalPush op) `shouldSatisfy` (not . snd)+    it "is false for an opcode that pushes nothing" $+      isMinimalPush OP_DUP `shouldBe` False++smallIntSpec :: Spec+smallIntSpec = do+  it "maps 1 to 16 to OP_1 to OP_16" $ do+    intToScriptOp 1 `shouldBe` Right OP_1+    intToScriptOp 16 `shouldBe` Right OP_16+    scriptOpToInt OP_1 `shouldBe` Right 1+    scriptOpToInt OP_16 `shouldBe` Right 16+  it "fails for an integer outside 1 to 16" $ do+    intToScriptOp 0 `shouldSatisfy` isLeft+    intToScriptOp 17 `shouldSatisfy` isLeft+    intToScriptOp (-1) `shouldSatisfy` isLeft+  it "fails for an opcode that is not OP_1 to OP_16" $ do+    scriptOpToInt OP_0 `shouldSatisfy` isLeft+    scriptOpToInt OP_1NEGATE `shouldSatisfy` isLeft+    scriptOpToInt (OP_PUSHDATA "\x01" OPCODE) `shouldSatisfy` isLeft+    scriptOpToInt (OP_PUSHDATA (B.replicate 100 0) OPCODE) `shouldSatisfy` isLeft+  prop "has intToScriptOp . scriptOpToInt identity" $+    forAll arbitraryIntScriptOp $ \i ->+      (intToScriptOp =<< scriptOpToInt i) `shouldBe` Right i++{- Standard outputs -}++-- | Standard output scripts with their decoded form: name, hex, value.+standardOutputs :: Ctx -> [(String, Text, ScriptOutput)]+standardOutputs ctx =+  [ ("P2PK", "21" <> gKeyHex <> "ac", PayPK g),+    ("P2PKH", "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac", PayPKHash gHash),+    -- The P2SH-P2WPKH example of BIP 143.+    ( "P2SH",+      "a9144733f37cf4db86fbc2efed2500b4f4e49f31202387",+      PayScriptHash "4733f37cf4db86fbc2efed2500b4f4e49f312023"+    ),+    ( "P2SH32",+      "aa20" <> thirtyTwo <> "87",+      PayScript32Hash "5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0"+    ),+    -- The BIP 173 example addresses.+    ("P2WPKH", "0014751e76e8199196d454941c45d1b3a323f1433bd6", PayWitnessPKHash gHash),+    ( "P2WSH",+      "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+      PayWitnessScriptHash "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"+    ),+    -- The first output of the BIP 341 wallet test vectors.+    ( "P2TR",+      "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",+      PayWitness 1 (hex "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343")+    ),+    ("witness v16 of 40 bytes", "6028" <> T.replicate 40 "01", PayWitness 16 (B.replicate 40 1)),+    ("witness v1 of 2 bytes", "51020102", PayWitness 1 "\x01\x02"),+    -- The first BIP 67 test vector.+    ("2-of-2 multisig", bip67ScriptHex, PayMulSig [bip67Key ctx bip67KeyB, bip67Key ctx bip67KeyA] 2),+    -- 17 and 20 are one-byte pushes, as Bitcoin Core writes counts above 16.+    ( "17-of-20 multisig",+      "0111" <> T.concat (replicate 20 ("21" <> gKeyHex)) <> "0114ae",+      PayMulSig (replicate 20 g) 17+    ),+    ("OP_RETURN alone", "6a", DataCarrier []),+    ("OP_RETURN with 36 bytes", "6a24" <> T.replicate 36 "ab", DataCarrier [B.replicate 36 0xab]),+    ("OP_RETURN with two pushes", "6a021122023344", DataCarrier ["\x11\x22", "\x33\x44"]),+    ("OP_RETURN with an empty push", "6a00", DataCarrier [""]),+    ("OP_RETURN with OP_1", "6a51", DataCarrier ["\x01"]),+    ("OP_RETURN with OP_1NEGATE", "6a4f", DataCarrier ["\x81"])+  ]+  where+    g = gKey ctx+    thirtyTwo = "5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0"++-- | Scripts that look like a standard output but are not one: name, hex.+nonStandardOutputs :: [(String, Text)]+nonStandardOutputs =+  [ -- Not witness programs by consensus: the program is not a direct push.+    ("P2WPKH with OP_PUSHDATA1", "004c14" <> twenty),+    ("P2WSH with OP_PUSHDATA1", "004c20" <> thirtyTwo),+    ("P2TR with OP_PUSHDATA1", "514c20" <> thirtyTwo),+    ("P2TR with OP_PUSHDATA2", "514d2000" <> thirtyTwo),+    ("witness v0 of 25 bytes with OP_PUSHDATA1", "004c19" <> T.replicate 25 "01"),+    -- Not pay-to-script-hash by consensus.+    ("P2SH with OP_PUSHDATA1", "a94c14" <> twenty <> "87"),+    ("P2SH32 with OP_PUSHDATA1", "aa4c20" <> thirtyTwo <> "87"),+    -- Non-standard in Bitcoin Core.+    ("P2PKH with OP_PUSHDATA1", "76a94c14" <> twenty <> "88ac"),+    ("P2PK with OP_PUSHDATA1", "4c21" <> gKeyHex <> "ac"),+    -- Wrong lengths.+    ("witness v0 of 25 bytes", "0019" <> T.replicate 25 "01"),+    ("witness v1 of 41 bytes", "5129" <> T.replicate 41 "01"),+    ("witness v1 of 1 byte", "510101"),+    ("P2PKH with a 19-byte hash", "76a913" <> T.replicate 19 "ab" <> "88ac"),+    ("P2PKH with a 21-byte hash", "76a915" <> T.replicate 21 "ab" <> "88ac"),+    ("P2SH with a 19-byte hash", "a913" <> T.replicate 19 "ab" <> "87"),+    ("P2SH with a 21-byte hash", "a915" <> T.replicate 21 "ab" <> "87"),+    ("P2SH32 with a 31-byte hash", "aa1f" <> T.replicate 31 "cd" <> "87"),+    ("P2SH32 with a 33-byte hash", "aa21" <> T.replicate 33 "cd" <> "87"),+    -- A key must fill its push.+    ("P2PK with 32 bytes after a compressed key", "41" <> gKeyHex <> thirtyTwo <> "ac"),+    ("multisig with 32 bytes after a compressed key", "5141" <> gKeyHex <> thirtyTwo <> "51ae"),+    ("P2PK with a 32-byte key", "20" <> thirtyTwo <> "ac"),+    -- Multisig counts.+    ("multisig with a count of 0", "0021" <> gKeyHex <> "51ae"),+    ("multisig with OP_1NEGATE as count", "4f21" <> gKeyHex <> "51ae"),+    ("multisig with 1 as a byte push", "010121" <> gKeyHex <> "0101ae"),+    -- Bitcoin Core reads a count as a minimal script number: 16 is OP_16,+    -- and 17 is a direct push of one byte.+    ("multisig with 16 as a byte push", "0110" <> T.concat (replicate 16 ("21" <> gKeyHex)) <> "60ae"),+    ("multisig with 17 pushed with OP_PUSHDATA1", "4c0111" <> T.concat (replicate 17 ("21" <> gKeyHex)) <> "0111ae"),+    ("multisig with 21 keys", "0115" <> T.concat (replicate 21 ("21" <> gKeyHex)) <> "0115ae"),+    ("multisig requiring more signatures than keys", "5221" <> gKeyHex <> "51ae"),+    ("multisig whose key count is wrong", "5121" <> gKeyHex <> "52ae"),+    ("multisig ending in OP_CHECKMULTISIGVERIFY", "5121" <> gKeyHex <> "51af"),+    ("multisig without OP_CHECKMULTISIG", "5121" <> gKeyHex <> "51"),+    -- Not an output template at all.+    ("empty script", ""),+    ("OP_1", "51"),+    ("OP_RETURN followed by OP_DUP", "6a76"),+    ("OP_RETURN with a truncated push", "6a4c")+  ]+  where+    twenty = T.replicate 20 "ab"+    thirtyTwo = T.replicate 32 "cd"++outputSpec :: Ctx -> Spec+outputSpec ctx = do+  let g = gKey ctx+      decodeHexOutput = unmarshal ctx . hex :: Text -> Either String ScriptOutput+      encodeHexOutput = fmap (encodeHex . runPutS . serialize) . encodeOutput ctx+  it "decodes the standard output templates" $+    forM_ (standardOutputs ctx) $ \(name, h, out) ->+      (name, decodeHexOutput h) `shouldBe` (name, Right out)+  it "encodes the standard outputs to their templates" $+    forM_ (standardOutputs ctx) $ \(name, h, out) ->+      (name, encodeHexOutput out) `shouldBe` (name, Right h)+  it "rejects scripts that only look like a standard output" $+    forM_ nonStandardOutputs $ \(name, h) ->+      (name, decodeHexOutput h) `shouldSatisfy` (isLeft . snd)+  it "accepts a multisig key pushed with OP_PUSHDATA1, as the solver of Bitcoin Core does" $+    decodeHexOutput ("514c21" <> gKeyHex <> "51ae") `shouldBe` Right (PayMulSig [g] 1)+  it "writes such a multisig back with direct pushes, which are other bytes" $+    -- A ScriptOutput does not keep the form of a push. The result must not+    -- be hashed or signed in place of the script that was read.+    (encodeHexOutput =<< decodeHexOutput ("514c21" <> gKeyHex <> "51ae"))+      `shouldBe` Right ("5121" <> gKeyHex <> "51ae")+  it "writes a data carrier back with the shortest pushes, which can be other bytes" $+    (encodeHexOutput =<< decodeHexOutput "6a4c0401020304") `shouldBe` Right "6a0401020304"+  it "does not decode a key that is not a point of the curve, where Bitcoin Core only checks its size" $ do+    -- decodescript of Bitcoin Core 32.1 says pubkey, pubkey, pubkey and+    -- multisig for these four scripts. A PublicKey of this library is a+    -- point, so they have no ScriptOutput.+    -- The key of private key 1 in hybrid form: 06, x and y.+    decodeHexOutput ("4106" <> gUncompressedXY <> "ac") `shouldSatisfy` isLeft+    -- 33 and 65 bytes with a valid first byte that are not on the curve+    decodeHexOutput ("2102" <> T.replicate 32 "ff" <> "ac") `shouldSatisfy` isLeft+    decodeHexOutput ("4104" <> T.replicate 64 "ff" <> "ac") `shouldSatisfy` isLeft+    -- A 1-of-2 multisig whose second key is not on the curve+    decodeHexOutput ("5121" <> gKeyHex <> "2102" <> T.replicate 32 "ff" <> "52ae") `shouldSatisfy` isLeft+  it "does not decode OP_RETURN followed by OP_RESERVED, which Bitcoin Core counts as a push" $+    decodeHexOutput "6a50" `shouldSatisfy` isLeft+  it "does not decode a Bitcoin Cash output that holds tokens" $+    -- The token prefix 0xef, a 32-byte category, the bitfield 0x10 for a+    -- fungible amount, the amount 100, then a pay-to-public-key-hash script+    decodeHexOutput ("ef" <> T.replicate 32 "aa" <> "1064" <> "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac")+      `shouldSatisfy` isLeft+  it "has 20 as the most multisig keys" $+    maxMultisigKeys `shouldBe` 20+  it "refuses to encode a multisig that no script can express" $ do+    encodeOutput ctx (PayMulSig [] 0) `shouldSatisfy` isLeft+    encodeOutput ctx (PayMulSig [g] 0) `shouldSatisfy` isLeft+    encodeOutput ctx (PayMulSig [g] 2) `shouldSatisfy` isLeft+    encodeOutput ctx (PayMulSig (replicate 21 g) 1) `shouldSatisfy` isLeft+  it "refuses to encode a witness program outside the versions and lengths of BIP 141" $ do+    encodeOutput ctx (PayWitness 0 (B.replicate 20 1)) `shouldSatisfy` isLeft+    encodeOutput ctx (PayWitness 17 (B.replicate 20 1)) `shouldSatisfy` isLeft+    encodeOutput ctx (PayWitness 1 "\x01") `shouldSatisfy` isLeft+    encodeOutput ctx (PayWitness 1 (B.replicate 41 1)) `shouldSatisfy` isLeft+  it "fails with an error call when marshalling an output that cannot be encoded" $+    evaluate (marshal ctx (PayMulSig [] 0)) `shouldThrow` anyErrorCall+  it "marshals an output to JSON as the hex of its script" $ do+    marshalValue ctx (PayPKHash gHash) `shouldBe` A.String "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac"+    A.parseMaybe (unmarshalValue ctx) (A.String "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac")+      `shouldBe` Just (PayPKHash gHash)+    A.parseMaybe (unmarshalValue ctx) (A.String ("004c14" <> T.replicate 20 "ab"))+      `shouldBe` (Nothing :: Maybe ScriptOutput)+  describe "toP2SH and toP2WSH" $ do+    it "hashes the redeem script of the P2SH-P2WPKH example of BIP 143" $+      toP2SH (Script [OP_0, opPushData (hex "79091972186c449eb1ded22b78e40d009bdf0089")])+        `shouldBe` Right (PayScriptHash "4733f37cf4db86fbc2efed2500b4f4e49f312023")+    it "hashes the witness script of the P2WSH example of BIP 143" $+      toP2WSH bip143WitnessScript+        `shouldBe` Right (PayWitnessScriptHash "5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0")+    it "toP2SH takes a redeem script of 520 bytes, the most a push can hold" $+      -- 520 one-byte opcodes. HASH160 computed with Python (hashlib, embit).+      toP2SH (Script (replicate 520 OP_NOP))+        `shouldBe` Right (PayScriptHash "b5c90009be7bc8faa0add03f76ee9c88fd36d6b4")+    it "toP2SH refuses a redeem script of 521 bytes" $+      toP2SH (Script (replicate 521 OP_NOP))+        `shouldBe` Left "The redeem script is 521 bytes long, and a pay-to-script-hash redeem script can have at most 520"+    it "toP2WSH takes a witness script of 10,000 bytes, the most the interpreter runs" $+      toP2WSH (Script (replicate 10000 OP_NOP))+        -- SHA256 of 10,000 bytes 0x61, computed with Python (hashlib)+        `shouldBe` Right (PayWitnessScriptHash "27dd1f61b867b6a0f6e9d8a41c43231de52107e53ae424de8f847b821db4b711")+    it "toP2WSH refuses a witness script of 10,001 bytes" $+      toP2WSH (Script (replicate 10001 OP_NOP))+        `shouldBe` Left "The witness script is 10001 bytes long, and a pay-to-witness-script-hash witness script can have at most 10000"+    it "p2shScriptHash and p2wshScriptHash give the hashes those outputs commit to" $ do+      p2shScriptHash (Script [OP_0, opPushData (hex "79091972186c449eb1ded22b78e40d009bdf0089")])+        `shouldBe` Right "4733f37cf4db86fbc2efed2500b4f4e49f312023"+      p2wshScriptHash bip143WitnessScript+        `shouldBe` Right "5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0"+    it "names the two limits" $ do+      maxRedeemScriptSize `shouldBe` 520+      maxWitnessScriptSize `shouldBe` 10000+  describe "sortMulSig" $ do+    it "sorts the keys of the first BIP 67 test vector" $ do+      let unsorted = PayMulSig [bip67Key ctx bip67KeyA, bip67Key ctx bip67KeyB] 2+      encodeHexOutput (sortMulSig ctx unsorted) `shouldBe` Right bip67ScriptHex+      (addrToText btc <$> payToScriptAddress ctx (sortMulSig ctx unsorted))+        `shouldBe` Right (Just "39bgKC7RFbpoCRbtD5KEdkYKtNyhpsNa3Z")+    it "leaves any other output unchanged" $+      sortMulSig ctx (PayPKHash gHash) `shouldBe` PayPKHash gHash+    prop "sorts the keys by their serialized bytes" $+      forAll (arbitraryMSOutput ctx) $ \out ->+        let keyList = map (marshal ctx) (sortMulSig ctx out).keys+         in keyList `shouldBe` sort keyList+  it "classifies every output type with exactly its own predicate" $ do+    let samples =+          [ PayPK g,+            PayPKHash gHash,+            PayMulSig [g] 1,+            PayScriptHash gHash,+            PayWitnessPKHash gHash,+            PayWitnessScriptHash "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+            PayWitness 1 "\x01\x02",+            PayScript32Hash "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+            DataCarrier []+          ]+        predicates =+          [ isPayPK,+            isPayPKHash,+            isPayMulSig,+            isPayScriptHash,+            isPayWitnessPKHash,+            isPayWitnessScriptHash,+            isPayWitness,+            isPayScript32Hash,+            isDataCarrier+          ]+    [[p s | s <- samples] | p <- predicates]+      `shouldBe` [[i == j | j <- [0 .. 8 :: Int]] | i <- [0 .. 8]]+  where+    bip143WitnessScript =+      either error id $+        runGetS deserialize $+          hex+            "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aead\+            \ab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac"++{- Standard inputs -}++-- | Standard input scripts with their decoded form: name, hex, value.+standardInputs :: Ctx -> [(String, Text, ScriptInput)]+standardInputs ctx =+  [ ("P2PK", sigAllPushHex, RegularInput (SpendPK sig)),+    ("P2PKH", sigAllPushHex <> "21" <> gKeyHex, RegularInput (SpendPKHash sig g)),+    ("2-of-n multisig", "00" <> sigAllPushHex <> sigAllPushHex, RegularInput (SpendMulSig [sig, sig])),+    ("multisig with one empty signature", "0000" <> sigAllPushHex, RegularInput (SpendMulSig [TxSignatureEmpty, sig])),+    ("P2PKH with an empty signature", "0021" <> gKeyHex, RegularInput (SpendPKHash TxSignatureEmpty g)),+    -- The P2SH-P2WPKH and P2SH-P2WSH examples of BIP 143.+    ( "P2SH-P2WPKH",+      "16001479091972186c449eb1ded22b78e40d009bdf0089",+      NestedWitnessInput (PayWitnessPKHash "79091972186c449eb1ded22b78e40d009bdf0089")+    ),+    ( "P2SH-P2WSH",+      "2200205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0",+      NestedWitnessInput (PayWitnessScriptHash "5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0")+    ),+    ("P2SH multisig without signatures yet", "0047" <> bip67ScriptHex, ScriptHashInput (SpendMulSig []) multisig),+    ("P2SH multisig with one signature", "00" <> sigAllPushHex <> "47" <> bip67ScriptHex, ScriptHashInput (SpendMulSig [sig]) multisig),+    ("P2SH P2PK", sigAllPushHex <> "2321" <> gKeyHex <> "ac", ScriptHashInput (SpendPK sig) (PayPK g)),+    ( "P2SH P2PKH",+      sigAllPushHex <> "21" <> gKeyHex <> "1976a914751e76e8199196d454941c45d1b3a323f1433bd688ac",+      ScriptHashInput (SpendPKHash sig g) (PayPKHash gHash)+    )+  ]+  where+    g = gKey ctx+    sig = TxSignature (sigDer ctx) sigHashAll+    multisig = PayMulSig [bip67Key ctx bip67KeyB, bip67Key ctx bip67KeyA] 2++-- | Scripts that are not a standard input: name, hex.+nonStandardInputs :: [(String, Text)]+nonStandardInputs =+  [ ("a push that is not a signature", "03010203"),+    ("a signature with a key followed by 32 bytes", sigAllPushHex <> "41" <> gKeyHex <> T.replicate 32 "ee"),+    ("a multisig redeem script without OP_0 before the signatures", sigAllPushHex <> "47" <> bip67ScriptHex),+    ("OP_0 before the signature of a P2PK redeem script", "00" <> sigAllPushHex <> "2321" <> gKeyHex <> "ac"),+    ("two signatures for a P2PKH redeem script", sigAllPushHex <> sigAllPushHex <> "1976a914751e76e8199196d454941c45d1b3a323f1433bd688ac"),+    ("a P2SH script as redeem script", sigAllPushHex <> "17a9144733f37cf4db86fbc2efed2500b4f4e49f31202387"),+    ("a data carrier as redeem script", sigAllPushHex <> "026a00"),+    ("a signature before a nested witness program", sigAllPushHex <> "16001479091972186c449eb1ded22b78e40d009bdf0089"),+    ("a signature before a nested P2WSH program", sigAllPushHex <> "2200205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0"),+    -- BIP 141: the script must be exactly a push of the program. A push+    -- with OP_PUSHDATA1 is invalid by consensus.+    ("a nested P2WPKH program pushed with OP_PUSHDATA1", "4c16001479091972186c449eb1ded22b78e40d009bdf0089"),+    ("a nested P2WSH program pushed with OP_PUSHDATA1", "4c2200205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0"),+    -- The redeem script must be in the form this library writes, or its+    -- hash would change when it is written back.+    ("a multisig redeem script whose key is pushed with OP_PUSHDATA1", "0026514c21" <> gKeyHex <> "51ae"),+    ("a nested witness program of version 1", "22512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343"),+    ("a redeem script alone that is not a witness program", "2321" <> gKeyHex <> "ac"),+    ("a non-push opcode", "76")+  ]++inputSpec :: Ctx -> Spec+inputSpec ctx = do+  let g = gKey ctx+      decodeHexInput = unmarshal (btc, ctx) . hex :: Text -> Either String ScriptInput+      encodeHexInput = encodeHex . marshal (btc, ctx)+  it "decodes the standard input scripts" $+    forM_ (standardInputs ctx) $ \(name, h, inp) ->+      (name, decodeHexInput h) `shouldBe` (name, Right inp)+  it "encodes the standard inputs to their scripts" $+    forM_ (standardInputs ctx) $ \(name, h, inp) ->+      (name, encodeHexInput inp) `shouldBe` (name, h)+  it "rejects scripts that are not a standard input" $+    forM_ nonStandardInputs $ \(name, h) ->+      (name, decodeHexInput h) `shouldSatisfy` (isLeft . snd)+  it "reads the multisig redeem script in its direct push form, which the one above is not" $+    decodeHexInput ("00255121" <> gKeyHex <> "51ae")+      `shouldBe` Right (ScriptHashInput (SpendMulSig []) (PayMulSig [g] 1))+  it "reads a redeem script pushed with OP_PUSHDATA1, and writes it back with a direct push" $ do+    -- The push of the redeem script is not part of what is hashed, so its+    -- form does not change the address of the input+    let decoded = decodeHexInput ("004c255121" <> gKeyHex <> "51ae")+    decoded `shouldBe` Right (ScriptHashInput (SpendMulSig []) (PayMulSig [g] 1))+    (encodeHexInput <$> decoded) `shouldBe` Right ("00255121" <> gKeyHex <> "51ae")+  it "does not decode an input whose signature only consensus accepts" $ do+    -- A high S: r = 1 and s = n - 1, which is strict DER+    let highS = "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+    -- 40 bytes of signature and the hash type byte: a push of 41 bytes+    decodeHexInput ("29" <> highS <> "01" <> "21" <> gKeyHex) `shouldSatisfy` isLeft+    -- An undefined hash type byte, 0x04+    decodeHexInput ("47" <> sigDerHex <> "04" <> "21" <> gKeyHex) `shouldSatisfy` isLeft+  it "reads a single empty push as a pay-to-public-key input with an empty signature" $ do+    -- The same script is a multisig input without signatures. Without the+    -- output script, the single-signature reading is taken.+    decodeInput btc ctx (Script [OP_0]) `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty))+    decodeInput btc ctx (Script [OP_0, OP_0]) `shouldBe` Right (RegularInput (SpendMulSig [TxSignatureEmpty]))+    decodeInput btc ctx (Script [OP_0, OP_0, OP_0, OP_0])+      `shouldBe` Right (RegularInput (SpendMulSig (replicate 3 TxSignatureEmpty)))+  it "writes an empty signature as OP_0" $ do+    encodeHexInput (RegularInput (SpendPK TxSignatureEmpty)) `shouldBe` "00"+    encodeHexInput (RegularInput (SpendMulSig [TxSignatureEmpty, TxSignatureEmpty])) `shouldBe` "000000"+    encodeHexInput (RegularInput (SpendMulSig [])) `shouldBe` "00"+  it "classifies every input type with exactly its own predicate" $ do+    let samples =+          [ RegularInput (SpendPK TxSignatureEmpty),+            RegularInput (SpendPKHash TxSignatureEmpty g),+            RegularInput (SpendMulSig []),+            ScriptHashInput (SpendPK TxSignatureEmpty) (PayPK g),+            NestedWitnessInput (PayWitnessPKHash gHash)+          ]+        predicates = [isSpendPK, isSpendPKHash, isSpendMulSig, isScriptHashInput, isNestedWitnessInput]+    [[p s | s <- samples] | p <- predicates]+      `shouldBe` [[i == j | j <- [0 .. 4 :: Int]] | i <- [0 .. 4]]++{- Sighash types -}++sigHashTypeSpec :: Spec+sigHashTypeSpec = 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 "adds the fork id of the network, which is 0 on Bitcoin Cash" $ do+    sigHashAddNetworkId btc 0x01 `shouldBe` 0x01+    sigHashAddNetworkId bch 0x41 `shouldBe` 0x41+    sigHashAddNetworkId bch 0xaa0041 `shouldBe` 0x41+  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+    isSigHashAll 0x81 `shouldBe` True+    isSigHashNone sigHashSingle `shouldBe` False+    isSigHashNone sigHashNone `shouldBe` True+    isSigHashNone 0xc2 `shouldBe` True+    isSigHashSingle sigHashAll `shouldBe` False+    isSigHashSingle sigHashSingle `shouldBe` True+    isSigHashSingle 0x43 `shouldBe` True+  it "knows the hash type bytes that IsDefinedHashtypeSignature of Bitcoin Core accepts" $ do+    forM_ [0x01, 0x02, 0x03, 0x81, 0x82, 0x83, 0x41, 0xc1, 0xc3] $ \sh ->+      (sh, isSigHashUnknown sh) `shouldBe` (sh, False)+    forM_ [0x00, 0x04, 0x05, 0x1f, 0x20, 0x21, 0x22, 0x40, 0x80, 0xc0, 0xff] $ \sh ->+      (sh, isSigHashUnknown sh) `shouldBe` (sh, True)+  it "ignores a fork id above the hash type byte" $+    isSigHashUnknown 0x00aa0041 `shouldBe` False+  it "gives the flags their values" $ do+    fromEnum SIGHASH_ALL `shouldBe` 0x01+    fromEnum SIGHASH_NONE `shouldBe` 0x02+    fromEnum SIGHASH_SINGLE `shouldBe` 0x03+    fromEnum SIGHASH_FORKID `shouldBe` 0x40+    fromEnum SIGHASH_ANYONECANPAY `shouldBe` 0x80+  prop "has toEnum . fromEnum identity on flags" $+    forAll arbitrarySigHashFlag $+      \f -> toEnum (fromEnum f) `shouldBe` f+  it "has no flag for other values" $+    evaluate (toEnum 0x05 :: SigHashFlag) `shouldThrow` anyErrorCall+  it "lists the flags in the order of their constructors" $ do+    [minBound .. maxBound] `shouldBe` [SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_FORKID, SIGHASH_ANYONECANPAY]+    [SIGHASH_ALL ..] `shouldBe` [SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_FORKID, SIGHASH_ANYONECANPAY]+    [SIGHASH_NONE .. SIGHASH_FORKID] `shouldBe` [SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_FORKID]+    [SIGHASH_ALL, SIGHASH_SINGLE ..] `shouldBe` [SIGHASH_ALL, SIGHASH_SINGLE, SIGHASH_ANYONECANPAY]+    [SIGHASH_ANYONECANPAY, SIGHASH_FORKID ..] `shouldBe` [SIGHASH_ANYONECANPAY, SIGHASH_FORKID, SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ALL]+  it "steps from a flag to the next and the previous one" $ do+    -- The values of SIGHASH_SINGLE and SIGHASH_FORKID are 0x03 and 0x40+    succ SIGHASH_SINGLE `shouldBe` SIGHASH_FORKID+    pred SIGHASH_FORKID `shouldBe` SIGHASH_SINGLE+    evaluate (succ SIGHASH_ANYONECANPAY) `shouldThrow` anyErrorCall+    evaluate (pred SIGHASH_ALL) `shouldThrow` anyErrorCall+  it "serializes to JSON as a number and reads only 32-bit values" $ do+    A.encode (0x81 :: SigHash) `shouldBe` "129"+    A.decode "129" `shouldBe` Just (0x81 :: SigHash)+    A.decode "-1" `shouldBe` (Nothing :: Maybe SigHash)+    A.decode "4294967296" `shouldBe` (Nothing :: Maybe SigHash)+    A.decode "1.5" `shouldBe` (Nothing :: Maybe SigHash)++{- Digests -}++digestSpec :: Ctx -> Spec+digestSpec ctx = do+  it "computes the BIP 143 digest of the P2WPKH example" $+    txSigHashForkId btc bip143Tx bip143ScriptCode bip143Value 1 sigHashAll+      `shouldBe` Right bip143Digest+  -- The digests below were computed with Python (hashlib) from the text of+  -- BIP 143 and from the legacy algorithm, not with this library.+  it "computes the BIP 143 digest of that input with SIGHASH_ALL and SIGHASH_ANYONECANPAY" $+    txSigHashForkId btc bip143Tx bip143ScriptCode bip143Value 1 (setAnyoneCanPay sigHashAll)+      `shouldBe` Right "fc5b6bbc855883bcfdaefb77071740ccde4929f15e6a13286584e779b2529d91"+  it "writes the hash type of the BIP 143 digest as four bytes on Bitcoin, as Bitcoin Core does" $+    -- The preimage ends in 01 01 00 00 for the hash type 0x101. A signature+    -- only carries one byte, so no signature matches this digest.+    txSigHashForkId btc bip143Tx bip143ScriptCode bip143Value 1 0x101+      `shouldBe` Right "991a362ee6f2b8a004118aa1f098165a84df10b434f5d14c9cb18661309423ef"+  it "writes the hash type of the legacy digest as four bytes" $ do+    txSigHash btc bip143Tx bip143ScriptCode bip143Value 1 sigHashAll+      `shouldBe` Right "c46030820cbc48402a47cc5b5d3d41648f4e3a711f56b804d601d09dc112a6a4"+    txSigHash btc bip143Tx bip143ScriptCode bip143Value 1 0x101+      `shouldBe` Right "8e4ac2d7b4f759fdd01de9b78a2c46fe361316f397b214c7f0f98f073128aa4c"+  it "computes the fork id digest of Bitcoin Cash for each of its six hash types" $+    -- The fork id of Bitcoin Cash is 0, so the last four bytes of the+    -- preimage are the hash type byte and three zero bytes+    forM_+      [ (0x41, "467f411d178762db122a6aced76370a1c8324355bf0796502bf82eeaeda86a35"),+        (0x42, "c0876aa9dfd131ac207be062e389741416a87a5d1b28e4857c178990454dd498"),+        (0x43, "abb61ba86e14313425d25846ed3a30904de1f081e013d80c385e165c2af1e020"),+        (0xc1, "a5890ce40dc95a89717ae6fa3c9d60bcf9372539058c7e9a0cd8ff7909723326"),+        (0xc2, "e58ebfd50f957a45c5f68439e55c6ae6054a3310ff2a116efa07d2bda2527bd3"),+        (0xc3, "4e303851715b6ee36582740f43cc288c969e88afc641f14e3e8e68d32c406c1b")+      ]+      $ \(sh, digest) ->+        (sh, txSigHashForkId bch bip143Tx bip143ScriptCode bip143Value 1 sh) `shouldBe` (sh, Right digest)+  it "fails for an input index the transaction does not have" $ do+    txSigHash btc bip143Tx bip143ScriptCode bip143Value 2 sigHashAll `shouldSatisfy` isLeft+    txSigHash btc bip143Tx bip143ScriptCode bip143Value (-1) sigHashAll `shouldSatisfy` isLeft+    txSigHashForkId btc bip143Tx bip143ScriptCode bip143Value 2 sigHashAll `shouldSatisfy` isLeft+    txSigHashForkId btc bip143Tx bip143ScriptCode bip143Value (-1) sigHashAll `shouldSatisfy` isLeft+  it "removes OP_CODESEPARATOR from the script code of the legacy digest, as SerializeScriptCode of Bitcoin Core does" $+    -- The same digest as for the script code without the opcode+    txSigHash btc bip143Tx (Script (OP_CODESEPARATOR : bip143ScriptCode.ops)) bip143Value 1 sigHashAll+      `shouldBe` Right "c46030820cbc48402a47cc5b5d3d41648f4e3a711f56b804d601d09dc112a6a4"+  it "keeps OP_CODESEPARATOR in the script code of the BIP 143 digest" $+    -- The script code is then 26 bytes: ab, then the 25 bytes of the script+    txSigHashForkId btc bip143Tx (Script (OP_CODESEPARATOR : bip143ScriptCode.ops)) bip143Value 1 sigHashAll+      `shouldBe` Right "f58633c60526565d17f4a38d202ab3cfc26fb900abd2f49cf2252488b5240964"+  it "requires SIGHASH_FORKID on Bitcoin Cash" $+    txSigHash bch bip143Tx bip143ScriptCode bip143Value 1 sigHashAll `shouldSatisfy` isLeft+  it "uses the fork id digest on Bitcoin Cash" $+    -- The digest of the table above for the hash type 0x41+    txSigHash bch bip143Tx bip143ScriptCode bip143Value 1 (setForkIdFlag sigHashAll)+      `shouldBe` Right "467f411d178762db122a6aced76370a1c8324355bf0796502bf82eeaeda86a35"+  it "uses the legacy digest on Bitcoin even with the SIGHASH_FORKID flag" $+    -- The legacy digest with the four hash type bytes 41 00 00 00+    txSigHash btc bip143Tx bip143ScriptCode bip143Value 1 (setForkIdFlag sigHashAll)+      `shouldBe` Right "5e0f360ad7f9a80982d2529061f963a8afe63dbb010eb01a94abfa4626797129"++{- Transaction signatures -}++txSignatureSpec :: Ctx -> Spec+txSignatureSpec ctx = do+  let sig = sigDer ctx+  it "writes the signature followed by its hash type byte" $ do+    encodeTxSig btc ctx (TxSignature sig sigHashAll) `shouldBe` hex (sigDerHex <> "01")+    encodeTxSig btc ctx (TxSignature sig 0x81) `shouldBe` hex (sigDerHex <> "81")+    encodeTxSig bch ctx (TxSignature sig 0x41) `shouldBe` hex (sigDerHex <> "41")+  it "writes an empty signature as the empty string" $+    encodeTxSig btc ctx TxSignatureEmpty `shouldBe` ""+  it "reads a signature with its hash type byte" $ do+    decodeTxSig btc ctx (hex (sigDerHex <> "01")) `shouldBe` Right (TxSignature sig 0x01)+    decodeTxSig btc ctx (hex (sigDerHex <> "82")) `shouldBe` Right (TxSignature sig 0x82)+    decodeTxSig bch ctx (hex (sigDerHex <> "c3")) `shouldBe` Right (TxSignature sig 0xc3)+  it "reads the empty string as an empty signature" $+    decodeTxSig btc ctx "" `shouldBe` Right TxSignatureEmpty+  it "requires the SIGHASH_FORKID flag exactly on networks with a fork id" $ do+    decodeTxSig btc ctx (hex (sigDerHex <> "41")) `shouldSatisfy` isLeft+    decodeTxSig btc ctx (hex (sigDerHex <> "c1")) `shouldSatisfy` isLeft+    decodeTxSig bch ctx (hex (sigDerHex <> "01")) `shouldSatisfy` isLeft+    decodeTxSig bch ctx (hex (sigDerHex <> "81")) `shouldSatisfy` isLeft+  it "rejects a hash type byte that no node accepts" $+    forM_ ["00", "04", "20", "21", "ff"] $ \sh ->+      (sh, decodeTxSig btc ctx (hex (sigDerHex <> sh))) `shouldSatisfy` (isLeft . snd)+  it "rejects a signature with a high S, the LOW_S rule of the relay policy" $ do+    -- r = 1 and s = n - 1. The encoding is strict DER, and consensus+    -- accepts it. decodeStrictSig reads it, decodeTxSig does not.+    let highS = "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+    decodeTxSig btc ctx (hex (highS <> "01"))+      `shouldBe` Left "Failed reading: Non-canonical signature: S value is unnecessarily high\nEmpty call stack\n"+    isJust (decodeStrictSig ctx (hex highS)) `shouldBe` True+  it "names the rule when it rejects an undefined hash type byte, the STRICTENC rule" $+    decodeTxSig btc ctx (hex (sigDerHex <> "04"))+      `shouldBe` Left "Failed reading: Non-canonical signature: unknown hashtype byte\nEmpty call stack\n"+  it "rejects a signature whose r is padded with a zero byte, the DERSIG rule of BIP 66" $+    -- 02 02 00 01 is the integer 1 with a needless leading zero+    decodeTxSig btc ctx (hex "300702020001020101" <> "\x01") `shouldSatisfy` isLeft+  it "rejects bytes after the hash type byte" $+    decodeTxSig btc ctx (hex (sigDerHex <> "0101")) `shouldSatisfy` isLeft+  it "rejects bytes after the hash type byte in the binary form too" $+    (unmarshal (btc, ctx) (hex (sigDerHex <> "01deadbeef")) :: Either String TxSignature)+      `shouldSatisfy` isLeft+  it "does not read a Schnorr signature on Bitcoin Cash, which this type cannot hold" $+    -- 64 bytes and the hash type byte 0x41. Bitcoin Cash Node takes a+    -- 64-byte signature as Schnorr.+    decodeTxSig bch ctx (B.replicate 64 0x11 <> "\x41") `shouldSatisfy` isLeft+  it "does not read SIGHASH_UTXOS on Bitcoin Cash, which the digest does not support" $+    -- 0x61 is SIGHASH_ALL with SIGHASH_UTXOS and SIGHASH_FORKID+    decodeTxSig bch ctx (hex (sigDerHex <> "61"))+      `shouldBe` Left "Failed reading: Non-canonical signature: unknown hashtype byte\nEmpty call stack\n"+  it "rejects a signature without a hash type byte" $+    decodeTxSig btc ctx (hex sigDerHex) `shouldSatisfy` isLeft+  it "rejects a signature that is not strict DER" $+    decodeTxSig btc ctx (hex ("3045022100" <> T.drop 8 sigDerHex <> "01")) `shouldSatisfy` isLeft+  it "marshals a signature to JSON as its hex" $ do+    marshalValue (btc, ctx) (TxSignature sig sigHashAll) `shouldBe` A.String (sigDerHex <> "01")+    A.parseMaybe (unmarshalValue (btc, ctx)) (A.String (sigDerHex <> "01")) `shouldBe` Just (TxSignature sig sigHashAll)+    A.parseMaybe (unmarshalValue (btc, ctx)) (A.String (sigDerHex <> "41")) `shouldBe` (Nothing :: Maybe TxSignature)++{- Per-network properties and vectors -}++props :: Network -> Ctx -> Spec+props net ctx = do+  standardSpec net ctx+  strictSigSpec net ctx+  scriptSpec net ctx+  txSigHashForkIdSpec net+  forkIdScriptSpec net ctx+  sigHashSpec net ctx+  txSigHashSpec net++standardSpec :: Network -> Ctx -> Spec+standardSpec net ctx = do+  prop "has decodeOutput . encodeOutput identity" $+    forAll (arbitraryScriptOutput net ctx) $ \so ->+      (decodeOutput ctx =<< encodeOutput ctx so) `shouldBe` Right so+  prop "has decodeInput . encodeInput identity" $+    forAll (arbitraryScriptInput net ctx) $ \si ->+      (decodeInput net ctx . encodeInput net ctx) si `shouldBe` Right si+  prop "gives every encodable output a script that decodes to it" $+    forAll (arbitraryScriptOutput net ctx) $ \so ->+      unmarshal ctx (marshal ctx so) `shouldBe` Right so++-- | The file @data/script_tests.json@ is not the @script_tests.json@ of+-- Bitcoin Core, which has more than a thousand vectors for its interpreter.+-- It is a selection of 86 of them on the standard templates, edited for a+-- library without an interpreter:+--+-- * 41 vectors carry the word DISABLED in their flags and do not run. They+--   need script execution or a rule this library does not have.+--+-- * 9 vectors that Bitcoin Core expects to pass without a policy flag have+--   the result NONSTRICT in place of OK. The signature or the input of each+--   one breaks a relay rule, such as strict DER, a low S, a defined hash type+--   or an empty dummy element. This library reads the relay form only, so it+--   refuses them.+--+-- * One vector, about the fork id flag, is not in Bitcoin Core.+--+-- So 45 vectors run: 9 valid spends and 36 refusals.+scriptSpec :: Network -> Ctx -> Spec+scriptSpec net ctx =+  when (net.name == "btc") $+    it "verifies the 45 standard-template vectors selected from script_tests.json of Bitcoin Core" $ 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+          -- A vector is disabled by the word DISABLED in its flags+          enabled = [v | v@(_, _, _, flags, _, _) <- vectors, not ("DISABLED" `isInfixOf` flags)]+      length vectors `shouldBe` 86+      length enabled `shouldBe` 45+      length [() | (_, _, _, _, "OK", _) <- enabled] `shouldBe` 9+      forM_ enabled $ \([val], siStr, soStr, _, res, desc) -> do+        let scriptSig = parseScript siStr+            scriptPubKey = parseScript soStr+            out = unmarshal ctx scriptPubKey+            sat = val * 100000000+            tx = spendTx scriptPubKey sat scriptSig+            ver o = verifyStdInput net ctx tx [(o, sat, (head tx.inputs).outpoint)] 0+            valid = either (const False) ver out+        assertBool desc $ if res == "OK" then valid else not valid+        -- A vector refused for the form of its input, not for a wrong+        -- signature, must already fail to read as a standard input. That+        -- tells such a refusal from an unrelated one.+        when (res `elem` encodingErrors) $+          assertBool (desc <> ": the input is not in the relay form") $+            isLeft (decodeInput net ctx =<< runGetS deserialize scriptSig)+  where+    -- The results of Bitcoin Core that name a rule on the form of an input,+    -- and NONSTRICT, which this file uses for a form only consensus accepts+    encodingErrors =+      [ "SIG_DER",+        "SIG_HASHTYPE",+        "SIG_NULLDUMMY",+        "SIG_PUSHONLY",+        "CLEANSTACK",+        "ILLEGAL_FORKID",+        "NONSTRICT"+      ]++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 [(o, val, (head tx.inputs).outpoint)] 0+            valid = either (const False) ver out+        case res of+          "OK" -> valid `shouldBe` True+          _ -> valid `shouldBe` False++creditTx :: ByteString -> Word64 -> Tx+creditTx scriptPubKey val =+  Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {value = val, script = scriptPubKey}+    txI =+      TxIn+        { outpoint = nullOutPoint,+          script = runPutS $ serialize $ Script [OP_0, OP_0],+          sequence = maxBound+        }++spendTx :: ByteString -> Word64 -> ByteString -> Tx+spendTx scriptPubKey val scriptSig =+  Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {value = val, script = B.empty}+    txI =+      TxIn+        { outpoint = OutPoint (txHash $ creditTx scriptPubKey val) 0,+          script = scriptSig,+          sequence = maxBound+        }++parseScript :: String -> ByteString+parseScript str =+  B.concat $ fromMaybe err $ mapM f $ words str+  where+    f = decodeHex . cs . dropHex . replaceToken+    dropHex ('0' : 'x' : xs) = xs+    dropHex xs = xs+    err = error $ "Could not decode script: " <> str++replaceToken :: String -> String+replaceToken str = case readMaybe $ "OP_" <> str of+  Just opcode -> "0x" <> cs (encodeHex $ runPutS $ serialize (opcode :: ScriptOp))+  _ -> str++strictSigSpec :: Network -> Ctx -> Spec+strictSigSpec net ctx =+  when (net.name == "btc") $ do+    it "can decode strict signatures" $ do+      xs <- readTestFile "sig_strict.json"+      let vectors = mapMaybe decodeHex xs+      length vectors `shouldBe` 3+      forM_ vectors $ \sig ->+        let eitherSig :: Either String TxSignature+            eitherSig = decodeTxSig net ctx sig+         in eitherSig `shouldSatisfy` isRight+    it "refuses the non-strict signatures, each for the rule its label names" $ do+      xs <- readTestFile "sig_nonstrict.json" :: IO [Text]+      -- The file is a list of labels, each followed by the signatures it+      -- describes. A signature is DER followed by a hash type byte.+      let labelled = [(label, sig) | (label, Just sig) <- zip (labelsOf xs) (map decodeHex xs)]+      length labelled `shouldBe` 17+      forM_ labelled $ \(label, sig) -> do+        let name = cs label <> " " <> cs (encodeHex sig)+            strictDer = isJust (decodeStrictSig ctx (B.init sig))+            refusal = either id (const "accepted") (decodeTxSig net ctx sig)+        case label of+          -- Strict DER, and a hash type byte that is not defined+          "hashtype:" -> do+            assertBool name strictDer+            assertBool name ("unknown hashtype byte" `isInfixOf` refusal)+          -- Strict DER, and an S above half the curve order+          "High S:" -> do+            assertBool name strictDer+            assertBool name ("S value is unnecessarily high" `isInfixOf` refusal)+          -- Every other label names a rule of BIP 66+          _ -> do+            assertBool name (not strictDer)+            assertBool name (refusal /= "accepted")++-- | The label that describes each entry of @sig_nonstrict.json@: an entry+-- that is not hex is a label, and it holds for the entries after it.+labelsOf :: [Text] -> [Text]+labelsOf = drop 1 . scanl (\label x -> if isNothing (decodeHex x) then x else label) ""++txSigHashSpec :: Network -> Spec+txSigHashSpec net =+  when (net.name == "btc") $+    it "can produce valid sighashes from sighash.json test vectors" $ do+      xs <- readTestFile "sighash.json" :: IO [A.Value]+      let vectors =+            mapMaybe (A.decode . A.encode) xs ::+              [ ( String,+                  String,+                  Int,+                  Integer,+                  String+                )+              ]+      length vectors `shouldBe` 500+      forM_ vectors $ \(txStr, scpStr, i, shI, resStr) -> do+        let tx = fromString txStr+            s =+              fromMaybe (error $ "Could not decode script: " <> cs scpStr) $+                eitherToMaybe . runGetS deserialize =<< decodeHex (cs scpStr)+            sh = fromIntegral shI+            res =+              eitherToMaybe . runGetS deserialize . B.reverse+                =<< decodeHex (cs resStr)+        -- A vector that did not decode would compare Nothing with Nothing+        res `shouldSatisfy` isJust+        eitherToMaybe (txSigHash net tx s 0 i sh) `shouldBe` res++txSigHashForkIdSpec :: Network -> Spec+txSigHashForkIdSpec net =+  when (net.name == "btc") $+    -- The file holds the thirteen digests of the examples of BIP 143. None+    -- has the SIGHASH_FORKID bit, so they run on Bitcoin. The fork id digest+    -- of Bitcoin Cash has literal values in the digest tests above.+    it "computes the thirteen BIP 143 digests of forkid_sighash.json" $ 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)+        res `shouldSatisfy` isJust+        eitherToMaybe (txSigHashForkId net tx s val i sh) `shouldBe` res++sigHashSpec :: Network -> Ctx -> Spec+sigHashSpec net ctx = do+  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+  if isNothing net.sigHashForkId+    then+      it "can produce the sighash one" $+        property $+          forAll (arbitraryTx net ctx) $+            forAll arbitraryScript . testSigHashOne net+    else+      it "never produces the sighash one, since the fork id digest has no such case" $+        property $+          forAll (arbitraryTx net ctx) $ \tx ->+            forAll arbitraryScript $ \s ->+              not (null tx.inputs) ==>+                txSigHash net tx s 0 (length tx.inputs - 1) (setForkIdFlag sigHashSingle)+                  `shouldSatisfy` \res -> isRight res && res /= Right sigHashOne++-- | The digest that the legacy algorithm gives for SIGHASH_SINGLE without a+-- matching output.+sigHashOne :: Hash256+sigHashOne = "0100000000000000000000000000000000000000000000000000000000000000"++testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property+testSigHashOne net tx s val acp =+  not (null tx.inputs) ==>+    if length tx.inputs > length tx.outputs+      then res `shouldBe` Right sigHashOne+      else res `shouldNotBe` Right sigHashOne+  where+    res = txSigHash net tx s val (length tx.inputs - 1) (f sigHashSingle)+    f =+      if acp+        then setAnyoneCanPay+        else id++{- Parse tests from bitcoin-qt repository -}++mapMulSigVector :: Ctx -> ((Text, Text), Int) -> Spec+mapMulSigVector ctx (v, i) =+  it name $ runMulSigVector ctx v+  where+    name = "check multisig vector " <> show i++runMulSigVector :: Ctx -> (Text, Text) -> Assertion+runMulSigVector ctx (a, ops) = assertBool "multisig vector" $ Just a == b+  where+    s = do+      s' <- decodeHex ops+      eitherToMaybe $ runGetS deserialize s'+    b = do+      o <- s+      d <- eitherToMaybe $ decodeOutput ctx o+      addrToText btc =<< eitherToMaybe (payToScriptAddress ctx d)++sigDecodeMap :: Network -> Ctx -> (Text, Int) -> Spec+sigDecodeMap net ctx (_, i) =+  it ("check signature " ++ show i) func+  where+    func = testSigDecode net ctx $ scriptSigSignatures !! i++testSigDecode :: Network -> Ctx -> Text -> Assertion+testSigDecode net ctx str =+  let bs = fromJust $ decodeHex str+      eitherSig = decodeTxSig net ctx bs+   in assertBool+        ( unwords+            [ "Decode failed:",+              fromLeft (error "Decode did not fail") eitherSig+            ]+        )+        $ isRight eitherSig++mulSigVectors :: [(Text, Text)]+mulSigVectors =+  [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",+      "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb\+      \129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8\+      \334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253\+      \e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccf\+      \fef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f997\+      \4ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f\+      \322a1863d4621353ae"+    )+  ]++scriptSigSignatures :: [Text]+scriptSigSignatures =+  -- Signature in input of txid+  -- 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053+  [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f333\+    \57119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a29\+    \41a2b1e401"+    -- Signature in input of txid+    -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.+    -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only+    -- decode strict signatures.+    -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378d\+    -- \b4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22\+    -- \e18fe1e7d1510db501"+  ]++encodeScriptVector :: Assertion+encodeScriptVector =+  assertEqual "Encode script" res (encodeHex $ runPutS $ serialize s)+  where+    res =+      "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\+      \bfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d\+      \348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2f\+      \cfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b\+      \25c15342af52ae"+    s =+      Script+        [ OP_1,+          opPushData $+            d+              "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef5\+              \8bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d\+              \11fcdd0d348ac4",+          opPushData $+            d+              "0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcf\+              \deb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39\+              \f58b25c15342af",+          OP_2,+          OP_CHECKMULTISIG+        ]+    d = fromJust . decodeHex
− test/Haskoin/Transaction/PartialSpec.hs
@@ -1,335 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.Transaction.PartialSpec (spec) where--import           Control.Monad.Fail          (MonadFail)-import           Data.ByteString             (ByteString)-import           Data.Either                 (fromRight, isLeft, isRight)-import           Data.HashMap.Strict         (fromList, singleton)-import           Data.Maybe                  (fromJust, isJust)-import           Data.Serialize              as S-import           Data.Text                   (Text)-import           Test.Hspec-import           Test.HUnit                  (Assertion, assertBool,-                                              assertEqual)-import           Test.QuickCheck--import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Crypto-import           Haskoin.Keys-import           Haskoin.Script-import           Haskoin.Transaction-import           Haskoin.Transaction.Partial-import           Haskoin.Util-import           Haskoin.Util.Arbitrary--spec :: Spec-spec = describe "partially signed bitcoin transaction unit tests" $ do-    it "encodes trivial psbt" $-        encodeHex (S.encode trivialPSBT) == trivialPSBTHex-    it "decodes trivial psbt" $-        decodeHexPSBT trivialPSBTHex == Right trivialPSBT-    it "encodes and decodes non-empty transactions" $-        S.decode (S.encode nonEmptyTransactionPSBT) == Right nonEmptyTransactionPSBT-    it "does not decode invalid bip vectors" $-        mapM_ invalidVecTest invalidVec-    it "encodes valid bip vecs" $-        mapM_ (uncurry encodeVecTest) validEncodeVec-    it "decodes valid bip vecs" $-        mapM_ (uncurry decodeVecTest) $ zip [1..] validVec-    it "decodes vector 2" vec2Test-    it "decodes vector 3" vec3Test-    it "decodes vector 4" vec4Test-    it "decodes vector 5" vec5Test-    it "decodes vector 6" vec6Test-    it "signed and finalized p2pkh PSBTs verify" $ property $-        forAll arbitraryKeyPair $ verifyNonWitnessPSBT btc . unfinalizedPkhPSBT btc-    it "signed and finalized multisig PSBTs verify" $ property $-        forAll arbitraryMultiSig $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc--vec2Test :: Assertion-vec2Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec2" validVec2Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    assertBool "final script sig" $ isJust (finalScriptSig . head $ inputs psbt)--    let rdmScript = fromJust . inputRedeemScript $ inputs psbt !! 1-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey $ inputs psbt !! 1-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    mapM_ (assertEqual "outputs are empty" emptyOutput) (outputs psbt)--vec3Test :: Assertion-vec3Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec3" validVec3Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let txInput = head . txIn $ unsignedTransaction psbt-        firstInput = head $ inputs psbt-        Just utx = nonWitnessUtxo firstInput-        OutPoint prevHash prevVOut = prevOutput txInput-    assertEqual "txids of inputs match" prevHash (txHash utx)-    let prevOutputKey = fromRight (error "Could not decode key")-                      . decodeOutputBS . scriptOutput $ txOut utx !! fromIntegral prevVOut-    assertBool "p2pkh" $ isPayPKHash prevOutputKey-    assertEqual "sighash type" sigHashAll (fromJust $ sigHashType firstInput)--vec4Test :: Assertion-vec4Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec4" validVec4Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let firstInput = head $ inputs psbt-        secondInput = inputs psbt !! 1-    assertEqual "first input not final script sig" Nothing (finalScriptSig firstInput)-    assertEqual "second input not final script sig" Nothing (finalScriptSig secondInput)--    let rdmScript = fromJust $ inputRedeemScript secondInput-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey secondInput-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    assertBool "all non-empty outputs" $ emptyOutput `notElem` outputs psbt--vec5Test :: Assertion-vec5Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)-    let input = head $ inputs psbt-    assertEqual "input not final script sig" Nothing (finalScriptSig input)--    let rdmScript = fromJust $ inputRedeemScript input-    assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey input-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut2 rdmScriptP2SH-  where-    expectedOut2 = fromRight (error "could not decode expected output")-                . decodeOutputBS . fromJust $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"--vec6Test :: Assertion-vec6Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)--    let tx = unsignedTransaction psbt-    assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)--    assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)-  where-    expectedUnknowns = UnknownMap $ singleton-        (Key 0x0f (fromJust $ decodeHex "010203040506070809"))-        (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")--expectedOut :: ScriptOutput-expectedOut = fromRight (error "could not decode expected output")-            . decodeOutputBS . fromJust $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"--witnessScriptPubKey :: Input -> ScriptOutput-witnessScriptPubKey = fromRight (error "could not decode witness utxo")-                    . decodeOutputBS . scriptOutput . fromJust . witnessUtxo--decodeHexPSBT :: Text -> Either String PartiallySignedTransaction-decodeHexPSBT = S.decode . fromJust . decodeHex--decodeHexPSBTM :: (Monad m, MonadFail m) => String -> Text -> m PartiallySignedTransaction-decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT--hexScript :: Text -> ByteString-hexScript =-    either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex-  where-    encodeScript :: Script -> ByteString-    encodeScript = S.encode--invalidVecTest :: Text -> Assertion-invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT--decodeVecTest :: Int -> Text -> Assertion-decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT--encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion-encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)--trivialPSBT :: PartiallySignedTransaction-trivialPSBT = PartiallySignedTransaction-    { unsignedTransaction = Tx { txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0 }-    , globalUnknown = UnknownMap mempty-    , inputs = []-    , outputs = []-    }--trivialPSBTHex :: Text-trivialPSBTHex = "70736274ff01000a0200000000000000000000"--nonEmptyTransactionPSBT :: PartiallySignedTransaction-nonEmptyTransactionPSBT = emptyPSBT testTx1--verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool-verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData-  where-    sigData = inputSigData =<< zip (inputs psbt) (txIn $ unsignedTransaction psbt)-    decodeOutScript = fromRight (error "Could not parse output script") . decodeOutputBS-    inputSigData (input, txInput) =-        map (\(TxOut val script) -> (decodeOutScript script, val, prevOutput txInput))-            (txOut . fromJust $ nonWitnessUtxo input)--unfinalizedPkhPSBT :: Network -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction-unfinalizedPkhPSBT net (prvKey, pubKey) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = addressToScript (pubKeyAddr pubKey)-    prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll--arbitraryMultiSig :: Gen ([(SecKeyI, PubKeyI)], Int)-arbitraryMultiSig = do-    (m, n) <- arbitraryMSParam-    keys <- vectorOf n arbitraryKeyPair-    return (keys, m)--unfinalizedMsPSBT :: Network -> ([(SecKeyI, PubKeyI)], Int) -> PartiallySignedTransaction-unfinalizedMsPSBT net (keys, m) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = sigs-                            , inputRedeemScript = Just prevOutScript-                            } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = encodeOutput $ PayMulSig (map snd keys) m-    prevOut = TxOut { outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript) }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sigs = fromList $ map sig keys-    sig (prvKey, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)--unfinalizedTx :: TxHash -> Tx-unfinalizedTx prevHash = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint prevHash 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--invalidVec :: [Text]-invalidVec =-    [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"-    , "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000"-    , "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    ]---validEncodeVec :: [(PartiallySignedTransaction, Text)]-validEncodeVec = [(validVec1, validVec1Hex)]--testTx1 :: Tx-testTx1 = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--testUtxo :: [TxOut] -> Tx-testUtxo prevOuts = Tx-        { txVersion = 1-        , txIn =-            [ TxIn-                { prevOutput = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1-                , scriptInput = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985"-                , txInSequence = 4294967295-                }-            , TxIn-                { prevOutput = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1-                , scriptInput = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353"-                , txInSequence = 4294967295-                }-            ]-        , txOut = prevOuts-        , txWitness =-            [-              [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01"-              , fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"-              ]-            , [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01"-              , fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"-              ]-            ]-        , txLockTime = 0-        }--testUtxo1 :: Tx-testUtxo1 = testUtxo-    [ TxOut { outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac" }-    , TxOut { outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587" }-    ]--validVec1 :: PartiallySignedTransaction-validVec1 = (emptyPSBT testTx1) { inputs = [ emptyInput { nonWitnessUtxo = Just testUtxo1 } ] }--validVec :: [Text]-validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]--validVec1Hex :: Text-validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"--validVec2Hex :: Text-validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"--validVec3Hex :: Text-validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"--validVec4Hex :: Text-validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"--validVec5Hex :: Text-validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"--validVec6Hex :: Text-validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"
+ test/Haskoin/Transaction/PsbtSpec.hs view
@@ -0,0 +1,1910 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++-- | The PSBT format of BIP 174, and its version 2 of BIP 370: the vectors+-- of both BIPs, the bytes of a PSBT and the refusals of what breaks the+-- spec, the fields of the spec, the roles: combining, signing, finalizing+-- and extracting, and the inputs and outputs a PSBT of version 2 takes.+module Haskoin.Transaction.PsbtSpec (spec) where++import Control.Monad (foldM, forM_)+import Data.Aeson ((.:), (.:?))+import Data.Aeson qualified as Json+import Data.ByteString (ByteString)+import Data.Bits (xor)+import Data.ByteString qualified as B+import Data.List (isPrefixOf, isSuffixOf)+import Crypto.Hash (Digest, SHA256, digestFromByteString)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (serialize)+import Data.Map.Strict qualified as Map+import Data.Maybe (fromJust, fromMaybe, isJust, listToMaybe, mapMaybe)+import Data.Serialize qualified as S+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding (encodeUtf8)+import Data.Word (Word32, Word64, Word8)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++{- The vectors of the BIP -}++-- The vectors live in data/psbt_bip174.json, copied from the BIP. Each has+-- the case the BIP gives, its hex, and its base64 when the BIP gives it.+data Vector = Vector+  { vecCase :: String,+    vecHex :: Text,+    vecBase64 :: Maybe Text+  }++instance Json.FromJSON Vector where+  parseJSON = Json.withObject "vector" $ \o -> Vector <$> o .: "case" <*> o .: "hex" <*> o .:? "base64"++-- | The invalid PSBTs, the valid ones, the PSBTs of the walk-through of the+-- roles, in the order of the BIP, and the transaction its extractor gives+data Vectors = Vectors+  { invalid :: [Vector],+    valid :: [Vector],+    roles :: [Vector],+    extracted :: Vector+  }++instance Json.FromJSON Vectors where+  parseJSON = Json.withObject "vectors" $ \o -> Vectors <$> o .: "invalid" <*> o .: "valid" <*> o .: "roles" <*> o .: "extracted"++-- The PSBTs of the walk-through, by their place in it+creator, updater, updaterWithSigHash, firstSigner, secondSigner, combiner, finalizer, unknownA, unknownB, unknownCombined, toCombineA, toCombineB, combined :: Vectors -> Vector+creator v = roles v !! 0+updater v = roles v !! 1+updaterWithSigHash v = roles v !! 2+firstSigner v = roles v !! 3+secondSigner v = roles v !! 4+combiner v = roles v !! 5+finalizer v = roles v !! 6+unknownA v = roles v !! 7+unknownB v = roles v !! 8+unknownCombined v = roles v !! 9+toCombineA v = roles v !! 10+toCombineB v = roles v !! 11+combined v = roles v !! 12++hexBytes :: Text -> ByteString+hexBytes = fromJust . decodeHex++bytesOf :: Vector -> ByteString+bytesOf = hexBytes . vecHex++expectRight :: Either String a -> IO a+expectRight = either fail return++forceRight :: Either String a -> a+forceRight = either error id++fingerprintOf :: Text -> Fingerprint+fingerprintOf = forceRight . textToFingerprint++-- | The PSBT of a vector, which must decode+psbtOfVector :: Vector -> IO Psbt+psbtOfVector v = expectRight (decodePsbt (bytesOf v))++-- | The secret keys 1, 2 and 3 of the signer tests. Their public keys are+-- the points G, 2G and 3G of the curve.+secretOne, secretTwo, secretThree :: SecKey+secretOne = "0000000000000000000000000000000000000000000000000000000000000001"+secretTwo = "0000000000000000000000000000000000000000000000000000000000000002"+secretThree = "0000000000000000000000000000000000000000000000000000000000000003"++-- | The compressed public key of a secret key+compressedKey :: Ctx -> SecKey -> PublicKey+compressedKey ctx secret = PublicKey (derivePubKey ctx secret) True++-- | The map of the input, or of the output, at the index+inputAt, outputAt :: Int -> Psbt -> PsbtMap+inputAt i psbt = psbt.inputs !! i+outputAt i psbt = psbt.outputs !! i++-- | A PSBT with the maps of its inputs replaced+withInputs :: [PsbtMap] -> Psbt -> Psbt+withInputs inputs psbt = Psbt psbt.version psbt.unsignedTx psbt.globals inputs psbt.outputs++-- | The PSBT with the flags given in its global map+withFlags :: Modifiable -> Psbt -> Psbt+withFlags flags psbt = Psbt psbt.version psbt.unsignedTx (insertTxModifiable flags psbt.globals) psbt.inputs psbt.outputs++spec :: Spec+spec = do+  vectors <- runIO (readTestFile "psbt_bip174.json" :: IO Vectors)+  vectors370 <- runIO (readTestFile "psbt_bip370.json" :: IO Vectors370)+  vectors371 <- runIO (readTestFile "psbt_bip371.json" :: IO Vectors371)+  prepareContext $ \ctx -> do+    vectorsSpec vectors+    vectors370Spec vectors370+    rolesSpec ctx vectors+    version2Spec+    bytesSpec vectors+    fieldsSpec ctx vectors+    signingSpec ctx+    signerChecksSpec ctx+    signerScopeSpec ctx+    coinsSpec ctx+    version2FieldsSpec+    taprootChecksSpec ctx vectors371+    decoderChecksSpec+    taprootSpec ctx vectors371+    complexSpec ctx+    core32Spec ctx+    propertiesSpec ctx++{- The vectors of BIP 370 -}++-- The vectors of BIP 370 live in data/psbt_bip370.json: the invalid+-- PSBTs, the valid ones, and the PSBTs of its lock time cases, each with+-- the lock time the BIP computes, none when it cannot be computed+data LockTimeVector = LockTimeVector+  { ltCase :: String,+    ltHex :: Text,+    ltLockTime :: Maybe Word32+  }++instance Json.FromJSON LockTimeVector where+  parseJSON = Json.withObject "vector" $ \o -> LockTimeVector <$> o .: "case" <*> o .: "hex" <*> o .:? "locktime"++data Vectors370 = Vectors370+  { refused :: [Vector],+    accepted :: [Vector],+    lockTimes :: [LockTimeVector]+  }++instance Json.FromJSON Vectors370 where+  parseJSON = Json.withObject "vectors" $ \o -> Vectors370 <$> o .: "invalid" <*> o .: "valid" <*> o .: "locktime"++-- | Why each invalid PSBT of BIP 370 is refused, in the order of the BIP:+-- a field of the other version, a field version 2 requires missing, a+-- lock time out of its range+refusals370 :: [String]+refusals370 =+  [ "The global map: the key 0x00 is not of PSBT version 2",+    "The global map: the key 0x02 is not of PSBT version 0",+    "The global map: the key 0x03 is not of PSBT version 0",+    "The global map: the key 0x04 is not of PSBT version 0",+    "The global map: the key 0x05 is not of PSBT version 0",+    "The global map: the key 0x06 is not of PSBT version 0",+    "Input 0: the key 0x0e is not of PSBT version 0",+    "Input 0: the key 0x0f is not of PSBT version 0",+    "Input 0: the key 0x10 is not of PSBT version 0",+    "Input 0: the key 0x11 is not of PSBT version 0",+    "Input 0: the key 0x12 is not of PSBT version 0",+    "Output 0: the key 0x03 is not of PSBT version 0",+    "Output 0: the key 0x04 is not of PSBT version 0",+    "The global map: the key 0x00 is not of PSBT version 2",+    "The global map has no input count",+    "The global map has no output count",+    "The global map has no transaction version",+    "Input 0 has no previous transaction id",+    "Input 0 has no output index",+    "Output 0 has no amount",+    "Output 0 has no script",+    "Input 0: the value of the key 0x11 is a time lock below 500000000",+    "Input 0: the value of the key 0x12 is not a height between 1 and 499999999",+    "Input 0: the value of the key 0x12 is not a height between 1 and 499999999"+  ]++vectors370Spec :: Vectors370 -> Spec+vectors370Spec vectors = do+  describe "The invalid PSBTs of BIP 370 are refused, each for its reason" $+    forM_ (zip vectors.refused refusals370) $ \(v, reason) ->+      it v.vecCase $ decodePsbt (bytesOf v) `shouldBe` Left reason++  describe "The valid PSBTs of BIP 370 are read as version 2, and written back byte for byte" $+    forM_ vectors.accepted $ \v ->+      it v.vecCase $ do+        psbt <- psbtOfVector v+        psbt.version `shouldBe` Version2+        encodePsbt psbt `shouldBe` bytesOf v++  describe "The transaction of a PSBT of version 2 is the one its fields describe" $+    -- The first valid PSBT of the BIP: one input, two outputs, nothing+    -- else. Its input has no sequence field, so its sequence is final,+    -- and no input requires a lock time, nor is a fallback given: lock+    -- time 0.+    it "for the first valid PSBT of the BIP, with a sequence and a lock time that no field gives" $ do+      psbt <- psbtOfVector (vectors.accepted !! 0)+      psbt.unsignedTx+        `shouldBe` Tx+          2+          [TxIn (OutPoint "c85f81844094f9f0eec1e41f8d63e0a99e9f73dc725d7319871c9c4121d90a0b" 0) "" 0xffffffff]+          [ TxOut 800000000 (hexBytes "0014c430f64c4756da310dbd1a085572ef299926272c"),+            TxOut 199998859 (hexBytes "00144dd193ac964a56ac1b9e1cca8454fe2f474f8513")+          ]+          []+          0++  describe "The lock time of a PSBT of version 2 is the one BIP 370 determines" $+    forM_ vectors.lockTimes $ \v ->+      it v.ltCase $+        ((.unsignedTx.locktime) <$> decodePsbt (hexBytes v.ltHex))+          `shouldBe` maybe (Left "No lock time fits every input: some require a height, others a time") Right v.ltLockTime++{- The vectors -}++-- | Why each invalid PSBT of BIP 174 is refused, in the order of the BIP,+-- as the start of the reason. The cases are: no magic bytes, a map+-- missing, a script in the unsigned transaction, no unsigned transaction,+-- a key twice, twelve keys whose data is not what their type takes, and+-- an unsigned transaction that is not in the serialization without witness+-- or not whole.+refusals174 :: [String]+refusals174 =+  [ "Not a PSBT: the bytes do not start with the magic bytes of BIP 174",+    "The PSBT ends inside output 0",+    "The unsigned transaction has an input with a script",+    "The global map has no unsigned transaction",+    "Input 0 has the key 0x00 twice",+    "The global map: the key 0x00 with the data 01 carries data",+    "Input 0: the key 0x01 with the data 00 carries data",+    "Input 0: the key 0x02 with the data 03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd is not a public key",+    "Input 0: the key 0x04 with the data 00 carries data",+    "Input 0: the key 0x05 with the data 00 carries data",+    "Input 0: the key 0x06 with the data 03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd is not a public key",+    "Input 0: the key 0x00 with the data 00 carries data",+    "Input 0: the key 0x07 with the data 00 carries data",+    "Input 1: the key 0x08 with the data 00 carries data",+    "Output 0: the key 0x02 with the data 03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca587 is not a public key",+    "Input 0: the key 0x03 with the data 00 carries data",+    "Output 0: the key 0x00 with the data 00 carries data",+    "Output 1: the key 0x01 with the data 0025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d",+    "The unsigned transaction does not decode",+    "The unsigned transaction does not decode"+  ]++vectorsSpec :: Vectors -> Spec+vectorsSpec vectors = do+  describe "The invalid PSBTs of BIP 174 are refused, each for its reason" $+    forM_ (zip vectors.invalid refusals174) $ \(v, reason) ->+      it v.vecCase $ decodePsbt (bytesOf v) `shouldSatisfy` either (reason `isPrefixOf`) (const False)++  describe "The valid PSBTs of BIP 174 are read, and written back byte for byte" $+    forM_ vectors.valid $ \v ->+      it v.vecCase $ (encodePsbt <$> decodePsbt (bytesOf v)) `shouldBe` Right (bytesOf v)++  describe "The valid PSBTs of BIP 174 read the same from their base64 text" $+    forM_ [(v, text) | v <- vectors.valid, Just text <- [v.vecBase64]] $ \(v, text) ->+      it v.vecCase $ (encodePsbt <$> decodePsbtBase64 (encodeUtf8 text)) `shouldBe` Right (bytesOf v)++  -- The combiner's PSBT of the two signed ones lists the signatures of+  -- its second input out of order: see 'rolesSpec'+  describe "The PSBTs of the roles of BIP 174 are read, and written back byte for byte" $+    forM_ [v | v <- vectors.roles, v.vecCase /= (combiner vectors).vecCase] $ \v ->+      it v.vecCase $ (encodePsbt <$> decodePsbt (bytesOf v)) `shouldBe` Right (bytesOf v)++rolesSpec :: Ctx -> Vectors -> Spec+rolesSpec ctx vectors =+  describe "The roles of BIP 174" $ do+    it "the creator's PSBT is the unsigned transaction with empty maps" $ do+      psbt <- psbtOfVector (creator vectors)+      psbtOf psbt.unsignedTx `shouldBe` psbt++    it "the transaction extractor gives the transaction of the finalizer's PSBT" $ do+      psbt <- psbtOfVector (finalizer vectors)+      (S.encode <$> psbtTransaction psbt) `shouldBe` Right (bytesOf (extracted vectors))++    -- BIP 174: the extractor "checks whether all inputs have complete+    -- scriptSigs and scriptWitnesses", and otherwise "must not modify the+    -- PSBT". Bitcoin Core gives no transaction then (FinalizeAndExtractPSBT).+    it "the transaction extractor refuses a PSBT with an input that is not final" $ do+      psbt <- psbtOfVector (creator vectors)+      psbtTransaction psbt `shouldBe` Left "Input 0 is not final"++    it "the transaction of a PSBT that is still being signed has the final scripts of the inputs that have them" $ do+      unsigned <- psbtOfVector (creator vectors)+      psbtPartialTransaction unsigned `shouldBe` Right unsigned.unsignedTx+      final <- psbtOfVector (finalizer vectors)+      -- The second input loses its final script and witness+      let halfFinal = withInputs [inputAt 0 final, Map.delete (Key 0x07 "") (Map.delete (Key 0x08 "") (inputAt 1 final))] final+      psbtTransaction halfFinal `shouldBe` Left "Input 1 is not final"+      (map (B.null . (.script)) . (.inputs) <$> psbtPartialTransaction halfFinal) `shouldBe` Right [False, True]+      ((.witness) <$> psbtPartialTransaction halfFinal) `shouldBe` Right []++    -- The updater's PSBT gives the coin of its second input, a P2SH-P2WSH+    -- one, as a witness output only, as BIP 174 did before CVE-2020-14199+    -- was known. A signer that wants previous transactions refuses it.+    it "a signer that trusts witness outputs makes the first signer's PSBT of the BIP from the updater's, and the second signer's" $ do+      psbt <- psbtOfVector (updaterWithSigHash vectors)+      let master = fromJust (xPrvImport btcTest "tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF")+          trusting = (xPrvSigner ctx master Nothing) {requirePreviousTx = False}+          holding paths = trusting {keyOfRecord = \public derivation -> if derivation.path `elem` paths then trusting.keyOfRecord public derivation else Nothing}+      (encodePsbt <$> signPsbt btcTest ctx (holding ["m/0'/0'/0'", "m/0'/0'/2'"]) psbt) `shouldBe` Right (bytesOf (firstSigner vectors))+      (encodePsbt <$> signPsbt btcTest ctx (holding ["m/0'/0'/1'", "m/0'/0'/3'"]) psbt) `shouldBe` Right (bytesOf (secondSigner vectors))+      signPsbt btcTest ctx (xPrvSigner ctx master Nothing) psbt+        `shouldBe` Left "Input 1: the coin is given as a witness output only, and the signer requires the previous transaction of a SegWit version 0 coin"++    -- The four cases the BIP lists under "Fails Signer checks", signed+    -- with the master key of the BIP. The input at fault never gains a+    -- signature. The numbers are the partial signatures of each input.+    describe "a signer adds no signature to the input at fault of the PSBTs that fail the signer checks of the BIP" $ do+      let master = fromJust (xPrvImport btcTest "tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF")+          trusting = (xPrvSigner ctx master Nothing) {requirePreviousTx = False}+          counts = mapM (fmap length . partialSignatures ctx) . (.inputs)+          failing i = psbtOfVector (vectors.valid !! i)+      -- No record names a key, so the signer holds none: nothing is signed+      it "a witness output for a legacy coin" $ do+        psbt <- failing 10+        counts psbt `shouldBe` Right [0, 0]+        (counts =<< signPsbt btcTest ctx trusting psbt) `shouldBe` Right [0, 0]+      -- The redeem script of input 0 is no standard script, and does not+      -- hash to the coin. Input 1 is sound and is signed.+      it "a redeem script that does not match the coin, with the previous transaction" $ do+        psbt <- failing 11+        counts psbt `shouldBe` Right [1, 1]+        (counts =<< signPsbt btcTest ctx trusting psbt) `shouldBe` Right [1, 2]+      it "a redeem script that does not match the coin, with the witness output" $ do+        psbt <- failing 12+        signPsbt btcTest ctx trusting psbt `shouldBe` Left "Input 1: the redeem script of the input does not hash to its coin"+      -- The witness script of input 1 is no standard script. Input 0 is+      -- sound and is signed.+      it "a witness script that does not match the redeem script" $ do+        psbt <- failing 13+        counts psbt `shouldBe` Right [1, 1]+        (counts =<< signPsbt btcTest ctx trusting psbt) `shouldBe` Right [2, 1]++    -- The spec does not say in which order a combiner writes the keys of a+    -- map. The BIP's combiner lists the two signatures of the second input+    -- out of order; the keys of a map are written in order here, as+    -- Bitcoin Core writes them. The PSBTs are the same, byte for byte once+    -- the BIP's is written back.+    it "the combiner's PSBT is that of the two signers combined, whatever the order of its inputs" $ do+      a <- psbtOfVector (firstSigner vectors)+      b <- psbtOfVector (secondSigner vectors)+      both <- psbtOfVector (combiner vectors)+      combinePsbt a b `shouldBe` Right both+      combinePsbt b a `shouldBe` Right both+      encodePsbt both `shouldNotBe` bytesOf (combiner vectors)+      decodePsbt (encodePsbt both) `shouldBe` Right both++    it "the two PSBTs with unknown keys combine as the combiner of the BIP that orders keys lexicographically does" $ do+      a <- psbtOfVector (unknownA vectors)+      b <- psbtOfVector (unknownB vectors)+      (encodePsbt <$> combinePsbt a b) `shouldBe` Right (bytesOf (unknownCombined vectors))+      (encodePsbt <$> combinePsbt b a) `shouldBe` Right (bytesOf (unknownCombined vectors))++    it "the two PSBTs to combine give the combiner's PSBT" $ do+      a <- psbtOfVector (toCombineA vectors)+      b <- psbtOfVector (toCombineB vectors)+      (encodePsbt <$> combinePsbt a b) `shouldBe` Right (bytesOf (combined vectors))++    it "PSBTs of different transactions are not combined" $ do+      a <- psbtOfVector (creator vectors)+      combinePsbt a (psbtOf emptyTx) `shouldBe` Left "The PSBTs are not of the same transaction"++    it "the updater's PSBT with SIGHASH_ALL is the updater's PSBT with the sighash type on every input" $ do+      psbt <- psbtOfVector (updater vectors)+      encodePsbt (withInputs (insertSigHashType sigHashAll <$> psbt.inputs) psbt) `shouldBe` bytesOf (updaterWithSigHash vectors)++    it "the finalizer's PSBT is the combiner's PSBT finalized" $ do+      psbt <- psbtOfVector (combiner vectors)+      (encodePsbt <$> finalizePsbt btc ctx psbt) `shouldBe` Right (bytesOf (finalizer vectors))++    it "the finalizer leaves an input without enough signatures as it is" $ do+      psbt <- psbtOfVector (firstSigner vectors)+      finalizePsbt btc ctx psbt `shouldBe` Right psbt++{- The roles of version 2 -}++-- | The input and output of the PSBTs below: the coin and the first output+-- of the first valid PSBT of BIP 370, and another output of the same+-- transaction as the coin+coinInput, otherInput :: TxIn+coinInput = TxIn (OutPoint "c85f81844094f9f0eec1e41f8d63e0a99e9f73dc725d7319871c9c4121d90a0b" 0) "" 0xfffffffd+otherInput = TxIn (OutPoint "c85f81844094f9f0eec1e41f8d63e0a99e9f73dc725d7319871c9c4121d90a0b" 1) "" 0xfffffffd++paidOutput :: TxOut+paidOutput = TxOut 800000000 (hexBytes "0014c430f64c4756da310dbd1a085572ef299926272c")++version2Spec :: Spec+version2Spec =+  describe "A PSBT of version 2" $ do+    -- A creator of BIP 370 starts with no input and no output. The+    -- created one, with its flags set so that inputs and outputs can be+    -- added, and with the input and the output added.+    let created = withVersion Version2 (psbtOf (Tx 2 [] [] [] 0))+        modifiable = withFlags (Modifiable True True False 0) created+        built = forceRight (addOutput paidOutput Map.empty =<< addInput coinInput Map.empty modifiable)+        -- The same without its flags, as a constructor leaves it once+        -- nothing is to be added+        unflagged = Psbt Version2 built.unsignedTx (deleteTxModifiable built.globals) built.inputs built.outputs+        requiring field value = addInput otherInput (field value Map.empty)++    -- The fields of the global map: the transaction version 2, the+    -- fallback lock time 0, the two counts of 0, and the PSBT version 2+    it "is created with no input and no output, and the fields that describe its transaction" $+      encodeHex (encodePsbt created) `shouldBe` "70736274ff0102040200000001030400000000010401000105010001fb040200000000"++    it "takes no input or output unless its flags allow it" $ do+      addInput coinInput Map.empty created `shouldBe` Left "The inputs of the PSBT may not be modified"+      addOutput paidOutput Map.empty created `shouldBe` Left "The outputs of the PSBT may not be modified"++    -- The input is described by the id of the transaction it spends from,+    -- in the byte order of a transaction, the index of the output and its+    -- sequence; the output by its amount and its script+    it "takes an input and an output at the end, counted, described in their maps, and in its transaction" $ do+      built.unsignedTx `shouldBe` Tx 2 [coinInput] [paidOutput] [] 0+      Map.lookup (Key 0x04 "") built.globals `shouldBe` Just "\x01"+      Map.lookup (Key 0x05 "") built.globals `shouldBe` Just "\x01"+      inputAt 0 built+        `shouldBe` Map.fromList+          [ (Key 0x0e "", hexBytes "0b0ad921419c1c8719735d72dc739f9ea9e0638d1fe4c1eef0f9944084815fc8"),+            (Key 0x0f "", hexBytes "00000000"),+            (Key 0x10 "", hexBytes "fdffffff")+          ]+      outputAt 0 built `shouldBe` Map.fromList [(Key 0x03 "", hexBytes "0008af2f00000000"), (Key 0x04 "", hexBytes "0014c430f64c4756da310dbd1a085572ef299926272c")]++    it "refuses an input that spends an output it spends already" $+      addInput coinInput Map.empty built `shouldBe` Left "An input of the PSBT spends that output already"++    it "is not of version 0, which takes no input or output" $+      addInput coinInput Map.empty (psbtOf emptyTx) `shouldBe` Left "A PSBT of version 0 takes no input or output: its transaction is fixed"++    it "takes the lock time its inputs require: the greatest height when every one that requires a lock time takes a height" $ do+      withHeight <- expectRight (addInput coinInput (insertRequiredHeightLockTime 10000 Map.empty) modifiable)+      withHeight.unsignedTx.locktime `shouldBe` 10000+      ((.unsignedTx.locktime) <$> requiring insertRequiredHeightLockTime 9000 withHeight) `shouldBe` Right 10000+      requiring insertRequiredTimeLockTime 1657048460 withHeight `shouldBe` Left "No lock time fits every input: some require a height, others a time"++    -- The input of the PSBT has its final script+    it "refuses an input that changes the lock time once an input is signed, and takes one that keeps it" $ do+      let signed = withInputs [insertFinalScriptSig "\x00" (inputAt 0 built)] built+      requiring insertRequiredHeightLockTime 10000 signed `shouldBe` Left "An input is signed already: the lock time may not change"+      ((.unsignedTx.locktime) <$> addInput otherInput Map.empty signed) `shouldBe` Right 0++    it "takes a sequence for an input, in its transaction and in its map, as an updater sets one" $ do+      updated <- expectRight (setInputSequence 0 1 built)+      map (.sequence) updated.unsignedTx.inputs `shouldBe` [1]+      Map.lookup (Key 0x10 "") (inputAt 0 updated) `shouldBe` Just (hexBytes "01000000")+      setInputSequence 1 1 built `shouldBe` Left "The PSBT has no input 1"+      setInputSequence 0 1 (psbtOf emptyTx) `shouldBe` Left "The sequences of a PSBT of version 0 are in its unsigned transaction, which is fixed"+      -- A signature commits to the sequence of its input+      setInputSequence 0 1 (withInputs [insertFinalScriptSig "\x00" (inputAt 0 built)] built) `shouldBe` Left "Input 0 is signed already: its sequence may not change"++    -- The id of its transaction with the sequence of its input at 0: the+    -- same whatever the sequence. A PSBT of version 0 is identified by+    -- its transaction as it is, with the sequence 0xfffffffd here.+    it "is identified by its transaction with every sequence at 0" $ do+      psbtId built `shouldBe` "79dd9a151a6cfc0dc3b7779a697817a80544e2c9dc0cfb399ecc7fa3b4da8f2c"+      (psbtId <$> setInputSequence 0 1 built) `shouldBe` Right "79dd9a151a6cfc0dc3b7779a697817a80544e2c9dc0cfb399ecc7fa3b4da8f2c"+      psbtId (withVersion Version0 built) `shouldBe` "7ca9f2c2ff60a7a3f7efdaa703bfb23b9415639e07b71e194bafb4d592e2c405"++    it "combines with the same PSBT whose sequence an updater changed, and keeps the sequence of the first" $ do+      updated <- expectRight (setInputSequence 0 1 built)+      (map (.sequence) . (.unsignedTx.inputs) <$> combinePsbt built updated) `shouldBe` Right [0xfffffffd]+      (map (.sequence) . (.unsignedTx.inputs) <$> combinePsbt updated built) `shouldBe` Right [1]++    it "does not combine with a PSBT of version 0" $+      combinePsbt built (withVersion Version0 built) `shouldBe` Left "The PSBTs are not of the same version"++    -- The first lets inputs be added and has a SIGHASH_SINGLE signature;+    -- the second lets inputs and outputs be added, with the undefined+    -- bit 3 set+    it "combines its flags as Bitcoin Core does: modifiable only when both say so, with a SIGHASH_SINGLE signature of either" $ do+      (txModifiable . (.globals) <$> combinePsbt (withFlags (Modifiable True False True 0) built) (withFlags (Modifiable True True False 8) built))+        `shouldBe` Right (Right (Just (Modifiable True False True 0)))+      (txModifiable . (.globals) <$> combinePsbt unflagged (withFlags (Modifiable True False True 0) built))+        `shouldBe` Right (Right (Just (Modifiable False False True 0)))++    it "once signed, takes no more inputs unless the sighash has ANYONECANPAY, no more outputs unless it is NONE, and tells a SIGHASH_SINGLE signature" $ do+      let open = withFlags (Modifiable True True False 0) built+          flagsAfter sighash psbt = txModifiable (afterSignature sighash psbt).globals+      flagsAfter sigHashAll open `shouldBe` Right (Just (Modifiable False False False 0))+      flagsAfter (setAnyoneCanPay sigHashNone) open `shouldBe` Right (Just (Modifiable True True False 0))+      flagsAfter sigHashSingle open `shouldBe` Right (Just (Modifiable False False True 0))+      flagsAfter sigHashAll unflagged `shouldBe` Right Nothing+      flagsAfter sigHashSingle unflagged `shouldBe` Right (Just (Modifiable False False True 0))++    it "converted to version 0 is the PSBT of its transaction" $+      withVersion Version0 built `shouldBe` psbtOf (Tx 2 [coinInput] [paidOutput] [] 0)++{- Bytes -}++-- | A transaction with nothing in it: version 0, no input, no output, lock+-- time 0, which is "0000000000000000000000" over ten bytes+emptyTx :: Tx+emptyTx = Tx 0 [] [] [] 0++-- | The global map of the PSBT of 'emptyTx': the key of the unsigned+-- transaction, one byte of type 0x00, and the ten bytes of the transaction+emptyTxEntry :: Text+emptyTxEntry = "01000a00000000000000000000"++bytesSpec :: Vectors -> Spec+bytesSpec vectors =+  describe "The bytes of a PSBT" $ do+    -- The vector of the BIP for a transaction without inputs or outputs+    it "are the magic bytes, the unsigned transaction, and a map for each input and output" $+      encodePsbt (psbtOf emptyTx) `shouldBe` hexBytes ("70736274ff" <> emptyTxEntry <> "00")++    -- The key is 7 bytes: the type 0xfc, the identifier "test" after its+    -- length 4, and the subtype 0. The value is the 3 bytes of "btc".+    it "write a key as its length, its type and its data, and its value after its length" $+      encodePsbt (Psbt Version0 emptyTx (insertProprietary "test" 0 "btc" Map.empty) [] [])+        `shouldBe` hexBytes ("70736274ff" <> emptyTxEntry <> "07fc047465737400" <> "03627463" <> "00")++    it "write the keys of a map in order, the type first, whatever the order they were added" $ do+      let globals = Map.fromList [(Key 0x20 "", hexBytes "bb"), (Key 0x10 (hexBytes "01"), hexBytes "aa")]+      encodePsbt (Psbt Version0 emptyTx globals [] [])+        `shouldBe` hexBytes ("70736274ff" <> emptyTxEntry <> "02100101aa" <> "012001bb" <> "00")++    it "write the unsigned transaction without the input scripts and the witness of a signed one" $ do+      signed <- expectRight $ S.decode (bytesOf (extracted vectors))+      psbt <- psbtOfVector (finalizer vectors)+      (psbtOf signed).unsignedTx `shouldBe` psbt.unsignedTx++    it "tell a PSBT from other bytes by its first five" $ do+      isPsbt (bytesOf (creator vectors)) `shouldBe` True+      isPsbt "{\"tx\": \"0200\"}" `shouldBe` False++    it "accept the unsigned transaction after another key of the global map" $+      decodePsbt (hexBytes ("70736274ff" <> "012001bb" <> emptyTxEntry <> "00"))+        `shouldBe` Right (Psbt Version0 emptyTx (Map.fromList [(Key 0x20 "", hexBytes "bb")]) [] [])++    it "refuse what is not a PSBT" $+      decodePsbt "{\"tx\": \"0200\"}" `shouldBe` Left "Not a PSBT: the bytes do not start with the magic bytes of BIP 174"++    it "refuse a key twice in a map, naming it" $+      decodePsbt (hexBytes ("70736274ff" <> emptyTxEntry <> "012001aa" <> "012001bb" <> "00"))+        `shouldBe` Left "The global map has the key 0x20 twice"++    it "refuse bytes after the last map" $+      decodePsbt (bytesOf (creator vectors) <> "\0") `shouldBe` Left "Bytes follow the last map"++    -- Version 1 was never defined+    it "refuse a PSBT of a version other than 0 and 2" $+      decodePsbt (hexBytes ("70736274ff" <> emptyTxEntry <> "01fb0401000000" <> "00"))+        `shouldBe` Left "The PSBT is of version 1: only versions 0 and 2 are supported"++    -- The length of the first key, 1, written over three bytes+    it "refuse a length that is not in its shortest form" $+      decodePsbt (hexBytes ("70736274ff" <> "fd0100" <> "000a00000000000000000000" <> "00"))+        `shouldBe` Left "A length in the global map is not in its shortest form"++    -- The PSBT with 0 inputs and 2 outputs of the BIP, without the two+    -- bytes that end its output maps+    it "refuse bytes that end inside a map" $ do+      let withoutOutputs = bytesOf (vectors.valid !! 9)+      decodePsbt (B.take (B.length withoutOutputs - 2) withoutOutputs) `shouldBe` Left "The PSBT ends inside output 0"++    it "refuse a key of the spec that carries data it should not, naming it" $+      decodePsbt (bytesOf (vectors.invalid !! 15)) `shouldBe` Left "Input 0: the key 0x03 with the data 00 carries data"++    it "refuse a derivation key that is not a public key" $+      decodePsbt (bytesOf (vectors.invalid !! 10)) `shouldSatisfy` either ("is not a public key" `isSuffixOf`) (const False)++    it "read the base64 text of a PSBT, with spaces and line breaks around it" $ do+      let v = vectors.valid !! 0+      (encodePsbt <$> decodePsbtBase64 (" " <> encodeUtf8 (fromMaybe "" v.vecBase64) <> "\n")) `shouldBe` Right (bytesOf v)++    it "write the base64 text the BIP gives" $ do+      let v = vectors.valid !! 0+      psbt <- psbtOfVector v+      Just (encodePsbtBase64 psbt) `shouldBe` (encodeUtf8 <$> v.vecBase64)++    it "refuse text that is not base64" $+      decodePsbtBase64 "not base64!" `shouldBe` Left "Not the base64 text of a PSBT"++{- Fields -}++-- | The signature of the first signer of the BIP on the first input, as+-- Bitcoin Core decodes its PSBT+firstSignature :: Text+firstSignature = "3044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01"++-- | The key of the first signer of the BIP, at m/0'/0'/0' of the master+-- key d90c6a4f+firstKey :: Text+firstKey = "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f"++-- The literal values below are those Bitcoin Core 25.0 shows for the+-- vectors with decodepsbt.+fieldsSpec :: Ctx -> Vectors -> Spec+fieldsSpec ctx vectors =+  describe "The fields of a PSBT" $ do+    let publicKey text = forceRight (unmarshal ctx (hexBytes text)) :: PublicKey+        shown :: [(PublicKey, Derivation)] -> [(Text, Derivation)]+        shown records = [(encodeHex (marshal ctx key), derivation) | (key, derivation) <- records]++    it "read the derivation records of an input: the public key, the fingerprint of the master key and the path" $ do+      psbt <- psbtOfVector (updater vectors)+      records <- expectRight $ inputDerivations ctx (inputAt 0 psbt)+      shown records+        `shouldBe` [ (firstKey, Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/0'"),+                     ("02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7", Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/1'")+                   ]++    it "read the derivation records of an output" $ do+      psbt <- psbtOfVector (updater vectors)+      records <- expectRight $ outputDerivations ctx (outputAt 0 psbt)+      shown records `shouldBe` [("03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771", Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/4'")]++    -- The path is 0' (0x80000000) three times, each over four bytes,+    -- little-endian+    it "write a derivation record as the public key under the type, and the fingerprint then the path, four bytes an index" $ do+      let key = publicKey firstKey+      Map.toList (insertInputDerivation ctx key (Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/0'") Map.empty)+        `shouldBe` [(Key 0x06 (hexBytes firstKey), hexBytes ("d90c6a4f" <> "00000080" <> "00000080" <> "00000080"))]+      Map.keys (insertOutputDerivation ctx key (Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/4'") Map.empty)+        `shouldBe` [Key 0x02 (hexBytes firstKey)]++    it "read the sighash type of an input, when it has one" $ do+      with <- psbtOfVector (updaterWithSigHash vectors)+      without <- psbtOfVector (updater vectors)+      mapM sigHashType with.inputs `shouldBe` Right [Just sigHashAll, Just sigHashAll]+      mapM sigHashType without.inputs `shouldBe` Right [Nothing, Nothing]++    -- SIGHASH_ALL with the FORKID flag of Bitcoin Cash, 0x41+    it "write the sighash type over four bytes, little-endian, and drop it as a finalizer does" $ do+      Map.toList (insertSigHashType (setForkIdFlag sigHashAll) Map.empty) `shouldBe` [(Key 0x03 "", hexBytes "41000000")]+      Map.keys (deleteSigHashType (insertSigHashType sigHashAll (insertNonWitnessUtxo emptyTx Map.empty))) `shouldBe` [Key 0x00 ""]++    it "read the whole previous transaction of an input" $ do+      psbt <- psbtOfVector (vectors.valid !! 0)+      parent <- expectRight $ nonWitnessUtxo (inputAt 0 psbt)+      (txHashToHex . txHash <$> parent) `shouldBe` Just "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126"++    it "write a previous transaction under the type 0x00" $ do+      psbt <- psbtOfVector (vectors.valid !! 0)+      parent <- expectRight $ nonWitnessUtxo (inputAt 0 psbt)+      Map.toList (insertNonWitnessUtxo emptyTx Map.empty) `shouldBe` [(Key 0x00 "", hexBytes "00000000000000000000")]+      nonWitnessUtxo (maybe Map.empty (`insertNonWitnessUtxo` Map.empty) parent) `shouldBe` Right parent++    -- The second input of the vector spends 1 BTC at a P2SH address+    it "read the output an input spends when the PSBT has it alone, and write it under the type 0x01" $ do+      psbt <- psbtOfVector (vectors.valid !! 1)+      let coin = TxOut 100000000 (hexBytes "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787")+      mapM witnessUtxo psbt.inputs `shouldBe` Right [Nothing, Just coin]+      Map.toList (insertWitnessUtxo coin Map.empty) `shouldBe` [(Key 0x01 "", hexBytes "00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787")]++    it "tell an input with partial signatures from one without" $ do+      partial <- psbtOfVector (vectors.valid !! 4)+      unsigned <- psbtOfVector (vectors.valid !! 0)+      hasPartialSignatures (inputAt 0 partial) `shouldBe` True+      hasPartialSignatures (inputAt 0 unsigned) `shouldBe` False++    -- The first signer of the BIP signs the first input with SIGHASH_ALL,+    -- the last byte 01 of the signature+    it "read the partial signatures of an input: the public key, and the signature with its sighash byte" $ do+      psbt <- psbtOfVector (firstSigner vectors)+      records <- expectRight (partialSignatures ctx (inputAt 0 psbt))+      [(encodeHex (marshal ctx key), encodeHex signature) | (key, signature) <- records] `shouldBe` [(firstKey, firstSignature)]++    it "write a partial signature under the type 0x02 with the public key, and drop them all as a finalizer does" $ do+      let m = insertPartialSignature ctx (publicKey firstKey) (hexBytes firstSignature) (insertSigHashType sigHashAll Map.empty)+      Map.toList m `shouldBe` [(Key 0x02 (hexBytes firstKey), hexBytes firstSignature), (Key 0x03 "", hexBytes "01000000")]+      Map.toList (deletePartialSignatures m) `shouldBe` [(Key 0x03 "", hexBytes "01000000")]++    -- The second input of the updater's PSBT spends a P2SH-P2WSH coin: a+    -- 2-of-2 multisig behind a witness program+    it "read the redeem script and the witness script of an input, and write them under the types 0x04 and 0x05" $ do+      psbt <- psbtOfVector (updater vectors)+      let redeem = Script [OP_0, opPushData (hexBytes "8c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903")]+          witness = Script [OP_2, opPushData (hexBytes "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc"), opPushData (hexBytes "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73"), OP_2, OP_CHECKMULTISIG]+      inputRedeemScript (inputAt 1 psbt) `shouldBe` Right (Just redeem)+      inputWitnessScript (inputAt 1 psbt) `shouldBe` Right (Just witness)+      inputRedeemScript (inputAt 0 psbt) `shouldSatisfy` (\found -> fmap (const ()) found == Right () && found /= Right Nothing)+      Map.toList (insertInputRedeemScript redeem (insertInputWitnessScript witness Map.empty))+        `shouldBe` [ (Key 0x04 "", hexBytes "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"),+                     (Key 0x05 "", hexBytes "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae")+                   ]++    it "read the redeem script and the witness script of an output, and write them under the types 0x00 and 0x01" $ do+      let redeem = Script [OP_0, opPushData (hexBytes "8c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903")]+          witness = Script [OP_1, OP_CHECKMULTISIG]+          m = insertOutputRedeemScript redeem (insertOutputWitnessScript witness Map.empty)+      Map.keys m `shouldBe` [Key 0x00 "", Key 0x01 ""]+      outputRedeemScript m `shouldBe` Right (Just redeem)+      outputWitnessScript m `shouldBe` Right (Just witness)+      outputWitnessScript Map.empty `shouldBe` Right Nothing++    it "read the final script and witness of a signed input" $ do+      psbt <- psbtOfVector (finalizer vectors)+      let first' = inputAt 0 psbt+          second = inputAt 1 psbt+      finalScriptSig first'+        `shouldBe` Just (hexBytes "00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae")+      finalScriptWitness first' `shouldBe` Right Nothing+      finalScriptSig second `shouldBe` Just (hexBytes "2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903")+      finalScriptWitness second+        `shouldBe` Right+          ( Just+              [ "",+                hexBytes "3044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01",+                hexBytes "3044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d201",+                hexBytes "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"+              ]+          )++    it "write a final script under the type 0x07, and a final witness as the number of its items then each after its length" $ do+      Map.toList (insertFinalScriptSig (hexBytes "aabb") Map.empty) `shouldBe` [(Key 0x07 "", hexBytes "aabb")]+      Map.toList (insertFinalScriptWitness [hexBytes "aabb", ""] Map.empty) `shouldBe` [(Key 0x08 "", hexBytes "0202aabb00")]++    it "read the extended public keys of the global map, with their derivations, and write them under the type 0x01" $ do+      psbt <- psbtOfVector (vectors.valid !! 7)+      let xpub = fromJust $ xPubImport btc ctx "xpub6CpGH79LXVkeiux2ZPWMpEubBrRfgcGCgy2HiagyN6NW3qdioJaqFYyD1fG6LDfxWEhMXJqcDuU5VneKt5UQYUGPa5Mfxdw2D2NArwX5TBm"+          record = (xpub, Derivation (fingerprintOf "27569c50") "m/49'/0'/0'")+      xpubRecords btc ctx psbt.globals `shouldBe` Right [record]+      let m = insertXpubRecord btc ctx xpub (snd record) Map.empty+      Map.keys m `shouldBe` [Key 0x01 (marshal (btc, ctx) xpub)]+      xpubRecords btc ctx m `shouldBe` Right [record]++    -- The two keys of the vector are of the test network+    it "leave aside the extended public keys of another network" $ do+      psbt <- psbtOfVector (vectors.valid !! 5)+      let tpub text = fromJust $ xPubImport btcTest ctx text+      xpubRecords btc ctx psbt.globals `shouldBe` Right []+      xpubRecords btcTest ctx psbt.globals+        `shouldBe` Right+          [ (tpub "tpubDBkJeJo2X94Yq3RVz65DoUgyLUkaDrkfyrn2VcgyCRSKCRonvKvCF2FpYDGJWDkdRHBajXJGpc63GnumUt63ySvqCu2XaTRGVTKMYGuFk9H", Derivation (fingerprintOf "d90c6a4f") "m/174'/0'"),+            (tpub "tpubDBkJeJo2X94YsvtBEU1eKoibEWiNv51nW5iHhs6VZp59jsE6nen8KZMFyGHuGbCvqjRqirgeMcfpVBkttpUUT6brm4duzSGoZeTbhqCNUu6", Derivation (fingerprintOf "d90c6a4f") "m/174'/1'")+          ]++    it "write a proprietary key as the type 0xfc, the identifier after its length, the subtype and the data" $ do+      proprietaryKey (Proprietary "test" 1 (hexBytes "ab")) `shouldBe` Key 0xfc (hexBytes "047465737401ab")+      proprietaryOf (Key 0xfc (hexBytes "047465737401ab")) `shouldBe` Right (Just (Proprietary "test" 1 (hexBytes "ab")))+      proprietaryOf (Key 0x20 "") `shouldBe` Right Nothing+      proprietaryOf (Key 0xfc "") `shouldBe` Left "the proprietary key 0xfc has no identifier and subtype"++    it "look a proprietary field up by its identifier and subtype" $ do+      let m = insertProprietary "test" 3 "Rent" Map.empty+      lookupProprietary "test" 3 m `shouldBe` Just "Rent"+      lookupProprietary "test" 2 m `shouldBe` Nothing+      lookupProprietary "other" 3 m `shouldBe` Nothing++{- Signing and finalizing -}++-- | A coin of 2 BTC at the script, spent whole by a transaction paying+-- two outputs, as a PSBT with the coin on its input: the whole previous+-- transaction, or the output alone for a Taproot coin, whose signature+-- commits to the output of every input (BIP 341)+spendingPsbt :: Ctx -> ScriptOutput -> (Psbt, [(ScriptOutput, Word64, OutPoint)])+spendingPsbt ctx coinScript = (Psbt Version0 tx Map.empty [input] [Map.empty, Map.empty], [(coinScript, 200000000, outpoint)])+  where+    parent = Tx 1 [TxIn (OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1) "" 0xffffffff] [TxOut 200000000 (marshal ctx coinScript)] [] 0+    outpoint = OutPoint (txHash parent) 0+    tx =+      Tx+        2+        [TxIn outpoint "" 0xfffffffe]+        [ TxOut 99999699 (hexBytes "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),+          TxOut 100000000 (hexBytes "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787")+        ]+        []+        1257139+    input = case coinScript of+      PayWitness 1 _ -> insertWitnessUtxo (TxOut 200000000 (marshal ctx coinScript)) Map.empty+      _ -> insertNonWitnessUtxo parent Map.empty++-- | A derivation record of a key, which a signer needs: the fingerprint+-- and the path are those of the BIP's first signer+recordOf :: Ctx -> PublicKey -> PsbtMap -> PsbtMap+recordOf ctx public = insertInputDerivation ctx public (Derivation (fingerprintOf "d90c6a4f") "m/0'/0'/0'")++-- | A signer holding the keys+keysSigner :: Ctx -> [PrivateKey] -> Signer+keysSigner ctx keys =+  Signer+    { keyOfRecord = \public derivation -> listToMaybe (mapMaybe (\key -> (keySigner ctx key.key).keyOfRecord public derivation) keys),+      keyOfHash = \h -> listToMaybe (mapMaybe (\key -> (keySigner ctx key.key).keyOfHash h) keys),+      acceptedSigHashes = defaultSigHashes,+      requirePreviousTx = True+    }++-- | Whether the transaction of the PSBT, once signed with the keys and+-- finalized, verifies against the coins it spends+signsAndVerifies :: Network -> Ctx -> [PrivateKey] -> (Psbt, [(ScriptOutput, Word64, OutPoint)]) -> Either String Bool+signsAndVerifies net ctx keys (psbt, coins) = do+  signed <- signPsbt net ctx (keysSigner ctx keys) psbt+  final <- finalizePsbt net ctx signed+  tx <- psbtTransaction final+  return (verifyStdTx net ctx tx coins)++signingSpec :: Ctx -> Spec+signingSpec ctx =+  describe "Signing and finalizing" $ do+    prop "a P2PKH coin, signed with the key of its address and finalized, verifies" $+      forAll (arbitraryKeyPair ctx) $ \(secret, public) ->+        let (psbt, coins) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+            withRecord = withInputs (recordOf ctx public <$> psbt.inputs) psbt+         in signsAndVerifies btc ctx [secret] (withRecord, coins) === Right True++    -- The finalized input keeps its coin, its final script, and the+    -- fields of version 2: the transaction it spends from, the output+    -- index and the sequence+    prop "a P2PKH coin of a PSBT of version 2, signed and finalized, verifies, and the PSBT stays of version 2 with the fields of its input" $+      forAll (arbitraryKeyPair ctx) $ \(secret, public) ->+        let (psbt, coins) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+            version2 = withVersion Version2 (withInputs (recordOf ctx public <$> psbt.inputs) psbt)+            final = finalizePsbt btc ctx =<< signPsbt btc ctx (keysSigner ctx [secret]) version2+         in (signsAndVerifies btc ctx [secret] (version2, coins), (.version) <$> final, Map.keys . inputAt 0 <$> final)+              === (Right True, Right Version2, Right [Key 0x00 "", Key 0x07 "", Key 0x0e "", Key 0x0f "", Key 0x10 ""])++    prop "a P2SH multisig coin, signed with enough of its keys and finalized, verifies" $+      -- At most 7 keys: the redeem script of more uncompressed keys would be+      -- over the 520 bytes a P2SH redeem script can have+      forAll ((,) <$> arbitraryP2SHMSParam <*> arbitraryKeyPair ctx) $ \((m, n), _) ->+        forAll (vectorOf n (arbitraryKeyPair ctx)) $ \pairs ->+          let redeem = either error id $ encodeOutput ctx (PayMulSig (snd <$> pairs) m)+              (psbt, coins) = spendingPsbt ctx (either error id (toP2SH redeem))+              withFields = withInputs ((insertInputRedeemScript redeem . foldr (\(_, public) f -> recordOf ctx public . f) id pairs) <$> psbt.inputs) psbt+           in signsAndVerifies btc ctx (fst <$> take m pairs) (withFields, coins) === Right True++    -- The keys are compressed: a SegWit version 0 coin is not signed with+    -- an uncompressed key, which 'signerScopeSpec' tests+    prop "a P2WPKH coin, and a P2SH-P2WPKH one with its redeem script, signed and finalized, verify" $+      forAll (arbitraryKeyPairC ctx) $ \(secret, public) ->+        let program = PayWitnessPKHash (addressHash (marshal ctx public))+            (native, nativeCoins) = spendingPsbt ctx program+            redeem = either error id $ encodeOutput ctx program+            (nested, nestedCoins) = spendingPsbt ctx (either error id (toP2SH redeem))+            nativeWithRecord = withInputs (recordOf ctx public <$> native.inputs) native+            nestedWithFields = withInputs ((insertInputRedeemScript redeem . recordOf ctx public) <$> nested.inputs) nested+         in signsAndVerifies btc ctx [secret] (nativeWithRecord, nativeCoins) === Right True+              .&&. signsAndVerifies btc ctx [secret] (nestedWithFields, nestedCoins) === Right True++    prop "a P2WSH multisig coin with its witness script, nested in P2SH or not, signed and finalized, verifies" $+      forAll arbitraryMSParam $ \(m, n) ->+        forAll (vectorOf n (arbitraryKeyPairC ctx)) $ \pairs ->+          let witness = either error id $ encodeOutput ctx (PayMulSig (snd <$> pairs) m)+              program = either error id (toP2WSH witness)+              records = foldr (\(_, public) f -> recordOf ctx public . f) id pairs+              (native, nativeCoins) = spendingPsbt ctx program+              (nested, nestedCoins) = spendingPsbt ctx (either error id (toP2SH programScript))+              nativeWithFields = withInputs ((insertInputWitnessScript witness . records) <$> native.inputs) native+              nestedWithFields = withInputs ((insertInputRedeemScript programScript . insertInputWitnessScript witness . records) <$> nested.inputs) nested+              programScript = either error id $ encodeOutput ctx program+           in signsAndVerifies btc ctx (fst <$> take m pairs) (nativeWithFields, nativeCoins) === Right True+                .&&. signsAndVerifies btc ctx (fst <$> take m pairs) (nestedWithFields, nestedCoins) === Right True++    it "signs with the sighash the input asks for when the signer accepts it, SIGHASH_ALL when it asks for none" $ do+      let secret = PrivateKey secretOne True+          public = derivePublicKey ctx secret+          (psbt, _) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+          asking sh = withInputs ((recordOf ctx public . maybe id insertSigHashType sh) <$> psbt.inputs) psbt+          sighashOf signed = map (B.last . snd) <$> partialSignatures ctx (inputAt 0 signed)+          acceptingNone = (keySigner ctx secret.key) {acceptedSigHashes = sigHashNone : defaultSigHashes}+      (sighashOf =<< signPsbt btc ctx (keySigner ctx secret.key) (asking Nothing)) `shouldBe` Right [0x01]+      (sighashOf =<< signPsbt btc ctx (keySigner ctx secret.key) (asking (Just sigHashAll))) `shouldBe` Right [0x01]+      -- SIGHASH_NONE leaves the outputs unsigned, so the signer must agree to it+      signPsbt btc ctx (keySigner ctx secret.key) (asking (Just sigHashNone)) `shouldBe` Left "Input 0: the sighash type 2 is not one the signer accepts"+      (sighashOf =<< signPsbt btc ctx acceptingNone (asking (Just sigHashNone))) `shouldBe` Right [0x02]++    it "leaves an input without its coin, or signed already, as it is" $ do+      let secret = PrivateKey secretOne True+          public = derivePublicKey ctx secret+          (psbt, _) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+          withoutCoin = withInputs [recordOf ctx public Map.empty] psbt+          final = withInputs ((insertFinalScriptSig "a script" . recordOf ctx public) <$> psbt.inputs) psbt+      signPsbt btc ctx (keySigner ctx secret.key) withoutCoin `shouldBe` Right withoutCoin+      signPsbt btc ctx (keySigner ctx secret.key) final `shouldBe` Right final++    it "refuses a previous transaction that is not the one the input spends" $ do+      let public = compressedKey ctx secretOne+          (psbt, _) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+          wrong = withInputs [insertNonWitnessUtxo emptyTx Map.empty] psbt+      signPsbt btc ctx (Signer (\_ _ -> Nothing) (const Nothing) defaultSigHashes True) wrong `shouldBe` Left "Input 0: the previous transaction is not the one the input spends"++    -- The derivation records name keys by the master key, b4a6ba67 with+    -- the full path, or by the extended key given, with the path from it+    it "finds the key an extended private key derives, by its own fingerprint or by the origin given" $ do+      let root = makeXPrvKey "PSBT signer test"+          rootFingerprint = xPubFP ctx (deriveXPubKey ctx root)+          account = derivePath ctx (Deriv :| 444 :: HardPath) root+          accountFingerprint = xPubFP ctx (deriveXPubKey ctx account)+          secretAt path = (derivePath ctx (path :: DerivPath) root).key+          publicOf secret = PublicKey (derivePubKey ctx secret) True+          signer = xPrvSigner ctx account (Just (Derivation rootFingerprint "m/444'"))+          unknownOrigin = xPrvSigner ctx account Nothing+      signer.keyOfRecord (publicOf (secretAt "m/444'/0")) (Derivation rootFingerprint "m/444'/0") `shouldBe` Just (secretAt "m/444'/0")+      signer.keyOfRecord (publicOf (secretAt "m/444'/1")) (Derivation accountFingerprint "m/1") `shouldBe` Just (secretAt "m/444'/1")+      signer.keyOfRecord (publicOf (secretAt "m/444'/1")) (Derivation rootFingerprint "m/445'/1") `shouldBe` Nothing+      signer.keyOfRecord (publicOf (secretAt "m/444'/1")) (Derivation accountFingerprint "m/2") `shouldBe` Nothing+      unknownOrigin.keyOfRecord (publicOf (secretAt "m/444'/0")) (Derivation rootFingerprint "m/444'/0") `shouldBe` Nothing+      unknownOrigin.keyOfRecord (publicOf (secretAt "m/444'/0")) (Derivation accountFingerprint "m/0") `shouldBe` Just (secretAt "m/444'/0")+      -- An extended key cannot know which path a hash is at+      signer.keyOfHash (addressHash (marshal ctx (publicOf (secretAt "m/444'/0")))) `shouldBe` Nothing++{- A PSBT of every kind of coin -}++-- | The PSBTs of a file that scripts/create_complex_psbt_data.sh wrote: a+-- transaction of nine coins of every kind, signed by twelve wallets of+-- Bitcoin Core one at a time, their combination, its finalization and the+-- transaction it gives+data Complex = Complex+  { initialPsbt :: Psbt,+    signedPsbts :: [Psbt],+    combinedPsbt :: Psbt,+    completePsbt :: Psbt,+    finalTx :: Tx+  }++instance Json.FromJSON Complex where+  parseJSON = Json.withObject "complex" $ \o ->+    Complex+      <$> psbtAt o "initial_psbt"+      <*> mapM (psbtAt o) ["miner_psbt", "p2pkh_psbt", "p2sh_ms_1_psbt", "p2sh_ms_2_psbt", "p2sh_pk_psbt", "p2sh_wsh_pk_psbt", "p2sh_wsh_ms_1_psbt", "p2sh_wsh_ms_2_psbt", "p2wpkh_psbt", "p2wsh_pk_psbt", "p2wsh_ms_1_psbt", "p2wsh_ms_2_psbt"]+      <*> psbtAt o "combined_psbt"+      <*> psbtAt o "complete_psbt"+      <*> (either fail return . S.decode . hexBytes =<< o .: "final_tx")+    where+      psbtAt o name = either fail return . decodePsbtBase64 . encodeUtf8 =<< o .: name++complexSpec :: Ctx -> Spec+complexSpec ctx = do+  -- data/complex_psbt.json is the file of an older Bitcoin Core, in+  -- version 0. Each wallet finalized the coins it could complete alone.+  describe "The PSBT of nine coins of every kind, which twelve wallets of Bitcoin Core signed one at a time" $ do+    complex <- runIO (readTestFile "complex_psbt.json" :: IO Complex)+    complexChecks ctx complex++  -- data/psbt_complex_core32.json is the file of Bitcoin Core 32.1, in+  -- version 2. The wallets signed with finalize=false, so that the+  -- combination holds partial signatures only.+  describe "The PSBT of nine coins of every kind, which twelve wallets of Bitcoin Core 32.1 signed without finalizing" $ do+    complex <- runIO (readTestFile "psbt_complex_core32.json" :: IO Complex)+    it "is a version 2 PSBT whose combination has no final input" $ do+      complex.combinedPsbt.version `shouldBe` Version2+      length complex.combinedPsbt.inputs `shouldBe` 9+      -- Types 0x07 and 0x08 are the final script and the final witness+      length [key | input <- complex.combinedPsbt.inputs, key <- Map.keys input, key.keyType `elem` [0x07, 0x08]] `shouldBe` 0+    complexChecks ctx complex++-- | What holds for both files: the library combines, finalizes and+-- extracts what Bitcoin Core combined, finalized and extracted+complexChecks :: Ctx -> Complex -> Spec+complexChecks ctx complex = do+  -- The PSBT Core created has no entry that a signed one lacks+  it "adds nothing to a signed PSBT when the PSBT Core created is combined with it" $+    mapM (combinePsbt complex.initialPsbt) complex.signedPsbts `shouldBe` Right complex.signedPsbts++  it "combines into the PSBT Core combined" $+    case complex.signedPsbts of+      first' : rest -> foldM combinePsbt first' rest `shouldBe` Right complex.combinedPsbt+      [] -> expectationFailure "No signed PSBT"++  it "finalizes into the PSBT Core finalized" $+    finalizePsbt btc ctx complex.combinedPsbt `shouldBe` Right complex.completePsbt++  it "gives the transaction Core extracted" $+    psbtTransaction complex.completePsbt `shouldBe` Right complex.finalTx++{- The files of Bitcoin Core 32.1 -}++-- The files live in data/psbt_core32.json: the PSBT of a payment as+-- walletcreatefundedpsbt gives it in version 0 and in version 2, the+-- version 2 one signed by walletprocesspsbt without finalizing, then+-- finalized, and the transaction finalizepsbt extracts+data Core32 = Core32+  { preparedV0 :: Psbt,+    preparedV2 :: Psbt,+    signedV2 :: Psbt,+    finalizedV2 :: Psbt,+    extractedTx :: Tx+  }++instance Json.FromJSON Core32 where+  parseJSON = Json.withObject "core32" $ \o ->+    Core32+      <$> psbtAt o "prepared_v0"+      <*> psbtAt o "prepared_v2"+      <*> psbtAt o "signed_v2"+      <*> psbtAt o "finalized_v2"+      <*> (either fail return . S.decode . hexBytes =<< o .: "final_tx")+    where+      psbtAt o name = either fail return . decodePsbtBase64 . encodeUtf8 =<< o .: name++core32Spec :: Ctx -> Spec+core32Spec ctx =+  describe "The files of Bitcoin Core 32.1, which writes version 2 unless asked for version 0" $ do+    core <- runIO (readTestFile "psbt_core32.json" :: IO Core32)+    it "are the same payment in both versions: the version 2 file is the version 0 file converted, and back" $ do+      core.preparedV2.version `shouldBe` Version2+      core.preparedV2.unsignedTx `shouldBe` core.preparedV0.unsignedTx+      withVersion Version2 core.preparedV0 `shouldBe` core.preparedV2+      withVersion Version0 core.preparedV2 `shouldBe` core.preparedV0++    it "finalize into the PSBT Core finalized" $+      finalizePsbt btc ctx core.signedV2 `shouldBe` Right core.finalizedV2++    it "give the transaction Core extracted" $+      psbtTransaction core.finalizedV2 `shouldBe` Right core.extractedTx++    -- The records name the key of the coin by the fingerprint bc560f95 of+    -- a seed and the path m/44'/0'/0'/0/0: the account key at m/44'/0'/0'+    -- of that seed signs. The coin is 50 BTC at the P2PKH script of that+    -- key.+    it "are signed with the account key the records name, into a transaction that verifies" $ do+      let account = fromJust (xPrvImport btc "xprv9yHxeaLAZvxXb9VtJNesqk8avfN8misGAW9DUW9eacZJNqsfZxqKLmK5jfmvFideQqGesviJeagzSQYCuQySjgvt7TdfowKja5aJqbgyuNh")+          signer = xPrvSigner ctx account (Just (Derivation (fingerprintOf "bc560f95") "m/44'/0'/0'"))+      coin <- expectRight (unmarshal ctx (hexBytes "76a914c80d55223e2f1061363add2a7b0363c285bf298d88ac"))+      final <- expectRight (finalizePsbt btc ctx =<< signPsbt btc ctx signer core.preparedV2)+      tx <- expectRight (psbtTransaction final)+      final.version `shouldBe` Version2+      verifyStdTx btc ctx tx [(coin, 5000000000, OutPoint "fede72ccadbf9f16f826e3078578b0660526a37b48d4f894ab26f4cf5a95e89c" 0)] `shouldBe` True++{- The checks of the signer and the finalizer -}++signerChecksSpec :: Ctx -> Spec+signerChecksSpec ctx =+  describe "The checks of the signer and the finalizer" $ do+    let secret = PrivateKey secretOne True+        public = derivePublicKey ctx secret+        otherPublic = compressedKey ctx secretTwo+        (psbt, coins) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+        parent = fromJust (either (const Nothing) id (nonWitnessUtxo (inputAt 0 psbt)))+        coin = head parent.outputs+        withInput m = withInputs [m] psbt+        signer = keySigner ctx secret.key+        signed m = signPsbt btc ctx signer (withInput (recordOf ctx public m))+        signatureCount s = fmap length (partialSignatures ctx (inputAt 0 s))++    it "refuses to sign a legacy coin known from its witness output only, which only a witness signature may use" $+      signed (insertWitnessUtxo coin Map.empty)+        `shouldBe` Left "Input 0: the coin is given as a witness output only, which may not be signed with a non-witness signature"++    it "takes the coin from the previous transaction first, and refuses a witness output that is not that output" $ do+      (signatureCount =<< signed (insertWitnessUtxo coin (insertNonWitnessUtxo parent Map.empty))) `shouldBe` Right 1+      signed (insertWitnessUtxo (TxOut 1 coin.script) (insertNonWitnessUtxo parent Map.empty))+        `shouldBe` Left "Input 0: the witness output of the input is not the output of its previous transaction"++    it "refuses a sighash type no node accepts, and one whose fork id flag does not fit the network" $ do+      signed (insertSigHashType 0 (insertNonWitnessUtxo parent Map.empty)) `shouldBe` Left "Input 0: the sighash type 0 is not one a node accepts"+      signed (insertSigHashType 0x21 (insertNonWitnessUtxo parent Map.empty)) `shouldBe` Left "Input 0: the sighash type 33 is not one a node accepts"+      signed (insertSigHashType (setForkIdFlag sigHashAll) (insertNonWitnessUtxo parent Map.empty)) `shouldBe` Left "Input 0: the sighash type has a fork id flag the network does not use"+      signPsbt bch ctx signer (withInput (recordOf ctx public (insertSigHashType sigHashAll (insertNonWitnessUtxo parent Map.empty))))+        `shouldBe` Left "Input 0: the sighash type lacks the fork id flag the network requires"+      -- With the flag, the Bitcoin Cash digest signs+      (signatureCount =<< signPsbt bch ctx signer (withInput (recordOf ctx public (insertSigHashType (setForkIdFlag sigHashAll) (insertNonWitnessUtxo parent Map.empty))))) `shouldBe` Right 1++    it "refuses a sighash type the signer does not accept, and signs with it once the signer lists it" $ do+      let acceptingSingle = signer {acceptedSigHashes = [sigHashSingle]}+          askingSingle = insertSigHashType sigHashSingle (insertNonWitnessUtxo parent Map.empty)+      signed askingSingle `shouldBe` Left "Input 0: the sighash type 3 is not one the signer accepts"+      (signatureCount =<< signPsbt btc ctx acceptingSingle (withInput (recordOf ctx public askingSingle))) `shouldBe` Right 1+      -- ANYONECANPAY makes another type: 0x81 is not 0x01+      signed (insertSigHashType (setAnyoneCanPay sigHashAll) (insertNonWitnessUtxo parent Map.empty)) `shouldBe` Left "Input 0: the sighash type 129 is not one the signer accepts"++    -- Two coins of the same script, spent by one transaction with one+    -- output: input 1 has no output at its index+    it "refuses SIGHASH_SINGLE on a legacy coin whose input has no output at its index, and signs it on a SegWit coin" $ do+      let witnessCoin = TxOut 200000000 (marshal ctx (PayWitnessPKHash (addressHash (marshal ctx public))))+          parentOf c = Tx 1 [TxIn (OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1) "" 0xffffffff] [c, c] [] 0+          spendingBoth p = Tx 2 [TxIn (OutPoint (txHash p) 0) "" 0xfffffffe, TxIn (OutPoint (txHash p) 1) "" 0xfffffffe] [TxOut 399999000 (hexBytes "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac")] [] 0+          askingSingle p = recordOf ctx public (insertSigHashType sigHashSingle (insertNonWitnessUtxo p Map.empty))+          psbtOf p = Psbt Version0 (spendingBoth p) Map.empty [askingSingle p, askingSingle p] [Map.empty]+          acceptingSingle = signer {acceptedSigHashes = [sigHashSingle]}+      signPsbt btc ctx acceptingSingle (psbtOf (parentOf coin)) `shouldBe` Left "Input 1: makeSignature: SIGHASH_SINGLE without an output at the input index"+      -- The BIP 143 digest commits to the coin, so the SegWit signature is fine+      (signatureCount =<< signPsbt btc ctx acceptingSingle (psbtOf (parentOf witnessCoin))) `shouldBe` Right 1+      (fmap length . partialSignatures ctx . inputAt 1 =<< signPsbt btc ctx acceptingSingle (psbtOf (parentOf witnessCoin))) `shouldBe` Right 1++    it "refuses a redeem script that does not hash to the coin, and one on a coin that is not pay-to-script-hash" $ do+      let ms = PayMulSig [public, otherPublic] 1+          msScript = either error id (encodeOutput ctx ms)+          wrongScript = either error id (encodeOutput ctx (PayMulSig [otherPublic, public] 1))+          (p2sh, _) = spendingPsbt ctx (either error id (toP2SH msScript))+          p2shParent = fromJust (either (const Nothing) id (nonWitnessUtxo (inputAt 0 p2sh)))+          p2shInput m = withInputs [recordOf ctx public (insertNonWitnessUtxo p2shParent m)] p2sh+      (signatureCount =<< signPsbt btc ctx signer (p2shInput (insertInputRedeemScript msScript Map.empty))) `shouldBe` Right 1+      signPsbt btc ctx signer (p2shInput (insertInputRedeemScript wrongScript Map.empty))+        `shouldBe` Left "Input 0: the redeem script of the input does not hash to its coin"+      signed (insertInputRedeemScript msScript (insertNonWitnessUtxo parent Map.empty))+        `shouldBe` Left "Input 0: the input has a redeem script, but its coin is not pay-to-script-hash"++    it "refuses a witness script that does not hash to the witness program" $ do+      let ms = PayMulSig [public, otherPublic] 1+          msScript = either error id (encodeOutput ctx ms)+          wrongScript = either error id (encodeOutput ctx (PayMulSig [otherPublic, public] 1))+          (p2wsh, _) = spendingPsbt ctx (either error id (toP2WSH msScript))+          p2wshInput m = withInputs [recordOf ctx public (Map.union m (inputAt 0 p2wsh))] p2wsh+      (signatureCount =<< signPsbt btc ctx signer (p2wshInput (insertInputWitnessScript msScript Map.empty))) `shouldBe` Right 1+      signPsbt btc ctx signer (p2wshInput (insertInputWitnessScript wrongScript Map.empty))+        `shouldBe` Left "Input 0: the witness script of the input does not hash to its witness program"+      signed (insertInputWitnessScript msScript (insertNonWitnessUtxo parent Map.empty))+        `shouldBe` Left "Input 0: the input has a witness script, but its coin is not pay-to-witness-script-hash"++    it "leaves an input whose coin script is not standard as it is, and signs the others" $ do+      let weird = TxOut 1000 (runPutS (serialize (Script [OP_1])))+          parent2 = Tx 1 parent.inputs [coin, weird] [] 0+          op0 = OutPoint (txHash parent2) 0+          op1 = OutPoint (txHash parent2) 1+          tx = Tx 2 [TxIn op0 "" 0xfffffffe, TxIn op1 "" 0xfffffffe] psbt.unsignedTx.outputs [] 0+          both = Psbt Version0 tx Map.empty [recordOf ctx public (insertNonWitnessUtxo parent2 Map.empty), insertNonWitnessUtxo parent2 Map.empty] [Map.empty, Map.empty]+      s <- expectRight (signPsbt btc ctx signer both)+      map hasPartialSignatures s.inputs `shouldBe` [True, False]+      f <- expectRight (finalizePsbt btc ctx s)+      map (isJust . finalScriptSig) f.inputs `shouldBe` [True, False]++    it "signs a coin whose key it holds without a derivation record, by the hash of the key" $ do+      (signatureCount =<< signPsbt btc ctx signer (withInput (insertNonWitnessUtxo parent Map.empty))) `shouldBe` Right 1+      -- A multisig coin names its keys+      let ms = PayMulSig [public, otherPublic] 1+          msScript = either error id (encodeOutput ctx ms)+          (p2sh, p2shCoins) = spendingPsbt ctx (either error id (toP2SH msScript))+          p2shParent = fromJust (either (const Nothing) id (nonWitnessUtxo (inputAt 0 p2sh)))+          p2shFields = withInputs [insertInputRedeemScript msScript (insertNonWitnessUtxo p2shParent Map.empty)] p2sh+      s <- expectRight (signPsbt btc ctx signer p2shFields)+      signatureCount s `shouldBe` Right 1+      signsAndVerifies btc ctx [secret] (p2shFields, p2shCoins) `shouldBe` Right True+      -- The other key is not held+      (signatureCount =<< signPsbt btc ctx (keySigner ctx secret.key) (fst (spendingPsbt ctx (PayPKHash (addressHash (marshal ctx otherPublic)))))) `shouldBe` Right 0+      coins `seq` return ()++    it "the finalizer refuses a signature that is not a signature, one with another sighash type than the input asks, and one that does not verify" $ do+      s <- expectRight (signed (insertNonWitnessUtxo parent Map.empty))+      signature <- case either error id (partialSignatures ctx (inputAt 0 s)) of+        [(_, sig)] -> return sig+        _ -> fail "one signature"+      let withSig sig m = withInput (insertPartialSignature ctx public sig m)+          keyHex = show (encodeHex (marshal ctx public))+      finalizePsbt btc ctx (withSig "3045aa" (insertNonWitnessUtxo parent Map.empty))+        `shouldBe` Left ("Input 0: the signature of the key " <> keyHex <> " is not a valid signature")+      finalizePsbt btc ctx (withSig signature (insertSigHashType sigHashNone (insertNonWitnessUtxo parent Map.empty)))+        `shouldBe` Left ("Input 0: the signature of the key " <> keyHex <> " does not use the sighash type of the input")+      -- The signature of the same key on another transaction+      let sameKeyOther = withInputs [recordOf ctx public (insertNonWitnessUtxo parent Map.empty)] (Psbt Version0 (Tx 2 psbt.unsignedTx.inputs [] [] 0) Map.empty psbt.inputs [])+      elsewhere <- expectRight (signPsbt btc ctx signer sameKeyOther)+      foreignSig <- case either error id (partialSignatures ctx (inputAt 0 elsewhere)) of+        [(_, sig)] -> return sig+        _ -> fail "one signature"+      finalizePsbt btc ctx (withSig foreignSig (insertNonWitnessUtxo parent Map.empty))+        `shouldBe` Left ("Input 0: the signature of the key " <> keyHex <> " does not verify")+      -- The right signature finalizes+      f <- expectRight (finalizePsbt btc ctx (withSig signature (insertNonWitnessUtxo parent Map.empty)))+      isJust (finalScriptSig (inputAt 0 f)) `shouldBe` True++{- Taproot -}++data Vectors371 = Vectors371+  { refused371 :: [Vector],+    accepted371 :: [Vector]+  }++instance Json.FromJSON Vectors371 where+  parseJSON = Json.withObject "vectors" $ \o -> Vectors371 <$> o .: "invalid" <*> o .: "valid"++-- | Why each invalid PSBT of BIP 371 is refused, as the end of the reason+refusals371 :: [String]+refusals371 =+  [ "Input 0: the value of the key 0x17 is not an x-only public key",+    "Input 0: the value of the key 0x13 is not a Taproot signature: decodeTaprootSig: a signature is 64 or 65 bytes, not 63",+    "Input 0: the value of the key 0x13 is not a Taproot signature: decodeTaprootSig: a signature is 64 or 65 bytes, not 66",+    "is not an x-only public key",+    "Output 1: the value of the key 0x05 is not an x-only public key",+    "is not an x-only public key",+    "is not an x-only public key and a leaf hash",+    "is not a Taproot signature: decodeTaprootSig: a signature is 64 or 65 bytes, not 66",+    "is not a Taproot signature: decodeTaprootSig: a signature is 64 or 65 bytes, not 63",+    "is not a control block",+    "is not a control block"+  ]++taprootSpec :: Ctx -> Vectors371 -> Spec+taprootSpec ctx vectors =+  describe "The Taproot fields of BIP 371" $ do+    describe "the invalid PSBTs are refused, each for its reason" $+      forM_ (zip vectors.refused371 refusals371) $ \(v, reason) ->+        it v.vecCase $ decodePsbt (bytesOf v) `shouldSatisfy` either (reason `isSuffixOf`) (const False)++    describe "the valid PSBTs are read, and written back byte for byte" $+      forM_ vectors.accepted371 $ \v ->+        it v.vecCase $ (encodePsbt <$> decodePsbt (bytesOf v)) `shouldBe` Right (bytesOf v)++    let vectorAt i = psbtOfVector (vectors.accepted371 !! i)+        xOnly text = fromJust (importXOnlyPubKey ctx (hexBytes text))+        digest text = fromJust (digestFromByteString (hexBytes text)) :: Digest SHA256+        keyScript text = Script [opPushData (hexBytes text), OP_CHECKSIG]++    it "read the internal key and the derivation of a key path input" $ do+      psbt <- vectorAt 0+      inputTapInternalKey ctx (inputAt 0 psbt) `shouldBe` Right (Just (xOnly "fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232"))+      inputTapDerivations ctx (inputAt 0 psbt) `shouldBe` Right [(xOnly "fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232", TapDerivation [] (Derivation (fingerprintOf "772b2da7") "m/86'/1'/0'/1/0"))]+      inputTapMerkleRoot (inputAt 0 psbt) `shouldBe` Right Nothing+      tapKeySig (inputAt 0 psbt) `shouldBe` Right Nothing++    it "read the key path signature of a signed input, and finalize it into a witness of that signature" $ do+      psbt <- vectorAt 1+      let sigHex = "bb53ec917bad9d906af1ba87181c48b86ace5aae2b53605a725ca74625631476fc6f5baedaf4f2ee0f477f36f58f3970d5b8273b7e497b97af2e3f125c97af34"+          sig = fromJust (schnorrSig (hexBytes sigHex))+      tapKeySig (inputAt 0 psbt) `shouldBe` (Just <$> taprootSignature sig sigHashDefault)+      final <- expectRight (finalizePsbt btc ctx psbt)+      finalScriptWitness (inputAt 0 final) `shouldBe` Right (Just [hexBytes sigHex])+      Map.keys (inputAt 0 final) `shouldBe` [Key 0x01 "", Key 0x08 ""]+      tx <- expectRight (psbtTransaction final)+      let coin = TxOut 5000000000 (hexBytes "51205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757")+      verifyStdTx btc ctx tx [(either error id (unmarshal ctx coin.script), coin.value, (head tx.inputs).outpoint)] `shouldBe` True++    it "read the internal key and the derivation of a key path output" $ do+      psbt <- vectorAt 2+      outputTapInternalKey ctx (outputAt 0 psbt) `shouldBe` Right (Just (xOnly "1124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e671"))+      outputTapDerivations ctx (outputAt 0 psbt) `shouldBe` Right [(xOnly "1124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e671", TapDerivation [] (Derivation (fingerprintOf "772b2da7") "m/86'/1'/0'/0/5"))]+      outputTapTree (outputAt 0 psbt) `shouldBe` Right Nothing++    it "read the leaf scripts, the derivations with their leaves, the internal key and the Merkle root of a script path input" $ do+      psbt <- vectorAt 3+      leaves <- expectRight (tapLeafScripts ctx (inputAt 0 psbt))+      map (.script) leaves+        `shouldBe` [ keyScript "2cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2",+                     keyScript "4320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2",+                     keyScript "fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9"+                   ]+      map (.leafVersion) leaves `shouldBe` [0xc0, 0xc0, 0xc0]+      map (B.length . (.controlBlock)) leaves `shouldBe` [97, 65, 97]+      inputTapInternalKey ctx (inputAt 0 psbt) `shouldBe` Right (Just (xOnly "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"))+      inputTapMerkleRoot (inputAt 0 psbt) `shouldBe` Right (Just (digest "f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65"))+      records <- expectRight (inputTapDerivations ctx (inputAt 0 psbt))+      lookup (xOnly "2cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2") records+        `shouldBe` Just (TapDerivation [digest "cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09"] (Derivation (fingerprintOf "772b2da7") "m/86'/1'/2'/0/0"))+      lookup (xOnly "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0") records+        `shouldBe` Just (TapDerivation [] (Derivation (fingerprintOf "7c461e5d") "m"))+      -- The leaf hash of the record is the hash of the leaf script+      leafHash 0xc0 (keyScript "2cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2") `shouldBe` digest "cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09"+      -- The control blocks commit the leaves to the output key, with the Merkle root+      let outKey = fromJust (importXOnlyPubKey ctx (hexBytes "c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b692"))+      forM_ leaves $ \leaf -> do+        (v, internal, proof) <- expectRight (decodeControlBlock ctx leaf.controlBlock)+        verifyScriptPathData ctx outKey (ScriptPathData Nothing [] leaf.script (odd v) 0xc0 internal proof Nothing) `shouldBe` True++    it "read the script tree of a script path output, whose leaves build the tree of its output key" $ do+      psbt <- vectorAt 4+      let leaves =+            [ TapLeaf 2 0xc0 (keyScript "736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02"),+              TapLeaf 2 0xc0 (keyScript "631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969"),+              TapLeaf 1 0xc0 (keyScript "44faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c4273")+            ]+      outputTapTree (outputAt 0 psbt) `shouldBe` Right (Just leaves)+      outputTapInternalKey ctx (outputAt 0 psbt) `shouldBe` Right (Just (xOnly "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"))+      let tree = fromJust (tapTreeFromLeaves leaves)+          output = TaprootOutput (xOnly "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0") (Just tree)+      marshal ctx (taprootScriptOutput ctx output) `shouldBe` (head psbt.unsignedTx.outputs).script+      Map.lookup (Key 0x06 "") (insertOutputTapTree leaves Map.empty) `shouldBe` Map.lookup (Key 0x06 "") (outputAt 0 psbt)+      records <- expectRight (outputTapDerivations ctx (outputAt 0 psbt))+      length records `shouldBe` 4++    it "read the script path signatures of a signed input, and finalize it with the first leaf that has one" $ do+      psbt <- vectorAt 5+      sigs <- expectRight (tapScriptSigs ctx (inputAt 0 psbt))+      map (\((k, h), s) -> (exportXOnlyPubKey ctx k, h, B.length (encodeTaprootSig s))) sigs+        `shouldBe` [ (hexBytes "2cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2", digest "cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09", 64),+                     (hexBytes "4320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2", digest "115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8", 64),+                     (hexBytes "fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9", digest "6f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970", 64)+                   ]+      final <- expectRight (finalizePsbt btc ctx psbt)+      witness <- expectRight (finalScriptWitness (inputAt 0 final))+      fmap (map B.length) witness `shouldBe` Just [64, 34, 97]+      tx <- expectRight (psbtTransaction final)+      let coin = TxOut 5000000000 (hexBytes "5120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b692")+      verifyStdTx btc ctx tx [(either error id (unmarshal ctx coin.script), coin.value, (head tx.inputs).outpoint)] `shouldBe` True++    it "write the fields back as they were read" $+      forM_ [0 .. 5] $ \i -> do+        psbt <- vectorAt i+        forM_ psbt.inputs $ \m -> do+          key <- expectRight (inputTapInternalKey ctx m)+          forM_ key $ \k -> Map.lookup (Key 0x17 "") (insertInputTapInternalKey ctx k Map.empty) `shouldBe` Map.lookup (Key 0x17 "") m+          root <- expectRight (inputTapMerkleRoot m)+          forM_ root $ \r -> Map.lookup (Key 0x18 "") (insertInputTapMerkleRoot r Map.empty) `shouldBe` Map.lookup (Key 0x18 "") m+          sig <- expectRight (tapKeySig m)+          forM_ sig $ \s -> Map.lookup (Key 0x13 "") (insertTapKeySig s Map.empty) `shouldBe` Map.lookup (Key 0x13 "") m+          leaves <- expectRight (tapLeafScripts ctx m)+          Map.toList (foldr insertTapLeafScript Map.empty leaves) `shouldBe` [(k, v) | (k, v) <- Map.toList m, k.keyType == 0x15]+          records <- expectRight (inputTapDerivations ctx m)+          Map.toList (foldr (uncurry (insertInputTapDerivation ctx)) Map.empty records) `shouldBe` [(k, v) | (k, v) <- Map.toList m, k.keyType == 0x16]+          sigs <- expectRight (tapScriptSigs ctx m)+          Map.toList (foldr (\((k, h), s) -> insertTapScriptSig ctx k h s) Map.empty sigs) `shouldBe` [(k, v) | (k, v) <- Map.toList m, k.keyType == 0x14]+        forM_ psbt.outputs $ \m -> do+          key <- expectRight (outputTapInternalKey ctx m)+          forM_ key $ \k -> Map.lookup (Key 0x05 "") (insertOutputTapInternalKey ctx k Map.empty) `shouldBe` Map.lookup (Key 0x05 "") m+          records <- expectRight (outputTapDerivations ctx m)+          Map.toList (foldr (uncurry (insertOutputTapDerivation ctx)) Map.empty records) `shouldBe` [(k, v) | (k, v) <- Map.toList m, k.keyType == 0x07]++    prop "a Taproot coin without script tree, signed by its key path with the internal key and finalized, verifies" $+      forAll (arbitraryKeyPair ctx) $ \(secret, public) ->+        let coinScript = taprootScriptOutput ctx (TaprootOutput public.point Nothing)+            (psbt, coins) = spendingPsbt ctx coinScript+            fields = insertInputTapInternalKey ctx public.point . insertInputTapDerivation ctx public.point (TapDerivation [] (Derivation (fingerprintOf "d90c6a4f") "m/86'/0'/0'/0/0"))+            withFields = withInputs (fields <$> psbt.inputs) psbt+            signed = signPsbt btc ctx (keySigner ctx secret.key) withFields+         in (signsAndVerifies btc ctx [secret] (withFields, coins), fmap (fmap (fmap (.hash)) . tapKeySig . inputAt 0) signed) === (Right True, Right (Right (Just sigHashDefault)))++    prop "a Taproot coin with a leaf that checks one key, signed by that leaf and finalized, verifies" $+      forAll ((,) <$> arbitraryKeyPair ctx <*> arbitraryKeyPair ctx) $ \((secret, public), (_, internal)) ->+        let leaf = Script [opPushData (exportXOnlyPubKey ctx public.point), OP_CHECKSIG]+            tree = MASTBranch (MASTLeaf 0xc0 leaf) (MASTLeaf 0xc0 (Script [OP_RETURN]))+            output = TaprootOutput internal.point (Just tree)+            coinScript = taprootScriptOutput ctx output+            (psbt, coins) = spendingPsbt ctx coinScript+            proof = case getMerkleProofs tree of+              (_, _, p) : _ -> p+              [] -> []+            control = encodeControlBlock ctx (0xc0 + keyParity ctx (taprootOutputKey ctx output)) internal.point proof+            fields =+              insertInputTapInternalKey ctx internal.point+                . insertInputTapMerkleRoot (mastCommitment tree)+                . insertTapLeafScript (TapLeafScript control leaf 0xc0)+                . insertInputTapDerivation ctx public.point (TapDerivation [leafHash 0xc0 leaf] (Derivation (fingerprintOf "d90c6a4f") "m/86'/0'/0'/0/1"))+            withFields = withInputs (fields <$> psbt.inputs) psbt+         in signsAndVerifies btc ctx [secret] (withFields, coins) === Right True++    it "the signer refuses a sighash type a Taproot signature may not use, and an internal key that does not give the coin" $ do+      let secret = PrivateKey secretOne True+          public = derivePublicKey ctx secret+          other = compressedKey ctx secretTwo+          coinScript = taprootScriptOutput ctx (TaprootOutput public.point Nothing)+          (psbt, _) = spendingPsbt ctx coinScript+          withFields m = withInputs [m (insertInputTapInternalKey ctx public.point (inputAt 0 psbt))] psbt+      signPsbt btc ctx (keySigner ctx secret.key) (withFields (insertSigHashType (setForkIdFlag sigHashAll))) `shouldBe` Left "Input 0: the sighash type 65 is not one a Taproot signature may use"+      signPsbt btc ctx (keySigner ctx secret.key) (withInputs [insertInputTapInternalKey ctx other.point (inputAt 0 psbt)] psbt) `shouldBe` Left "Input 0: the internal key and the Merkle root do not give the output key of the coin"+      -- Without the internal key, the input is left alone+      signPsbt btc ctx (keySigner ctx secret.key) psbt `shouldBe` Right psbt++{- Properties -}++propertiesSpec :: Ctx -> Spec+propertiesSpec ctx =+  describe "Any PSBT" $ do+    prop "is read back as it was written" $+      forAll (arbitraryPsbt btc ctx) $ \psbt -> decodePsbt (encodePsbt psbt) == Right psbt++    prop "is read back from its base64 text" $+      forAll (arbitraryPsbt btc ctx) $ \psbt -> decodePsbtBase64 (encodePsbtBase64 psbt) == Right psbt++    prop "combined with itself is itself" $+      forAll (arbitraryPsbt btc ctx) $ \psbt -> combinePsbt psbt psbt == Right psbt++    -- The combiner of BIP 174 keeps every entry that either PSBT has. The+    -- key type 0x7e is one no map knows.+    prop "combined with a copy that has one more entry in every map, in either order, is that copy" $+      forAll (arbitraryPsbt btc ctx) $ \psbt ->+        let oneMore = Map.insert (Key 0x7e "combiner test") "one more entry"+            copy = Psbt psbt.version psbt.unsignedTx (oneMore psbt.globals) (oneMore <$> psbt.inputs) (oneMore <$> psbt.outputs)+         in combinePsbt psbt copy == Right copy && combinePsbt copy psbt == Right copy++{- What the decoder refuses -}++-- | The unsigned transaction of 'tinyPsbt': version 2, one input that+-- spends output 0 of the transaction whose id bytes are 00 01 … 1f, one+-- output of 1,000 satoshi, lock time 0. It is 82 bytes long.+tinyTx :: Text+tinyTx = "0200000001000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f0000000000ffffffff01e803000000000000160014111111111111111111111111111111111111111100000000"++-- | A PSBT of version 0 over 'tinyTx', with the entries given, in hex, in+-- its global map, in the map of its input and in the map of its output.+-- An entry is a key after its length, then a value after its length. The+-- first entry is the transaction: 0x52 is its 82 bytes.+tinyPsbt :: Text -> Text -> Text -> ByteString+tinyPsbt globalEntries inputEntries outputEntries =+  hexBytes ("70736274ff" <> "010052" <> tinyTx <> globalEntries <> "00" <> inputEntries <> "00" <> outputEntries <> "00")++-- | The same payment as 'tinyPsbt' in version 2, with the entries given in+-- the map of its input: the transaction version 2, one input, one output+-- and the PSBT version 2 in the global map, the transaction id and the+-- output index 0 in the input, the amount and the script in the output.+tinyPsbt2 :: Text -> ByteString+tinyPsbt2 inputEntries =+  hexBytes $+    "70736274ff"+      <> ("01020402000000" <> "01040101" <> "01050101" <> "01fb0402000000" <> "00")+      <> (inputEntries <> "010e20000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" <> "010f0400000000" <> "00")+      <> ("010308e803000000000000" <> "01041600141111111111111111111111111111111111111111" <> "00")++-- | 33 bytes that start like a compressed public key and are no point of+-- the curve: the x coordinate ff…ff is above the field size+notAPoint :: Text+notAPoint = "02" <> T.replicate 32 "ff"++-- | A public key of the BIP 174 vectors+aPoint :: Text+aPoint = "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f"++-- | A transaction of 61 bytes that the input of 'tinyTx' does not spend:+-- one input, one output of 5,000 satoshi with the script OP_1+anotherTx :: Text+anotherTx = "0100000001" <> T.replicate 32 "00" <> "ffffffff" <> "00" <> "ffffffff" <> "01" <> "8813000000000000" <> "0151" <> "00000000"++-- | A leaf of a script tree as PSBT_OUT_TAP_TREE lists it: its depth, its+-- leaf version, and after its length of 34 bytes the script+-- @<32 bytes of 0x22> OP_CHECKSIG@. It is 37 bytes long.+treeLeaf :: Text -> Text -> Text+treeLeaf depth version = depth <> version <> "22" <> "20" <> T.replicate 32 "22" <> "ac"++decoderChecksSpec :: Spec+decoderChecksSpec =+  describe "What the decoder refuses, as Bitcoin Core 32.1 does" $ do+    it "reads the small PSBT that the cases below add one entry to, in both versions" $ do+      (encodePsbt <$> decodePsbt (tinyPsbt "" "" "")) `shouldBe` Right (tinyPsbt "" "" "")+      (encodePsbt <$> decodePsbt (tinyPsbt2 "")) `shouldBe` Right (tinyPsbt2 "")++    -- The type of a key is a compact size integer. fd0000 is 0, and fd0200+    -- is 2, over three bytes where one is enough.+    it "refuses a key whose type is not in its shortest form" $ do+      decodePsbt (tinyPsbt "03fd000001aa" "" "") `shouldBe` Left "The global map has a key whose type is not in its shortest form"+      decodePsbt (tinyPsbt "" "03fd020001aa" "") `shouldBe` Left "Input 0 has a key whose type is not in its shortest form"+      decodePsbt (tinyPsbt "" "" "09ff010000000000000001aa") `shouldBe` Left "Output 0 has a key whose type is not in its shortest form"++    -- fe announces a type of four more bytes, and the key has one more+    it "refuses a key that ends inside its type" $+      decodePsbt (tinyPsbt "" "02fe0101aa" "") `shouldBe` Left "Input 0 has a key that ends inside its type"++    -- Bitcoin Core reads no compact size above 0x02000000. fe00000003 is+    -- 0x03000000, and ff0000000001000000 is 2^32.+    it "refuses a key whose type is above the largest compact size Bitcoin Core reads" $ do+      decodePsbt (tinyPsbt "" "05fe0000000301aa" "") `shouldBe` Left "Input 0 has a key whose type is beyond any type"+      decodePsbt (tinyPsbt "" "09ff000000000100000001aa" "") `shouldBe` Left "Input 0 has a key whose type is beyond any type"++    -- fdfd00 is the type 253 and fe00000002 the type 0x02000000, both in+    -- their shortest form+    it "reads a key whose type takes three or five bytes, and writes it back" $ do+      (encodePsbt <$> decodePsbt (tinyPsbt "" "03fdfd0001aa" "")) `shouldBe` Right (tinyPsbt "" "03fdfd0001aa" "")+      (encodePsbt <$> decodePsbt (tinyPsbt "" "05fe0000000201aa" "")) `shouldBe` Right (tinyPsbt "" "05fe0000000201aa" "")++    -- The key is the type 0x02 and 33 bytes, 34 in all (0x22). The value is+    -- a signature of nine bytes, the smallest BIP 66 allows, with SIGHASH_ALL.+    it "refuses a partial signature under a key that is not a point of the curve" $+      decodePsbt (tinyPsbt "" ("2202" <> notAPoint <> "09300602010102010101") "")+        `shouldBe` Left ("Input 0: the key 0x02 with the data " <> T.unpack notAPoint <> " is not a public key")++    it "reads a partial signature that is in the encoding of BIP 66 with a defined sighash byte" $+      (encodePsbt <$> decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "09300602010102010101") ""))+        `shouldBe` Right (tinyPsbt "" ("2202" <> aPoint <> "09300602010102010101") "")++    -- "garbage" in ASCII, then no value at all, then the signature above+    -- with the sighash byte 0x04, which no node accepts+    it "refuses a partial signature that is not in the encoding of BIP 66, or whose sighash byte is not defined" $ do+      let refusal = Left ("Input 0: the value of the key 0x02 with the data " <> T.unpack aPoint <> " is not a signature in the encoding of BIP 66 with a defined sighash byte")+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "0767617262616765") "") `shouldBe` refusal+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "00") "") `shouldBe` refusal+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "09300602010102010104") "") `shouldBe` refusal++    -- Each value ends with the sighash byte 0x01, which is defined, and+    -- breaks one rule of BIP 66: bytes that are no DER at all, a length+    -- byte that does not cover the signature (0x07 for 6 bytes), an R that+    -- is negative (0x80), and an R with a zero byte it does not need (0001)+    it "refuses a partial signature with a defined sighash byte that breaks a rule of BIP 66" $ do+      let refusal = Left ("Input 0: the value of the key 0x02 with the data " <> T.unpack aPoint <> " is not a signature in the encoding of BIP 66 with a defined sighash byte")+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "0aaabbccddeeff00112201") "") `shouldBe` refusal+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "09300702010102010101") "") `shouldBe` refusal+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "09300602018002010101") "") `shouldBe` refusal+      decodePsbt (tinyPsbt "" ("2202" <> aPoint <> "0a30070202000102010101") "") `shouldBe` refusal++    -- The value is the fingerprint d90c6a4f and the path m/0'+    it "refuses a derivation record under a key that is not a point of the curve, on an input and on an output" $ do+      decodePsbt (tinyPsbt "" ("2206" <> notAPoint <> "08d90c6a4f00000080") "")+        `shouldBe` Left ("Input 0: the key 0x06 with the data " <> T.unpack notAPoint <> " is not a public key")+      decodePsbt (tinyPsbt "" "" ("2202" <> notAPoint <> "08d90c6a4f00000080"))+        `shouldBe` Left ("Output 0: the key 0x02 with the data " <> T.unpack notAPoint <> " is not a public key")++    -- The key is the type 0x01 and the 78 bytes of an extended key, 79 in+    -- all (0x4f): the version 0488b21e, depth 0, no parent, index 0, a chain+    -- code of zeros, and the public key+    it "refuses an extended public key whose key is not a point of the curve" $ do+      let xpubWith key = "4f01" <> "0488b21e" <> "00" <> "00000000" <> "00000000" <> T.replicate 32 "00" <> key <> "04d90c6a4f"+      (encodePsbt <$> decodePsbt (tinyPsbt (xpubWith aPoint) "" "")) `shouldBe` Right (tinyPsbt (xpubWith aPoint) "" "")+      decodePsbt (tinyPsbt (xpubWith notAPoint) "" "") `shouldSatisfy` either ("is not an extended public key" `isSuffixOf`) (const False)++    -- 0x3d is the 61 bytes of the transaction+    it "refuses a previous transaction that is not the one the input spends, in both versions" $ do+      decodePsbt (tinyPsbt "" ("01003d" <> anotherTx) "") `shouldBe` Left "Input 0: the previous transaction is not the one the input spends"+      decodePsbt (tinyPsbt2 ("01003d" <> anotherTx)) `shouldBe` Left "Input 0: the previous transaction is not the one the input spends"++    -- An empty tree, then a leaf of the leaf version 0xc1, a leaf at depth+    -- 129 (0x81), one leaf alone at depth 1, and three leaves at depth 1.+    -- 0x25 is the 37 bytes of a leaf, 0x6f the 111 bytes of three.+    it "refuses a script tree that is empty, has an odd leaf version, a leaf deeper than 128, or leaves that make no tree" $ do+      decodePsbt (tinyPsbt "" "" "010600") `shouldBe` Left "Output 0: the value of the key 0x06 is an empty script tree"+      decodePsbt (tinyPsbt "" "" ("010625" <> treeLeaf "00" "c1")) `shouldBe` Left "Output 0: the value of the key 0x06 has a leaf with an odd leaf version"+      decodePsbt (tinyPsbt "" "" ("010625" <> treeLeaf "81" "c0")) `shouldBe` Left "Output 0: the value of the key 0x06 has a leaf deeper than 128"+      decodePsbt (tinyPsbt "" "" ("010625" <> treeLeaf "01" "c0")) `shouldBe` Left "Output 0: the value of the key 0x06 is not a whole script tree"+      decodePsbt (tinyPsbt "" "" ("01066f" <> treeLeaf "01" "c0" <> treeLeaf "01" "c0" <> treeLeaf "01" "c0")) `shouldBe` Left "Output 0: the value of the key 0x06 is not a whole script tree"++    it "reads a script tree of one leaf at depth 0, and one of two leaves at depth 1" $ do+      (encodePsbt <$> decodePsbt (tinyPsbt "" "" ("010625" <> treeLeaf "00" "c0"))) `shouldBe` Right (tinyPsbt "" "" ("010625" <> treeLeaf "00" "c0"))+      (encodePsbt <$> decodePsbt (tinyPsbt "" "" ("01064a" <> treeLeaf "01" "c0" <> treeLeaf "01" "c0"))) `shouldBe` Right (tinyPsbt "" "" ("01064a" <> treeLeaf "01" "c0" <> treeLeaf "01" "c0"))++    -- The length 1 of the value "aa", over five bytes and over nine+    it "refuses a length written over five or nine bytes that is not in its shortest form" $ do+      decodePsbt (hexBytes ("70736274ff" <> emptyTxEntry <> "0120" <> "fe01000000" <> "aa" <> "00")) `shouldBe` Left "A length in the global map is not in its shortest form"+      decodePsbt (hexBytes ("70736274ff" <> emptyTxEntry <> "0120" <> "ff0100000000000000" <> "aa" <> "00")) `shouldBe` Left "A length in the global map is not in its shortest form"++    -- The key is the type 0x15 and a control block of 33 bytes plus 32 per+    -- node. 128 nodes make 4,130 bytes with the type (fd2210), and 129 make+    -- 4,162 (fd4210). The value is a script of 34 bytes and the leaf version.+    it "refuses a control block of 129 nodes, and reads one of 128" $ do+      let leafEntry nodes len = len <> "15" <> "c0" <> T.replicate 32 "22" <> T.replicate (32 * nodes) "33" <> "23" <> "20" <> T.replicate 32 "22" <> "ac" <> "c0"+      (B.length . encodePsbt <$> decodePsbt (tinyPsbt "" (leafEntry 128 "fd2210") "")) `shouldBe` Right (B.length (tinyPsbt "" (leafEntry 128 "fd2210") ""))+      decodePsbt (tinyPsbt "" (leafEntry 129 "fd4210") "") `shouldSatisfy` either ("is not a control block" `isSuffixOf`) (const False)++    -- An extended key of depth 0 has no path. The value is the fingerprint+    -- and the one index 0'.+    it "refuses an extended public key of depth 0 with a path of one index" $ do+      let xpub = "4f01" <> "0488b21e" <> "00" <> "00000000" <> "00000000" <> T.replicate 32 "00" <> aPoint <> "08d90c6a4f00000080"+      decodePsbt (tinyPsbt xpub "" "") `shouldSatisfy` either ("has a path that is not as deep as the key" `isSuffixOf`) (const False)++    -- Two entries with the same key: the type 0x20 and 1,000 bytes of 0xab,+    -- 1,001 in all (fde903)+    it "names at most 40 bytes of the data of a key in a refusal, then its length" $ do+      let entry = "fde903" <> "20" <> T.replicate 1000 "ab" <> "00"+      decodePsbt (tinyPsbt (entry <> entry) "" "")+        `shouldBe` Left ("The global map has the key 0x20 with the data " <> concat (replicate 40 "ab") <> "… (1000 bytes) twice")++{- What the signer and the finalizer sign, and what they leave alone -}++-- | A transaction that spends the two outputs of a previous transaction+-- paying the two scripts, as a PSBT with that previous transaction on both+-- inputs. Each output holds 2 BTC.+spendingTwo :: ByteString -> ByteString -> Psbt+spendingTwo scriptA scriptB = Psbt Version0 tx Map.empty [input, input] [Map.empty]+  where+    parent = Tx 1 [TxIn (OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1) "" 0xffffffff] [TxOut 200000000 scriptA, TxOut 200000000 scriptB] [] 0+    tx = Tx 2 [TxIn (OutPoint (txHash parent) 0) "" 0xfffffffe, TxIn (OutPoint (txHash parent) 1) "" 0xfffffffe] [TxOut 399990000 (hexBytes "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac")] [] 0+    input = insertNonWitnessUtxo parent Map.empty++signerScopeSpec :: Ctx -> Spec+signerScopeSpec ctx =+  describe "What the signer and the finalizer sign, and what they leave alone" $ do+    let secret = PrivateKey secretOne True+        public = derivePublicKey ctx secret+        otherPublic = compressedKey ctx secretTwo+        signer = keySigner ctx secretOne+        ownScript = marshal ctx (PayPKHash (addressHash (marshal ctx public)))+        otherScript = marshal ctx (PayPKHash (addressHash (marshal ctx otherPublic)))+        (psbt, _) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx public)))+        parent = fromJust (either (const Nothing) id (nonWitnessUtxo (inputAt 0 psbt)))+        coin = head parent.outputs+        signatureCounts = mapM (fmap length . partialSignatures ctx) . (.inputs)+        keyHex = show (encodeHex (marshal ctx public))++    -- BIP 32 gives a key a depth of one byte, so no key is 256 steps below+    -- another+    it "a signer built from an extended key holds no key for a record whose path has 256 steps" $ do+      let master = makeXPrvKey "PSBT signer test"+          tooDeep = Derivation (xPubFP ctx (deriveXPubKey ctx master)) (listToPath (replicate 256 0))+      (xPrvSigner ctx master Nothing).keyOfRecord public tooDeep `shouldBe` Nothing++    it "signing leaves unsigned an input whose record names the signer's key with a path of 256 steps" $ do+      let master = makeXPrvKey "PSBT signer test"+          tooDeep = Derivation (xPubFP ctx (deriveXPubKey ctx master)) (listToPath (replicate 256 0))+          withRecord = withInputs (insertInputDerivation ctx public tooDeep <$> psbt.inputs) psbt+      (signatureCounts =<< signPsbt btc ctx (xPrvSigner ctx master Nothing) withRecord) `shouldBe` Right [0]++    -- The second record names a key the coin does not pay. A signer that+    -- derives a key for every record can be made to work without end.+    it "asks the signer for the key of a record only when the script of the coin names that key" $ do+      let onlyOwn = signer {keyOfRecord = \asked _ -> if asked == public then Just secretOne else error "asked for the key of a record the coin does not name"}+          withRecords = withInputs ((recordOf ctx public . recordOf ctx otherPublic) <$> psbt.inputs) psbt+      (signatureCounts =<< signPsbt btc ctx onlyOwn withRecords) `shouldBe` Right [1]++    -- The coin pays the hash of key 2, and the record names key 1, which+    -- the signer holds. BIP 174: "If a Signer cannot sign a transaction, it+    -- must not add a Partial Signature."+    it "adds no signature for a key the script of the coin does not name, even when a derivation record names it" $ do+      let (other, _) = spendingPsbt ctx (PayPKHash (addressHash (marshal ctx otherPublic)))+      (signatureCounts =<< signPsbt btc ctx signer (withInputs (recordOf ctx public <$> other.inputs) other)) `shouldBe` Right [0]++    it "adds no signature to a P2WSH coin whose witness script is missing, nor to a P2SH coin whose redeem script is missing" $ do+      let multisig = forceRight (encodeOutput ctx (PayMulSig [public, otherPublic] 2))+          (p2wsh, _) = spendingPsbt ctx (forceRight (toP2WSH multisig))+          (p2sh, _) = spendingPsbt ctx (forceRight (toP2SH multisig))+      (signatureCounts =<< signPsbt btc ctx signer (withInputs (recordOf ctx public <$> p2wsh.inputs) p2wsh)) `shouldBe` Right [0]+      (signatureCounts =<< signPsbt btc ctx signer (withInputs (recordOf ctx public <$> p2sh.inputs) p2sh)) `shouldBe` Right [0]++    it "signs a P2SH-P2WPKH coin with its redeem script by the hash of its key, without a derivation record" $ do+      let redeem = forceRight (encodeOutput ctx (PayWitnessPKHash (addressHash (marshal ctx public))))+          (nested, _) = spendingPsbt ctx (forceRight (toP2SH redeem))+      (signatureCounts =<< signPsbt btc ctx signer (withInputs (insertInputRedeemScript redeem <$> nested.inputs) nested)) `shouldBe` Right [1]++    -- 0x41 is SIGHASH_ALL (0x01) with SIGHASH_FORKID (0x40)+    it "on the Bitcoin Cash networks, signs an input that asks for no sighash type with SIGHASH_ALL and the fork id flag" $+      forM_ [bch, bchTest, bchTest4, bchRegTest] $ \net ->+        (map (B.last . snd) <$> (partialSignatures ctx . inputAt 0 =<< signPsbt net ctx signer psbt)) `shouldBe` Right [0x41]++    -- Input 0 is a coin of key 1. Input 1 is a P2WSH coin of someone else,+    -- whose witness script is <key 2> OP_CHECKSIGVERIFY <144>+    -- OP_CHECKSEQUENCEVERIFY, which is no template the library knows.+    it "signs and finalizes its own input, and leaves alone an input whose witness script is not a standard template" $ do+      let timelocked = Script [opPushData (marshal ctx otherPublic), OP_CHECKSIGVERIFY, opPushData "\x90\x00", OP_CHECKSEQUENCEVERIFY]+          both = spendingTwo ownScript (marshal ctx (forceRight (toP2WSH timelocked)))+          withScript = withInputs [inputAt 0 both, insertInputWitnessScript timelocked (inputAt 1 both)] both+      signed <- expectRight (signPsbt btc ctx signer withScript)+      signatureCounts signed `shouldBe` Right [1, 0]+      inputAt 1 signed `shouldBe` inputAt 1 withScript+      final <- expectRight (finalizePsbt btc ctx signed)+      map (isJust . finalScriptSig) final.inputs `shouldBe` [True, False]+      inputAt 1 final `shouldBe` inputAt 1 withScript++    -- Input 1 is a P2PKH coin of key 2, known from its output alone. The+    -- signer could not sign it anyway. Bitcoin Core leaves such an input+    -- unsigned and signs the others.+    it "leaves alone a legacy coin known from its witness output only when it holds no key for it, and signs its own input" $ do+      let both = spendingTwo ownScript otherScript+          witnessOnly = withInputs [inputAt 0 both, insertWitnessUtxo (TxOut 200000000 otherScript) Map.empty] both+      (signatureCounts =<< signPsbt btc ctx signer witnessOnly) `shouldBe` Right [1, 0]++    -- Bitcoin Core refuses the whole PSBT too: a sighash type that the+    -- caller did not ask for is a mismatch on any input that has its coin+    it "refuses a PSBT in which another party's input asks for a sighash type the signer does not accept" $ do+      let both = spendingTwo ownScript otherScript+          asking = withInputs [inputAt 0 both, insertSigHashType (setAnyoneCanPay sigHashSingle) (inputAt 1 both)] both+      signPsbt btc ctx signer asking `shouldBe` Left "Input 1: the sighash type 131 is not one the signer accepts"++    it "refuses to sign a SegWit version 0 coin with an uncompressed key" $ do+      let uncompressed = PublicKey public.point False+          (p2wpkh, _) = spendingPsbt ctx (PayWitnessPKHash (addressHash (marshal ctx uncompressed)))+      signPsbt btc ctx signer p2wpkh `shouldBe` Left "Input 0: a SegWit version 0 coin may not be signed with an uncompressed key"++    -- The signature is a real one of key 1, on another coin. The finalizer+    -- must refuse it for the form of the key, whatever it signs.+    it "the finalizer refuses a signature of an uncompressed key on a SegWit version 0 coin" $ do+      let uncompressed = PublicKey public.point False+          (p2wpkh, _) = spendingPsbt ctx (PayWitnessPKHash (addressHash (marshal ctx uncompressed)))+      signed <- expectRight (signPsbt btc ctx signer psbt)+      signature <- case partialSignatures ctx (inputAt 0 signed) of+        Right [(_, sig)] -> return sig+        _ -> fail "one signature"+      finalizePsbt btc ctx (withInputs (insertPartialSignature ctx uncompressed signature <$> p2wpkh.inputs) p2wpkh)+        `shouldBe` Left ("Input 0: the signature of the key " <> show (encodeHex (marshal ctx uncompressed)) <> " is of an uncompressed key, which a SegWit version 0 coin may not be spent with")++    -- The second signature is the one of key 1, filed under key 2: it does+    -- not verify for key 2. The coin does not name key 2, so the finalizer+    -- has no reason to look at it.+    it "the finalizer does not look at a partial signature of a key the script of the coin does not name" $ do+      signed <- expectRight (signPsbt btc ctx signer psbt)+      signature <- case partialSignatures ctx (inputAt 0 signed) of+        Right [(_, sig)] -> return sig+        _ -> fail "one signature"+      let withStray = withInputs (insertPartialSignature ctx otherPublic signature <$> signed.inputs) signed+      (finalScriptSig . inputAt 0 <$> finalizePsbt btc ctx withStray) `shouldBe` (finalScriptSig . inputAt 0 <$> finalizePsbt btc ctx signed)+      (isJust . finalScriptSig . inputAt 0 <$> finalizePsbt btc ctx withStray) `shouldBe` Right True++    -- Bitcoin Core takes SIGHASH_ALL for an input that names no sighash+    -- type, and gives no final script when a signature uses another+    it "the finalizer refuses a SIGHASH_NONE signature on an input that names no sighash type" $ do+      let acceptingNone = signer {acceptedSigHashes = [sigHashNone]}+      signed <- expectRight (signPsbt btc ctx acceptingNone (withInputs (insertSigHashType sigHashNone <$> psbt.inputs) psbt))+      finalizePsbt btc ctx (withInputs (deleteSigHashType <$> signed.inputs) signed)+        `shouldBe` Left ("Input 0: the signature of the key " <> keyHex <> " does not use SIGHASH_ALL, and the input names no sighash type")++    it "the finalizer leaves alone a signed legacy coin that is known from its witness output only" $ do+      signed <- expectRight (signPsbt btc ctx signer psbt)+      let witnessOnly = withInputs [insertWitnessUtxo coin (Map.delete (Key 0x00 "") (inputAt 0 signed))] signed+      finalizePsbt btc ctx witnessOnly `shouldBe` Right witnessOnly++    -- A signer written by a caller may answer with a key that is not the+    -- one asked for. Its signature would be of no use to anyone.+    it "signs with a key the signer gives only when it is the key the coin names" $ do+      let wrongByRecord = signer {keyOfRecord = \_ _ -> Just secretTwo, keyOfHash = const Nothing}+          wrongByHash = signer {keyOfRecord = \_ _ -> Nothing, keyOfHash = const (Just (PrivateKey secretTwo True))}+          withRecord = withInputs (recordOf ctx public <$> psbt.inputs) psbt+      (signatureCounts =<< signPsbt btc ctx wrongByRecord withRecord) `shouldBe` Right [0]+      (signatureCounts =<< signPsbt btc ctx wrongByHash psbt) `shouldBe` Right [0]++    it "a signer of one key holds no key for the record of another key" $ do+      let record = Derivation (fingerprintOf "00000000") "m/0"+      (keySigner ctx secretOne).keyOfRecord otherPublic record `shouldBe` Nothing+      (keySigner ctx secretOne).keyOfRecord public record `shouldBe` Just secretOne++    -- The time lock 100 is below 500,000,000, so it would be read as a height+    it "a constructor refuses an input whose map breaks the spec, such as a required time lock below 500000000" $ do+      let modifiable = withFlags (Modifiable True True False 0) (withVersion Version2 (psbtOf (Tx 2 [] [] [] 0)))+      addInput coinInput (Map.singleton (Key 0x11 "") (hexBytes "64000000")) modifiable+        `shouldBe` Left "Input 0: the value of the key 0x11 is a time lock below 500000000"+      addOutput paidOutput (Map.singleton (Key 0x06 "") "") modifiable+        `shouldBe` Left "Output 0: the value of the key 0x06 is an empty script tree"+      -- The previous transaction has one output, and the input spends its second+      let previous = Tx 1 [coinInput] [paidOutput] [] 0+      addInput (TxIn (OutPoint (txHash previous) 1) "" 0xffffffff) (insertNonWitnessUtxo previous Map.empty) modifiable+        `shouldBe` Left "Input 0: the input spends an output its previous transaction does not have"+      addInput (TxIn (OutPoint (txHash previous) 0) "" 0xffffffff) (insertNonWitnessUtxo emptyTx Map.empty) modifiable+        `shouldBe` Left "Input 0: the previous transaction is not the one the input spends"++    -- 0065cd1d is 500,000,000, the first lock time that is a time, and+    -- ff64cd1d is 499,999,999+    it "a required time lock of 500000000 is the lock time of the transaction, and one of 499999999 is refused" $ do+      ((.unsignedTx.locktime) <$> decodePsbt (tinyPsbt2 "0111040065cd1d")) `shouldBe` Right 500000000+      decodePsbt (tinyPsbt2 "011104ff64cd1d") `shouldBe` Left "Input 0: the value of the key 0x11 is a time lock below 500000000"++{- The checks on Taproot signatures -}++-- | The PSBT with each of its input maps changed+onInputs :: (PsbtMap -> PsbtMap) -> Psbt -> Psbt+onInputs f psbt = withInputs (f <$> psbt.inputs) psbt++-- | The map with the entries of one key type changed+onKeyType :: Word8 -> (Key -> ByteString -> (Key, ByteString)) -> PsbtMap -> PsbtMap+onKeyType t f m = Map.fromList [if key.keyType == t then f key value else (key, value) | (key, value) <- Map.toList m]++-- | Bytes with their last bit flipped+flipLastBit :: ByteString -> ByteString+flipLastBit bytes = B.init bytes <> B.singleton (B.last bytes `xor` 1)++-- | An entry with the last bit of its value flipped, and one with the last+-- bit of the data of its key flipped+flipValue, flipKeyData :: Key -> ByteString -> (Key, ByteString)+flipValue key value = (key, flipLastBit value)+flipKeyData key value = (Key key.keyType (flipLastBit key.keyData), value)++taprootChecksSpec :: Ctx -> Vectors371 -> Spec+taprootChecksSpec ctx vectors =+  describe "The checks on Taproot signatures" $ do+    let vectorAt i = psbtOfVector (vectors.accepted371 !! i)+        internal = derivePubKey ctx secretOne+        leafKey = derivePubKey ctx secretTwo+        leaf = Script [opPushData (exportXOnlyPubKey ctx leafKey), OP_CHECKSIG]+        tree = MASTBranch (MASTLeaf 0xc0 leaf) (MASTLeaf 0xc0 (Script [OP_RETURN]))+        output = TaprootOutput internal (Just tree)+        control = encodeControlBlock ctx (0xc0 + keyParity ctx (taprootOutputKey ctx output)) internal [leafHash 0xc0 (Script [OP_RETURN])]+        (withTree, _) = spendingPsbt ctx (taprootScriptOutput ctx output)+        treeFields = insertInputTapInternalKey ctx internal . insertInputTapMerkleRoot (mastCommitment tree) . insertTapLeafScript (TapLeafScript control leaf 0xc0)+        derivation = Derivation (fingerprintOf "d90c6a4f") "m/86'/0'/0'/0/0"+        -- A signer that finds its keys by derivation record only, as one+        -- built from an extended key does+        byRecord secret = (keySigner ctx secret) {keyOfHash = const Nothing}++    it "the finalizer refuses a key path signature that does not verify" $ do+      psbt <- vectorAt 1+      finalizePsbt btc ctx (onInputs (onKeyType 0x13 flipValue) psbt)+        `shouldBe` Left "Input 0: the key path signature does not verify"++    it "the finalizer refuses a script path signature that does not verify" $ do+      psbt <- vectorAt 5+      finalizePsbt btc ctx (onInputs (onKeyType 0x14 flipValue) psbt)+        `shouldBe` Left "Input 0: the script path signature does not verify"++    it "the finalizer refuses a leaf whose control block does not commit it to the output key" $ do+      psbt <- vectorAt 5+      finalizePsbt btc ctx (onInputs (onKeyType 0x15 flipKeyData) psbt)+        `shouldBe` Left "Input 0: the control block of the leaf does not commit it to the output key"++    -- The signatures of both vectors are 64 bytes, which is SIGHASH_DEFAULT+    it "the finalizer refuses a Taproot signature that does not use the sighash type the input names" $ do+      keyPath <- vectorAt 1+      scriptPath <- vectorAt 5+      finalizePsbt btc ctx (onInputs (insertSigHashType sigHashAll) keyPath) `shouldBe` Left "Input 0: a Taproot signature does not use the sighash type of the input"+      finalizePsbt btc ctx (onInputs (insertSigHashType sigHashAll) scriptPath) `shouldBe` Left "Input 0: a Taproot signature does not use the sighash type of the input"++    -- Bitcoin Core takes SIGHASH_DEFAULT for a Taproot input that names no+    -- sighash type, and then wants every signature to be 64 bytes+    it "the finalizer refuses a SIGHASH_ALL key path signature on an input that names no sighash type" $ do+      let (keyOnly, _) = spendingPsbt ctx (taprootScriptOutput ctx (TaprootOutput internal Nothing))+          asking = onInputs (insertSigHashType sigHashAll . insertInputTapInternalKey ctx internal) keyOnly+      signed <- expectRight (signPsbt btc ctx (keySigner ctx secretOne) asking)+      (fmap (fmap (.hash)) . tapKeySig . inputAt 0) signed `shouldBe` Right (Just sigHashAll)+      finalizePsbt btc ctx (onInputs deleteSigHashType signed)+        `shouldBe` Left "Input 0: a Taproot signature does not use SIGHASH_DEFAULT, and the input names no sighash type"++    it "the signer refuses a Taproot coin that asks for a sighash type the signer does not accept" $ do+      let (keyOnly, _) = spendingPsbt ctx (taprootScriptOutput ctx (TaprootOutput internal Nothing))+      signPsbt btc ctx (keySigner ctx secretOne) (onInputs (insertSigHashType sigHashSingle . insertInputTapInternalKey ctx internal) keyOnly)+        `shouldBe` Left "Input 0: the sighash type 3 is not one the signer accepts"++    -- BIP 371: the leaf hashes of a record "are of the leaves which involve+    -- this public key". A key that is the internal key and also a leaf key+    -- has a record with a leaf hash, and Bitcoin Core writes it that way.+    it "signs the key path of a Taproot coin when the record of its internal key lists a leaf" $ do+      let record = insertInputTapDerivation ctx internal (TapDerivation [leafHash 0xc0 leaf] derivation)+      signed <- expectRight (signPsbt btc ctx (byRecord secretOne) (onInputs (record . treeFields) withTree))+      (fmap isJust . tapKeySig . inputAt 0) signed `shouldBe` Right True++    it "signs a leaf with the key of a record that lists that leaf, and not with one that lists another leaf" $ do+      let listing hashes = insertInputTapDerivation ctx leafKey (TapDerivation hashes derivation)+          signatures f = fmap length . tapScriptSigs ctx . inputAt 0 =<< signPsbt btc ctx (byRecord secretTwo) (onInputs (f . treeFields) withTree)+      signatures (listing [leafHash 0xc0 leaf]) `shouldBe` Right 1+      signatures (listing [leafHash 0xc0 (Script [OP_RETURN])]) `shouldBe` Right 0++    -- The leaf is in no tree of the coin: its control block names another+    -- internal key+    it "signs no leaf whose control block does not commit it to the coin" $ do+      let stray = encodeControlBlock ctx 0xc0 leafKey [leafHash 0xc0 (Script [OP_RETURN])]+          fields = insertInputTapInternalKey ctx internal . insertInputTapMerkleRoot (mastCommitment tree) . insertTapLeafScript (TapLeafScript stray leaf 0xc0)+      signed <- expectRight (signPsbt btc ctx (keySigner ctx secretTwo) (onInputs fields withTree))+      (fmap length . tapScriptSigs ctx . inputAt 0) signed `shouldBe` Right 0++    it "signs a Taproot coin of a PSBT of version 2 by its key path, and the PSBT stays of version 2" $ do+      let (keyOnly, coins) = spendingPsbt ctx (taprootScriptOutput ctx (TaprootOutput internal Nothing))+          version2 = withVersion Version2 (onInputs (insertInputTapInternalKey ctx internal) keyOnly)+      final <- expectRight (finalizePsbt btc ctx =<< signPsbt btc ctx (keySigner ctx secretOne) version2)+      final.version `shouldBe` Version2+      tx <- expectRight (psbtTransaction final)+      verifyStdTx btc ctx tx coins `shouldBe` True++{- The coins of a PSBT, and the previous transaction a signer may require -}++coinsSpec :: Ctx -> Spec+coinsSpec ctx =+  describe "The coins of a PSBT" $ do+    let public = compressedKey ctx secretOne+        signer = keySigner ctx secretOne+        program = PayWitnessPKHash (addressHash (marshal ctx public))+        (psbt, _) = spendingPsbt ctx program+        parent = fromJust (either (const Nothing) id (nonWitnessUtxo (inputAt 0 psbt)))+        output = head parent.outputs+        witnessOnly = withInputs [insertWitnessUtxo output Map.empty] psbt+        signatureCounts = mapM (fmap length . partialSignatures ctx) . (.inputs)++    it "tells the coin of each input, and whether it comes from the previous transaction of the input" $ do+      inputCoins psbt `shouldBe` Right [Just (InputCoin output True)]+      inputCoins witnessOnly `shouldBe` Right [Just (InputCoin output False)]+      inputCoins (withInputs [Map.empty] psbt) `shouldBe` Right [Nothing]++    -- The coin holds 200,000,000 satoshi and the outputs pay 99,999,699 and+    -- 100,000,000: 200,000,000 − 199,999,699 = 301+    it "gives the fee when every coin comes from its previous transaction" $+      psbtFee psbt `shouldBe` Right 301++    it "gives no fee when the amount of a coin is only a claim, when a coin is missing, or when the outputs pay more than the coins hold" $ do+      psbtFee witnessOnly `shouldBe` Left "Input 0: the coin is given as a witness output only, so its amount is not checked"+      psbtFee (withInputs [Map.empty] psbt) `shouldBe` Left "Input 0 has no coin"+      -- The same transaction with one more output of 302 satoshi+      let tx = psbt.unsignedTx+          paysMore = Psbt Version0 (Tx tx.version tx.inputs (tx.outputs <> [TxOut 302 ""]) [] tx.locktime) Map.empty psbt.inputs [Map.empty, Map.empty, Map.empty]+      psbtFee paysMore `shouldBe` Left "The outputs pay more than the coins hold"++    -- BIP 174: "The Signer may choose to fail to sign a segwit input if a+    -- non-witness UTXO is not provided."+    it "a signer refuses a SegWit version 0 coin known from its witness output only, and signs it with its previous transaction" $ do+      signPsbt btc ctx signer witnessOnly+        `shouldBe` Left "Input 0: the coin is given as a witness output only, and the signer requires the previous transaction of a SegWit version 0 coin"+      (signatureCounts =<< signPsbt btc ctx signer psbt) `shouldBe` Right [1]+      (signatureCounts =<< signPsbt btc ctx signer (withInputs [insertWitnessUtxo output (inputAt 0 psbt)] psbt)) `shouldBe` Right [1]++    it "a signer told to trust witness outputs signs a SegWit version 0 coin known from its witness output only" $+      (signatureCounts =<< signPsbt btc ctx (signer {requirePreviousTx = False}) witnessOnly) `shouldBe` Right [1]++    -- CVE-2020-14199. The signer holds two coins of 15 and 20 BTC. The+    -- writer of the PSBT understates the first as 5 BTC and overstates the+    -- second. A signature of each input commits to the amount of that input+    -- alone, so the signature of the second input would be valid in a+    -- transaction that burns the 10 BTC of difference as fee.+    it "a signer refuses a PSBT that states the amounts of its SegWit version 0 coins without their previous transactions" $ do+      let both = spendingTwo (marshal ctx program) (marshal ctx program)+          claimed = withInputs [insertWitnessUtxo (TxOut 500000000 (marshal ctx program)) Map.empty, insertWitnessUtxo (TxOut 2000000000 (marshal ctx program)) Map.empty] both+      signPsbt btc ctx signer claimed+        `shouldBe` Left "Input 0: the coin is given as a witness output only, and the signer requires the previous transaction of a SegWit version 0 coin"++    -- A Taproot signature commits to the amount of every coin (BIP 341)+    it "a signer signs a Taproot coin known from its witness output only" $ do+      let internal = derivePubKey ctx secretOne+          (taproot, _) = spendingPsbt ctx (taprootScriptOutput ctx (TaprootOutput internal Nothing))+      (fmap isJust . tapKeySig . inputAt 0 =<< signPsbt btc ctx signer (withInputs (insertInputTapInternalKey ctx internal <$> taproot.inputs) taproot)) `shouldBe` Right True++{- The fields of version 2, read one by one -}++version2FieldsSpec :: Spec+version2FieldsSpec =+  describe "The lock time fields of a PSBT of version 2" $ do+    -- 0x03 is the fallback lock time of the global map, 0x11 and 0x12 the+    -- lock times an input requires as a time and as a height+    it "read the fallback lock time of the global map, when it has one" $ do+      fallbackLockTime (Map.singleton (Key 0x03 "") (hexBytes "10270000")) `shouldBe` Right (Just 10000)+      fallbackLockTime Map.empty `shouldBe` Right Nothing+      fallbackLockTime (Map.singleton (Key 0x03 "") (hexBytes "1027")) `shouldSatisfy` either ("is not a 32-bit integer" `isSuffixOf`) (const False)++    it "read the lock times an input requires, as a time and as a height" $ do+      requiredTimeLockTime (Map.singleton (Key 0x11 "") (hexBytes "0065cd1d")) `shouldBe` Right (Just 500000000)+      requiredHeightLockTime (Map.singleton (Key 0x12 "") (hexBytes "10270000")) `shouldBe` Right (Just 10000)+      requiredTimeLockTime Map.empty `shouldBe` Right Nothing+      requiredHeightLockTime Map.empty `shouldBe` Right Nothing++    -- The global map of version 2 with no input and no output: the+    -- transaction version 2, the two counts of 0, and the PSBT version 2+    it "build a PSBT from its maps, and refuse maps that are not as many as the global map counts" $ do+      let globals = Map.fromList [(Key 0x02 "", hexBytes "02000000"), (Key 0x04 "", hexBytes "00"), (Key 0x05 "", hexBytes "00"), (Key 0xfb "", hexBytes "02000000")]+      (encodeHex . encodePsbt <$> psbtOfMaps globals [] []) `shouldBe` Right "70736274ff01020402000000010401000105010001fb040200000000"+      psbtOfMaps globals [Map.empty] [] `shouldBe` Left "The PSBT has 1 inputs, and its global map counts 0"+      psbtOfMaps globals [] [Map.empty] `shouldBe` Left "The PSBT has 1 outputs, and its global map counts 0"
+ test/Haskoin/Transaction/TaprootSpec.hs view
@@ -0,0 +1,908 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}++-- | Taproot: the script trees, output keys and control blocks of the BIP+-- 341 vectors, key path spending with its signature digest, the witness of+-- a spend, and the BIP 86 addresses.+module Haskoin.Transaction.TaprootSpec (spec) where++import Control.Applicative ((<|>))+import Control.Monad (forM, forM_, (<=<))+import Crypto.Hash (Digest, SHA256, digestFromByteString, hashFinalize, hashUpdate)+import Data.Aeson+import Data.Aeson.Types+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (putByteString, putWord8, runPutS)+import Data.Bytes.Serial (deserialize, serialize)+import Data.Bytes.VarInt qualified as Base128+import Data.Either (isLeft)+import Data.Maybe (fromJust, isJust, isNothing)+import Data.Serialize qualified as S+import Data.Text (Text)+import Data.Text qualified+import Data.Word (Word32, Word64, Word8)+import Haskoin+import Haskoin.Util.Arbitrary+import Test.HUnit (assertBool, (@?=))+import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++spec :: Spec+spec = prepareContext $ \ctx -> do+  TestVector {testScriptPubKey, testKeyPath} <-+    runIO $+      readTestFileParser (testVectorParseJSON ctx) "bip341.json"+  describe "Script trees and output keys, from the scriptPubKey vectors of BIP 341" $ do+    it "calculate the leaf hashes and the Merkle root" $+      mapM_ testHashes testScriptPubKey+    it "build the output key" $+      mapM_ (testOutputKey ctx) testScriptPubKey+    it "build the script output" $+      mapM_ (testScriptOutput ctx) testScriptPubKey+    it "build the control blocks, which verify the script path" $+      mapM_ (testControlBlocks ctx) testScriptPubKey+    it "give the address" $+      mapM_ (testAddress ctx) testScriptPubKey+    it "list the leaves of a tree depth first and build the tree back from them" $+      mapM_ testTreeLeaves testScriptPubKey++  describe "The output key" $ do+    -- BIP 341 tweaks the x-only internal key, so the output does not+    -- depend on the parity of the y coordinate of the key given+    prop "is the same for a key and its negation" $+      forAll (arbitraryKeyPair ctx) $ \(_, p) ->+        let x = exportXOnlyPubKey ctx p.point+            negated = (.point) (either error id (unmarshal ctx (BS.cons (if keyParity ctx p.point == 0 then 0x03 else 0x02) x)) :: PublicKey)+         in taprootScriptOutput ctx (TaprootOutput negated Nothing) === taprootScriptOutput ctx (TaprootOutput p.point Nothing)++    prop "has the x coordinate of the public key of the output secret key" $+      forAll (arbitraryKeyPair ctx) $ \(k, p) ->+        let tweaked = fromJust (taprootOutputSecKey ctx k.key Nothing)+         in exportXOnlyPubKey ctx (derivePubKey ctx tweaked) === exportXOnlyPubKey ctx (taprootOutputKey ctx (TaprootOutput p.point Nothing))++    -- The test vectors of BIP 86: the keys at m/86'/0'/0'/0/0, 0/1 and+    -- 1/0 of the mnemonic "abandon ... about"+    forM_ bip86Vectors $ \(path, internalKey, outputKey, address) ->+      it ("of the BIP 86 key at " <> show path <> " is " <> show outputKey) $ do+        let root = fromJust (xPrvImport btc "xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu")+            secret = (derivePath ctx (path :: DerivPath) root).key+            output = TaprootOutput (derivePubKey ctx secret) Nothing+        encodeHex (exportXOnlyPubKey ctx (derivePubKey ctx secret)) `shouldBe` internalKey+        encodeHex (exportXOnlyPubKey ctx (taprootOutputKey ctx output)) `shouldBe` outputKey+        encodeHex (marshal ctx (taprootScriptOutput ctx output)) `shouldBe` "5120" <> outputKey+        (addrToText btc =<< outputAddress btc ctx (taprootScriptOutput ctx output)) `shouldBe` Just address+        -- The output secret key signs for the output key+        fmap (encodeHex . exportXOnlyPubKey ctx . derivePubKey ctx) (taprootOutputSecKey ctx secret Nothing) `shouldBe` Just outputKey++  describe "Key path spending, from the keyPathSpending vector of BIP 341" $ do+    forM_ testKeyPath.cases $ \c ->+      it ("input " <> show c.index <> ": the tweak, the digest and the signature, with hash type " <> show (fromIntegral c.hashType :: Word32)) $ do+        let output = TaprootOutput (derivePubKey ctx c.internalSecret) (MASTCommitment <$> c.merkleRoot)+            program = BS.drop 2 (testKeyPath.spent !! c.index).script+        encodeHex (exportXOnlyPubKey ctx (derivePubKey ctx c.internalSecret)) `shouldBe` c.internalPubKey+        exportXOnlyPubKey ctx (taprootOutputKey ctx output) `shouldBe` program+        let tweaked = fromJust (taprootOutputSecKey ctx c.internalSecret (MASTCommitment <$> c.merkleRoot))+        tweaked `shouldBe` fromJust (secKey (hexBytes c.tweakedSecret))+        digest <- expectRight (taprootSigHash testKeyPath.unsignedTx testKeyPath.spent c.index c.hashType Nothing Nothing)+        encodeHex (runPutS (serialize digest)) `shouldBe` c.sigHash+        -- The signature of the vector was made with 32 zero bytes of+        -- auxiliary randomness, as signSchnorr signs+        fmap (encodeHex . encodeTaprootSig) (taprootSignature (signSchnorr ctx tweaked (hashToMsg digest)) c.hashType) `shouldBe` Right (head c.witness)++    it "the fully signed transaction of the vector verifies against the coins it spends" $+      verifyStdTx btc ctx testKeyPath.signedTx (coinsOf ctx testKeyPath) `shouldBe` True++    it "the fully signed transaction of the vector does not verify when a Taproot coin is given a wrong amount" $ do+      let coins = [(so, if i == (0 :: Int) then v + 1 else v, op) | (i, (so, v, op)) <- zip [0 ..] (coinsOf ctx testKeyPath)]+      verifyStdTx btc ctx testKeyPath.signedTx coins `shouldBe` False++    it "signTx signs the Taproot inputs of the vector by their key path with the internal keys, and the result verifies" $ do+      let tx = testKeyPath.unsignedTx+          coins = coinsOf ctx testKeyPath+          -- The internal keys of the Taproot inputs, with the key of the+          -- output as signTx finds it: the tweak of a key without a tree+          -- is the output only when the vector has no Merkle root+          noTree = [c | c <- testKeyPath.cases, isNothing c.merkleRoot]+          sigInputs = [SigInput so v op c.hashType Nothing | c <- noTree, let (so, v, op) = coins !! c.index]+          others = [SigInput so v op sigHashAll Nothing | (i, (so, v, op)) <- zip [0 ..] coins, i `notElem` map (.index) noTree]+      signed <- expectRight (signTx btc ctx tx (sigInputs <> others) (map (.internalSecret) noTree))+      forM_ noTree $ \c ->+        (signed.witness !! c.index) `shouldBe` [hexBytes (head c.witness)]++    -- The witness of each input is in the vector, so this checks the+    -- signer against the BIP for all seven inputs and hash types+    it "signTaprootInput signs each Taproot input of the vector with its output secret key, and gives the witness of the vector" $+      forM_ testKeyPath.cases $ \c -> do+        let tweaked = fromJust (taprootOutputSecKey ctx c.internalSecret (MASTCommitment <$> c.merkleRoot))+        signed <- expectRight (signTaprootInput ctx testKeyPath.unsignedTx testKeyPath.spent c.index c.hashType tweaked)+        (signed.witness !! c.index) `shouldBe` [hexBytes (head c.witness)]++    it "taprootSigHash refuses an index out of range, a hash type a Taproot signature may not use, missing coins and SIGHASH_SINGLE without an output" $ do+      let tx = testKeyPath.unsignedTx+          spent = testKeyPath.spent+      taprootSigHash tx spent 9 sigHashDefault Nothing Nothing `shouldBe` Left "taprootSigHash: input index out of range"+      taprootSigHash tx spent (-1) sigHashDefault Nothing Nothing `shouldBe` Left "taprootSigHash: input index out of range"+      taprootSigHash tx (take 8 spent) 0 sigHashDefault Nothing Nothing `shouldBe` Left "taprootSigHash: one spent output per input is required"+      taprootSigHash tx spent 0 0x04 Nothing Nothing `shouldBe` Left "taprootSigHash: invalid hash type 4"+      taprootSigHash tx spent 0 (setForkIdFlag sigHashAll) Nothing Nothing `shouldBe` Left "taprootSigHash: invalid hash type 65"+      -- The transaction has two outputs: input 3 has none at its index+      taprootSigHash tx spent 3 sigHashSingle Nothing Nothing `shouldBe` Left "taprootSigHash: SIGHASH_SINGLE without an output at the input index"++    it "taprootSigHash commits to the annex and to the leaf of a script path spend" $ do+      let tx = testKeyPath.unsignedTx+          spent = testKeyPath.spent+          digest annex leaf = fmap (encodeHex . runPutS . serialize) (taprootSigHash tx spent 0 sigHashDefault annex leaf)+          leaf = leafHash tapscriptLeafVersion (Script [OP_1])+      digest Nothing Nothing `shouldNotBe` digest (Just "\x50") Nothing+      digest Nothing Nothing `shouldNotBe` digest Nothing (Just (leaf, 0xffffffff))+      digest Nothing (Just (leaf, 0xffffffff)) `shouldNotBe` digest Nothing (Just (leaf, 0))++  describe "Taproot signatures" $ do+    it "are 64 bytes with the default hash type, and 65 with another" $ do+      let sig = fromJust (schnorrSig (BS.replicate 64 7))+      fmap encodeTaprootSig (taprootSignature sig sigHashDefault) `shouldBe` Right (BS.replicate 64 7)+      fmap encodeTaprootSig (taprootSignature sig sigHashAll) `shouldBe` Right (BS.replicate 64 7 <> "\x01")+      fmap encodeTaprootSig (taprootSignature sig (setAnyoneCanPay sigHashSingle)) `shouldBe` Right (BS.replicate 64 7 <> "\x83")+      fmap (\s -> (s.sig, s.hash)) (decodeTaprootSig (BS.replicate 64 7)) `shouldBe` Right (sig, sigHashDefault)+      fmap (\s -> (s.sig, s.hash)) (decodeTaprootSig (BS.replicate 64 7 <> "\x02")) `shouldBe` Right (sig, sigHashNone)++    it "are not built with a hash type that Taproot does not define" $ do+      let sig = fromJust (schnorrSig (BS.replicate 64 7))+      taprootSignature sig 0x04 `shouldBe` Left "taprootSignature: invalid hash type 4"+      taprootSignature sig 0x41 `shouldBe` Left "taprootSignature: invalid hash type 65"+      taprootSignature sig 0x80 `shouldBe` Left "taprootSignature: invalid hash type 128"+      -- Cut to one byte, 0x100 would be a 65-byte signature ending in 00,+      -- which BIP 341 forbids, and 0x101 would say SIGHASH_ALL+      taprootSignature sig 0x100 `shouldBe` Left "taprootSignature: invalid hash type 256"+      taprootSignature sig 0x101 `shouldBe` Left "taprootSignature: invalid hash type 257"++    it "of each of the seven hash types are 64 or 65 bytes that read back as the same signature" $ do+      let sig = fromJust (schnorrSig (BS.replicate 64 7))+      forM_ [(0x00, 64), (0x01, 65), (0x02, 65), (0x03, 65), (0x81, 65), (0x82, 65), (0x83, 65)] $ \(hashType, size) -> do+        signature <- expectRight (taprootSignature sig hashType)+        BS.length (encodeTaprootSig signature) `shouldBe` size+        decodeTaprootSig (encodeTaprootSig signature) `shouldBe` Right signature++    it "are refused with a hash type byte of zero, an undefined one, or another length" $ do+      decodeTaprootSig (BS.replicate 64 7 <> "\x00") `shouldBe` Left "decodeTaprootSig: invalid hash type byte 0"+      decodeTaprootSig (BS.replicate 64 7 <> "\x04") `shouldBe` Left "decodeTaprootSig: invalid hash type byte 4"+      decodeTaprootSig (BS.replicate 64 7 <> "\x41") `shouldBe` Left "decodeTaprootSig: invalid hash type byte 65"+      decodeTaprootSig (BS.replicate 63 7) `shouldBe` Left "decodeTaprootSig: a signature is 64 or 65 bytes, not 63"+      decodeTaprootSig (BS.replicate 66 7) `shouldBe` Left "decodeTaprootSig: a signature is 64 or 65 bytes, not 66"++    it "may use the default, ALL, NONE and SINGLE hash types, with ANYONECANPAY or not, and no other" $ do+      filter isTaprootSigHash [0 .. 0xff] `shouldBe` [0x00, 0x01, 0x02, 0x03, 0x81, 0x82, 0x83]+      sigHashDefault `shouldBe` 0++  describe "Witnesses" $ do+    let (_, p) = bip86Key ctx+        -- The internal key as a control block gives it back: the point of even y+        internalKey = fromJust (importXOnlyPubKey ctx (exportXOnlyPubKey ctx p.point))+        leaf = Script [OP_2DROP, OP_1]+        tree = MASTBranch (MASTLeaf tapscriptLeafVersion leaf) (MASTLeaf tapscriptLeafVersion (Script [OP_1]))+        output = TaprootOutput internalKey (Just tree)+        outputKey = taprootOutputKey ctx output+        [(_, _, proof), _] = getMerkleProofs tree+        spend = ScriptPathData {annex = Nothing, stack = ["aa", "bbbb"], script = leaf, extIsOdd = odd (keyParity ctx outputKey), leafVersion = tapscriptLeafVersion, internalKey, control = proof, opaqueScript = Nothing}+        control = encodeControlBlock ctx (tapscriptLeafVersion + keyParity ctx outputKey) internalKey proof+        scriptBytes = runPutS (serialize leaf)++    it "a key path spend is one signature, with the annex after it when there is one" $ do+      encodeTaprootWitness ctx (KeyPathSpend "sig" Nothing) `shouldBe` Right ["sig"]+      encodeTaprootWitness ctx (KeyPathSpend "sig" (Just "\x50\&annex")) `shouldBe` Right ["sig", "\x50\&annex"]+      viewTaprootWitness ctx ["sig"] `shouldBe` Just (KeyPathSpend "sig" Nothing)+      viewTaprootWitness ctx ["sig", "\x50\&annex"] `shouldBe` Just (KeyPathSpend "sig" (Just "\x50\&annex"))+      -- One item is the signature even when it starts with the annex byte+      viewTaprootWitness ctx ["\x50sig"] `shouldBe` Just (KeyPathSpend "\x50sig" Nothing)+      viewTaprootWitness ctx [] `shouldBe` Nothing++    it "a script path spend is the stack, the script and the control block, in the order of BIP 341, then the annex" $ do+      encodeTaprootWitness ctx (ScriptPathSpend spend) `shouldBe` Right ["aa", "bbbb", scriptBytes, control]+      encodeTaprootWitness ctx (ScriptPathSpend spend {annex = Just "\x50"}) `shouldBe` Right ["aa", "bbbb", scriptBytes, control, "\x50"]+      viewTaprootWitness ctx ["aa", "bbbb", scriptBytes, control] `shouldBe` Just (ScriptPathSpend spend)+      viewTaprootWitness ctx ["aa", "bbbb", scriptBytes, control, "\x50"] `shouldBe` Just (ScriptPathSpend spend {annex = Just "\x50"})+      viewTaprootWitness ctx [scriptBytes, control] `shouldBe` Just (ScriptPathSpend spend {stack = []})+      -- The control block and the script in the other order are not a+      -- spend: the script is not a control block+      viewTaprootWitness ctx [control, scriptBytes] `shouldBe` Nothing++    -- BIP 342: a Tapscript that holds an OP_SUCCESSx opcode succeeds, "even+    -- if later bytes in the tapscript would fail to decode otherwise". 50 is+    -- OP_SUCCESS80, and 4c is an OP_PUSHDATA1 with no length after it.+    it "a script path spend is read whatever the bytes of its script are" $ do+      let opaque = "\x50\x4c"+          opaqueControl = encodeControlBlock ctx 0xc0 internalKey []+          expected = ScriptPathData {annex = Nothing, stack = [], script = Script [], extIsOdd = False, leafVersion = 0xc0, internalKey, control = [], opaqueScript = Just opaque}+      viewTaprootWitness ctx [opaque, opaqueControl] `shouldBe` Just (ScriptPathSpend expected)+      scriptPathScriptBytes expected `shouldBe` opaque+      encodeTaprootWitness ctx (ScriptPathSpend expected) `shouldBe` Right [opaque, opaqueControl]+      -- A leaf of another version, whose bytes are a push cut short+      fmap (\case ScriptPathSpend d -> (d.leafVersion, d.opaqueScript); _ -> (0, Nothing)) (viewTaprootWitness ctx ["\x02\xaa", encodeControlBlock ctx 0xc2 internalKey []])+        `shouldBe` Just (0xc2, Just "\x02\xaa")++    it "a script path spend of bytes that are not opcodes verifies against the output key that commits to those bytes" $ do+      let opaque = "\x50\x4c"+          key = taprootOutputKey ctx (TaprootOutput internalKey (Just (MASTCommitment (leafHashBytes 0xc0 opaque))))+          opaqueSpend = ScriptPathData {annex = Nothing, stack = [], script = Script [], extIsOdd = odd (keyParity ctx key), leafVersion = 0xc0, internalKey, control = [], opaqueScript = Just opaque}+      verifyScriptPathData ctx key opaqueSpend `shouldBe` True+      verifyScriptPathData ctx key opaqueSpend {opaqueScript = Just "\x50\x4d"} `shouldBe` False++    it "a witness is not written with an annex that is empty or does not start with 0x50" $ do+      encodeTaprootWitness ctx (KeyPathSpend "sig" (Just "hello")) `shouldBe` Left "encodeTaprootWitness: an annex starts with 0x50"+      encodeTaprootWitness ctx (KeyPathSpend "sig" (Just "")) `shouldBe` Left "encodeTaprootWitness: an annex starts with 0x50"+      encodeTaprootWitness ctx (ScriptPathSpend spend {annex = Just "hello"}) `shouldBe` Left "encodeTaprootWitness: an annex starts with 0x50"++    it "a witness is written with a proof of 128 hashes, and not with one of 129" $ do+      let node = fromJust (digestFromByteString @SHA256 (BS.replicate 32 1))+      -- two stack items, the script and the control block+      fmap length (encodeTaprootWitness ctx (ScriptPathSpend spend {control = replicate 128 node})) `shouldBe` Right 4+      encodeTaprootWitness ctx (ScriptPathSpend spend {control = replicate 129 node}) `shouldBe` Left "encodeTaprootWitness: a control block holds at most 128 nodes"++    it "a witness is not written with a leaf version that a control block cannot carry" $ do+      encodeTaprootWitness ctx (ScriptPathSpend spend {leafVersion = 0xc1}) `shouldBe` Left "encodeTaprootWitness: a control block cannot carry the leaf version 193"+      encodeTaprootWitness ctx (ScriptPathSpend spend {leafVersion = 0x50}) `shouldBe` Left "encodeTaprootWitness: a control block cannot carry the leaf version 80"++    it "a script path spend verifies against its output key, and not with the other parity or another proof" $ do+      verifyScriptPathData ctx outputKey spend `shouldBe` True+      verifyScriptPathData ctx outputKey spend {extIsOdd = not spend.extIsOdd} `shouldBe` False+      verifyScriptPathData ctx outputKey spend {control = []} `shouldBe` False+      verifyScriptPathData ctx outputKey spend {script = Script [OP_1]} `shouldBe` False+      verifyScriptPathData ctx (taprootOutputKey ctx (TaprootOutput internalKey Nothing)) spend `shouldBe` False++    it "a control block is 33 bytes plus 32 per node, with at most 128 nodes" $ do+      let node = BS.replicate 32 1+          base = BS.take 33 control+          short = BS.take 20 control+          tooLong = control <> "junk"+          deepest = base <> BS.concat (replicate 128 node)+          tooDeep = base <> BS.concat (replicate 129 node)+      maxControlBlockNodes `shouldBe` 128+      fmap (\(v, k, nodes) -> (v, exportXOnlyPubKey ctx k == exportXOnlyPubKey ctx internalKey, length nodes)) (decodeControlBlock ctx deepest) `shouldBe` Right (tapscriptLeafVersion + keyParity ctx outputKey, True, 128)+      decodeControlBlock ctx short `shouldBe` Left "decodeControlBlock: a control block is 33 bytes plus 32 per node, not 20"+      decodeControlBlock ctx tooLong `shouldBe` Left "decodeControlBlock: a control block is 33 bytes plus 32 per node, not 69"+      decodeControlBlock ctx tooDeep `shouldBe` Left "decodeControlBlock: more than 128 nodes"+      viewTaprootWitness ctx [scriptBytes, short] `shouldBe` Nothing+      viewTaprootWitness ctx [scriptBytes, tooLong] `shouldBe` Nothing+      viewTaprootWitness ctx [scriptBytes, tooDeep] `shouldBe` Nothing+      fmap (\case ScriptPathSpend d -> length d.control; _ -> 0) (viewTaprootWitness ctx [scriptBytes, deepest]) `shouldBe` Just 128++    it "a control block starts with the leaf version and the parity bit, then the x-only internal key" $+      encodeHex (encodeControlBlock ctx 0xc1 p.point [])+        `shouldBe` "c1cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"++    prop "encoding a witness and viewing it gives the witness back" $+      forAll ((,) <$> arbitraryBS1 <*> arbitraryMaybe arbitraryBS) $ \(sig, annexData) ->+        let annex = ("\x50" <>) <$> annexData+            spendWith a = ScriptPathSpend spend {annex = a}+            viewed witness = viewTaprootWitness ctx =<< eitherToMaybe (encodeTaprootWitness ctx witness)+         in (viewed (KeyPathSpend sig annex), viewed (spendWith annex))+              === (Just (KeyPathSpend sig annex), Just (spendWith annex))++  describe "Script trees as their leaves" $ do+    let a = MASTLeaf 0xc0 (Script [OP_1])+        b = MASTLeaf 0xc0 (Script [OP_2])+        c = MASTLeaf 0xc0 (Script [OP_3])+        leafOf d (MASTLeaf v s) = TapLeaf d v s+        leafOf _ _ = error "not a leaf"+    it "lists the leaves depth first with their depth" $ do+      tapTreeLeaves (MASTBranch (MASTBranch a b) c) `shouldBe` Just [leafOf 2 a, leafOf 2 b, leafOf 1 c]+      tapTreeLeaves a `shouldBe` Just [leafOf 0 a]+      tapTreeLeaves (MASTBranch a (MASTCommitment (mastCommitment b))) `shouldBe` Nothing+    it "builds the tree back from its leaves, with the same commitment" $ do+      let tree = MASTBranch (MASTBranch a b) c+      fmap mastCommitment (tapTreeFromLeaves [leafOf 2 a, leafOf 2 b, leafOf 1 c]) `shouldBe` Just (mastCommitment tree)+      fmap mastCommitment (tapTreeFromLeaves [leafOf 0 a]) `shouldBe` Just (mastCommitment a)+      fmap mastCommitment (tapTreeFromLeaves [leafOf 1 a, leafOf 2 b, leafOf 2 c]) `shouldBe` Just (mastCommitment (MASTBranch a (MASTBranch b c)))+    it "refuses leaves that do not make one tree" $ do+      tapTreeFromLeaves [] `shouldBe` Nothing+      tapTreeFromLeaves [leafOf 1 a] `shouldBe` Nothing+      tapTreeFromLeaves [leafOf 1 a, leafOf 1 b, leafOf 1 c] `shouldBe` Nothing++  describe "X-only keys" $ do+    it "serialize to the x coordinate, and read back as the point of even y" $ do+      let (_, p) = bip86Key ctx+          x = exportXOnlyPubKey ctx p.point+      marshal ctx (XOnlyPubKey p.point) `shouldBe` x+      fmap (keyParity ctx . (.point)) (unmarshal ctx x :: Either String XOnlyPubKey) `shouldBe` Right 0+      fmap (XOnlyPubKey p.point ==) (unmarshal ctx x) `shouldBe` Right True+      unmarshalJSON ctx (marshalJSON ctx (XOnlyPubKey p.point)) `shouldBe` Just (XOnlyPubKey p.point)+      (unmarshal ctx (BS.replicate 32 0xff) :: Either String XOnlyPubKey) `shouldSatisfy` isLeft+    it "the parity of a key is that of its y coordinate" $ do+      keyParity ctx (fromJust (importPubKey ctx (hexBytes "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))) `shouldBe` 0+      keyParity ctx (fromJust (importPubKey ctx (hexBytes "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))) `shouldBe` 1+    it "the Tapscript leaf version is 0xc0" $+      tapscriptLeafVersion `shouldBe` 0xc0++  describe "Leaf hashes" $ do+    -- BIP 341: the tapleaf hash is hash_TapLeaf(v || compact_size(size of s) || s).+    -- The literals were computed from that sentence with Python's hashlib:+    -- SHA256(SHA256("TapLeaf") || SHA256("TapLeaf") || c0 || length || script),+    -- for a script of n bytes 0x61 (OP_NOP).+    it "of a script of 127 bytes, whose length is written 7f" $+      digestHex (leafHash 0xc0 (Script (replicate 127 OP_NOP))) `shouldBe` "768beb2ec05d13f86bef3b81dda9ce0b4f22d82a6a5ec5ab5a5f2ca6da6567d0"+    it "of a script of 128 bytes, whose length is written 80" $+      digestHex (leafHash 0xc0 (Script (replicate 128 OP_NOP))) `shouldBe` "8c247c24a8b5def0f3cc6fe79098bf761275c4c4e1b27739d6b5caf7fabe0ef7"+    it "of a script of 252 bytes, whose length is written fc" $+      digestHex (leafHash 0xc0 (Script (replicate 252 OP_NOP))) `shouldBe` "bed46d76590727c80034f9c383ca18e25e1c71ede72a134ccf772d2852747408"+    it "of a script of 253 bytes, whose length is written fd fd 00" $+      digestHex (leafHash 0xc0 (Script (replicate 253 OP_NOP))) `shouldBe` "03a32feb39d860fbc91750ba7ad075ce987a0b1a86828115748e07e32e3dfc5d"++    -- tr(cba52b43…6970,multi_a(2,b2fff6fd…,53f8149c…,a07f1b7d…,b7e3bfb9…)).+    -- Bitcoin Core 32.1 gives this leaf hash and this output script+    -- (decodepsbt and deriveaddresses on regtest). The leaf is 138 bytes:+    -- 4 keys × 34 bytes + OP_2 + OP_NUMEQUAL.+    it "of the 138-byte multi_a leaf is the one Bitcoin Core derives, and so is the output script" $ do+      let leaf = scriptOf multiALeaf+          internal = fromJust (importXOnlyPubKey ctx (hexBytes "cba52b4389f798ec8ba66d9520ce532af2fe642b970010e2fbd0ec36bce36970"))+      BS.length (hexBytes multiALeaf) `shouldBe` 138+      digestHex (leafHash 0xc0 leaf) `shouldBe` "c6831caa9b274f379336d6bcc789aa6e82760d573e3750b97df37e624d8e6c90"+      encodeHex (marshal ctx (taprootScriptOutput ctx (TaprootOutput internal (Just (MASTLeaf 0xc0 leaf)))))+        `shouldBe` "51207fd815afed80f6bc581ac94aa52d85c50d2bddff7347e1094936eb71e2f18936"++    it "of script bytes that are not a list of opcodes is taken on the bytes" $+      -- 50 4c is OP_SUCCESS80 followed by an OP_PUSHDATA1 with no length.+      -- SHA256(tag || tag || c0 || 02 || 50 4c), computed with Python's hashlib.+      digestHex (leafHashBytes 0xc0 "\x50\x4c") `shouldBe` "070bb467edca609e1a3246a8ba31e6266509d0d4e74e5fb2a32359b5f1565272"++  -- Versions up to 1.5.1 wrote the script length of a leaf as a base-128+  -- varint. For a leaf of 128 bytes or more that gave another leaf hash,+  -- and so another output key. Coins sent to such an output are still+  -- reachable: the old leaf hash goes in the tree as a commitment. The old+  -- values below were printed by the unfixed library and recomputed in+  -- Python with the base-128 length.+  describe "An output made by versions up to 1.5.1 with a leaf of 128 bytes or more" $ do+    let internalSecret = fromJust (secKey (hexBytes "01c58048b9108d028cdde54d8ea8fa545cd4145157fad6e5a908809c7e35388d"))+        internal = derivePubKey ctx internalSecret+        oldLeafHash = leafHashBefore152 0xc0 (scriptOf multiALeaf)+        xOnly = encodeHex . exportXOnlyPubKey ctx+    it "has a leaf hash that is rebuilt with the base-128 length" $ do+      xOnly internal `shouldBe` "cba52b4389f798ec8ba66d9520ce532af2fe642b970010e2fbd0ec36bce36970"+      digestHex oldLeafHash `shouldBe` "4a0b2a93f8430729d9467c0bc91d26d54b63802e627783bf808694c8e8dbc372"+    it "has the output key of the tree that holds the old leaf hash as a commitment" $+      xOnly (taprootOutputKey ctx (TaprootOutput internal (Just (MASTCommitment oldLeafHash))))+        `shouldBe` "15e87fb4cb019af07cf16fa2b9a8451536bd86321dcc11053c70d9038ec97c80"+    it "is spent by its key path with the secret key tweaked with that commitment" $+      fmap (xOnly . derivePubKey ctx) (taprootOutputSecKey ctx internalSecret (Just (MASTCommitment oldLeafHash)))+        `shouldBe` Just "15e87fb4cb019af07cf16fa2b9a8451536bd86321dcc11053c70d9038ec97c80"+    it "keeps the script path of a short leaf next to the long one" $ do+      -- The tree {pk(1cf7198d…2500), multi_a leaf}+      let short = Script [opPushData (hexBytes "1cf7198d10d0552c8df4d0380372a5fa224a1226fc3a49546a416086442d2500"), OP_CHECKSIG]+          oldTree = MASTBranch (MASTLeaf 0xc0 short) (MASTCommitment oldLeafHash)+          oldKey = taprootOutputKey ctx (TaprootOutput internal (Just oldTree))+          proofs = [proof | (_, _, proof) <- getMerkleProofs oldTree]+          spendOf proof = ScriptPathData {annex = Nothing, stack = [], script = short, extIsOdd = odd (keyParity ctx oldKey), leafVersion = 0xc0, internalKey = internal, control = proof, opaqueScript = Nothing}+      xOnly oldKey `shouldBe` "120ed04aacce013f8ed3b63a53143aec81a4c05fa0a263a693d14aa8bebd7efd"+      map (map digestHex) proofs `shouldBe` [["4a0b2a93f8430729d9467c0bc91d26d54b63802e627783bf808694c8e8dbc372"]]+      map (verifyScriptPathData ctx oldKey . spendOf) proofs `shouldBe` [True]+    it "is not the output of the same tree today" $ do+      let short = Script [opPushData (hexBytes "1cf7198d10d0552c8df4d0380372a5fa224a1226fc3a49546a416086442d2500"), OP_CHECKSIG]+          tree = MASTBranch (MASTLeaf 0xc0 short) (MASTLeaf 0xc0 (scriptOf multiALeaf))+      -- Root and key computed in Python from the text of BIP 341+      digestHex (mastCommitment tree) `shouldBe` "4bff8cf0bd46ff4d4470c11a7f407dfee7078c7675b877691310c0ee0058c620"+      xOnly (taprootOutputKey ctx (TaprootOutput internal (Just tree))) `shouldBe` "b672ffdc20bf79cf4bda88ec7a0ad9c53c91e032f2bc169af425eae9dac8afe0"++  describe "Merkle proofs" $ do+    -- tr(ecd5eddb…7670,{pk(A),{pk(B),{pk(C),pk(D)}}}). The Merkle root and+    -- the four control blocks are what Bitcoin Core 32.1 prints in+    -- decodepsbt for this descriptor. A proof lists the siblings from the+    -- leaf up to the root, as taproot_tree_helper does in BIP 341.+    it "of a tree of depth 3 give each leaf the control block Bitcoin Core gives it" $ do+      let pkLeaf key = MASTLeaf 0xc0 (Script [opPushData (hexBytes key), OP_CHECKSIG])+          a = pkLeaf "1cf7198d10d0552c8df4d0380372a5fa224a1226fc3a49546a416086442d2500"+          b = pkLeaf "d1a2717d14f4798094719c904372393797b88d365766d95a021333fe3e6a526c"+          c = pkLeaf "5807c60bab1f93b36747e3d120c29fa898ab37e616b3b7635b21e5a71888e6da"+          d = pkLeaf "33045c7755fc6aef51859ad00d7393f1d23ff8b0bd6acf0d6c6d7a72502d905a"+          tree = MASTBranch a (MASTBranch b (MASTBranch c d))+          internal = fromJust (importXOnlyPubKey ctx (hexBytes "ecd5eddb647ff233131e292cef048228bce3a3b045319d563b57d59e294c7670"))+          outputKey = taprootOutputKey ctx (TaprootOutput internal (Just tree))+          controlBlocks = [encodeHex (encodeControlBlock ctx (0xc0 + keyParity ctx outputKey) internal proof) | (_, _, proof) <- getMerkleProofs tree]+      digestHex (mastCommitment tree) `shouldBe` "e0f4dd44e6affb70c64661b32d78666ce2296fcdf9abdcbbe9482e91398f4ac5"+      controlBlocks+        `shouldBe` [ "c1ecd5eddb647ff233131e292cef048228bce3a3b045319d563b57d59e294c7670cd8b2619baff8006c3b4f8d86a79d76d2a636b226a43602aa7b17908152a2db6",+                     "c1ecd5eddb647ff233131e292cef048228bce3a3b045319d563b57d59e294c767096de41a8ea89de5ab00d7fe07ca5ef19dc4877e8869b08d70228add11bb98965c0243dd79cee2b5ec8ac1e254869b9a9a0a4a40a684a13e28ec8ef013588a1f3",+                     "c1ecd5eddb647ff233131e292cef048228bce3a3b045319d563b57d59e294c76701995a0d81bdbda264923687c3593a67e5cba9b2adab467777885a6177368f3dbd864c47073b2a8367b8943dde8f76cd8856a071faf86f3bfb2680c6adc1fbddec0243dd79cee2b5ec8ac1e254869b9a9a0a4a40a684a13e28ec8ef013588a1f3",+                     "c1ecd5eddb647ff233131e292cef048228bce3a3b045319d563b57d59e294c7670a6f339703d444033227bbd073048f0821f6c31b9441d7b986af598bdaf3a5c77d864c47073b2a8367b8943dde8f76cd8856a071faf86f3bfb2680c6adc1fbddec0243dd79cee2b5ec8ac1e254869b9a9a0a4a40a684a13e28ec8ef013588a1f3"+                   ]++  describe "Any script tree" $ do+    prop "has the Merkle root and the proofs that taproot_tree_helper of BIP 341 gives" $+      forAll (arbitraryMAST True) $ \tree ->+        let (paths, root) = referenceTree tree+         in ( BA.convert (mastCommitment tree) :: ByteString,+              [(runPutS (serialize script), map BA.convert proof) | (_, script, proof) <- getMerkleProofs tree]+            )+              === (root, paths)+    prop "gives each leaf a proof of as many hashes as the leaf is deep, which verifies against the output key" $+      forAll (arbitraryMAST True) $ \tree ->+        let (_, p) = bip86Key ctx+            internal = fromJust (importXOnlyPubKey ctx (exportXOnlyPubKey ctx p.point))+            outputKey = taprootOutputKey ctx (TaprootOutput internal (Just tree))+            spendOf (version, script, proof) = ScriptPathData {annex = Nothing, stack = [], script, extIsOdd = odd (keyParity ctx outputKey), leafVersion = version, internalKey = internal, control = proof, opaqueScript = Nothing}+            proofs = getMerkleProofs tree+         in (map (\(_, _, proof) -> length proof) proofs, map (verifyScriptPathData ctx outputKey . spendOf) proofs)+              === (leafDepths tree, map (const True) proofs)+    prop "without a commitment is built back from the list of its leaves" $+      forAll (arbitraryMAST False) $ \tree ->+        fmap mastCommitment (tapTreeFromLeaves =<< tapTreeLeaves tree) === Just (mastCommitment tree)+    prop "lists its leaves with the depths they have" $+      forAll (arbitraryMAST False) $ \tree ->+        fmap (map (fromIntegral . (.depth))) (tapTreeLeaves tree) === Just (leafDepths tree)++  describe "Script path spends from Bitcoin mainnet" $ do+    spends <- runIO (readTestFileParser mainnetSpendsParseJSON "taproot_mainnet_spends.json")+    -- Each spend of the file is in a mainnet block, so its script and its+    -- control block do commit to the output key of the coin it spends.+    it "are read, and each script and control block commit to the output key of the coin spent" $ do+      length spends `shouldBe` 24+      let verdict (txid, spentScript, witness) =+            ( txid,+              case (viewTaprootWitness ctx witness, importXOnlyPubKey ctx (BS.drop 2 spentScript)) of+                (Just (ScriptPathSpend spd), Just outputKey) -> verifyScriptPathData ctx outputKey spd+                _ -> False+            )+      filter (not . snd) (map verdict spends) `shouldBe` []++  describe "The signature digest, on transactions that Bitcoin Core 32.1 mined on regtest" $ do+    let digestOf tx spent hashType annex leaf = fmap (encodeHex . runPutS . serialize) (taprootSigHash tx spent 0 hashType annex leaf)+        coin = TxOut 100000 . hexBytes+        digest32 = fromJust . digestFromByteString @SHA256 . hexBytes+    it "of a key path spend with an annex commits to the annex with its length" $+      digestOf (txOf keyPathAnnexTx) [coin "512028066f61c28af1ecb48c7948f8b5770838eec15223c8697a8e40510c2c29ea04"] sigHashDefault (Just (hexBytes "5068656c6c6f")) Nothing+        `shouldBe` Right "36a6017a9e1b1d246643172c6f0a9fe93136afd5f3e12d43b29a1f52a70a014f"+    it "of a script path spend with an annex and SIGHASH_ALL" $+      digestOf (txOf scriptPathAnnexTx) [coin "5120a354b6c111a5f8339fd8ea5bc6147f8ca829029188e4d3f7622751417328be18"] sigHashAll (Just (hexBytes "50616e6e6578206f662061207363726970742070617468")) (Just (digest32 "3da27bc75f49edd14e69e2cb4a3de1b00d614fd6049f2b5bb3403e39f4dbef51", 0xffffffff))+        `shouldBe` Right "1a8471791448654da927da98e00df97d853a79a5cc9b903ea59562dc2df4d591"+    it "of a script path spend commits to the position of the last OP_CODESEPARATOR executed" $ do+      let tx = txOf codeSeparatorTx+          spent = [coin "512057d7dc3026daad454ab05b7a533b5d3746fc726234ee966d0bc1464f19837426"]+          leaf = digest32 "9e8a23f6730007920039b3d164b85c54f69ff80f103fbf70c837e25918ffec59"+      -- The first key signs before the separator: position 0xffffffff+      digestOf tx spent sigHashDefault Nothing (Just (leaf, 0xffffffff)) `shouldBe` Right "0adc9a14a76ffe44333da52277f6cf8a469f9cd0caaceaf06283f204c06ed7e3"+      -- The second key signs after the separator, which is opcode number 2+      digestOf tx spent sigHashDefault Nothing (Just (leaf, 2)) `shouldBe` Right "057a66c7ba4129c6d30565056a76a6ac7189ebae58ec6ff7ef630ea7ad24bca9"+    it "is refused for an annex that does not start with 0x50, or that is empty" $ do+      let tx = txOf keyPathAnnexTx+          spent = [coin "512028066f61c28af1ecb48c7948f8b5770838eec15223c8697a8e40510c2c29ea04"]+      digestOf tx spent sigHashDefault (Just "hello") Nothing `shouldBe` Left "taprootSigHash: an annex starts with 0x50"+      digestOf tx spent sigHashDefault (Just "") Nothing `shouldBe` Left "taprootSigHash: an annex starts with 0x50"+    it "is refused with SIGHASH_SINGLE at the first input that has no output at its index" $+      -- The transaction of the BIP 341 vector has two outputs: input 2 is+      -- the first input without one+      taprootSigHash testKeyPath.unsignedTx testKeyPath.spent 2 sigHashSingle Nothing Nothing+        `shouldBe` Left "taprootSigHash: SIGHASH_SINGLE without an output at the input index"++  describe "verifyStdTx on Taproot inputs" $ do+    let coinOfTx = oneCoin ctx+    it "accepts the key path spend with an annex that Bitcoin Core 32.1 mined on regtest" $ do+      let tx = txOf keyPathAnnexTx+      verifyStdTx btc ctx tx (coinOfTx tx "512028066f61c28af1ecb48c7948f8b5770838eec15223c8697a8e40510c2c29ea04") `shouldBe` True+    it "accepts the script path spend of a single key leaf with an annex that Bitcoin Core 32.1 mined on regtest" $ do+      let tx = txOf scriptPathAnnexTx+      verifyStdTx btc ctx tx (coinOfTx tx "5120a354b6c111a5f8339fd8ea5bc6147f8ca829029188e4d3f7622751417328be18") `shouldBe` True+    -- The witness holds a valid signature of the leaf key over the leaf+    -- "<key> OP_CHECKSIG", and a control block that names the same key as+    -- internal key. The coin's output key does not commit to that leaf.+    it "refuses a script path spend whose leaf the output key of the coin does not commit to" $ do+      let tx = txOf uncommittedLeafTx+      verifyStdTx btc ctx tx (coinOfTx tx "5120da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21") `shouldBe` False+    it "refuses a Taproot input that has an input script" $ do+      let tx = txOf keyPathAnnexTx+          withScript = tx {inputs = [i {script = "\x51"} | i <- tx.inputs]} :: Tx+      verifyStdTx btc ctx withScript (coinOfTx tx "512028066f61c28af1ecb48c7948f8b5770838eec15223c8697a8e40510c2c29ea04") `shouldBe` False++    -- BIP 342 defines the rules of leaf version 0xc0 only. A leaf of another+    -- version is not a Tapscript, so its key check is not something this+    -- function can vouch for.+    it "verifies the script path spend of a single key leaf of version 0xc0, and not of version 0xc2" $ do+      let (leafSecret, leafPublic) = bip86Key ctx+          internal = fromJust (importXOnlyPubKey ctx (hexBytes "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))+          leafScript = Script [opPushData (exportXOnlyPubKey ctx leafPublic.point), OP_CHECKSIG]+          outpoint = OutPoint "1111111111111111111111111111111111111111111111111111111111111111" 0+          spendWith version = do+            let output = TaprootOutput internal (Just (MASTLeaf version leafScript))+                coinScript = taprootScriptOutput ctx output+                unsigned = buildTx ctx [outpoint] [(coinScript, 99000)]+            digest <- taprootSigHash unsigned [TxOut 100000 (marshal ctx coinScript)] 0 sigHashDefault Nothing (Just (leafHash version leafScript, 0xffffffff))+            signature <- taprootSignature (signSchnorr ctx leafSecret.key (hashToMsg digest)) sigHashDefault+            witness <-+              encodeTaprootWitness ctx . ScriptPathSpend $+                ScriptPathData {annex = Nothing, stack = [encodeTaprootSig signature], script = leafScript, extIsOdd = odd (keyParity ctx (taprootOutputKey ctx output)), leafVersion = version, internalKey = internal, control = [], opaqueScript = Nothing}+            return (verifyStdTx btc ctx (Tx unsigned.version unsigned.inputs unsigned.outputs [witness] unsigned.locktime) [(coinScript, 100000, outpoint)])+      spendWith 0xc0 `shouldBe` Right True+      spendWith 0xc2 `shouldBe` Right False++  describe "Control blocks" $+    it "are refused when the internal key is not the x coordinate of a point" $+      -- ff…ff is above the field size, so it is no x coordinate+      decodeControlBlock ctx ("\xc0" <> BS.replicate 32 0xff) `shouldBe` Left "decodeControlBlock: the internal key is not an x-only public key"++  describe "X-only keys as text and bytes" $ do+    let x = "7a338b2a113a14b5a008560cbf5d3c46a6c282967c05f739f30668d32fc538b5"+        json t = unmarshalJSON ctx (encode (String t)) :: Maybe XOnlyPubKey+    it "are read from the hex of 32 bytes" $+      fmap (encodeHex . marshal ctx) (json x) `shouldBe` Just x+    it "are refused from the hex of 33 bytes, of 64 bytes and of 31 bytes" $ do+      json (x <> "ff") `shouldBe` Nothing+      json (x <> x) `shouldBe` Nothing+      json (Data.Text.dropEnd 2 x) `shouldBe` Nothing+    it "are refused from 33 bytes" $+      (unmarshal ctx (hexBytes x <> "\xff") :: Either String XOnlyPubKey) `shouldSatisfy` isLeft++  describe "The depth limit of a script tree" $ do+    -- BIP 341: a control block holds at most 128 nodes, so a leaf deeper+    -- than 128 can never be spent. Bitcoin Core's TaprootBuilder refuses a+    -- depth above TAPROOT_CONTROL_MAX_NODE_COUNT = 128.+    let leaf = MASTLeaf 0xc0 (Script [OP_1])+        -- A tree whose two deepest leaves are at depth n+        chain :: Int -> MAST+        chain 0 = leaf+        chain n = MASTBranch leaf (chain (n - 1))+        chainLeaves :: Int -> [TapLeaf]+        chainLeaves n = [TapLeaf (fromIntegral d) 0xc0 (Script [OP_1]) | d <- [1 .. n]] <> [TapLeaf (fromIntegral n) 0xc0 (Script [OP_1])]+    it "lists the leaves of a tree whose deepest leaves are at depth 128" $+      fmap (map (.depth)) (tapTreeLeaves (chain 128)) `shouldBe` Just ([1 .. 128] <> [128])+    it "does not list the leaves of a tree whose deepest leaves are at depth 129" $+      fmap length (tapTreeLeaves (chain 129)) `shouldBe` Nothing+    it "does not list the leaves of a tree whose deepest leaves are at depth 256" $+      fmap length (tapTreeLeaves (chain 256)) `shouldBe` Nothing+    it "builds the tree of leaves down to depth 128, and not down to depth 129" $ do+      fmap mastCommitment (tapTreeFromLeaves (chainLeaves 128)) `shouldBe` Just (mastCommitment (chain 128))+      fmap mastCommitment (tapTreeFromLeaves (chainLeaves 129)) `shouldBe` Nothing+    it "checkTapTree accepts a tree down to depth 128 and refuses one down to depth 129" $ do+      checkTapTree (chain 128) `shouldBe` Right ()+      checkTapTree (chain 129) `shouldBe` Left "checkTapTree: a node is deeper than 128"+      checkTapTree (MASTBranch leaf (MASTCommitment (mastCommitment leaf))) `shouldBe` Right ()+    it "checkTapTree refuses a leaf version that a control block cannot carry" $ do+      -- BIP 341: the leaf version is c[0] & 0xfe, so it is even, and it+      -- cannot be 0x50, which would read as an annex+      checkTapTree (MASTLeaf 0xc2 (Script [OP_1])) `shouldBe` Right ()+      checkTapTree (MASTLeaf 0xc1 (Script [OP_1])) `shouldBe` Left "checkTapTree: the leaf version 193 is odd"+      checkTapTree (MASTLeaf 0x50 (Script [OP_1])) `shouldBe` Left "checkTapTree: the leaf version 0x50 would read as an annex"+    it "the proof of a leaf at depth 128 verifies, and the proof of a leaf at depth 129 does not" $ do+      let (_, p) = bip86Key ctx+          internal = fromJust (importXOnlyPubKey ctx (exportXOnlyPubKey ctx p.point))+          deepest tree =+            let outputKey = taprootOutputKey ctx (TaprootOutput internal (Just tree))+                (version, script, proof) = last (getMerkleProofs tree)+                spend = ScriptPathData {annex = Nothing, stack = [], script, extIsOdd = odd (keyParity ctx outputKey), leafVersion = version, internalKey = internal, control = proof, opaqueScript = Nothing}+             in (length proof, verifyScriptPathData ctx outputKey spend)+      deepest (chain 128) `shouldBe` (128, True)+      deepest (chain 129) `shouldBe` (129, False)++-- | The first key of the BIP 86 vectors, m/86'/0'/0'/0/0+bip86Key :: Ctx -> (PrivateKey, PublicKey)+bip86Key ctx = (PrivateKey secret True, PublicKey (derivePubKey ctx secret) True)+  where+    root = fromJust (xPrvImport btc "xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu")+    secret = (derivePath ctx ("m/86'/0'/0'/0/0" :: DerivPath) root).key++-- | The test vectors of BIP 86: path, internal key, output key, address+bip86Vectors :: [(DerivPath, Text, Text, Text)]+bip86Vectors =+  [ ("m/86'/0'/0'/0/0", "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115", "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c", "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"),+    ("m/86'/0'/0'/0/1", "83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145", "a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb", "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"),+    ("m/86'/0'/0'/1/0", "399f1b2f4393f29a18c937859c5dd8a77350103157eb880f02e8c08214277cef", "882d74e5d0572d5a816cef0041a96b6c1de832f6f9676d9605c44d5e9a97d3dc", "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7")+  ]++-- | The leaf hash as versions up to 1.5.1 computed it: the script length is+-- the base-128 varint of the bytes package, where BIP 341 has a CompactSize.+-- The two are the same byte for a length below 128 only.+leafHashBefore152 :: Word8 -> Script -> Digest SHA256+leafHashBefore152 version script =+  hashFinalize . hashUpdate (initTaggedHash "TapLeaf") . runPutS $ do+    putWord8 version+    serialize (Base128.VarInt (BS.length bytes))+    putByteString bytes+  where+    bytes = runPutS (serialize script)++-- | @taproot_tree_helper@ of BIP 341, written from the text of the BIP with+-- SHA256 alone: for each leaf its script bytes and its path, and the hash+-- of the tree. A branch known only by its commitment has no leaves.+referenceTree :: MAST -> ([(ByteString, [ByteString])], ByteString)+referenceTree = \case+  MASTLeaf version script ->+    let bytes = runPutS (serialize script)+     in ([(bytes, [])], tagged "TapLeaf" (BS.singleton version <> compactSize (BS.length bytes) <> bytes))+  MASTBranch l r ->+    let (left, leftHash) = referenceTree l+        (right, rightHash) = referenceTree r+     in ( [(leaf, path <> [rightHash]) | (leaf, path) <- left] <> [(leaf, path <> [leftHash]) | (leaf, path) <- right],+          tagged "TapBranch" (min leftHash rightHash <> max leftHash rightHash)+        )+  MASTCommitment commitment -> ([], BA.convert commitment)+  where+    hash32 = runPutS . serialize . sha256+    tagged tag message = hash32 (hash32 tag <> hash32 tag <> message)+    compactSize :: Int -> ByteString+    compactSize n+      | n < 0xfd = BS.singleton (fromIntegral n)+      | n <= 0xffff = BS.pack [0xfd, fromIntegral (n `mod` 256), fromIntegral (n `div` 256)]+      | otherwise = error "referenceTree: script too long for this test"++-- | The depth of each leaf of a tree, depth first+leafDepths :: MAST -> [Int]+leafDepths = go 0+  where+    go d = \case+      MASTBranch l r -> go (d + 1) l <> go (d + 1) r+      MASTLeaf {} -> [d]+      MASTCommitment {} -> []++-- | A script tree of depth 0 to 6. The scripts are 1 to 600 bytes long, so+-- that their lengths fall on both sides of 128 and of 253, where the+-- CompactSize of the leaf hash changes. With the flag set, one node in ten+-- is known only by its commitment.+arbitraryMAST :: Bool -> Gen MAST+arbitraryMAST withCommitments = choose (0, 6 :: Int) >>= go+  where+    go 0 = node+    go d = frequency [(1, node), (3, MASTBranch <$> go (d - 1) <*> go (d - 1))]+    node+      | withCommitments = frequency [(9, leaf), (1, commitment)]+      | otherwise = leaf+    leaf = MASTLeaf <$> elements [0xc0, 0xc0, 0xc0, 0xc2] <*> script+    commitment = MASTCommitment . fromJust . digestFromByteString . BS.pack <$> vectorOf 32 arbitrary+    script = do+      n <- frequency [(3, choose (1, 127)), (3, choose (128, 252)), (2, choose (253, 600)), (2, elements [127, 128, 252, 253])]+      return (Script (replicate n OP_NOP))++digestHex :: Digest SHA256 -> Text+digestHex = encodeHex . BA.convert++scriptOf :: Text -> Script+scriptOf = either error id . runGetS deserialize . hexBytes++-- | The coin of 100,000 satoshi that the first input of a transaction+-- spends, with the output script given in hex+oneCoin :: Ctx -> Tx -> Text -> [(ScriptOutput, Word64, OutPoint)]+oneCoin ctx tx script = [(either error id (unmarshal ctx (hexBytes script)), 100000, (head tx.inputs).outpoint)]++txOf :: Text -> Tx+txOf = either error id . S.decode . hexBytes++-- | multi_a(2,A,B,C,D) of BIP 387: <A> OP_CHECKSIG <B> OP_CHECKSIGADD <C>+-- OP_CHECKSIGADD <D> OP_CHECKSIGADD OP_2 OP_NUMEQUAL+multiALeaf :: Text+multiALeaf = "20b2fff6fd9636bea638845402774fa41b7ab1a68e757c669949fa2f35d0949fedac2053f8149cd733fed0b0eb50388db3f142638bdc819f6ae6ac5d81d0a8caebc9afba20a07f1b7d9804bdc3cf63f30b8f69b2ef53b0fc68d9253ed61b9c31593f5cb075ba20b7e3bfb9e6cb88e668fee38674cef6d155bba19a5cc7403678bf91a7b7a52725ba529c"++-- | Transactions that Bitcoin Core 32.1 accepted in a regtest block. Each+-- spends one coin of 100,000 satoshi.+keyPathAnnexTx, scriptPathAnnexTx, codeSeparatorTx :: Text+keyPathAnnexTx = "010000000001016a7d9c7f155cc46f63ab350387f74b6a585d7a179941592d6fb2151e384740810100000000ffffffff01d07e010000000000160014cabe51bbc4203c9221901541c9e9d3ac219e6ca80240dac56ae755419956efbb11ff69d46916c956612e03bdafe382e48ca1fc4bdf12fc768daf297548a89d22f19451c08107e763523a88d10b9a5044de20789895d2065068656c6c6f00000000"+scriptPathAnnexTx = "010000000001016be84449b8f2ec0559b321b2aed59ba273f79710a73c5a7e3cbce8e57178c2260100000000ffffffff01d07e010000000000160014cabe51bbc4203c9221901541c9e9d3ac219e6ca80441fe2a5388123bdc28dc0635796683a24418375c1e94b9d9074889d4b6865224dd6a0ae16d63d454d1dcdbd86f7291f5be5ce2d0cb785adddc1cbd9bd9b8cda3bd012220864f1c660fd3842d2b45acae2d2c181378c3c397d9c8f6a36dce7e1b3f2fa01fac21c1bc4d541d1be26036455459e9fd4fb8c9e2b829b17eb18fb5f66bbbdedc1c61681750616e6e6578206f66206120736372697074207061746800000000"+codeSeparatorTx = "01000000000101a1b61db207932647112026e232f793c6d807f666f46ebb23b526e68342630f820100000000ffffffff01d07e010000000000160014cabe51bbc4203c9221901541c9e9d3ac219e6ca8044093e3dfc003f3d11aca5c6301aa52f55fe916b01036710653c56ff829d393489a26834188c932372f93aadf116a55c540f68e049f2b17b69760ea22190b944b57401f03095eb9fe08f9462b86196fccd3dfffc2702c6c0b186593bed2ae31f4b963a5b36a40a4c0c76c3da7caf03f39b358773ce914999a772d638d3f5f18d49aa945201da5064215e4cad73afbc2f5cc5d1c537452b49b3660ab201ef739b808950e5cadab203ef9208927082a726fea24d87dabbb476a30c669f1aa0d98bdfc29ad7e30d33dac21c0db5c3a57b4b60a18c53d60623fc64fbd97c21fe61e9a0508919f1bbe7e658aa900000000"++-- | A script path spend of a coin whose output key does not commit to the+-- leaf. The leaf is "<key> OP_CHECKSIG", its signature is valid, and the+-- control block names the same key as internal key.+uncommittedLeafTx :: Text+uncommittedLeafTx = "0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff01b8820100000000001600149b652a14674a506079f574d20ca7daef6f9a66bb0340ef928ef7a11908475a2f841e482cf8e72e356cccd6f2a1d0c954527fcc2f6f9a49e62e422d55bbacbabb1676fdcc422ff5fc8ad6b0dfcb29219af450769da1602220c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac21c0c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee500000000"++-- | The spends of data/taproot_mainnet_spends.json: transaction id, output+-- script of the coin spent, witness items+mainnetSpendsParseJSON :: Value -> Parser [(Text, ByteString, [ByteString])]+mainnetSpendsParseJSON = withObject "mainnet spends" $ \obj -> do+  spends <- obj .: "spends"+  forM spends $ \s ->+    (,,)+      <$> s .: "txid"+      <*> (s .: "spentScript" >>= jsonHex)+      <*> (s .: "witness" >>= mapM jsonHex)++hexBytes :: Text -> ByteString+hexBytes = fromJust . decodeHex++expectRight :: Either String a -> IO a+expectRight = either fail return++coinsOf :: Ctx -> KeyPathVector -> [(ScriptOutput, Word64, OutPoint)]+coinsOf ctx v =+  [ (either error id (unmarshal ctx coin.script), coin.value, input.outpoint)+  | (input, coin) <- zip v.unsignedTx.inputs v.spent+  ]++testHashes :: TestScriptPubKey -> IO ()+testHashes testData =+  mapM_ checkMASTDetails $ ((.mast) . tspkGiven) testData+  where+    checkMASTDetails theMAST = do+      -- Leaf hashes+      (Just . getLeafHashes) theMAST @?= (spkiLeafHashes . tspkIntermediary) testData+      -- Merkle root+      (Just . BA.convert . mastCommitment) theMAST @?= (spkiMerkleRoot . tspkIntermediary) testData++    getLeafHashes = \case+      MASTBranch branchL branchR -> getLeafHashes branchL <> getLeafHashes branchR+      leaf@MASTLeaf {} -> [BA.convert $ mastCommitment leaf]+      MASTCommitment {} -> mempty -- The test vectors have complete trees++testOutputKey :: Ctx -> TestScriptPubKey -> IO ()+testOutputKey ctx testData = do+  XOnlyPubKey (taprootOutputKey ctx theOutput) @?= theOutputKey+  where+    theOutput = tspkGiven testData+    theOutputKey = XOnlyPubKey . spkiTweakedPubKey $ tspkIntermediary testData++testScriptOutput :: Ctx -> TestScriptPubKey -> IO ()+testScriptOutput ctx testData =+  taprootScriptOutput ctx (tspkGiven testData) @?= (spkeScriptPubKey . tspkExpected) testData++testControlBlocks :: Ctx -> TestScriptPubKey -> IO ()+testControlBlocks ctx testData = do+  mapM_ onExamples exampleControlBlocks+  mapM_ checkVerification scriptPathSpends+  where+    theOutput = tspkGiven testData+    theOutputKey = taprootOutputKey ctx theOutput+    exampleControlBlocks = spkeControlBlocks $ tspkExpected testData+    -- The control block is the last item of the witness: the stack is+    -- empty, so it comes after the script+    calculatedControlBlocks =+      either error last . encodeTaprootWitness ctx . ScriptPathSpend <$> scriptPathSpends+    scriptPathSpends =+      mkScriptPathSpend <$> maybe mempty getMerkleProofs theOutput.mast+    mkScriptPathSpend (leafVersion, script, proof) =+      ScriptPathData+        { annex = Nothing,+          stack = mempty,+          script,+          extIsOdd = odd $ keyParity ctx theOutputKey,+          leafVersion,+          internalKey = theOutput.internalKey,+          control = proof,+          opaqueScript = Nothing+        }+    onExamples expected = map encodeHex calculatedControlBlocks @?= map encodeHex expected+    checkVerification = assertBool "Script verifies" . verifyScriptPathData ctx theOutputKey++testAddress :: Ctx -> TestScriptPubKey -> IO ()+testAddress ctx testData =+  computedAddress @?= (Just . spkeAddress . tspkExpected) testData+  where+    computedAddress =+      (addrToText btc <=< outputAddress btc ctx)+        . taprootScriptOutput ctx+        $ tspkGiven testData++testTreeLeaves :: TestScriptPubKey -> IO ()+testTreeLeaves testData =+  forM_ (tspkGiven testData).mast $ \theMAST -> do+    let leaves = tapTreeLeaves theMAST+    leaves `shouldSatisfy` isJust+    fmap mastCommitment (tapTreeFromLeaves =<< leaves) `shouldBe` Just (mastCommitment theMAST)++newtype SpkGiven = SpkGiven {unSpkGiven :: TaprootOutput}++spkGivenParseJSON :: Ctx -> Value -> Parser SpkGiven+spkGivenParseJSON ctx = withObject "SpkGiven" $ \obj -> do+  pxopk@XOnlyPubKey {} <- unmarshalValue ctx =<< obj .: "internalPubkey"+  tree <- traverse parseScriptTree =<< obj .:? "scriptTree"+  return $ SpkGiven $ TaprootOutput pxopk.point tree+  where+    parseScriptTree v =+      parseScriptLeaf v+        <|> parseScriptBranch v+        <|> fail "Unable to parse scriptTree"+    parseScriptLeaf = withObject "ScriptTree leaf" $ \obj ->+      MASTLeaf+        <$> obj .: "leafVersion"+        <*> (obj .: "script" >>= hexScript)+    parseScriptBranch v =+      parseJSON v >>= \case+        [v1, v2] -> MASTBranch <$> parseScriptTree v1 <*> parseScriptTree v2+        _ -> fail "ScriptTree branch"+    hexScript = either fail pure . runGetS deserialize <=< jsonHex++data SpkIntermediary = SpkIntermediary+  { spkiLeafHashes :: Maybe [ByteString],+    spkiMerkleRoot :: Maybe ByteString,+    spkiTweakedPubKey :: PubKey+  }++spkIntermediaryParseJSON :: Ctx -> Value -> Parser SpkIntermediary+spkIntermediaryParseJSON ctx = withObject "SpkIntermediary" $ \obj ->+  SpkIntermediary+    <$> (obj .:? "leafHashes" >>= (traverse . traverse) jsonHex)+    <*> (obj .: "merkleRoot" >>= traverse jsonHex)+    <*> fmap+      (\(XOnlyPubKey k) -> k)+      (unmarshalValue ctx =<< obj .: "tweakedPubkey")++data SpkExpected = SpkExpected+  { spkeScriptPubKey :: ScriptOutput,+    spkeControlBlocks :: Maybe [ByteString],+    spkeAddress :: Text+  }++spkExpectedParseJSON :: Ctx -> Value -> Parser SpkExpected+spkExpectedParseJSON ctx = withObject "SpkExpected" $ \obj ->+  SpkExpected+    <$> (unmarshalValue ctx =<< obj .: "scriptPubKey")+    <*> ((traverse . traverse) jsonHex =<< obj .:? "scriptPathControlBlocks")+    <*> obj .: "bip350Address"++data TestScriptPubKey = TestScriptPubKey+  { tspkGiven :: TaprootOutput,+    tspkIntermediary :: SpkIntermediary,+    tspkExpected :: SpkExpected+  }++testScriptPubKeyParseJSON :: Ctx -> Value -> Parser TestScriptPubKey+testScriptPubKeyParseJSON ctx = withObject "TestScriptPubKey" $ \obj -> do+  given <- unSpkGiven <$> (spkGivenParseJSON ctx =<< obj .: "given")+  inter <- spkIntermediaryParseJSON ctx =<< obj .: "intermediary"+  expect <- spkExpectedParseJSON ctx =<< obj .: "expected"+  return $ TestScriptPubKey given inter expect++-- | The keyPathSpending vector: the unsigned transaction, the outputs it+-- spends, one case per Taproot input, and the fully signed transaction+data KeyPathVector = KeyPathVector+  { unsignedTx :: Tx,+    spent :: [TxOut],+    cases :: [KeyPathCase],+    signedTx :: Tx+  }++data KeyPathCase = KeyPathCase+  { index :: Int,+    internalSecret :: SecKey,+    merkleRoot :: Maybe (Digest SHA256),+    hashType :: SigHash,+    internalPubKey :: Text,+    tweakedSecret :: Text,+    sigHash :: Text,+    witness :: [Text]+  }++keyPathParseJSON :: Value -> Parser KeyPathVector+keyPathParseJSON = withObject "keyPathSpending" $ \obj -> do+  given <- obj .: "given"+  rawTx <- given .: "rawUnsignedTx" >>= jsonHex+  tx <- either fail return (S.decode rawTx)+  utxos <- given .: "utxosSpent"+  spent <- forM utxos $ \u -> TxOut <$> u .: "amountSats" <*> (u .: "scriptPubKey" >>= jsonHex)+  inputCases <- obj .: "inputSpending"+  cases <- forM inputCases $ \c -> do+    g <- c .: "given"+    i <- c .: "intermediary"+    e <- c .: "expected"+    secretBytes <- g .: "internalPrivkey" >>= jsonHex+    rootM <- g .: "merkleRoot" >>= traverse jsonHex+    KeyPathCase+      <$> g .: "txinIndex"+      <*> maybe (fail "internal private key") return (secKey secretBytes)+      <*> pure (digestFromByteString =<< rootM)+      <*> (fromIntegral <$> (g .: "hashType" :: Parser Word32))+      <*> i .: "internalPubkey"+      <*> i .: "tweakedPrivkey"+      <*> i .: "sigHash"+      <*> e .: "witness"+  aux <- obj .: "auxiliary"+  signedRaw <- aux .: "fullySignedTx" >>= jsonHex+  signed <- either fail return (S.decode signedRaw)+  return (KeyPathVector tx spent cases signed)++data TestVector = TestVector+  { testScriptPubKey :: [TestScriptPubKey],+    testKeyPath :: KeyPathVector+  }++testVectorParseJSON :: Ctx -> Value -> Parser TestVector+testVectorParseJSON ctx = withObject "TestVector" $ \obj ->+  TestVector+    <$> (mapM (testScriptPubKeyParseJSON ctx) =<< obj .: "scriptPubKey")+    <*> (obj .: "keyPathSpending" >>= \case [v] -> keyPathParseJSON v; _ -> fail "one keyPathSpending vector")++jsonHex :: Text -> Parser ByteString+jsonHex = maybe (fail "Unable to decode hex") pure . decodeHex
+ test/Haskoin/Transaction/VerdictSpec.hs view
@@ -0,0 +1,288 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Transaction.VerdictSpec (spec) where++import Crypto.Secp256k1+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (deserialize, serialize)+import Data.Either (rights)+import Data.Maybe (fromJust, mapMaybe)+import Data.Word (Word64, Word8)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++secret :: Word8 -> SecKey+secret n = fromJust (secKey (B.replicate 31 0 <> B.singleton n))++-- | The compressed bytes of the public key of a secret.+pubBytes :: Ctx -> SecKey -> ByteString+pubBytes ctx = exportPubKey ctx True . derivePubKey ctx++coin :: OutPoint+coin = OutPoint "0101010101010101010101010101010101010101010101010101010101010101" 0++-- | A transaction that spends the coin and pays most of 100,000.+spending :: ByteString -> [ByteString] -> Tx+spending inputScript witness =+  Tx+    { version = 2,+      inputs = [TxIn coin inputScript maxBound],+      outputs = [TxOut 90000 (B.replicate 25 0x51)],+      witness = [witness | not (null witness)],+      locktime = 0+    }++-- | An input script that pushes these items.+pushing :: [ByteString] -> ByteString+pushing = runPutS . serialize . Script . map opPushData++-- | Pay one satoshi less: what was signed is no longer what is there.+tampered :: Tx -> Tx+tampered tx = tx {outputs = [o {value = o.value - 1} | o <- tx.outputs]}++ecdsa :: Ctx -> SecKey -> Word8 -> Either String Hash256 -> ByteString+ecdsa ctx k hashType = either error (\h -> exportSig ctx (signHash ctx k h) <> B.singleton hashType)++-- | A Schnorr signature of Bitcoin Cash, made the plain way: this is a+-- test, and the nonce is not a secret worth keeping.+cashSchnorr :: Ctx -> SecKey -> Hash256 -> ByteString+cashSchnorr ctx k h = head (mapMaybe attempt [1 ..])+  where+    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 :: Integer+    p = 2 ^ (256 :: Int) - 2 ^ (32 :: Int) - 977 :: Integer+    word256 i = let b = integerToBS i in B.replicate (32 - B.length b) 0 <> b+    d = bsToInteger k.get+    attempt nonce = do+      r <- derivePubKey ctx <$> secKey (word256 nonce)+      let (x, y) = B.splitAt 32 (B.drop 1 (exportPubKey ctx False r))+          square = powMod (bsToInteger y) ((p - 1) `div` 2) == 1+          e = bsToInteger (runPutS (serialize (sha256 (x <> pubBytes ctx k <> runPutS (serialize h))))) `mod` n+      if square then Just (x <> word256 ((nonce + e * d) `mod` n)) else Nothing+    powMod _ 0 = 1 :: Integer+    powMod b i+      | even i = let half = powMod b (i `div` 2) in half * half `mod` p+      | otherwise = b * powMod b (i - 1) `mod` p++spec :: Spec+spec = prepareContext $ \ctx -> do+  let k = secret 7+      pub = pubBytes ctx k+      h160 = addressHash pub+      value = 100000 :: Word64+      prev so = Just (TxOut value (marshal ctx so))+      pkhScript = either error id (encodeOutput ctx (PayPKHash h160))+      three net tx prevOut = do+        judgeInputs net ctx tx [prevOut] `shouldBe` [InputVerified]+        judgeInputs net ctx (tampered tx) [prevOut] `shouldBe` [InputForged]+        judgeInputs net ctx tx [Nothing] `shouldBe` [InputUndecided]++  describe "judgeInputs on coins of one key" $ do+    it "tells a key hash input that verifies from a forged one, and from one it cannot see" $ do+      let unsigned = spending "" []+          sig = ecdsa ctx k 0x01 (txSigHash btc unsigned pkhScript value 0 sigHashAll)+      three btc (spending (pushing [sig, pub]) []) (prev (PayPKHash h160))+    it "says forged of another key than the one the coin names" $ do+      let other = secret 8+          unsigned = spending "" []+          sig = ecdsa ctx other 0x01 (txSigHash btc unsigned pkhScript value 0 sigHashAll)+      judgeInputs btc ctx (spending (pushing [sig, pubBytes ctx other]) []) [prev (PayPKHash h160)]+        `shouldBe` [InputForged]+    it "does the same for a witness key hash, plain and nested" $ do+      let unsigned = spending "" []+          sig = ecdsa ctx k 0x01 (txSigHashForkId btc unsigned pkhScript value 0 sigHashAll)+          program = marshal ctx (PayWitnessPKHash h160)+      three btc (spending "" [sig, pub]) (prev (PayWitnessPKHash h160))+      three btc (spending (pushing [program]) [sig, pub]) (prev (PayScriptHash (addressHash program)))+    it "does the same for a Taproot key" $ do+      let outKey = B.drop 1 pub+          out = TxOut value (marshal ctx (PayWitness 1 outKey))+          -- the secret of the key with an even y, whichever of the two it is+          signed = head (rights [signTaprootInput ctx (spending "" []) [out] 0 sigHashDefault s | s <- [k, negated]])+          negated = fromJust (secKey . (.get) =<< tweakNegate ctx =<< tweak k.get)+      three btc signed (Just out)+    it "leaves a hash type other than all, and a script, to a node" $ do+      let unsigned = spending "" []+          none = ecdsa ctx k 0x02 (txSigHash btc unsigned pkhScript value 0 sigHashNone)+      judgeInputs btc ctx (tampered (spending (pushing [none, pub]) [])) [prev (PayPKHash h160)]+        `shouldBe` [InputVerified]+      judgeInputs btc ctx (spending (pushing ["anything"]) []) [prev (PayScriptHash h160)]+        `shouldBe` [InputUndecided]++  describe "judgeInputs on Bitcoin Cash" $ do+    let unsigned = spending "" []+        digest prefix = txSigHashCashToken bch unsigned prefix pkhScript value 0 (setForkIdFlag sigHashAll)+        tokens = B.singleton 0xef <> B.replicate 32 0xaa <> B.pack [0x10, 0xfd, 0xe8, 0x03]+    it "reads a Schnorr signature" $ do+      let sig = cashSchnorr ctx k (either error id (digest "")) <> B.singleton 0x41+      B.length sig `shouldBe` 65+      three bch (spending (pushing [sig, pub]) []) (prev (PayPKHash h160))+    it "reads an ECDSA signature" $+      three bch (spending (pushing [ecdsa ctx k 0x41 (digest ""), pub]) []) (prev (PayPKHash h160))+    it "reads a coin that holds tokens, whose digest covers them" $ do+      let withTokens = Just (TxOut value (tokens <> marshal ctx (PayPKHash h160)))+      three bch (spending (pushing [ecdsa ctx k 0x41 (digest tokens), pub]) []) withTokens+      three bch (spending (pushing [cashSchnorr ctx k (either error id (digest tokens)) <> "\x41", pub]) []) withTokens+      -- signed as if the coin held none: not a signature of this coin+      judgeInputs bch ctx (spending (pushing [ecdsa ctx k 0x41 (digest ""), pub]) []) [withTokens]+        `shouldBe` [InputForged]++  describe "judgeInputs on what Bitcoin Cash Node signed" $ do+    -- Two transactions of the wallet of Bitcoin Cash Node 29.2.0 on+    -- regtest, both with Schnorr signatures. The first spends a coinbase+    -- of 50 coins to the same key and makes 1,000 fungible tokens. The+    -- second spends that output, tokens and all.+    let parse h = either error id (runGetS deserialize (fromJust (decodeHex h))) :: Tx+        genesis =+          parse+            "0200000001f28fd1cdb4293ae6bb76e580c5a3469fb926690471f3ffb798ef0a\+          \cda9886525000000006441e31c237fe3a1a45f6c32c779c78025f29bd735fa44\+          \d18c182bc1c08fbbcab25f08cbd8c1937dccd38acf37ef2bf8907e75029d622c\+          \5a9f6f5161ffe534fa2775412103bcacac8993931b1f0867991841a140425906\+          \839d26c4c906a737a4f2f29dd79effffffff01c0aff629010000003eeff28fd1\+          \cdb4293ae6bb76e580c5a3469fb926690471f3ffb798ef0acda988652510fde8\+          \0376a9145dd6bc82b5a42053b8f7802e68d296ac3578537a88ac00000000"+        spend =+          parse+            "0200000001db5f2a09c11a2aaceb0d9bfa755006380257d48dab94a73cfd4d69\+          \c2e2d122a6000000006441f52f838cf46e41b8c630334d2b6edefdb49f67fd19\+          \45ee864321da50b1fa652b6b2db4b517110ee5598cbf5193eac763a56d59bfe2\+          \e20d8f3fb9af4b08bfe340412103bcacac8993931b1f0867991841a140425906\+          \839d26c4c906a737a4f2f29dd79effffffff01806de729010000003eeff28fd1\+          \cdb4293ae6bb76e580c5a3469fb926690471f3ffb798ef0acda988652510fde8\+          \0376a9145dd6bc82b5a42053b8f7802e68d296ac3578537a88ac00000000"+        coinbase = TxOut 5000000000 (fromJust (decodeHex "76a9145dd6bc82b5a42053b8f7802e68d296ac3578537a88ac"))+    it "verifies its Schnorr signature of a coin without tokens" $ do+      judgeInputs bchRegTest ctx genesis [Just coinbase] `shouldBe` [InputVerified]+      judgeInputs bchRegTest ctx (tampered genesis) [Just coinbase] `shouldBe` [InputForged]+    it "verifies its Schnorr signature of a coin that holds tokens" $ do+      let held = head genesis.outputs+      fmap fst (splitTokenPrefix held.script) `shouldSatisfy` maybe False (not . B.null)+      judgeInputs bchRegTest ctx spend [Just held] `shouldBe` [InputVerified]+      judgeInputs bchRegTest ctx (tampered spend) [Just held] `shouldBe` [InputForged]+      -- the same coin without its tokens is another coin+      let bare = held {script = maybe held.script snd (splitTokenPrefix held.script)} :: TxOut+      judgeInputs bchRegTest ctx spend [Just bare] `shouldBe` [InputForged]++  describe "judgeInputs on a multisig of Bitcoin Cash signed with Schnorr signatures" $ do+    let secrets = map secret [11, 12, 13]+        keys = map (wrapPubKey True . derivePubKey ctx) secrets+        twoOfThree = PayMulSig keys 2+        redeem = marshal ctx twoOfThree+        locked = prev (PayScriptHash (addressHash redeem))+        code = either error id (encodeOutput ctx twoOfThree)+        digest = either error id (txSigHashForkId bch (spending "" []) code value 0 (setForkIdFlag sigHashAll))+        sign s = cashSchnorr ctx s digest <> B.singleton 0x41+        -- the field of bits is a small number, pushed by its own opcode+        spendWith bits sigs =+          spending (runPutS (serialize (Script ([bits] <> map opPushData (sigs <> [redeem]))))) []+        verdict tx = judgeInputs bch ctx tx [locked]+    it "verifies the signatures of the keys that the bits name, in the order of the keys" $ do+      verdict (spendWith OP_5 [sign (head secrets), sign (secrets !! 2)]) `shouldBe` [InputVerified]+      verdict (spendWith OP_3 [sign (head secrets), sign (secrets !! 1)]) `shouldBe` [InputVerified]+      verdict (spendWith OP_6 [sign (secrets !! 1), sign (secrets !! 2)]) `shouldBe` [InputVerified]+    it "does not verify bits that name other keys, too many or too few, or keys past the last" $ do+      let sigs = [sign (head secrets), sign (secrets !! 2)]+      verdict (spendWith OP_3 sigs) `shouldBe` [InputUndecided]+      verdict (spendWith OP_7 sigs) `shouldBe` [InputUndecided]+      verdict (spendWith OP_1 sigs) `shouldBe` [InputUndecided]+      verdict (spendWith OP_9 sigs) `shouldBe` [InputUndecided]+      -- in the other order+      verdict (spendWith OP_5 (reverse sigs)) `shouldBe` [InputUndecided]+    it "does not verify what was not signed, nor an ECDSA signature among them" $ do+      verdict (tampered (spendWith OP_5 [sign (head secrets), sign (secrets !! 2)])) `shouldBe` [InputUndecided]+      let ecdsaSig = ecdsa ctx (head secrets) 0x41 (Right digest)+      verdict (spendWith OP_5 [ecdsaSig, sign (secrets !! 2)]) `shouldBe` [InputUndecided]++  describe "verifyStdInput on a multisig of Taproot" $ do+    let secrets = map secret [11, 12, 13]+        xOnly = B.drop 1 . pubBytes ctx+        leaf =+          Script $+            [opPushData (xOnly (head secrets)), OP_CHECKSIG]+              <> concat [[opPushData (xOnly s), OP_CHECKSIGADD] | s <- drop 1 secrets]+              <> [OP_2, OP_NUMEQUAL]+        internal = derivePubKey ctx (secret 14)+        tree = TaprootOutput {internalKey = internal, mast = Just (MASTLeaf tapscriptLeafVersion leaf)}+        out = TxOut value (marshal ctx (taprootScriptOutput ctx tree))+        unsigned = spending "" []+        leafOf = Just (leafHash tapscriptLeafVersion leaf, 0xffffffff)+        digest = either error id (taprootSigHash unsigned [out] 0 sigHashDefault Nothing leafOf)+        sign s = (signSchnorr ctx s (hashToMsg digest)).get+        spendWith items =+          let path =+                ScriptPathSpend+                  ScriptPathData+                    { annex = Nothing,+                      stack = items,+                      script = leaf,+                      extIsOdd = odd (keyParity ctx (taprootOutputKey ctx tree)),+                      leafVersion = tapscriptLeafVersion,+                      internalKey = internal,+                      control = [],+                      opaqueScript = Nothing+                    }+           in unsigned {witness = [either error id (encodeTaprootWitness ctx path)]} :: Tx+        verdict tx = judgeInputs btc ctx tx [Just out]+    it "verifies two signatures of three keys, with an item for each key, the last first" $ do+      verdict (spendWith [sign (secrets !! 2), "", sign (head secrets)]) `shouldBe` [InputVerified]+      verdict (spendWith ["", sign (secrets !! 1), sign (head secrets)]) `shouldBe` [InputVerified]+      let lastTwo = spendWith [sign (secrets !! 2), sign (secrets !! 1), ""]+      verifyStdInput btc ctx lastTwo [(taprootScriptOutput ctx tree, value, coin)] 0 `shouldBe` True+    it "does not verify one signature, three, a signature of another key, or one of other outputs" $ do+      verdict (spendWith ["", "", sign (head secrets)]) `shouldBe` [InputUndecided]+      verdict (spendWith [sign (secrets !! 2), sign (secrets !! 1), sign (head secrets)]) `shouldBe` [InputUndecided]+      -- the signature of the first key where that of the third goes+      verdict (spendWith [sign (head secrets), "", sign (head secrets)]) `shouldBe` [InputUndecided]+      verdict (tampered (spendWith [sign (secrets !! 2), "", sign (head secrets)])) `shouldBe` [InputUndecided]+      -- an item for a key is missing+      verdict (spendWith [sign (secrets !! 1), sign (head secrets)]) `shouldBe` [InputUndecided]++  describe "splitTokenPrefix" $ do+    let category = B.replicate 32 0xaa+        script = "\x76\xa9"+    it "leaves a script without tokens alone" $+      splitTokenPrefix script `shouldBe` Just ("", script)+    it "takes off fungible tokens, a non-fungible one, and one with a commitment" $ do+      let fungible = "\xef" <> category <> "\x10\x05"+          nft = "\xef" <> category <> "\x22"+          committed = "\xef" <> category <> "\x70\x02\xbe\xef\xfd\x00\x01"+      splitTokenPrefix (fungible <> script) `shouldBe` Just (fungible, script)+      splitTokenPrefix (nft <> script) `shouldBe` Just (nft, script)+      splitTokenPrefix (committed <> script) `shouldBe` Just (committed, script)+    it "refuses a prefix that is not well formed" $ do+      -- no tokens at all, a commitment without a token to carry it, an+      -- amount of zero, a number not in its shortest form, and one cut short+      splitTokenPrefix ("\xef" <> category <> "\x00" <> script) `shouldBe` Nothing+      splitTokenPrefix ("\xef" <> category <> "\x50\x01\x00\x01" <> script) `shouldBe` Nothing+      splitTokenPrefix ("\xef" <> category <> "\x10\x00" <> script) `shouldBe` Nothing+      splitTokenPrefix ("\xef" <> category <> "\x10\xfd\x05\x00" <> script) `shouldBe` Nothing+      splitTokenPrefix ("\xef" <> category) `shouldBe` Nothing++  describe "judgeInputs on signed transactions" $+    prop "never says forged of an input that verifies" $+      forAll (elements [btc, bch] >>= \net -> (,) net <$> arbitrarySigningData net ctx) $ \(net, (tx, sigis, keys)) ->+        case signTx net ctx tx sigis (map (.key) keys) of+          Left _ -> property True+          Right signed ->+            let prevOf i = lookup i.outpoint [(s.outpoint, TxOut s.value (marshal ctx s.script)) | s <- sigis]+                verdicts = judgeInputs net ctx signed (map prevOf signed.inputs)+                coins = [(s.script, s.value, s.outpoint) | s <- sigis]+             in counterexample (show verdicts) $+                  InputForged `notElem` verdicts+                    && (not (verifyStdTx net ctx signed coins) || all (== InputVerified) verdicts)
test/Haskoin/TransactionSpec.hs view
@@ -1,326 +1,1455 @@-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.TransactionSpec (spec) where--import qualified Data.ByteString            as B-import           Data.Either-import           Data.Maybe-import           Data.Serialize             as S-import           Data.String                (fromString)-import           Data.String.Conversions-import           Data.Text                  (Text)-import           Data.Word                  (Word32, Word64)-import           Haskoin.Address-import           Haskoin.Constants-import           Haskoin.Keys-import           Haskoin.Script-import           Haskoin.Transaction-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.HUnit-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-    ]--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---- Txid Vectors--testTxidVector :: (Text, Text) -> Assertion-testTxidVector (tid, tx) =-    assertEqual "txid" (Just tid) (txHashToHex . txHash <$> txM)-  where-    txM = eitherToMaybe . S.decode =<< decodeHex tx--txidVectors :: [(Text, Text)]-txidVectors =-    [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"-      , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa4\-        \3ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75\-        \418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb\-        \60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f0000000000\-        \1976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"-      )-    , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"-      , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3\-        \bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a\-        \04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7\-        \d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68\-        \350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b\-        \2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d0000\-        \0000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac000000\-        \00"-      )-    , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"-      , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2\-        \f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332ad\-        \fca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c\-        \70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f\-        \09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919a\-        \d1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737\-        \864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a473044\-        \0220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa\-        \2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f8\-        \1e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc8\-        \0a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd32273276\-        \94c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df3\-        \2949d4646dfa10a92458cfaa88ac00000000"-      )-    , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"-      , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c5\-        \9da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678\-        \fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab\-        \486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b\-        \52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbe\-        \a097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045\-        \022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf9506\-        \50802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f\-        \68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e3\-        \77c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e1\-        \8217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a378\-        \8dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972\-        \d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2\-        \e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffff\-        \ff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9c\-        \fdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675\-        \d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa\-        \34b26f62db4a88ac00000000"-      )-    ]---- Build address transactions vectors generated from bitcoin-core raw tx API--testPKHashVector :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion-testPKHashVector (is, os, res) =-    assertEqual "Build PKHash Tx" (Right res) (encodeHex . S.encode <$> txE)-  where-    txE = buildAddrTx btc (map f is) os-    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix--pkHashVectors :: [([(Text, Word32)], [(Text, Word64)], Text)]-pkHashVectors =-    [ ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"-          , 14)-        ]-      , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 90000000)]-      , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\-        \a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc58\-        \2292b97668bedd91dd5bf9374c88ac00000000")-    , ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"-          , 0)-        , ( "0001000000000000000000000000000000000000000000000000000000000000"-          , 2147483647)-        ]-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)-        ]-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\-        \a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000\-        \000000000000000000000100ffffff7f00ffffffff0201000000000000001976a9\-        \1424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976\-        \a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000")-    , ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"-          , 0)-        , ( "0001000000000000000000000000000000000000000000000000000000000000"-          , 2147483647)-        ]-      , []-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a\-        \1eb29eb0000000000ffffffff000000000000000000000000000000000000000000\-        \0000000000000000000100ffffff7f00ffffffff0000000000")-    , ( []-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)-        ]-      , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91d\-        \d5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954\-        \993cbca54f88ac00000000")-    ]---- Transaction Properties ----testBuildAddrTx :: Network -> Address -> TestCoin -> Bool-testBuildAddrTx net a (TestCoin v)-    | isPubKeyAddress a = Right (PayPKHash (getAddrHash160 a)) == out-    | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out-    | otherwise = undefined-  where-    tx = buildAddrTx net [] [(fromJust (addrToText net a), v)]-    out =-        decodeOutputBS $-        scriptOutput $-        head $ txOut (fromRight (error "Could not build transaction") tx)---- We compute an upper bound but it should be close enough to the real size--- We give 2 bytes of slack on every signature (1 on r and 1 on s)-testGuessSize :: Network -> Tx -> Bool-testGuessSize net tx =-    guess >= len && guess <= len + 2 * delta-  where-    delta = pki + sum (map fst msi)-    guess = guessTxSize pki msi pkout msout-    len = B.length $ S.encode tx-    ins = map f $ txIn tx-    f i =-        fromRight (error "Could not decode input") $-        decodeInputBS net $ scriptInput i-    pki = length $ filter isSpendPKHash ins-    msi = concatMap shData ins-    shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]-    shData _ = []-    out =-        map-            (fromRight (error "Could not decode transaction output") .-             decodeOutputBS . scriptOutput) $-        txOut tx-    pkout = length $ filter isPayPKHash out-    msout = length $ filter isPayScriptHash out--testChooseCoins :: [TestCoin] -> Word64 -> Word64 -> Int -> Property-testChooseCoins coins target byteFee nOut = nOut >= 0 ==>-    case chooseCoins target byteFee nOut True coins of-        Right (chosen, change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessTxFee byteFee nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessTxFee byteFee nOut (length coins)-            in target == 0 || s < target + fee-  where-    s  = sum $ map coinValue coins--testChooseMSCoins :: (Int, Int) -> [TestCoin]-                  -> Word64 -> Word64 -> Int -> Property-testChooseMSCoins (m, n) coins target byteFee nOut = nOut >= 0 ==>-    case chooseMSCoins target byteFee (m,n) nOut True coins of-        Right (chosen,change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessMSTxFee byteFee (m,n) nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessMSTxFee byteFee (m,n) nOut (length coins)-            in target == 0 || s < target + fee-  where-    s = sum $ map coinValue coins--{- Signing Transactions -}--testDetSignTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignTx net (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) &&-    verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signTx net tx sigis (map secKeyData (tail prv))-    txSigC =-        fromRight (error "Could not decode transaction") $-        signTx net txSigP sigis [secKeyData (head prv)]-    verData = map (\(SigInput s v o _ _) -> (s, v, o)) sigis--testDetSignNestedTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignNestedTx net  (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) &&-    verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net tx sigis (secKeyData <$> tail prv)-    txSigC =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net txSigP sigis [secKeyData (head prv)]-    verData = handleSegwit <$> sigis-    handleSegwit (SigInput s v o _ _)-        | isSegwit s = (toP2SH $ encodeOutput s, v, o)-        | otherwise  = (s, v, o)--testMergeTx :: Network -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool-testMergeTx net (txs, os) =-    and [ isRight mergeRes-        , length (txIn mergedTx) == length os-        , if enoughSigs-              then isValid-              else not isValid-        -- Signature count == min (length txs) (sum required signatures)-        , sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))-        ]-  where-    outs = map (\(so, val, op, _, _) -> (so, val, op)) os-    mergeRes = mergeTxs net txs outs-    mergedTx = fromRight (error "Could not merge") mergeRes-    isValid = verifyStdTx net mergedTx outs-    enoughSigs = all (\(m, c) -> c >= m) sigMap-    sigMap =-        map (\((_, _, _, m, _), inp) -> (m, sigCnt inp)) $-        zip os $ txIn mergedTx-    sigCnt inp =-        case decodeInputBS net $ scriptInput inp of-            Right (RegularInput (SpendMulSig sigs)) -> length sigs-            Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs-            _ -> error "Invalid input script type"+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++-- | Transactions: their ids, their bytes and sizes, building, signing and+-- verifying them, merging partial signatures, coin selection and fee+-- estimation, the SegWit witness helpers and the genesis coinbases.+module Haskoin.TransactionSpec (spec) where++import Control.Exception (evaluate)+import Control.Monad (forM_, guard, unless)+import Data.Aeson (decode, eitherDecode, withObject, (.:))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either+import Data.List (find, inits, isInfixOf, isPrefixOf)+import Data.Maybe+import Data.String (fromString)+import Data.String.Conversions+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word32, Word64, Word8)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Read (readMaybe)++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox arbitraryTxHash,+          ReadBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          ReadBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          ReadBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          ReadBox arbitraryOutPoint+        ],+      jsonTests =+        [ JsonBox arbitraryTxHash,+          JsonBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryWitnessTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryLegacyTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          JsonBox arbitraryOutPoint+        ],+      serialTests =+        [ SerialBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryWitnessTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryLegacyTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          SerialBox arbitraryOutPoint+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "Transaction properties" $ do+    prop "decode and encode txid" $+      forAll arbitraryTxHash $+        \h -> hexToTxHash (txHashToHex h) == Just h+    prop "from string transaction id" $+      forAll arbitraryTxHash $+        \h -> fromString (cs $ txHashToHex h) == h+    prop "building address tx" $+      forAll arbitraryNetAddress $ \(net, addr) ->+        forAll (arbitrarySatoshi net) $ testBuildAddrTx net ctx addr+    prop "the size guess is at least the virtual size of a signed legacy transaction, with little slack" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryAddrOnlyTxFull net ctx) (testGuessSize net ctx)+    prop "the weight guess is at least the weight of a signed transaction whose inputs have a kind" $+      forAll arbitraryNetwork $ \net -> testGuessWeight net ctx False+    prop "the weight guess is at least the weight of a signed transaction with its SegWit inputs nested" $+      forAll arbitraryNetwork $ \net -> testGuessWeight net ctx True+    prop "choose coins" $+      forAll arbitraryNetwork $ \net ->+        forAll (listOf (arbitrarySatoshi net)) $ \coins ->+          forAll (listOf arbitraryOutputKind) (testChooseCoins coins)+    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)+    prop "nosigTxHash does not change when a transaction is signed, whether in the input scripts or in the witness" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitrarySigningData net ctx) $ \(tx, sigis, prv) ->+          nosigTxHash (either error id (signTx net ctx tx sigis (map (.key) prv))) === nosigTxHash tx+  describe "Transaction vectors" $ do+    it "compute txid from tx" $ mapM_ testTxidVector txidVectors+    it "build pkhash transaction (generated from bitcoind)" $+      mapM_ (testPKHashVector ctx) pkHashVectors+  idsSpec+  bytesSpec ctx+  sizeSpec ctx+  coinSelectionSpec+  buildSpec ctx+  verificationSpec ctx+  coreSignedSpec ctx+  consensusSpec ctx+  thiefSpec ctx+  signerSpec ctx+  mergeSpec ctx+  taprootSigningSpec ctx+  segwitSpec ctx+  genesisSpec++-- Txid Vectors++testTxidVector :: (Text, Text) -> Assertion+testTxidVector (tid, tx) =+  assertEqual "txid" (Just tid) (txHashToHex . txHash <$> txM)+  where+    txM = eitherToMaybe . runGetS deserialize =<< decodeHex tx++txidVectors :: [(Text, Text)]+txidVectors =+  [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63",+      "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa4\+      \3ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75\+      \418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb\+      \60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f0000000000\+      \1976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+    ),+    ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73",+      "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3\+      \bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a\+      \04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7\+      \d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68\+      \350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b\+      \2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d0000\+      \0000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac000000\+      \00"+    ),+    ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb",+      "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2\+      \f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332ad\+      \fca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c\+      \70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f\+      \09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919a\+      \d1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737\+      \864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a473044\+      \0220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa\+      \2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f8\+      \1e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc8\+      \0a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd32273276\+      \94c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df3\+      \2949d4646dfa10a92458cfaa88ac00000000"+    ),+    ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae",+      "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c5\+      \9da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678\+      \fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab\+      \486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b\+      \52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbe\+      \a097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045\+      \022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf9506\+      \50802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f\+      \68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e3\+      \77c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e1\+      \8217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a378\+      \8dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972\+      \d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2\+      \e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffff\+      \ff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9c\+      \fdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675\+      \d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa\+      \34b26f62db4a88ac00000000"+    )+  ]++-- Build address transactions vectors generated from bitcoin-core raw tx API++testPKHashVector :: Ctx -> ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion+testPKHashVector ctx (is, os, res) =+  assertEqual+    "Build PKHash Tx"+    (Right res)+    (encodeHex . runPutS . serialize <$> txE)+  where+    txE = buildAddrTx btc ctx (map f is) os+    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix++pkHashVectors :: [([(Text, Word32)], [(Text, Word64)], Text)]+pkHashVectors =+  [ ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          14+        )+      ],+      [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 90000000)],+      "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+      \a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc58\+      \2292b97668bedd91dd5bf9374c88ac00000000"+    ),+    ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          0+        ),+        ( "0001000000000000000000000000000000000000000000000000000000000000",+          2147483647+        )+      ],+      [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1),+        ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)+      ],+      "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+      \a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000\+      \000000000000000000000100ffffff7f00ffffffff0201000000000000001976a9\+      \1424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976\+      \a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"+    ),+    ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          0+        ),+        ( "0001000000000000000000000000000000000000000000000000000000000000",+          2147483647+        )+      ],+      [],+      "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a\+      \1eb29eb0000000000ffffffff000000000000000000000000000000000000000000\+      \0000000000000000000100ffffff7f00ffffffff0000000000"+    ),+    ( [],+      [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1),+        ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)+      ],+      "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91d\+      \d5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954\+      \993cbca54f88ac00000000"+    )+  ]++-- Transaction Properties --++testBuildAddrTx :: Network -> Ctx -> Address -> TestCoin -> IO ()+testBuildAddrTx net ctx a coin+  | isPubKeyAddress a = (PayPKHash <$> addressHash160 a) `shouldBe` Just out+  | isScriptAddress a = (PayScriptHash <$> addressHash160 a) `shouldBe` Just out+  | otherwise = discard+  where+    out = either error id $ do+      tx <- buildAddrTx net ctx [] [(fromJust (addrToText net a), coin.getTestCoin)]+      unmarshal ctx (head tx.outputs).script++-- The guess is an upper bound on the virtual size that counts 72 bytes+-- per signature. A signature with a short r or s is 1 or 2 bytes less.+testGuessSize :: Network -> Ctx -> Tx -> Bool+testGuessSize net ctx tx =+  guess >= size && guess <= size + 2 * signatures+  where+    guess = guessTxSize (map inputKind ins) (map outputKind outs)+    size = txVirtualSize tx+    ins = map (either error id . unmarshal (net, ctx) . (.script)) tx.inputs+    outs = map (either error id . unmarshal ctx . (.script)) tx.outputs+    inputKind (ScriptHashInput _ (PayMulSig keys r)) = InputMulSig r (length keys)+    inputKind _ = InputPKHash+    outputKind o+      | isPayPKHash o = OutputPKHash+      | otherwise = OutputScriptHash+    signatures = sum (map sigCount ins)+    sigCount (ScriptHashInput (SpendMulSig sigs) _) = length sigs+    sigCount _ = 1++-- The guess is an upper bound on the weight of the transaction once signed:+-- by signTx, or by signNestedWitnessTx with its SegWit inputs nested. Only+-- signing data whose inputs all have a kind is generated.+testGuessWeight :: Network -> Ctx -> Bool -> Property+testGuessWeight net ctx nested =+  forAll (arbitrarySigningData net ctx `suchThat` allKnown) $ \(tx, sigis, keys) ->+    let signed = either error id (sign net ctx tx sigis (map (.key) keys))+        kinds = mapMaybe (sigInputKind ctx nested keys) sigis+        guess = guessTxWeight kinds (map (txOutKind ctx) tx.outputs)+     in counterexample ("weight " <> show (txWeight signed) <> ", guess " <> show guess) $+          txWeight signed <= guess+  where+    sign = if nested then signNestedWitnessTx else signTx+    allKnown (_, sigis, keys) = all (isJust . sigInputKind ctx nested keys) sigis++-- The kind of input a SigInput is signed as, when the estimator has one.+-- The key of a public key hash is found among the signing keys, to tell+-- whether it is compressed. These are left out, since the estimator has no+-- kind for them: bare multisig, pay-to-script-hash and+-- pay-to-witness-script-hash of a single key, multisig with an uncompressed+-- key, and pay-to-witness-public-key-hash with an uncompressed key, which+-- the estimator counts as compressed.+sigInputKind :: Ctx -> Bool -> [PrivateKey] -> SigInput -> Maybe InputKind+sigInputKind ctx nested keys si = case (si.script, si.redeem) of+  (PayPK _, Nothing) -> Just InputPK+  (PayPKHash h, Nothing) -> do+    k <- keyOfHash h+    Just $ if k.compress then InputPKHash else InputUncompressedPKHash+  (PayScriptHash _, Just (PayMulSig ps m)) -> compressedMultisig InputMulSig ps m+  (PayWitnessPKHash h, Nothing) -> do+    k <- keyOfHash h+    guard k.compress+    Just $ if nested then InputNestedWitnessPKHash else InputWitnessPKHash+  (PayWitnessScriptHash _, Just (PayMulSig ps m)) ->+    compressedMultisig (if nested then InputNestedWitnessMulSig else InputWitnessMulSig) ps m+  (PayWitness 1 program, Nothing) | B.length program == 32 -> Just InputTaproot+  _ -> Nothing+  where+    keyOfHash h = find (\k -> addressHash160 (pubKeyAddr ctx (derivePublicKey ctx k)) == Just h) keys+    compressedMultisig kind ps m = do+      guard (all (.compress) ps)+      Just (kind m (length ps))++-- The kind of an output, from its script. A script of no named kind is+-- 'OutputScript' with its size.+txOutKind :: Ctx -> TxOut -> OutputKind+txOutKind ctx out = case unmarshal ctx out.script of+  Right (PayPKHash _) -> OutputPKHash+  Right (PayScriptHash _) -> OutputScriptHash+  Right (PayScript32Hash _) -> OutputScript32Hash+  Right (PayWitnessPKHash _) -> OutputWitnessPKHash+  Right (PayWitnessScriptHash _) -> OutputWitnessScriptHash+  Right (PayWitness 1 program) | B.length program == 32 -> OutputTaproot+  _ -> OutputScript (B.length out.script)++-- A selection covers the target, the fee of the coins chosen, and the+-- change, and weighs no more than nodes relay. The sums are made as+-- unbounded integers, so that a sum that wraps around cannot pass. When+-- there is no selection, no prefix of the coins both fits that weight and+-- covers the target and its own fee, since the coins are taken in order.+testChooseCoins :: [TestCoin] -> [OutputKind] -> Word64 -> Word64 -> Property+testChooseCoins coins outs target byteFee =+  case chooseCoins target byteFee outs True coins of+    Right (chosen, change) ->+      (value chosen, weight chosen <= maxStandardTxWeight)+        === (toInteger target + toInteger change + fee chosen, True)+    Left _ ->+      property $ target == 0 || not (any covers (takeWhile ((<= maxStandardTxWeight) . weight) (drop 1 (inits coins))))+  where+    value = sum . map (toInteger . coinValue)+    weight cs = guessTxWeight (map coinInputKind cs) outs+    -- The fee rate times the virtual size, which is the weight over four, rounded up+    fee cs = toInteger byteFee * toInteger ((weight cs + 3) `div` 4)+    covers prefix = value prefix >= toInteger target + fee prefix++{- Signing Transactions -}++testDetSignTx :: Network -> Ctx -> (Tx, [SigInput], [PrivateKey]) -> Bool+testDetSignTx net ctx (tx, sigis, prv) =+  not verify1 && not verify2 && verify3+  where+    verify1 = verifyStdTx net ctx tx verData+    verify2 = verifyStdTx net ctx txSigP verData+    verify3 = verifyStdTx net ctx txSigC verData+    txSigP = either error id $ signTx net ctx tx sigis (map (.key) (tail prv))+    txSigC = either error id $ signTx net ctx txSigP sigis [(head prv).key]+    sigData SigInput {..} = (script, value, outpoint)+    verData = map sigData sigis++testDetSignNestedTx :: Network -> Ctx -> (Tx, [SigInput], [PrivateKey]) -> Bool+testDetSignNestedTx net ctx (tx, sigis, prv) =+  not verify1 && not verify2 && verify3+  where+    verify1 = verifyStdTx net ctx tx verData+    verify2 = verifyStdTx net ctx txSigP verData+    verify3 = verifyStdTx net ctx txSigC verData+    txSigP =+      either error id $+        signNestedWitnessTx net ctx tx sigis ((.key) <$> tail prv)+    txSigC =+      either error id $+        signNestedWitnessTx net ctx txSigP sigis [(head prv).key]+    verData = handleSegwit <$> sigis+    handleSegwit (SigInput s v o _ _)+      | isSegwit s = (either error id (toP2SH =<< encodeOutput ctx s), v, o)+      | otherwise = (s, v, o)++testMergeTx :: Network -> Ctx -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool+testMergeTx net ctx (txs, os) =+  and+    [ isRight mergeRes,+      length mergedTx.inputs == length os,+      if enoughSigs+        then isValid+        else not isValid,+      -- Signature count == min (length txs) (sum required signatures)+      sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))+    ]+  where+    outs = map (\(so, val, op, _, _) -> (so, val, op)) os+    mergeRes = mergeTxs net ctx txs outs+    mergedTx = fromRight (error "Could not merge") mergeRes+    isValid = verifyStdTx net ctx mergedTx outs+    enoughSigs = all (\(m, c) -> c >= m) sigMap+    sigFun (_, _, _, m, _) inp = (m, sigCnt inp)+    sigMap = zipWith sigFun os mergedTx.inputs+    sigCnt inp =+      case unmarshal (net, ctx) inp.script of+        Right (RegularInput (SpendMulSig sigs)) -> length sigs+        Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs+        _ -> error "Invalid input script type"++{- Ids -}++-- The id of the genesis coinbase, in display order+genesisTxId :: Text+genesisTxId = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"++-- The signed transaction of the P2WPKH example of BIP 143: 343 bytes, 233+-- without its witness+bip143SignedTx :: Text+bip143SignedTx =+  "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"++hexBytes :: Text -> ByteString+hexBytes = fromJust . decodeHex++parseTx :: Text -> Either String Tx+parseTx = runGetS deserialize . hexBytes++-- | The key pair whose secret key is the number given, in compressed form.+-- Tests use these fixed keys, so that a failure can be run again.+fixedKey :: Ctx -> Word8 -> (PrivateKey, PublicKey)+fixedKey ctx n = (k, derivePublicKey ctx k)+  where+    k = wrapSecKey True (fromJust (secKey (B.replicate 31 0 <> B.singleton n)))++-- | The same key pair with the public key in uncompressed form+fixedKeyU :: Ctx -> Word8 -> (PrivateKey, PublicKey)+fixedKeyU ctx n = (k, derivePublicKey ctx k)+  where+    k = wrapSecKey False (fst (fixedKey ctx n)).key++-- | HASH160 of a public key as it is serialized+keyHash :: Ctx -> PublicKey -> Hash160+keyHash ctx = addressHash . marshal ctx++-- | A coin to spend: an output of the genesis coinbase transaction id+coinAt :: Word32 -> OutPoint+coinAt = OutPoint (fromJust (hexToTxHash genesisTxId))++-- | The transaction with the script of one input replaced+withInputScript :: Tx -> Int -> ByteString -> Tx+withInputScript tx i script =+  Tx tx.version (zipWith replace [0 :: Int ..] tx.inputs) tx.outputs tx.witness tx.locktime+  where+    replace j input+      | j == i = TxIn input.outpoint script input.sequence+      | otherwise = input++-- | The transaction with its witness replaced+withWitness :: Tx -> WitnessData -> Tx+withWitness tx stacks = Tx tx.version tx.inputs tx.outputs stacks tx.locktime++idsSpec :: Spec+idsSpec =+  describe "Transaction ids" $ do+    it "hexToTxHash reads 32 bytes of hex in display order, in either case" $ do+      fmap (encodeHex . runPutS . serialize) (hexToTxHash genesisTxId)+        `shouldBe` Just "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+      hexToTxHash "4A5E1E4BAAB89F3A32518A88C31BC87F618F76673E2CC77AB2127B7AFDEDA33B" `shouldBe` hexToTxHash genesisTxId+      fmap txHashToHex (hexToTxHash genesisTxId) `shouldBe` Just genesisTxId++    it "hexToTxHash refuses hex that is not exactly 32 bytes, or not hex" $ do+      hexToTxHash (genesisTxId <> "ff") `shouldBe` Nothing+      hexToTxHash (T.take 62 genesisTxId) `shouldBe` Nothing+      hexToTxHash "" `shouldBe` Nothing+      hexToTxHash "zz5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" `shouldBe` Nothing++    it "the Read, string and JSON forms of a transaction id refuse 33 bytes" $ do+      (readMaybe (show (cs (genesisTxId <> "ff") :: String)) :: Maybe TxHash) `shouldBe` Nothing+      (decode (cs ("\"" <> genesisTxId <> "ff\"")) :: Maybe TxHash) `shouldBe` Nothing+      (decode (cs ("{\"txid\":\"" <> genesisTxId <> "ff\",\"index\":0}")) :: Maybe OutPoint) `shouldBe` Nothing+      (decode (cs ("{\"txid\":\"" <> genesisTxId <> "\",\"index\":0}")) :: Maybe OutPoint) `shouldBe` Just (OutPoint (fromJust (hexToTxHash genesisTxId)) 0)++    it "the witness transaction id of the BIP 143 example is the hash of all its bytes, and its id leaves the witness out" $ do+      tx <- either fail return (parseTx bip143SignedTx)+      txHashToHex (txHash tx) `shouldBe` "e8151a2af31c368a35053ddd4bdb285a8595c769a3ad83e0fa02314a602d4609"+      txHashToHex (txWitnessHash tx) `shouldBe` "c36c38370907df2324d9ce9d149d191192f338b37665a82e78e76a12c909b762"++    it "the witness transaction id of a transaction without witness is its id" $+      txWitnessHash mainnetGenesisTx `shouldBe` txHash mainnetGenesisTx++    -- The genesis coinbase with its input script removed hashes to+    -- 2a9487e4…78c1, which is neither its id nor the hash of its script+    it "nosigTxHash is the hash of the transaction with its input scripts and witness removed" $ do+      txHashToHex (nosigTxHash mainnetGenesisTx) `shouldBe` "2a9487e467412bc4b0a993a770bc73d5f4230f0ea5907b3f5df2669f0d7e78c1"+      tx <- either fail return (parseTx bip143SignedTx)+      let unsigned = Tx tx.version [TxIn i.outpoint "" i.sequence | i <- tx.inputs] tx.outputs [] tx.locktime+      nosigTxHash tx `shouldBe` nosigTxHash unsigned+      nosigTxHash unsigned `shouldBe` txHash unsigned++{- Bytes -}++bytesSpec :: Ctx -> Spec+bytesSpec ctx =+  describe "Transaction bytes" $ do+    -- The bytes of the first pkhash vector, with the input count written+    -- as 0xffffffffffffffff, which wraps around to -1 as an Int+    it "refuse a count that is beyond the limit of a list" $ do+      parseTx ("01000000" <> "ffffffffffffffffff" <> "02" <> "804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "00000000")+        `shouldSatisfy` either ("List count too large" `isInfixOf`) (const False)+      parseTx ("01000000" <> "0001" <> "01db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "ffffffffffffffffff" <> "00000000")+        `shouldSatisfy` either ("List count too large" `isInfixOf`) (const False)+      parseTx ("01000000" <> "01db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e000000" <> "ffffffffffffffffff" <> "ffffffff" <> "01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "00000000")+        `shouldSatisfy` either ("Byte count too large" `isInfixOf`) (const False)++    it "refuse the witness marker on a transaction whose witness is empty, as Bitcoin Core does" $ do+      parseTx ("01000000" <> "0001" <> "01db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "00" <> "00000000")+        `shouldSatisfy` either ("Superfluous witness record" `isInfixOf`) (const False)+      -- No inputs, so no witness at all+      parseTx ("01000000" <> "0001" <> "00" <> "01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "00000000")+        `shouldSatisfy` either ("Superfluous witness record" `isInfixOf`) (const False)++    -- An input count of zero makes the next byte the flags of BIP 144+    it "refuse a flags byte other than the witness bit after an empty input list, as Bitcoin Core does" $ do+      parseTx ("01000000" <> "00" <> "02" <> "02e80300000000000000e8030000000000000000000000")+        `shouldSatisfy` either ("Unknown transaction optional data" `isInfixOf`) (const False)+      parseTx ("01000000" <> "00" <> "03" <> "01db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac" <> "00" <> "00000000")+        `shouldSatisfy` either ("Unknown transaction optional data" `isInfixOf`) (const False)++    it "read an empty input list with a zero flags byte as a transaction with no inputs and no outputs, as Bitcoin Core does" $+      parseTx ("01000000" <> "00" <> "00" <> "00000000") `shouldBe` Right (Tx 1 [] [] [] 0)++    it "decodeTx reads a transaction on its own, and refuses bytes after it" $ do+      let legacy = "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      fmap txHash (decodeTx (hexBytes legacy)) `shouldBe` fmap txHash (parseTx legacy)+      fmap (length . (.inputs)) (decodeTx (hexBytes legacy)) `shouldBe` Right 1+      decodeTx (hexBytes (legacy <> "00")) `shouldSatisfy` either ("Trailing bytes after transaction" `isInfixOf`) (const False)+      decodeTx (hexBytes bip143SignedTx <> "\x00") `shouldSatisfy` either ("Trailing bytes after transaction" `isInfixOf`) (const False)+      fmap txWeight (decodeTx (hexBytes bip143SignedTx)) `shouldBe` Right 1042++    it "refuse a literal with bytes after the transaction" $ do+      let legacy = "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      length (fromString legacy :: Tx).inputs `shouldBe` 1+      evaluate (fromString (legacy <> "00") :: Tx) `shouldThrow` errorCall "Could not read transaction from hex string"++    it "write a transaction whose witness stacks are all empty without the marker, and read it back without witness" $ do+      let legacy = "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      tx <- either fail return (parseTx legacy)+      hasWitness tx `shouldBe` False+      hasWitness tx {witness = [[]]} `shouldBe` False+      hasWitness tx {witness = [["x"]]} `shouldBe` True+      encodeHex (runPutS (serialize tx {witness = [[]]})) `shouldBe` legacy+      runGetS deserialize (runPutS (serialize tx {witness = [[]]})) `shouldBe` Right tx+      txWitnessHash tx {witness = [[]]} `shouldBe` txHash tx++    it "write one witness stack per input" $ do+      let legacy = "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      tx <- either fail return (parseTx legacy)+      -- A second stack has no input to go with, so its bytes are not written+      fmap (.witness) (decodeTx (runPutS (serialize tx {witness = [["ab"], ["cd"]]}))) `shouldBe` Right [["ab"]]++    -- Only an input has a witness. A stack beyond the inputs is not part of+    -- the transaction, so it must not turn the witness format on.+    it "a witness item in a stack beyond the inputs is not a witness" $ do+      let legacy = "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      tx <- either fail return (parseTx legacy)+      let beyond = withWitness tx [[], ["ab"]]+      hasWitness beyond `shouldBe` False+      encodeHex (runPutS (serialize beyond)) `shouldBe` legacy+      txWitnessHash beyond `shouldBe` txHash tx+      -- 85 bytes, four weight units each+      txWeight beyond `shouldBe` 340++    it "write an empty stack for an input that the witness list leaves out" $ do+      let tx = Tx 1 [TxIn (coinAt 0) "" 0xffffffff, TxIn (coinAt 1) "" 0xffffffff] [TxOut 1 "\x51"] [["ab"]] 0+          input n = "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" <> n <> "00" <> "ffffffff"+      -- version, marker and flag, two inputs, one output of 1 satoshi to+      -- OP_1, the stack of one item "ab", the empty stack, lock time+      encodeHex (runPutS (serialize tx))+        `shouldBe` "01000000" <> "0001" <> "02" <> input "00000000" <> input "01000000" <> "01" <> "0100000000000000" <> "0151" <> "0102" <> "6162" <> "00" <> "00000000"++    it "the JSON form of a transaction has one witness stack per input, or none" $ do+      let json stacks = cs ("{\"version\":1,\"inputs\":[{\"prevoutput\":{\"txid\":\"" <> genesisTxId <> "\",\"index\":0},\"inputscript\":\"\",\"sequence\":4294967295}],\"outputs\":[],\"witnessdata\":" <> stacks <> ",\"locktime\":0}")+      fmap (.witness) (decode (json "[]") :: Maybe Tx) `shouldBe` Just []+      fmap (.witness) (decode (json "[[\"6162\"]]") :: Maybe Tx) `shouldBe` Just [["ab"]]+      (decode (json "[[\"6162\"],[\"6364\"]]") :: Maybe Tx) `shouldBe` Nothing+      (decode "{\"version\":1,\"inputs\":[],\"outputs\":[],\"witnessdata\":[[\"6162\"]],\"locktime\":0}" :: Maybe Tx) `shouldBe` Nothing++    -- 253 is the first count that takes three bytes: fd, then 253 as two+    -- bytes. Each input without a script is 41 bytes, and the output of an+    -- empty script 9: 4 + 3 + 253 × 41 + 1 + 9 + 4 = 10394 bytes.+    it "write and read a count of 253 inputs in three bytes" $ do+      let tx = Tx 1 [TxIn (coinAt i) "" 0xffffffff | i <- [0 .. 252]] [TxOut 1 ""] [] 0+          bytes = runPutS (serialize tx)+      B.length bytes `shouldBe` 10394+      encodeHex (B.take 3 (B.drop 4 bytes)) `shouldBe` "fdfd00"+      fmap (length . (.inputs)) (runGetS deserialize bytes :: Either String Tx) `shouldBe` Right 253++    it "give the weight and the virtual size of BIP 141: the BIP 143 example and the genesis coinbase" $ do+      tx <- either fail return (parseTx bip143SignedTx)+      -- 3 × 233 + 343 = 1042 weight units, 260.5 rounded up+      txWeight tx `shouldBe` 1042+      txVirtualSize tx `shouldBe` 261+      -- 204 bytes without witness: four times the size+      txWeight mainnetGenesisTx `shouldBe` 816+      txVirtualSize mainnetGenesisTx `shouldBe` 204++    it "the JSON form of an output refuses a negative amount" $ do+      (eitherDecode "{\"value\":-1,\"outputscript\":\"\"}" :: Either String TxOut) `shouldSatisfy` isLeft+      (eitherDecode "{\"value\":5000000000,\"outputscript\":\"51\"}" :: Either String TxOut) `shouldBe` Right (TxOut 5000000000 "\x51")++{- Sizes and fees -}++sizeSpec :: Ctx -> Spec+sizeSpec ctx =+  describe "Size and fee estimation" $ do+    it "counts the bytes of each kind of input, without and with witness" $ do+      -- outpoint 36, sequence 4, script length 1, signature push 73+      inputWeight InputPK `shouldBe` (114, 0)+      -- outpoint 36, sequence 4, script length 1, signature push 73, key push 34+      inputWeight InputPKHash `shouldBe` (148, 0)+      -- the same with a 65-byte key: push 66+      inputWeight InputUncompressedPKHash `shouldBe` (180, 0)+      -- an empty script; the witness: count 1, signature 73, key 34+      inputWeight InputWitnessPKHash `shouldBe` (41, 108)+      -- the push of the 22-byte program: 23 bytes after their length+      inputWeight InputNestedWitnessPKHash `shouldBe` (64, 108)+      -- the witness: count 1, a 65-byte signature after its length+      inputWeight InputTaproot `shouldBe` (41, 67)+      -- OP_0, two signatures of 73, the 105-byte script pushed with OP_PUSHDATA1 (107): 254 bytes, after a 3-byte length+      inputWeight (InputMulSig 2 3) `shouldBe` (297, 0)+      -- the witness: count 1, the empty item 1, two signatures of 73, the script after its length 106+      inputWeight (InputWitnessMulSig 2 3) `shouldBe` (41, 254)+      -- the push of the 34-byte program: 35 bytes after their length+      inputWeight (InputNestedWitnessMulSig 2 3) `shouldBe` (76, 254)+      -- OP_0, one signature, the 37-byte script pushed directly (38): 112 bytes+      inputWeight (InputMulSig 1 1) `shouldBe` (153, 0)++    it "counts two bytes for each count of a multisig script above 16" $ do+      -- 17-of-20: OP_m 2, 20 keys of 34 (680), OP_n 2, OP_CHECKMULTISIG 1: a 685-byte script.+      -- the witness: count 1, the empty item 1, 17 signatures of 73 (1241), the 685-byte script after its 3-byte length (688)+      inputWeight (InputWitnessMulSig 17 20) `shouldBe` (41, 1931)+      inputWeight (InputNestedWitnessMulSig 17 20) `shouldBe` (76, 1931)++    -- A redeem script is at most 520 bytes, so 15 compressed keys are the+    -- most of a pay-to-script-hash multisig+    it "counts the largest pay-to-script-hash multisig, 15 of 15" $+      -- OP_15, 15 keys of 34 (510), OP_15, OP_CHECKMULTISIG: a 513-byte script.+      -- OP_0, 15 signatures of 73 (1095), the script pushed with OP_PUSHDATA2 (516): 1612 bytes after a 3-byte length, and 40+      inputWeight (InputMulSig 15 15) `shouldBe` (1655, 0)++    it "counts the bytes of each kind of output" $ do+      outputSize OutputPKHash `shouldBe` 34+      outputSize OutputScriptHash `shouldBe` 32+      -- amount 8, length 1, OP_HASH256, a 32-byte push, OP_EQUAL: 35+      outputSize OutputScript32Hash `shouldBe` 44+      outputSize OutputWitnessPKHash `shouldBe` 31+      outputSize OutputWitnessScriptHash `shouldBe` 43+      outputSize OutputTaproot `shouldBe` 43+      -- a bare 1-of-1 multisig script, OP_1, a 33-byte key push, OP_1, OP_CHECKMULTISIG: 37 bytes after a 1-byte length+      outputSize (OutputScript 37) `shouldBe` 46+      -- an empty script is its 1-byte length+      outputSize (OutputScript 0) `shouldBe` 9+      -- a 1000-byte script after its 3-byte length+      outputSize (OutputScript 1000) `shouldBe` 1011++    it "gives the weight and virtual size of a transaction of each shape" $ do+      -- 8 + 1 + 148 + 1 + 34 = 192 bytes, no witness+      guessTxWeight [InputPKHash] [OutputPKHash] `shouldBe` 768+      guessTxSize [InputPKHash] [OutputPKHash] `shouldBe` 192+      -- 8 + 1 + 41 + 1 + 31 = 82 bytes without witness, 2 + 108 of witness: 438 units, 109.5 rounded up+      guessTxWeight [InputWitnessPKHash] [OutputWitnessPKHash] `shouldBe` 438+      guessTxSize [InputWitnessPKHash] [OutputWitnessPKHash] `shouldBe` 110+      -- 8 + 1 + 64 + 1 + 32 = 106 bytes, 110 of witness: 534 units+      guessTxSize [InputNestedWitnessPKHash] [OutputScriptHash] `shouldBe` 134+      -- 8 + 1 + 41 + 1 + 43 = 94 bytes, 2 + 67 of witness: 445 units+      guessTxSize [InputTaproot] [OutputTaproot] `shouldBe` 112+      -- 8 + 1 + 297 + 1 + 34 + 32 = 373 bytes, no witness+      guessTxSize [InputMulSig 2 3] [OutputPKHash, OutputScriptHash] `shouldBe` 373+      -- 8 + 1 + 41 + 1 + 43 = 94 bytes, 2 + 254 of witness: 632 units+      guessTxSize [InputWitnessMulSig 2 3] [OutputWitnessScriptHash] `shouldBe` 158+      -- 8 + 1 + 76 + 1 + 34 = 120 bytes, 256 of witness: 736 units+      guessTxSize [InputNestedWitnessMulSig 2 3] [OutputPKHash] `shouldBe` 184+      -- 8 + 1 + 148 + 41 + 1 + 34 = 233 bytes; the witness: 2, the empty stack of the legacy input 1, 108: 1043 units, 260.75 rounded up+      guessTxSize [InputPKHash, InputWitnessPKHash] [OutputPKHash] `shouldBe` 261++    it "counts the empty witness stack of each input without a witness when another has one" $ do+      -- 4 + 1 + 3 × 148 + 41 + 1 + 34 + 4 = 529 bytes; the witness: 2 + 1 + 1 + 1 + 108 = 113; 3 × 529 + 642 = 2229 units, 557.25 rounded up+      guessTxWeight [InputPKHash, InputPKHash, InputPKHash, InputWitnessPKHash] [OutputPKHash] `shouldBe` 2229+      guessTxSize [InputPKHash, InputPKHash, InputPKHash, InputWitnessPKHash] [OutputPKHash] `shouldBe` 558+      -- 8 + 1 + 114 + 41 + 1 + 44 = 209 bytes; the witness: 2 + 1 + 67 = 70: 906 units, 226.5 rounded up+      guessTxWeight [InputPK, InputTaproot] [OutputScript32Hash] `shouldBe` 906+      guessTxSize [InputPK, InputTaproot] [OutputScript32Hash] `shouldBe` 227+      -- without a witness, no stack is counted: 4 × (8 + 1 + 296 + 1 + 34)+      guessTxWeight [InputPKHash, InputPKHash] [OutputPKHash] `shouldBe` 1360++    it "charges the fee rate per virtual byte" $ do+      guessTxFee 10 [InputPKHash] [OutputPKHash] `shouldBe` Right 1920+      guessTxFee 10 [InputWitnessPKHash] [OutputWitnessPKHash] `shouldBe` Right 1100+      guessTxFee 0 [InputPKHash] [OutputPKHash] `shouldBe` Right 0++    -- 192 bytes at 2^58 satoshi per byte is three times 2^64+    it "refuses a fee that does not fit in 64 bits" $ do+      guessTxFee 288230376151711744 [InputPKHash] [OutputPKHash] `shouldBe` Left "guessTxFee: The fee does not fit in 64 bits"+      -- 192 × 96076792050570581 = 2^64 - 64, the largest multiple of 192 below 2^64+      guessTxFee 96076792050570581 [InputPKHash] [OutputPKHash] `shouldBe` Right 18446744073709551552+      guessTxFee 96076792050570582 [InputPKHash] [OutputPKHash] `shouldBe` Left "guessTxFee: The fee does not fit in 64 bits"++    it "refuses a kind of input or output that cannot exist" $ do+      -- 16 compressed keys make a redeem script of 547 bytes+      guessTxFee 1 [InputMulSig 16 16] [OutputPKHash] `shouldBe` Left "guessTxFee: The input kind InputMulSig 16 16 is not valid: m of n needs 1 <= m <= n <= 15"+      guessTxFee 1 [InputMulSig 3 2] [OutputPKHash] `shouldBe` Left "guessTxFee: The input kind InputMulSig 3 2 is not valid: m of n needs 1 <= m <= n <= 15"+      guessTxFee 1 [InputWitnessMulSig 0 2] [OutputPKHash] `shouldBe` Left "guessTxFee: The input kind InputWitnessMulSig 0 2 is not valid: m of n needs 1 <= m <= n <= 20"+      guessTxFee 1 [InputNestedWitnessMulSig 1 21] [OutputPKHash] `shouldBe` Left "guessTxFee: The input kind InputNestedWitnessMulSig 1 21 is not valid: m of n needs 1 <= m <= n <= 20"+      guessTxFee 1 [InputPKHash] [OutputScript (-1)] `shouldBe` Left "guessTxFee: The output kind OutputScript (-1) is not valid: a script has from 0 to 10000 bytes"+      chooseCoins 1000 1 [OutputPKHash] False [TestCoin 100000 (InputMulSig (-30) 2)]+        `shouldBe` Left "chooseCoins: The input kind InputMulSig (-30) 2 is not valid: m of n needs 1 <= m <= n <= 15"++    -- GetDustThreshold of Bitcoin Core at 3 satoshi per byte: the output+    -- and the smallest input that spends it, 148 bytes without a witness+    -- and 67 with one+    it "gives the dust threshold of each kind of output" $ do+      -- (34 + 148) × 3+      dustThreshold OutputPKHash `shouldBe` 546+      -- (32 + 148) × 3+      dustThreshold OutputScriptHash `shouldBe` 540+      -- (31 + 67) × 3+      dustThreshold OutputWitnessPKHash `shouldBe` 294+      -- (43 + 67) × 3+      dustThreshold OutputWitnessScriptHash `shouldBe` 330+      dustThreshold OutputTaproot `shouldBe` 330+      -- a bare 1-of-1 multisig: (46 + 148) × 3+      dustThreshold (OutputScript 37) `shouldBe` 582++    it "is at least the virtual size of a signed P2WPKH spend, and of a Taproot key path spend" $ do+      let (k, p) = fixedKey ctx 1+          h = fromJust (addressHash160 (pubKeyAddr ctx p))+          op = OutPoint (fromJust (hexToTxHash genesisTxId)) 0+          witnessTx = buildTx ctx [op] [(PayWitnessPKHash h, 90000)]+          taprootOutput = taprootScriptOutput ctx (TaprootOutput p.point Nothing)+          taprootTx = buildTx ctx [op] [(taprootOutput, 90000)]+      signedWitness <- either fail return (signTx btc ctx witnessTx [SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing] [k.key])+      signedTaproot <- either fail return (signTx btc ctx taprootTx [SigInput taprootOutput 100000 op sigHashDefault Nothing] [k.key])+      -- 82 bytes without witness. The witness: marker and flag 2, count 1,+      -- the signature with its hash type after its length, 72 or 73, and+      -- the key after its length 34: 437 or 438 units, 110 virtual bytes+      -- either way+      txVirtualSize signedWitness `shouldBe` 110+      -- 94 bytes without witness. The witness: 2, count 1, a 64-byte+      -- signature after its length 65: 444 units, one less than the guess+      txVirtualSize signedTaproot `shouldBe` 111++{- Coin selection -}++coinSelectionSpec :: Spec+coinSelectionSpec =+  describe "Coin selection" $ do+    -- One P2PKH input and two P2PKH outputs: 8 + 1 + 148 + 1 + 68 = 226+    -- virtual bytes, 2260 satoshi at 10 satoshi per virtual byte. The first+    -- coin covers 60000 + 2260, with 37740 of change.+    it "selects the first coins that cover the target and the fee of the transaction they make" $ do+      let coins = [TestCoin 100000 InputPKHash, TestCoin 50000 InputPKHash]+      chooseCoins 60000 10 [OutputPKHash, OutputPKHash] False coins `shouldBe` Right ([TestCoin 100000 InputPKHash], 37740)+      chooseCoins 60000 10 [OutputPKHash, OutputPKHash] True coins `shouldBe` Right ([TestCoin 100000 InputPKHash], 37740)++    -- Two P2WPKH inputs and one P2WPKH output: 8 + 1 + 82 + 1 + 31 = 123+    -- bytes without witness, 2 + 216 with it: 710 units, 178 virtual+    -- bytes, 1780 satoshi. Neither coin alone covers 60000 and the fee of+    -- one input (8 + 1 + 41 + 1 + 31 = 82, 2 + 108: 438 units, 110 bytes,+    -- 1100 satoshi), both together do, with 90000 - 60000 - 1780 of change.+    it "adds coins until they cover the target and the fee, which grows with them" $ do+      let coins = [TestCoin 50000 InputWitnessPKHash, TestCoin 40000 InputWitnessPKHash]+      chooseCoins 60000 10 [OutputWitnessPKHash] False coins `shouldBe` Right ([TestCoin 40000 InputWitnessPKHash, TestCoin 50000 InputWitnessPKHash], 28220)++    it "looks for a smaller solution after the first when asked to continue" $ do+      let coins = [TestCoin 30000 InputPKHash, TestCoin 40000 InputPKHash, TestCoin 65000 InputPKHash]+      -- 30000 + 40000 covers 60000 + 10 × (8 + 1 + 296 + 1 + 34) = 60000 + 3400; then 65000 alone covers 60000 + 1920, and is smaller+      chooseCoins 60000 10 [OutputPKHash] False coins `shouldBe` Right ([TestCoin 40000 InputPKHash, TestCoin 30000 InputPKHash], 6600)+      chooseCoins 60000 10 [OutputPKHash] True coins `shouldBe` Right ([TestCoin 65000 InputPKHash], 3080)++    -- One P2PKH input and one P2PKH output are 192 bytes: 1920 satoshi+    it "selects a coin that covers the target and the fee exactly, with no change" $+      chooseCoins 98080 10 [OutputPKHash] False [TestCoin 100000 InputPKHash] `shouldBe` Right ([TestCoin 100000 InputPKHash], 0)++    -- With no fee, each coin alone covers the target, and the second is no+    -- smaller than the first+    it "keeps the first solution when a later one is not smaller" $+      chooseCoins 60000 0 [OutputPKHash] True [TestCoin 70000 InputPKHash, TestCoin 70000 InputWitnessPKHash]+        `shouldBe` Right ([TestCoin 70000 InputPKHash], 10000)++    -- 2^64 - 101 plus the fee of 192 is above 2^64, and must not wrap+    -- around to 91+    it "refuses a target that the coins cannot cover, even when target and fee add up to more than 2^64" $ do+      chooseCoins 18446744073709551515 1 [OutputPKHash] False [TestCoin 1000 InputPKHash] `shouldBe` Left "chooseCoins: No solution found"+      -- 192 bytes at 2^58 satoshi per byte is three times 2^64+      chooseCoins 60000 288230376151711744 [OutputPKHash] False [TestCoin 100000 InputPKHash] `shouldBe` Left "chooseCoins: No solution found"++    -- 1 + (2^64 - 1) = 2^64 covers 5 with 2^64 - 5 of change+    it "adds coin values without wrapping around" $+      chooseCoins 5 0 [OutputPKHash] False [TestCoin 1 InputPKHash, TestCoin 18446744073709551615 InputPKHash]+        `shouldBe` Right ([TestCoin 18446744073709551615 InputPKHash, TestCoin 1 InputPKHash], 18446744073709551611)++    -- k P2WPKH inputs and one P2WPKH output, for k from 253: 4 × (8 + 3 ++    -- 41 k + 1 + 31) + 2 + 108 k = 174 + 272 k weight units. 1469 inputs+    -- are 399742 units, 99936 virtual bytes, and 1470 are 400014, above the+    -- 400000 that nodes relay.+    it "selects up to the standard transaction weight and no further" $ do+      let coins = replicate 2000 (TestCoin 10000 InputWitnessPKHash)+      -- 1469 coins hold 14690000, less 99936 of fee at 1 satoshi per byte+      fmap (\(chosen, change) -> (length chosen, change)) (chooseCoins 14590064 1 [OutputWitnessPKHash] False coins) `shouldBe` Right (1469, 0)+      chooseCoins 14590065 1 [OutputWitnessPKHash] False coins `shouldBe` Left "chooseCoins: The selection exceeds the standard transaction weight of 400000"++    it "refuses a target of zero and reports when the coins do not suffice" $ do+      chooseCoins 0 10 [OutputPKHash] False [TestCoin 100000 InputPKHash] `shouldBe` Left "chooseCoins: Target must be > 0"+      chooseCoins 100000 10 [OutputPKHash] False [TestCoin 100000 InputPKHash] `shouldBe` Left "chooseCoins: No solution found"+      chooseCoins 1 10 [OutputPKHash] False ([] :: [TestCoin]) `shouldBe` Left "chooseCoins: No solution found"++{- Building -}++buildSpec :: Ctx -> Spec+buildSpec ctx =+  describe "Building a transaction to addresses" $ do+    let address = "14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb"+    -- AmountFromValue of Bitcoin Core refuses an amount out of range when+    -- createrawtransaction builds a transaction+    it "refuses an amount above the 21 million coins that can exist" $ do+      buildAddrTx btc ctx [coinAt 0] [(address, 2100000000000000)] `shouldSatisfy` isRight+      buildAddrTx btc ctx [coinAt 0] [(address, 2100000000000001)] `shouldBe` Left "buildAddrTx: An amount is above the maximum of 2100000000000000 satoshi"+      buildAddrTx btc ctx [coinAt 0] [(address, 18446744073709551615)] `shouldBe` Left "buildAddrTx: An amount is above the maximum of 2100000000000000 satoshi"++    it "refuses an address of another network" $+      buildAddrTx bch ctx [coinAt 0] [("bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", 1000)] `shouldBe` Left "buildAddrTx: Invalid address bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"++{- Verification -}++verificationSpec :: Ctx -> Spec+verificationSpec ctx =+  describe "Verification" $ do+    let coinOf = OutPoint (fromJust (hexToTxHash genesisTxId))+    it "accepts a correct P2PKH spend, and refuses a witness on its input, a wrong key, a wrong amount and a missing coin" $ do+      let (k, p) = fixedKey ctx 1+          (_, other) = fixedKey ctx 2+      let h = fromJust (addressHash160 (pubKeyAddr ctx p))+          otherHash = fromJust (addressHash160 (pubKeyAddr ctx other))+          op = coinOf 0+          tx = buildTx ctx [op] [(PayPKHash h, 90000)]+      signed <- either fail return (signTx btc ctx tx [SigInput (PayPKHash h) 100000 op sigHashAll Nothing] [k.key])+      verifyStdTx btc ctx signed [(PayPKHash h, 100000, op)] `shouldBe` True+      verifyStdTx btc ctx signed {witness = [["junk"]]} [(PayPKHash h, 100000, op)] `shouldBe` False+      verifyStdTx btc ctx signed [(PayPKHash otherHash, 100000, op)] `shouldBe` False+      -- The legacy digest does not commit to the amount, but the outputs may not spend more than the coin+      verifyStdTx btc ctx signed [(PayPKHash h, 89999, op)] `shouldBe` False+      verifyStdTx btc ctx signed [(PayPKHash h, 100000, coinOf 1)] `shouldBe` False+      verifyStdTx btc ctx signed [] `shouldBe` False+      verifyStdInput btc ctx signed [(PayPKHash h, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx signed [(PayPKHash h, 100000, op)] 1 `shouldBe` False+      verifyStdInput btc ctx signed [(PayPKHash h, 100000, op)] (-1) `shouldBe` False++    it "refuses a transaction without inputs, and the outputs of a transaction spending more than its coins" $ do+      let (k, p) = fixedKey ctx 1+      let h = fromJust (addressHash160 (pubKeyAddr ctx p))+          ops = [coinOf 0, coinOf 1]+          tx = buildTx ctx ops [(PayPKHash h, 150000)]+          sigis = [SigInput (PayPKHash h) 100000 op sigHashAll Nothing | op <- ops]+          coins = [(PayPKHash h, 100000, op) | op <- ops]+      signed <- either fail return (signTx btc ctx tx sigis [k.key])+      verifyStdTx btc ctx signed coins `shouldBe` True+      verifyStdTx btc ctx signed (take 1 coins) `shouldBe` False+      verifyStdTx btc ctx (buildTx ctx [] [(PayPKHash h, 1)]) [] `shouldBe` False+      -- The same coins hold less than the outputs spend+      verifyStdTx btc ctx signed [(PayPKHash h, 70000, op) | op <- ops] `shouldBe` False++    it "signs SIGHASH_SINGLE on a legacy input with an output at its index and on a SegWit input, but refuses it on a legacy input without one" $ do+      let (k, p) = fixedKey ctx 1+      let h = fromJust (addressHash160 (pubKeyAddr ctx p))+          ops = [coinOf 0, coinOf 1]+          -- one output, so input 1 has none at its index+          tx = buildTx ctx ops [(PayPKHash h, 150000)]+          legacy op = SigInput (PayPKHash h) 100000 op sigHashSingle Nothing+          segwit op = SigInput (PayWitnessPKHash h) 100000 op sigHashSingle Nothing+      signedFirst <- either fail return (signTx btc ctx tx [legacy (coinOf 0)] [k.key])+      verifyStdInput btc ctx signedFirst [(PayPKHash h, 100000, coinOf 0)] 0 `shouldBe` True+      signTx btc ctx tx [legacy (coinOf 1)] [k.key] `shouldBe` Left "makeSignature: SIGHASH_SINGLE without an output at the input index"+      -- The BIP 143 digest commits to the coin even without an output+      signedWitness <- either fail return (signTx btc ctx tx [segwit (coinOf 1)] [k.key])+      verifyStdInput btc ctx signedWitness [(PayWitnessPKHash h, 100000, coinOf 1)] 1 `shouldBe` True++    -- A node accepts the signature over the constant digest 1 that a+    -- legacy SIGHASH_SINGLE input without an output has, so the verifier+    -- must too, although the signer refuses to make one+    it "accepts a legacy SIGHASH_SINGLE signature over the constant digest 1, as a node does" $ do+      let (k, p) = fixedKey ctx 1+      let h = fromJust (addressHash160 (pubKeyAddr ctx p))+          ops = [coinOf 0, coinOf 1]+          tx = buildTx ctx ops [(PayPKHash h, 150000)]+          one = "0100000000000000000000000000000000000000000000000000000000000000" :: Hash256+          overOne = marshal (btc, ctx) (RegularInput (SpendPKHash (TxSignature (signHash ctx k.key one) sigHashSingle) p))+          spent = Tx tx.version [TxIn (coinOf 0) "" 0xffffffff, TxIn (coinOf 1) overOne 0xffffffff] tx.outputs [] tx.locktime+          spentFirst = Tx tx.version [TxIn (coinOf 0) overOne 0xffffffff, TxIn (coinOf 1) "" 0xffffffff] tx.outputs [] tx.locktime+      verifyStdInput btc ctx spent [(PayPKHash h, 100000, coinOf 1)] 1 `shouldBe` True+      -- The same signature on input 0, which has an output, does not verify+      verifyStdInput btc ctx spentFirst [(PayPKHash h, 100000, coinOf 0)] 0 `shouldBe` False++    it "refuses a SegWit input with a non-empty input script and a wrong amount" $ do+      let (k, p) = fixedKey ctx 1+      let h = fromJust (addressHash160 (pubKeyAddr ctx p))+          op = coinOf 0+          tx = buildTx ctx [op] [(PayWitnessPKHash h, 90000)]+      signed <- either fail return (signTx btc ctx tx [SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing] [k.key])+      verifyStdTx btc ctx signed [(PayWitnessPKHash h, 100000, op)] `shouldBe` True+      -- The BIP 143 digest commits to the amount+      verifyStdTx btc ctx signed [(PayWitnessPKHash h, 100001, op)] `shouldBe` False+      let pushed = Tx signed.version [TxIn op "\x51" i.sequence | i <- signed.inputs] signed.outputs signed.witness signed.locktime+      verifyStdTx btc ctx pushed [(PayWitnessPKHash h, 100000, op)] `shouldBe` False++{- Merging -}++mergeSpec :: Ctx -> Spec+mergeSpec ctx =+  describe "Merging partially signed transactions" $ do+    it "leaves an input that no transaction signed unsigned, and merges the others" $ do+      let (k1, p1) = fixedKey ctx 1+          (k2, p2) = fixedKey ctx 2+      let ms = PayMulSig [p1, p2] 2+          so = PayScriptHash (fromJust (either (const Nothing) addressHash160 (payToScriptAddress ctx ms)))+          ops = [OutPoint (fromJust (hexToTxHash genesisTxId)) 0, OutPoint (fromJust (hexToTxHash genesisTxId)) 1]+          tx = buildTx ctx ops [(PayPKHash (fromJust (addressHash160 (pubKeyAddr ctx p1))), 90000)]+          coins = [(so, 100000, op) | op <- ops]+          sigi = SigInput so 100000 (head ops) sigHashAll (Just ms)+      a <- either fail return (signTx btc ctx tx [sigi] [k1.key])+      b <- either fail return (signTx btc ctx tx [sigi] [k2.key])+      merged <- either fail return (mergeTxs btc ctx [a, b] coins)+      (merged.inputs !! 1).script `shouldBe` ""+      verifyStdInput btc ctx merged coins 0 `shouldBe` True+      verifyStdTx btc ctx merged coins `shouldBe` False+      -- The second input signed by both completes the transaction+      let sigi2 = SigInput so 100000 (ops !! 1) sigHashAll (Just ms)+      c <- either fail return (signTx btc ctx tx [sigi2] [k1.key])+      d <- either fail return (signTx btc ctx tx [sigi2] [k2.key])+      complete <- either fail return (mergeTxs btc ctx [a, b, c, d] coins)+      verifyStdTx btc ctx complete coins `shouldBe` True++    it "keeps the witness of an input that is not merged" $ do+      let (k1, p1) = fixedKey ctx 1+          (k2, p2) = fixedKey ctx 2+          (k3, p3) = fixedKey ctx 3+          ms = PayMulSig [p1, p2] 2+          so = either error id (toP2SH =<< encodeOutput ctx ms)+          wpkh = PayWitnessPKHash (keyHash ctx p3)+          tx = buildTx ctx [coinAt 0, coinAt 1] [(PayPKHash (keyHash ctx p1), 150000)]+          msSigi = SigInput so 100000 (coinAt 0) sigHashAll (Just ms)+          coins = [(so, 100000, coinAt 0), (wpkh, 100000, coinAt 1)]+      withSegwit <- either fail return (signTx btc ctx tx [SigInput wpkh 100000 (coinAt 1) sigHashAll Nothing] [k3.key])+      a <- either fail return (signTx btc ctx withSegwit [msSigi] [k1.key])+      b <- either fail return (signTx btc ctx withSegwit [msSigi] [k2.key])+      merged <- either fail return (mergeTxs btc ctx [a, b] [(so, 100000, coinAt 0)])+      merged.witness `shouldBe` withSegwit.witness+      verifyStdTx btc ctx merged coins `shouldBe` True++    it "mergeTxInput refuses an index that a transaction does not have" $ do+      let (_, p1) = fixedKey ctx 1+          ms = PayMulSig [p1] 1+          tx = buildTx ctx [coinAt 0] []+          short = buildTx ctx [] []+      mergeTxInput btc ctx [tx] tx ((ms, 100000), 3) `shouldBe` Left "mergeTxInput: input index out of range"+      mergeTxInput btc ctx [tx, short] tx ((ms, 100000), 0) `shouldBe` Left "mergeTxInput: input index out of range"++    it "refuses to merge inputs that carry different redeem scripts" $ do+      let (k1, p1) = fixedKey ctx 1+          (k2, p2) = fixedKey ctx 2+          (_, p3) = fixedKey ctx 3+          msA = PayMulSig [p1, p2] 2+          msB = PayMulSig [p1, p2, p3] 2+          hashOf ms = either error id (toP2SH =<< encodeOutput ctx ms)+          tx = buildTx ctx [coinAt 0] [(PayPKHash (keyHash ctx p1), 90000)]+      a <- either fail return (signTx btc ctx tx [SigInput (hashOf msA) 100000 (coinAt 0) sigHashAll (Just msA)] [k1.key])+      b <- either fail return (signTx btc ctx tx [SigInput (hashOf msB) 100000 (coinAt 0) sigHashAll (Just msB)] [k2.key])+      mergeTxs btc ctx [a, b] [(hashOf msA, 100000, coinAt 0)] `shouldBe` Left "Redeem scripts do not match"++    it "countMulSig counts the signatures that verify, in key order" $ do+      let (k1, p1) = fixedKey ctx 1+          (k2, p2) = fixedKey ctx 2+          (k3, p3) = fixedKey ctx 3+      let ms = PayMulSig [p1, p2, p3] 2+          msScript = either error id (encodeOutput ctx ms)+          op = OutPoint (fromJust (hexToTxHash genesisTxId)) 0+          tx = buildTx ctx [op] [(PayPKHash (fromJust (addressHash160 (pubKeyAddr ctx p1))), 90000)]+          sigOf k = TxSignature (signHash ctx k.key (either error id (txSigHash btc tx msScript 100000 0 sigHashAll))) sigHashAll+          count = countMulSig btc ctx tx msScript 100000 0 (map (.point) [p1, p2, p3])+      count [sigOf k1, sigOf k3] `shouldBe` 2+      count [sigOf k1, sigOf k2, sigOf k3] `shouldBe` 3+      -- The count is not a verdict: verifyStdInput refuses this input+      count [sigOf k1, TxSignatureEmpty, sigOf k3] `shouldBe` 2+      -- A signature out of key order is not counted, nor a signature of another digest+      count [sigOf k3, sigOf k1] `shouldBe` 1+      count [TxSignature (signHash ctx k1.key (sha256 ("other" :: ByteString))) sigHashAll] `shouldBe` 0+      count [] `shouldBe` 0++    it "findSigInput pairs the signing parameters with the index of their input" $ do+      let ops = [OutPoint (fromJust (hexToTxHash genesisTxId)) i | i <- [0 .. 2]]+          tx = buildTx ctx ops []+          sigi op = SigInput (PayPKHash "0000000000000000000000000000000000000000") 1 op sigHashAll Nothing+      map snd (findSigInput [sigi (ops !! 2), sigi (ops !! 0)] tx.inputs) `shouldBe` [0, 2]+      findSigInput [sigi (OutPoint (fromJust (hexToTxHash genesisTxId)) 9)] tx.inputs `shouldBe` []++    it "buildInput refuses an index out of range, a Taproot output and a mismatched redeem script" $ do+      let (k, p) = fixedKey ctx 1+      let op = OutPoint (fromJust (hexToTxHash genesisTxId)) 0+          tx = buildTx ctx [op] []+          sig = TxSignature (signHash ctx k.key (sha256 ("digest" :: ByteString))) sigHashAll+          h = fromJust (addressHash160 (pubKeyAddr ctx p))+      buildInput btc ctx tx 1 (PayPKHash h) 1 Nothing sig p `shouldBe` Left "buildInput: Invalid input index"+      buildInput btc ctx tx 0 (taprootScriptOutput ctx (TaprootOutput p.point Nothing)) 1 Nothing sig p `shouldBe` Left "buildInput: a Taproot input has no input script"+      buildInput btc ctx tx 0 (PayPKHash h) 1 (Just (PayPK p)) sig p `shouldBe` Left "buildInput: Invalid output/redeem script combination"+      buildInput btc ctx tx 0 (PayPKHash h) 1 Nothing sig p `shouldBe` Right (RegularInput (SpendPKHash sig p))++    it "refuses an empty list and transactions that differ" $ do+      mergeTxs btc ctx [] [] `shouldBe` Left "Transaction list is empty"+      let op = OutPoint (fromJust (hexToTxHash genesisTxId)) 0+          one = buildTx ctx [op] []+          other = buildTx ctx [op] [(PayPK (PublicKey (derivePubKey ctx (fromJust (secKey (B.replicate 32 1)))) True), 1)]+      mergeTxs btc ctx [one] [] `shouldBe` Right one+      mergeTxs btc ctx [one, other] [] `shouldBe` Left "Transactions do not match"++{- Taproot -}++taprootSigningSpec :: Ctx -> Spec+taprootSigningSpec ctx =+  describe "Taproot key path signing" $ do+    let op = OutPoint (fromJust (hexToTxHash genesisTxId)) 0+    it "signs with the internal key of an output without script tree, as BIP 86 spends, and with the output key itself" $ do+      let (k, p) = fixedKey ctx 1+      let so = taprootScriptOutput ctx (TaprootOutput p.point Nothing)+          tx = buildTx ctx [op] [(so, 90000)]+          sigi = SigInput so 100000 op sigHashDefault Nothing+          tweaked = fromJust (taprootOutputSecKey ctx k.key Nothing)+      byInternal <- either fail return (signTx btc ctx tx [sigi] [k.key])+      byOutput <- either fail return (signTx btc ctx tx [sigi] [tweaked])+      byInternal `shouldBe` byOutput+      map length byInternal.witness `shouldBe` [1]+      map (B.length . head) byInternal.witness `shouldBe` [64]+      verifyStdTx btc ctx byInternal [(so, 100000, op)] `shouldBe` True+      -- A signature with a hash type is 65 bytes and verifies as well+      withType <- either fail return (signTx btc ctx tx [sigi {sighash = setAnyoneCanPay sigHashAll}] [k.key])+      map (B.length . head) withType.witness `shouldBe` [65]+      verifyStdTx btc ctx withType [(so, 100000, op)] `shouldBe` True+      -- Another key does not sign+      let (other, _) = fixedKey ctx 2+      fmap (.witness) (signTx btc ctx tx [sigi] [other.key]) `shouldBe` Right []++    it "needs the output every input spends, and refuses signInput" $ do+      let (k, p) = fixedKey ctx 1+      let so = taprootScriptOutput ctx (TaprootOutput p.point Nothing)+          other = OutPoint (fromJust (hexToTxHash genesisTxId)) 1+          tx = buildTx ctx [op, other] [(so, 90000)]+          sigi = SigInput so 100000 op sigHashDefault Nothing+      signTx btc ctx tx [sigi] [k.key] `shouldBe` Left "signTx: a Taproot signature needs the output every input spends"+      signInput btc ctx tx 0 sigi k `shouldBe` Left "signInput: a Taproot input is signed with signTaprootInput"+      signTaprootInput ctx tx [TxOut 100000 (marshal ctx so)] 0 sigHashDefault k.key `shouldBe` Left "taprootSigHash: one spent output per input is required"+      signTaprootInput ctx tx [TxOut 100000 (marshal ctx so), TxOut 1 ""] 0 (setForkIdFlag sigHashAll) k.key `shouldBe` Left "taprootSigHash: invalid hash type 65"++{- SegWit helpers -}++segwitSpec :: Ctx -> Spec+segwitSpec ctx =+  describe "SegWit witness helpers" $ do+    let (k, p) = fixedKey ctx 1+        digest = sha256 ("message" :: ByteString)+        sig = TxSignature (signHash ctx k.key digest) sigHashAll+        sigBytes = encodeTxSig btc ctx sig+        keyBytes = marshal ctx p+        h = fromJust (addressHash160 (pubKeyAddr ctx p))+        multisig = PayMulSig [p] 1+        multisigScript = either error id (encodeOutput ctx multisig)+        wsh = either error id (toP2WSH multisigScript)+    it "isSegwit is true for the version 0 programs only" $ do+      isSegwit (PayWitnessPKHash h) `shouldBe` True+      isSegwit wsh `shouldBe` True+      isSegwit (PayPKHash h) `shouldBe` False+      isSegwit (PayWitness 1 (B.replicate 32 0)) `shouldBe` False++    it "a simple input stacks its signatures, keys and the empty item of multisig" $ do+      simpleInputStack btc ctx (SpendPK sig) `shouldBe` [sigBytes]+      simpleInputStack btc ctx (SpendPKHash sig p) `shouldBe` [sigBytes, keyBytes]+      simpleInputStack btc ctx (SpendMulSig [sig, TxSignatureEmpty]) `shouldBe` ["", sigBytes, ""]++    it "a witness program encodes to its stack, with the script last for P2WSH" $ do+      toWitnessStack btc ctx (P2WPKH (WitnessProgramPKH sig p)) `shouldBe` [sigBytes, keyBytes]+      toWitnessStack btc ctx (P2WSH (WitnessProgramSH ["", sigBytes] multisigScript)) `shouldBe` ["", sigBytes, runPutS (serialize multisigScript)]+      toWitnessStack btc ctx EmptyWitnessProgram `shouldBe` []++    it "a witness is read against the output it spends" $ do+      viewWitnessProgram btc ctx (PayWitnessPKHash h) [sigBytes, keyBytes] `shouldBe` Right (P2WPKH (WitnessProgramPKH sig p))+      viewWitnessProgram btc ctx (PayWitnessPKHash h) [sigBytes] `shouldBe` Left "viewWitnessProgram: Invalid witness program"+      viewWitnessProgram btc ctx wsh ["", sigBytes, runPutS (serialize multisigScript)] `shouldBe` Right (P2WSH (WitnessProgramSH ["", sigBytes] multisigScript))+      viewWitnessProgram btc ctx (PayPKHash h) [] `shouldBe` Right EmptyWitnessProgram+      viewWitnessProgram btc ctx (PayPKHash h) [sigBytes] `shouldBe` Left "viewWitnessProgram: Invalid witness program"++    it "a witness program decodes to the input it stands for" $ do+      decodeWitnessInput btc ctx (P2WPKH (WitnessProgramPKH sig p)) `shouldBe` Right (Nothing, SpendPKHash sig p)+      decodeWitnessInput btc ctx (P2WSH (WitnessProgramSH ["", sigBytes] multisigScript)) `shouldBe` Right (Just multisig, SpendMulSig [sig])+      decodeWitnessInput btc ctx (P2WSH (WitnessProgramSH [sigBytes] multisigScript)) `shouldBe` Left "decodeWitnessInput: Non-standard script output"+      decodeWitnessInput btc ctx EmptyWitnessProgram `shouldBe` Left "decodeWitnessInput: Empty witness program"++    it "a witness program is computed from a standard input, native or nested" $ do+      calcWitnessProgram btc ctx (PayWitnessPKHash h) (RegularInput (SpendPKHash sig p)) `shouldBe` Right (P2WPKH (WitnessProgramPKH sig p))+      calcWitnessProgram btc ctx (either error id (toP2SH =<< encodeOutput ctx (PayWitnessPKHash h))) (RegularInput (SpendPKHash sig p)) `shouldBe` Right (P2WPKH (WitnessProgramPKH sig p))+      calcWitnessProgram btc ctx wsh (ScriptHashInput (SpendMulSig [sig]) multisig) `shouldBe` Right (P2WSH (WitnessProgramSH ["", sigBytes] multisigScript))+      calcWitnessProgram btc ctx (PayPKHash h) (RegularInput (SpendPKHash sig p)) `shouldBe` Left "calcWitnessProgram: Invalid segwit SigInput"++{- Genesis -}++genesisSpec :: Spec+genesisSpec =+  describe "Genesis coinbases" $ do+    it "genesisCoinbase builds a version 1 transaction with one null input and one output of 50 BTC" $ do+      let tx = genesisCoinbase "in" "out"+      tx `shouldBe` Tx 1 [TxIn nullOutPoint "in" 0xffffffff] [TxOut 5000000000 "out"] [] 0+      nullOutPoint `shouldBe` OutPoint "0000000000000000000000000000000000000000000000000000000000000000" 0xffffffff+    it "the mainnet coinbase has the id of the genesis block's only transaction" $+      txHashToHex (txHash mainnetGenesisTx) `shouldBe` "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"+    it "the testnet4 coinbase has its own id" $+      txHashToHex (txHash testnet4GenesisTx) `shouldBe` "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e"++{- Transactions signed by Bitcoin Core -}++-- | The vectors of data/core_signed_txs.json: the kind of coin, the signed+-- transaction in hex, and the output script in hex and the amount of the+-- coin that each of its two inputs spends+coreSignedVectors :: IO [(String, Text, Text, Word64)]+coreSignedVectors = readTestFileParser file "core_signed_txs.json"+  where+    file = withObject "file" $ \o -> o .: "vectors" >>= mapM vector+    vector = withObject "vector" $ \v -> (,,,) <$> v .: "kind" <*> v .: "tx" <*> v .: "script" <*> v .: "amount"++-- These signatures were made by Bitcoin Core 32.1 on regtest, not by the+-- library, so they check the verifier against an independent signer.+coreSignedSpec :: Ctx -> Spec+coreSignedSpec ctx =+  describe "Transactions signed by Bitcoin Core" $ do+    let verdict coinAmount (_, txHex, scriptHex, amount) = do+          tx <- decodeTx (hexBytes txHex)+          coin <- unmarshal ctx (hexBytes scriptHex)+          return (verifyStdTx btcRegTest ctx tx [(coin, coinAmount amount, i.outpoint) | i <- tx.inputs])+        kindOf (kind, _, _, _) = kind+        -- The kinds whose digest commits to the amount of the coin+        commitsToAmount v = any (`isPrefixOf` kindOf v) ["p2w", "p2sh-p2w", "p2tr"]++    -- 16 kinds of coin with the six hash types of ECDSA, and the default+    -- type as well for the Taproot key path: 16 × 6 + 1+    it "verifies the 97 spends of the file, of every kind of coin and hash type" $ do+      vectors <- coreSignedVectors+      length vectors `shouldBe` 97+      [kindOf v | v <- vectors, verdict id v /= Right True] `shouldBe` []++    -- 6 SegWit kinds with six hash types and the Taproot key path with seven+    it "refuses the 43 SegWit and Taproot spends of the file when the coin is given with one satoshi more" $ do+      vectors <- filter commitsToAmount <$> coreSignedVectors+      length vectors `shouldBe` 43+      [kindOf v | v <- vectors, verdict (+ 1) v /= Right False] `shouldBe` []++{- Inputs that no block can contain -}++-- A 2-of-3 multisig coin of the fixed keys 1, 2 and 3, in its three forms:+-- bare, pay-to-script-hash and pay-to-witness-script-hash. Each test gives+-- the signature items of the input, and gets the verdict for each form.+consensusSpec :: Ctx -> Spec+consensusSpec ctx =+  describe "Verification of inputs that no block can contain" $ do+    let (k1, p1) = fixedKey ctx 1+        (k2, p2) = fixedKey ctx 2+        (k3, p3) = fixedKey ctx 3+        (k4, _) = fixedKey ctx 4+        op = coinAt 0+        tx = buildTx ctx [op] [(PayPKHash (keyHash ctx p1), 90000)]+        ms = PayMulSig [p1, p2, p3] 2+        msScript = either error id (encodeOutput ctx ms)+        -- The digest that a signature of the bare and the pay-to-script-hash+        -- form signs, and the BIP 143 digest of the witness form+        legacyDigest = either error id (txSigHash btc tx msScript 100000 0 sigHashAll)+        witnessDigest = either error id (txSigHashForkId btc tx msScript 100000 0 sigHashAll)+        sig digest k = TxSignature (signHash ctx k.key digest) sigHashAll+        p2sh = either error id (toP2SH msScript)+        p2wsh = either error id (toP2WSH msScript)+        bare items = withInputScript tx 0 (marshal (btc, ctx) (RegularInput (SpendMulSig items)))+        hashed items = withInputScript tx 0 (marshal (btc, ctx) (ScriptHashInput (SpendMulSig items) ms))+        inWitness items = withWitness tx [simpleInputStack btc ctx (SpendMulSig items) <> [runPutS (serialize msScript)]]+        verdicts itemsOf =+          ( verifyStdInput btc ctx (bare (itemsOf legacyDigest)) [(ms, 100000, op)] 0,+            verifyStdInput btc ctx (hashed (itemsOf legacyDigest)) [(p2sh, 100000, op)] 0,+            verifyStdInput btc ctx (inWitness (itemsOf witnessDigest)) [(p2wsh, 100000, op)] 0+          )++    it "accepts a 2-of-3 multisig input with the signatures of two of its keys, in key order" $ do+      verdicts (\d -> [sig d k1, sig d k2]) `shouldBe` (True, True, True)+      verdicts (\d -> [sig d k1, sig d k3]) `shouldBe` (True, True, True)++    -- OP_CHECKMULTISIG takes as many signatures as the script requires. An+    -- item more becomes its dummy element, which must be empty.+    it "refuses a 2-of-3 multisig input with a third item after its two signatures" $ do+      verdicts (\d -> [sig d k1, sig d k2, sig d k4]) `shouldBe` (False, False, False)+      verdicts (\d -> [sig d k1, sig d k2, sig d k3]) `shouldBe` (False, False, False)++    -- The two items taken as signatures are the empty one and the last.+    -- The first signature is left in the place of the dummy element.+    it "refuses a 2-of-3 multisig input with an empty item among its signatures" $ do+      verdicts (\d -> [sig d k1, TxSignatureEmpty, sig d k3]) `shouldBe` (False, False, False)+      verdicts (\d -> [sig d k1, TxSignatureEmpty]) `shouldBe` (False, False, False)++    -- The empty item becomes the dummy element, and the one written for+    -- that stays on the stack. A witness must leave a clean stack (BIP+    -- 141). The legacy forms may be in a block, but nodes do not relay them+    -- (CLEANSTACK), and the verifier refuses what is not relayed.+    it "refuses a 2-of-3 multisig input with an empty item before its two signatures" $+      verdicts (\d -> [TxSignatureEmpty, sig d k1, sig d k2]) `shouldBe` (False, False, False)++    it "refuses a 2-of-3 multisig input with one signature, or with two out of key order" $ do+      verdicts (\d -> [sig d k1]) `shouldBe` (False, False, False)+      verdicts (\d -> [sig d k3, sig d k1]) `shouldBe` (False, False, False)++    -- BIP 141: the input script of a nested witness program "must be+    -- exactly a push of the BIP16 redeemScript"+    it "refuses a P2SH-P2WPKH input whose script pushes the program with OP_PUSHDATA1" $ do+      let h = keyHash ctx p1+          coin = either error id (toP2SH =<< encodeOutput ctx (PayWitnessPKHash h))+          sigi = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing+          program = B.pack [0x00, 0x14] <> runPutS (serialize h)+      signed <- either fail return (signNestedWitnessTx btc ctx tx [sigi] [k1.key])+      (head signed.inputs).script `shouldBe` B.singleton 0x16 <> program+      verifyStdInput btc ctx signed [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx (withInputScript signed 0 (B.pack [0x4c, 0x16] <> program)) [(coin, 100000, op)] 0 `shouldBe` False++    it "refuses a Taproot input that has an input script" $ do+      let so = taprootScriptOutput ctx (TaprootOutput p1.point Nothing)+      signed <- either fail return (signTx btc ctx tx [SigInput so 100000 op sigHashDefault Nothing] [k1.key])+      verifyStdInput btc ctx signed [(so, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx (withInputScript signed 0 "\x51") [(so, 100000, op)] 0 `shouldBe` False++    -- CheckTransaction of Bitcoin Core: bad-txns-inputs-duplicate+    it "refuses a transaction that lists the same outpoint twice" $ do+      let h = keyHash ctx p1+          twice = buildTx ctx [op, op] [(PayPKHash h, 90000)]+          sigi = SigInput (PayPKHash h) 100000 op sigHashAll Nothing+      signed <- either fail return (signTx btc ctx twice [sigi, sigi] [k1.key])+      map (B.null . (.script)) signed.inputs `shouldBe` [False, False]+      verifyStdTx btc ctx signed [(PayPKHash h, 100000, op)] `shouldBe` False+      verifyStdTx btc ctx signed [(PayPKHash h, 100000, op), (PayPKHash h, 100000, op)] `shouldBe` False++    it "refuses a transaction with no inputs and no outputs" $+      verifyStdTx btc ctx (Tx 1 [] [] [] 0) [] `shouldBe` False++    it "accepts outputs that spend exactly what the coins hold, and refuses one satoshi more" $ do+      let h = keyHash ctx p1+          sigi = SigInput (PayPKHash h) 100000 op sigHashAll Nothing+          exact = buildTx ctx [op] [(PayPKHash h, 100000)]+          over = buildTx ctx [op] [(PayPKHash h, 100001)]+      signedExact <- either fail return (signTx btc ctx exact [sigi] [k1.key])+      signedOver <- either fail return (signTx btc ctx over [sigi] [k1.key])+      verifyStdTx btc ctx signedExact [(PayPKHash h, 100000, op)] `shouldBe` True+      verifyStdTx btc ctx signedOver [(PayPKHash h, 100000, op)] `shouldBe` False++    -- Bitcoin Cash has no SegWit, so a witness program is no template there+    it "refuses a witness spend on a network without SegWit" $ do+      let h = keyHash ctx p1+          forkId = setForkIdFlag sigHashAll+          code = either error id (encodeOutput ctx (PayPKHash h))+          digest = either error id (txSigHashForkId bch tx code 100000 0 forkId)+          stack = [encodeTxSig bch ctx (TxSignature (signHash ctx k1.key digest) forkId), marshal ctx p1]+      verifyStdInput bch ctx (withWitness tx [stack]) [(PayWitnessPKHash h, 100000, op)] 0 `shouldBe` False++{- A coin spent with a key or a script that is not its own -}++-- In each test the thief signs the right digest of the coin with key 2, and+-- puts key 2, or a script of key 2, where the coin commits to key 1.+thiefSpec :: Ctx -> Spec+thiefSpec ctx =+  describe "Verification of a coin spent with a key or a script that is not its own" $ do+    let (k1, p1) = fixedKey ctx 1+        (k2, p2) = fixedKey ctx 2+        h1 = keyHash ctx p1+        h2 = keyHash ctx p2+        op = coinAt 0+        tx = buildTx ctx [op] [(PayPKHash h1, 90000)]+        script = either error id . encodeOutput ctx+        legacy code = either error id (txSigHash btc tx (script code) 100000 0 sigHashAll)+        bip143 code = either error id (txSigHashForkId btc tx (script code) 100000 0 sigHashAll)+        sig digest k = TxSignature (signHash ctx k.key digest) sigHashAll+        owner sigi = either error id (signTx btc ctx tx [sigi] [k1.key])+        ownerNested sigi = either error id (signNestedWitnessTx btc ctx tx [sigi] [k1.key])++    it "refuses a P2PKH coin spent with another key" $ do+      let coin = PayPKHash h1+          thief = withInputScript tx 0 (marshal (btc, ctx) (RegularInput (SpendPKHash (sig (legacy coin) k2) p2)))+      verifyStdInput btc ctx (owner (SigInput coin 100000 op sigHashAll Nothing)) [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx thief [(coin, 100000, op)] 0 `shouldBe` False++    it "refuses a P2SH coin spent with another redeem script" $ do+      let coin = either error id (toP2SH (script (PayPK p1)))+          thief = withInputScript tx 0 (marshal (btc, ctx) (ScriptHashInput (SpendPK (sig (legacy (PayPK p2)) k2)) (PayPK p2)))+      verifyStdInput btc ctx (owner (SigInput coin 100000 op sigHashAll (Just (PayPK p1)))) [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx thief [(coin, 100000, op)] 0 `shouldBe` False++    it "refuses a P2WPKH coin spent with another key" $ do+      let coin = PayWitnessPKHash h1+          thief = withWitness tx [[encodeTxSig btc ctx (sig (bip143 (PayPKHash h1)) k2), marshal ctx p2]]+      verifyStdInput btc ctx (owner (SigInput coin 100000 op sigHashAll Nothing)) [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx thief [(coin, 100000, op)] 0 `shouldBe` False++    it "refuses a P2WSH coin spent with another witness script" $ do+      let own = PayMulSig [p1] 1+          other = PayMulSig [p2] 1+          coin = either error id (toP2WSH (script own))+          thief = withWitness tx [["", encodeTxSig btc ctx (sig (bip143 other) k2), runPutS (serialize (script other))]]+      verifyStdInput btc ctx (owner (SigInput coin 100000 op sigHashAll (Just own))) [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx thief [(coin, 100000, op)] 0 `shouldBe` False++    it "refuses a P2SH-P2WPKH coin spent with the program of another key" $ do+      let coin = either error id (toP2SH (script (PayWitnessPKHash h1)))+          thief =+            withWitness+              (withInputScript tx 0 (marshal (btc, ctx) (NestedWitnessInput (PayWitnessPKHash h2))))+              [[encodeTxSig btc ctx (sig (bip143 (PayPKHash h2)) k2), marshal ctx p2]]+      verifyStdInput btc ctx (ownerNested (SigInput (PayWitnessPKHash h1) 100000 op sigHashAll Nothing)) [(coin, 100000, op)] 0 `shouldBe` True+      verifyStdInput btc ctx thief [(coin, 100000, op)] 0 `shouldBe` False++{- What the signer refuses -}++signerSpec :: Ctx -> Spec+signerSpec ctx =+  describe "What the signer refuses" $ do+    let (k1, p1) = fixedKey ctx 1+        (k2, _) = fixedKey ctx 2+        (k1u, p1u) = fixedKeyU ctx 1+        h1 = keyHash ctx p1+        op = coinAt 0+        -- Two outputs, so that SIGHASH_SINGLE has one at the index of the input+        tx = buildTx ctx [op] [(PayPKHash h1, 50000), (PayPKHash h1, 40000)]+        signWith net coin sh = signTx net ctx tx [SigInput coin 100000 op sh Nothing] [k1.key]++    it "signs the six hash types that a node accepts" $+      forM_ [0x01, 0x02, 0x03, 0x81, 0x82, 0x83] $ \sh -> do+        signWith btc (PayPKHash h1) sh `shouldSatisfy` isRight+        signWith btc (PayWitnessPKHash h1) sh `shouldSatisfy` isRight++    -- IsDefinedHashtypeSignature of Bitcoin Core: without the+    -- SIGHASH_ANYONECANPAY bit the byte is 1, 2 or 3+    it "refuses a hash type that no node accepts on Bitcoin" $+      forM_ [PayPKHash h1, PayWitnessPKHash h1] $ \coin -> do+        signWith btc coin 0x00 `shouldBe` Left "makeSignature: the hash type 0 is not one a node accepts"+        signWith btc coin 0x04 `shouldBe` Left "makeSignature: the hash type 4 is not one a node accepts"+        signWith btc coin 0x21 `shouldBe` Left "makeSignature: the hash type 33 is not one a node accepts"+        signWith btc coin 0x80 `shouldBe` Left "makeSignature: the hash type 128 is not one a node accepts"+        signWith btc coin 0xff `shouldBe` Left "makeSignature: the hash type 255 has the fork id flag, which this network does not use"+        signWith btc coin 0x41 `shouldBe` Left "makeSignature: the hash type 65 has the fork id flag, which this network does not use"++    -- A signature carries one byte of hash type, and the digest four+    it "refuses a hash type that does not fit in one byte" $ do+      signWith btc (PayPKHash h1) 0x101 `shouldBe` Left "makeSignature: the hash type 257 does not fit in the one byte of a signature"+      signWith btc (PayWitnessPKHash h1) 0x101 `shouldBe` Left "makeSignature: the hash type 257 does not fit in the one byte of a signature"+      signWith bch (PayPKHash h1) 0x101 `shouldBe` Left "makeSignature: the hash type 257 does not fit in the one byte of a signature"++    -- 0x20 is SIGHASH_UTXOS on Bitcoin Cash, whose digest the library does+    -- not compute+    it "refuses a hash type that it cannot sign on Bitcoin Cash, and adds the fork id flag to the others" $ do+      signWith bch (PayPKHash h1) 0x04 `shouldBe` Left "makeSignature: the hash type 4 is not one a node accepts"+      signWith bch (PayPKHash h1) 0x21 `shouldBe` Left "makeSignature: the hash type 33 is not one a node accepts"+      signed <- either fail return (signWith bch (PayPKHash h1) 0x01)+      -- The signature is the first push of the input script, and its last byte the hash type+      fmap (\si -> case si of RegularInput (SpendPKHash (TxSignature _ sh) _) -> Just sh; _ -> Nothing) (unmarshal (bch, ctx) (head signed.inputs).script)+        `shouldBe` Right (Just 0x41)++    it "refuses a pay-to-script-hash coin whose redeem script is over 520 bytes" $ do+      let rdm = PayMulSig [snd (fixedKey ctx n) | n <- [1 .. 16]] 1+          -- OP_1, 16 keys of 34 bytes with their push, OP_16, OP_CHECKMULTISIG: 547 bytes+          h = addressHash (runPutS (serialize (either error id (encodeOutput ctx rdm))))+      signTx btc ctx tx [SigInput (PayScriptHash h) 100000 op sigHashAll (Just rdm)] [k1.key]+        `shouldBe` Left "The redeem script is 547 bytes long, and a pay-to-script-hash redeem script can have at most 520"++    it "refuses a pay-to-witness-script-hash coin whose witness script is over 10000 bytes" $ do+      let rdm = DataCarrier [B.replicate 10000 0]+          -- OP_RETURN, OP_PUSHDATA2 with its two length bytes, 10000 bytes: 10004+          h = sha256 (runPutS (serialize (either error id (encodeOutput ctx rdm))))+      makeSignature btc ctx tx 0 (SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just rdm)) k1+        `shouldBe` Left "The witness script is 10004 bytes long, and a pay-to-witness-script-hash witness script can have at most 10000"++    it "refuses a redeem script or a witness script that does not hash to the coin, and a coin without its script" $ do+      let other = "0000000000000000000000000000000000000000" :: Hash160+          other32 = "0000000000000000000000000000000000000000000000000000000000000000" :: Hash256+          rdm = PayPK p1+      makeSignature btc ctx tx 0 (SigInput (PayScriptHash other) 100000 op sigHashAll (Just rdm)) k1+        `shouldBe` Left "makeSignature: the redeem script does not hash to the coin"+      makeSignature btc ctx tx 0 (SigInput (PayWitnessScriptHash other32) 100000 op sigHashAll (Just rdm)) k1+        `shouldBe` Left "makeSignature: the witness script does not hash to the coin"+      makeSignature btc ctx tx 0 (SigInput (PayScriptHash other) 100000 op sigHashAll Nothing) k1+        `shouldBe` Left "makeSignature: a pay-to-script-hash coin needs its redeem script"+      makeSignature btc ctx tx 0 (SigInput (PayWitnessScriptHash other32) 100000 op sigHashAll Nothing) k1+        `shouldBe` Left "makeSignature: a pay-to-witness-script-hash coin needs its witness script"++    -- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE of Bitcoin Core: no node relays a+    -- SegWit version 0 spend with an uncompressed key+    it "signTx refuses a SegWit coin of an uncompressed key, native, in a witness script or nested" $ do+      let hu = keyHash ctx p1u+          refusal = Left "sigKeys: a SegWit input is signed with a compressed key only, and this coin pays to an uncompressed key"+          wsh rdm = PayWitnessScriptHash (sha256 (runPutS (serialize (either error id (encodeOutput ctx rdm)))))+      signTx btc ctx tx [SigInput (PayWitnessPKHash hu) 100000 op sigHashAll Nothing] [k1.key] `shouldBe` refusal+      signNestedWitnessTx btc ctx tx [SigInput (PayWitnessPKHash hu) 100000 op sigHashAll Nothing] [k1.key] `shouldBe` refusal+      signTx btc ctx tx [SigInput (wsh (PayPK p1u)) 100000 op sigHashAll (Just (PayPK p1u))] [k1.key] `shouldBe` refusal+      signTx btc ctx tx [SigInput (wsh (PayMulSig [p1u] 1)) 100000 op sigHashAll (Just (PayMulSig [p1u] 1))] [k1.key] `shouldBe` refusal+      -- The same key signs a legacy coin in its uncompressed form+      signTx btc ctx tx [SigInput (PayPKHash hu) 100000 op sigHashAll Nothing] [k1.key] `shouldSatisfy` isRight++    -- A block accepts such a spend, so one who knows what it does can still+    -- make it, with the key in the form to use+    it "signInput signs a SegWit coin with the uncompressed key it is given" $ do+      let hu = keyHash ctx p1u+          sigi = SigInput (PayWitnessPKHash hu) 100000 op sigHashAll Nothing+      signed <- either fail return (signInput btc ctx tx 0 sigi k1u)+      map (map B.length) signed.witness `shouldSatisfy` (\w -> map (drop 1) w == [[65]])+      verifyStdInput btc ctx signed [(PayWitnessPKHash hu, 100000, op)] 0 `shouldBe` True++    it "refuses to sign a SegWit or a Taproot coin on a network without SegWit" $ do+      let so = taprootScriptOutput ctx (TaprootOutput p1.point Nothing)+      signWith bch (PayWitnessPKHash h1) sigHashAll `shouldBe` Left "makeSignature: this network has no SegWit"+      signTx bch ctx tx [SigInput so 100000 op sigHashDefault Nothing] [k1.key] `shouldBe` Left "signTx: this network has no Taproot"++    it "signInput refuses a key that is not a key of the coin" $ do+      signInput btc ctx tx 0 (SigInput (PayPKHash h1) 100000 op sigHashAll Nothing) k2 `shouldBe` Left "signInput: the key is not a key of the coin"+      signInput btc ctx tx 0 (SigInput (PayWitnessPKHash h1) 100000 op sigHashAll Nothing) k2 `shouldBe` Left "signInput: the key is not a key of the coin"+      signInput btc ctx tx 0 (SigInput (PayMulSig [p1] 1) 100000 op sigHashAll Nothing) k2 `shouldBe` Left "signInput: the key is not a key of the coin"+      signInput btc ctx tx 0 (SigInput (PayPKHash h1) 100000 op sigHashAll Nothing) k1 `shouldSatisfy` isRight++    it "signTaprootInput refuses a key that is not the key of the output, and an output that is not Taproot" $ do+      let so = taprootScriptOutput ctx (TaprootOutput p1.point Nothing)+          spent = [TxOut 100000 (marshal ctx so)]+          tweaked = fromJust (taprootOutputSecKey ctx k1.key Nothing)+      -- The internal key is not the key of the output: its tweak is+      signTaprootInput ctx tx spent 0 sigHashDefault k1.key `shouldBe` Left "signTaprootInput: the key is not the key of the output"+      signTaprootInput ctx tx spent 0 sigHashDefault tweaked `shouldSatisfy` isRight+      signTaprootInput ctx tx [TxOut 100000 (marshal ctx (PayWitnessPKHash h1))] 0 sigHashDefault tweaked+        `shouldBe` Left "signTaprootInput: the output spent is not a Taproot output"++    it "refuses to sign a SegWit input of a transaction whose witness list does not match its inputs" $ do+      let two = buildTx ctx [op, coinAt 1] [(PayPKHash h1, 90000)]+          sigi = SigInput (PayWitnessPKHash h1) 100000 (coinAt 1) sigHashAll Nothing+          ms = PayMulSig [p1] 1+          wsh = either error id (toP2WSH =<< encodeOutput ctx ms)+      signTx btc ctx (withWitness two [[]]) [sigi] [k1.key] `shouldBe` Left "Invalid number of witness stacks"+      signTx btc ctx (withWitness two [[]]) [SigInput wsh 100000 (coinAt 1) sigHashAll (Just ms)] [k1.key] `shouldBe` Left "Invalid number of witness stacks"+      signTx btc ctx two [sigi] [k1.key] `shouldSatisfy` isRight
test/Haskoin/UtilSpec.hs view
@@ -1,58 +1,345 @@-module Haskoin.UtilSpec-    ( spec-    , customCerealID-    , readTestFile-    ) where+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} -import           Data.Aeson             (FromJSON, ToJSON)-import qualified Data.Aeson             as A-import           Data.Aeson.Encoding    (encodingToLazyByteString)-import           Data.Aeson.Types       (Parser, parseMaybe)-import qualified Data.ByteString        as BS-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.Serialize         as S-import           Haskoin.Util-import           Haskoin.Util.Arbitrary-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.QuickCheck+-- | The helpers of "Haskoin.Util": bytes and hex, integers, lists and+-- templates, bit conversion, JSON options, the serialization helpers and+-- the marshalling classes.+module Haskoin.UtilSpec (spec) where +import Control.Exception (ErrorCall, evaluate)+import System.Timeout (timeout)+import Control.Monad.Except (ExceptT, runExceptT)+import Data.Aeson+import Data.Aeson.Encoding (encodingToLazyByteString)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Builder (toLazyByteString)+import Data.ByteString.Lazy qualified as BL+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Default (def)+import Data.Either (isLeft)+import Data.Foldable (toList)+import Data.Int (Int32, Int64)+import Data.IntMap qualified as IntMap+import Data.List (isInfixOf, permutations)+import Data.Maybe+import Data.Sequence qualified as Seq+import Data.Serialize qualified as S+import Data.Word (Word8)+import GHC.Generics (Generic)+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: IdentityTests+identityTests =+  def+    { readTests =+        [ ReadBox arbitraryNetwork+        ]+    }++-- A record and a sum type for the JSON options+data Point = Point {ptX :: Int, ptY :: Int}+  deriving (Eq, Show, Generic)++instance ToJSON Point where+  toJSON = genericToJSON (dropFieldLabel 2)++instance FromJSON Point where+  parseJSON = genericParseJSON (dropFieldLabel 2)++data Shape = SCircle {scRadius :: Int} | SSquare {ssSide :: Int}+  deriving (Eq, Show, Generic)++instance ToJSON Shape where+  toJSON = genericToJSON (dropSumLabels 1 2 "type")++instance FromJSON Shape where+  parseJSON = genericParseJSON (dropSumLabels 1 2 "type")+ 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+spec = prepareContext $ \ctx ->+  describe "utility functions" $ do+    testIdentity identityTests -{- Various utilities -}+    describe "Integers and bytes" $ do+      it "integerToBS writes big-endian bytes without leading zeros, and zero as one byte" $ do+        integerToBS 0 `shouldBe` "\x00"+        integerToBS 1 `shouldBe` "\x01"+        integerToBS 256 `shouldBe` "\x01\x00"+        integerToBS 0xdeadbeef `shouldBe` "\xde\xad\xbe\xef"+      it "integerToBS refuses a negative number" $+        evaluate (integerToBS (-1)) `shouldThrow` anyErrorCall+      it "bsToInteger reads big-endian bytes, and nothing as zero" $ do+        bsToInteger "\x01\x00" `shouldBe` 256+        bsToInteger "\x00\x00\x01" `shouldBe` 1+        bsToInteger "" `shouldBe` 0+      prop "bsToInteger . integerToBS gives back a number that is not negative" bytesOfNonNegative -getPutInteger :: Integer -> Bool-getPutInteger i = bsToInteger (integerToBS $ abs i) == abs i+    describe "Hex" $ do+      it "encodes lower case and decodes either case" $ do+        encodeHex "\xab\xcd" `shouldBe` "abcd"+        encodeHexLazy "\xab\xcd" `shouldBe` "abcd"+        decodeHex "abcd" `shouldBe` Just "\xab\xcd"+        decodeHex "ABCD" `shouldBe` Just "\xab\xcd"+        decodeHexLazy "ABCD" `shouldBe` Just "\xab\xcd"+        decodeHex "" `shouldBe` Just ""+      it "refuses an odd length, a character that is not hex, and spaces" $ do+        decodeHex "abc" `shouldBe` Nothing+        decodeHex "0g" `shouldBe` Nothing+        decodeHex " 00" `shouldBe` Nothing+        decodeHex "00 " `shouldBe` Nothing+        decodeHexLazy "abc" `shouldBe` Nothing+      it "hexEncoding is a JSON string of hex, and hexBuilder the hex alone" $ do+        encodingToLazyByteString (hexEncoding "\xab\xcd") `shouldBe` "\"abcd\""+        toLazyByteString (hexBuilder "\xab\xcd") `shouldBe` "abcd"+      prop "decodeHex . encodeHex" $ forAll arbitraryBS fromToHex+      prop "decodeHexLazy . encodeHexLazy" $ forAll arbitraryBS $ \bs -> decodeHexLazy (encodeHexLazy (BL.fromStrict bs)) == Just (BL.fromStrict bs) -fromToHex :: BS.ByteString -> Bool+    describe "getBits" $ do+      it "takes that many bits from the start, padded with zero bits" $ do+        getBits 4 "\xff\x00" `shouldBe` "\xf0"+        getBits 8 "\xff\x00" `shouldBe` "\xff"+        getBits 12 "\xff\xff" `shouldBe` "\xff\xf0"+        getBits 0 "\xff" `shouldBe` ""+      it "raises an error when the bytes do not hold that many bits" $ do+        evaluate (getBits 12 "\xff") `shouldThrow` anyErrorCall+        evaluate (getBits 4 "") `shouldThrow` anyErrorCall+        evaluate (getBits (-1) "\xff") `shouldThrow` anyErrorCall++    describe "Maybe and Either" $ do+      prop "eitherToMaybe" testEitherToMaybe+      prop "maybeToEither" testMaybeToEither+      it "liftMaybe and liftEither lift into ExceptT" $ do+        runExceptT (liftMaybe "none" (Just (1 :: Int))) `shouldReturn` Right 1+        runExceptT (liftMaybe "none" (Nothing :: Maybe Int)) `shouldReturn` Left "none"+        runExceptT (liftEither (Right (2 :: Int)) :: ExceptT' Int) `shouldReturn` Right 2+        runExceptT (liftEither (Left "bad") :: ExceptT' Int) `shouldReturn` Left "bad"++    describe "updateIndex" $ do+      it "changes the element at the index, and nothing when the index is out of range" $ do+        updateIndex 1 [1, 2, 3 :: Int] (* 10) `shouldBe` [1, 20, 3]+        updateIndex 0 [1, 2, 3 :: Int] (* 10) `shouldBe` [10, 2, 3]+        updateIndex 3 [1, 2, 3 :: Int] (* 10) `shouldBe` [1, 2, 3]+        updateIndex (-1) [1, 2, 3 :: Int] (* 10) `shouldBe` [1, 2, 3]+      prop "compare updateIndex with Data.Sequence" testUpdateIndex++    describe "matchTemplate" $ do+      it "gives for each element of the template the first matching input, each input once" $ do+        matchTemplate [1, 2, 2 :: Int] [2, 2, 3] (==) `shouldBe` [Just 2, Just 2, Nothing]+        matchTemplate [2 :: Int] [2, 2] (==) `shouldBe` [Just 2, Nothing]+        matchTemplate [] [1, 2 :: Int] (==) `shouldBe` [Nothing, Nothing]+        matchTemplate [1 :: Int] [] (==) `shouldBe` []+        matchTemplate ["a", "bb" :: String] [2, 1 :: Int] (\s n -> length s == n) `shouldBe` [Just "bb", Just "a"]+      prop "matchTemplate" testMatchTemplate+      prop "testing matchTemplate with two lists" testMatchTemplateLen++    describe "Triples" $+      it "fst3, snd3 and lst3 take the parts of a triple" $ do+        fst3 (1 :: Int, "b", 'c') `shouldBe` 1+        snd3 (1 :: Int, "b", 'c') `shouldBe` "b"+        lst3 (1 :: Int, "b", 'c') `shouldBe` 'c'++    describe "JSON options" $ do+      it "dropFieldLabel drops the prefix of a field and lowers its case" $ do+        encode (Point 1 2) `shouldBe` "{\"x\":1,\"y\":2}"+        decode "{\"x\":1,\"y\":2}" `shouldBe` Just (Point 1 2)+      it "dropSumLabels also drops the prefix of a constructor, which goes in the tag field" $ do+        encode (SCircle 3) `shouldBe` "{\"radius\":3,\"type\":\"circle\"}"+        encode (SSquare 4) `shouldBe` "{\"side\":4,\"type\":\"square\"}"+        decode "{\"type\":\"square\",\"side\":4}" `shouldBe` Just (SSquare 4)++    describe "convertBits" $ do+      -- 0xff is 11111111: the group 11111 and 111 padded to 11100+      it "regroups bits, padding the last group with zeros when asked" $ do+        convertBits True 8 5 [0xff] `shouldBe` Just [31, 28]+        convertBits True 5 8 [1] `shouldBe` Just [8]+        convertBits True 8 5 [] `shouldBe` Just []+      it "without padding, refuses leftover bits that are not zero or that could hold a whole digit" $ do+        convertBits False 5 8 [31, 28] `shouldBe` Just [255]+        convertBits False 5 8 [31, 31] `shouldBe` Nothing+        convertBits False 5 8 [1, 0] `shouldBe` Just [8]+        convertBits False 5 8 [1] `shouldBe` Nothing+        convertBits False 5 8 [] `shouldBe` Just []+      -- The data part of the BIP 173 address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4, as 5-bit groups, is the key hash of its example+      it "converts the 5-bit groups of the BIP 173 example to its 20-byte program" $+        fmap (B.pack . map fromIntegral) (convertBits False 5 8 [14, 20, 15, 7, 13, 26, 0, 25, 18, 6, 11, 13, 8, 21, 4, 20, 3, 17, 2, 29, 3, 12, 29, 3, 4, 15, 24, 20, 6, 14, 30, 22])+          `shouldBe` Just "\x75\x1e\x76\xe8\x19\x91\x96\xd4\x54\x94\x1c\x45\xd1\xb3\xa3\x23\xf1\x43\x3b\xd6"+      it "refuses a digit that does not fit the width it is converted from" $+        -- 256 needs nine bits. The BIP 173 reference returns None here.+        convertBits True 8 5 [256] `shouldBe` Nothing+      it "refuses a width that is zero, negative or too wide for a Word, instead of looping" $ do+        -- The comparison is made inside the time limit, so that a result+        -- that never ends cannot be forced outside it.+        let refused frombits tobits digits =+              timeout 100000 (evaluate (convertBits True frombits tobits digits == Nothing))+        refused 8 0 [1] `shouldReturn` Just True+        refused 8 (-1) [1] `shouldReturn` Just True+        refused 0 8 [1] `shouldReturn` Just True+        refused (-8) 5 [1] `shouldReturn` Just True+        -- 60 + 8 bits do not fit the 64 bits of the accumulator+        refused 60 8 [1] `shouldReturn` Just True+      prop "converting to 5 bits with padding and back gives the bytes" $+        forAll arbitraryBS $ \bs ->+          let groups = fromJust (convertBits True 8 5 (map fromIntegral (B.unpack bs)))+           in fmap (map fromIntegral) (convertBits False 5 8 groups) === Just (B.unpack bs)++    describe "Serialization helpers" $ do+      it "putList and getList write a 64-bit count then the elements" $ do+        runPutS (putList putWord8 [1, 2]) `shouldBe` "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x02"+        runGetS (getList getWord8) "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x02" `shouldBe` Right [1, 2]+        runGetS (getList getWord8) "\x00\x00\x00\x00\x00\x00\x00\x00" `shouldBe` Right ([] :: [Word8])+      it "getList refuses a count that does not fit an Int instead of reading an empty list" $+        runGetS (getList getWord8) "\xff\xff\xff\xff\xff\xff\xff\xff\x41" `shouldSatisfy` isLeft+      it "putMaybe and getMaybe write a tag byte" $ do+        runPutS (putMaybe putWord8 Nothing) `shouldBe` "\x00"+        runPutS (putMaybe putWord8 (Just 7)) `shouldBe` "\x01\x07"+        runGetS (getMaybe getWord8) "\x01\x07" `shouldBe` Right (Just 7)+        runGetS (getMaybe getWord8) "\x00" `shouldBe` Right (Nothing :: Maybe Word8)+        runGetS (getMaybe getWord8) "\x02" `shouldSatisfy` isLeft+      it "putLengthBytes and getLengthBytes write a 64-bit length then the bytes" $ do+        runPutS (putLengthBytes "abc") `shouldBe` "\x00\x00\x00\x00\x00\x00\x00\x03\&abc"+        runGetS getLengthBytes "\x00\x00\x00\x00\x00\x00\x00\x03\&abc" `shouldBe` Right "abc"+        runGetS getLengthBytes "\xff\xff\xff\xff\xff\xff\xff\xff\x41" `shouldSatisfy` isLeft+        runGetS getLengthBytes "\x00\x00\x00\x00\x00\x00\x00\x03\&ab" `shouldSatisfy` isLeft+      it "putInteger writes a 32-bit number after a zero byte, and a wider one with its sign, its length and its bytes" $ do+        runPutS (putInteger 0) `shouldBe` "\x00\x00\x00\x00\x00"+        runPutS (putInteger (-1)) `shouldBe` "\x00\xff\xff\xff\xff"+        runPutS (putInteger 2147483647) `shouldBe` "\x00\x7f\xff\xff\xff"+        -- 2^31: sign 1, four bytes, little-endian+        runPutS (putInteger 2147483648) `shouldBe` "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x80"+        -- -(2^31 + 1): sign 0xff+        runPutS (putInteger (-2147483649)) `shouldBe` "\x01\xff\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x80"+      it "getInteger reads both forms back" $ do+        runGetS getInteger "\x00\xff\xff\xff\xff" `shouldBe` Right (-1)+        runGetS getInteger "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x80" `shouldBe` Right 2147483648+        runGetS getInteger "\x01\xff\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x80" `shouldBe` Right (-2147483649)+      -- A number has one encoding, the one putInteger writes. The cases+      -- below are other ways to write a number that getInteger used to read.+      it "getInteger refuses a tag byte other than 0 and 1" $+        -- tag 2, then sign 0, length 1 and the byte 5+        runGetS getInteger "\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x05"+          `shouldSatisfy` either ("tag" `isInfixOf`) (const False)+      it "getInteger refuses a sign byte other than 0x01 and 0xff" $ do+        -- 2^31 with the sign bytes 0x00 and 0x02+        runGetS getInteger "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x80"+          `shouldSatisfy` either ("sign" `isInfixOf`) (const False)+        runGetS getInteger "\x01\x02\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x80"+          `shouldSatisfy` either ("sign" `isInfixOf`) (const False)+      it "getInteger refuses a wide number with zero bytes at its top" $+        -- 2^31 in five bytes, little-endian, the fifth being zero+        runGetS getInteger "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x80\x00"+          `shouldSatisfy` either ("shortest" `isInfixOf`) (const False)+      it "getInteger refuses the wide form of a number that fits 32 bits" $ do+        -- 5, and -2^31, which both fit the form with a zero tag+        runGetS getInteger "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x05"+          `shouldSatisfy` either ("shortest" `isInfixOf`) (const False)+        runGetS getInteger "\x01\xff\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x80"+          `shouldSatisfy` either ("shortest" `isInfixOf`) (const False)+      it "getInteger refuses a wide number of no bytes" $+        runGetS getInteger "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00"+          `shouldSatisfy` either ("shortest" `isInfixOf`) (const False)+      it "getInteger reads the smallest 32-bit number, -2^31" $+        runGetS getInteger "\x00\x80\x00\x00\x00" `shouldBe` Right (-2147483648)+      it "getInteger reads a number of 200,000 bytes in well under two seconds" $ do+        -- 256^200000 - 1: every byte is 0xff. The old reader took 11 seconds.+        let input = "\x01\x01\x00\x00\x00\x00\x00\x03\x0d\x40" <> B.replicate 200000 0xff+            expected = 256 ^ (200000 :: Int) - 1 :: Integer+        timeout 2000000 (evaluate (runGetS getInteger input == Right expected))+          `shouldReturn` Just True+      prop "getInteger . putInteger" $ \i -> runGetS getInteger (runPutS (putInteger i)) === Right (i :: Integer)+      prop "getInteger . putInteger for wide numbers" $ \(i :: Integer) (k :: Int) ->+        let n = i * 2 ^ (abs k `mod` 256) in runGetS getInteger (runPutS (putInteger n)) === Right n+      -- Versions before 0.20 wrote the chain work of a header with the+      -- Integer instance of cereal. Headers stored then must still read.+      prop "getInteger reads what the Integer instance of cereal writes" $ \(i :: Integer) (k :: Int) ->+        let n = i * 2 ^ (abs k `mod` 256) in runGetS getInteger (S.encode n) === Right n+      it "putInt32be and putInt64be write two's complement, big-endian" $ do+        runPutS (putInt32be (-1)) `shouldBe` "\xff\xff\xff\xff"+        runPutS (putInt32be minBound) `shouldBe` "\x80\x00\x00\x00"+        runPutS (putInt32be 1) `shouldBe` "\x00\x00\x00\x01"+        runPutS (putInt64be (-2)) `shouldBe` "\xff\xff\xff\xff\xff\xff\xff\xfe"+        runPutS (putInt64be minBound) `shouldBe` "\x80\x00\x00\x00\x00\x00\x00\x00"+        runGetS getInt32be "\x80\x00\x00\x00" `shouldBe` Right (minBound :: Int32)+        runGetS getInt64be "\xff\xff\xff\xff\xff\xff\xff\xfe" `shouldBe` Right (-2 :: Int64)+      prop "getInt32be . putInt32be" $ \i -> runGetS getInt32be (runPutS (putInt32be i)) === Right (i :: Int32)+      prop "getInt64be . putInt64be" $ \i -> runGetS getInt64be (runPutS (putInt64be i)) === Right (i :: Int64)+      it "putIntMap and getIntMap write the pairs in key order after their count" $ do+        let m = IntMap.fromList [(2, 20), (1, 10 :: Word8)]+        runPutS (putIntMap (putWord8 . fromIntegral) putWord8 m) `shouldBe` "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x0a\x02\x14"+        runGetS (getIntMap (fromIntegral <$> getWord8) getWord8) "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x0a\x02\x14" `shouldBe` Right m+      it "getIntMap refuses keys that are not in ascending order, such as a key given twice" $ do+        -- two pairs with the key 1+        runGetS (getIntMap (fromIntegral <$> getWord8) getWord8) "\x00\x00\x00\x00\x00\x00\x00\x02\x01\x0a\x01\x0b"+          `shouldSatisfy` either ("ascending" `isInfixOf`) (const False)+        -- the keys 2 then 1+        runGetS (getIntMap (fromIntegral <$> getWord8) getWord8) "\x00\x00\x00\x00\x00\x00\x00\x02\x02\x14\x01\x0a"+          `shouldSatisfy` either ("ascending" `isInfixOf`) (const False)+      it "putTwo and getTwo write a pair" $ do+        runPutS (putTwo putWord8 putWord16be (1, 2)) `shouldBe` "\x01\x00\x02"+        runGetS (getTwo getWord8 getWord16be) "\x01\x00\x02" `shouldBe` Right (1, 2)++    describe "Marshalling" $ do+      let key = fromJust (secKey (B.replicate 32 1))+          public = PublicKey (derivePubKey ctx key) True+      it "marshal and unmarshal round trip, strict and lazy" $ do+        unmarshal ctx (marshal ctx public) `shouldBe` Right public+        unmarshalLazy ctx (marshalLazy ctx public) `shouldBe` Right public+        (unmarshalLazy ctx "junk" :: Either String PublicKey) `shouldSatisfy` isLeft+      it "unmarshal refuses bytes after the value, strict and lazy" $ do+        -- the compressed public key of secret key 1, then one more byte+        let bytes = fromJust (decodeHex "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ff")+        (unmarshal ctx bytes :: Either String PublicKey)+          `shouldSatisfy` either ("bytes left over" `isInfixOf`) (const False)+        (unmarshalLazy ctx (BL.fromStrict bytes) :: Either String PublicKey)+          `shouldSatisfy` either ("bytes left over" `isInfixOf`) (const False)+      it "marshalJSON and unmarshalJSON round trip, and refuse what is not the value" $ do+        unmarshalJSON ctx (marshalJSON ctx public) `shouldBe` Just public+        (unmarshalJSON ctx "\"junk\"" :: Maybe PublicKey) `shouldBe` Nothing+        (unmarshalJSON ctx "{" :: Maybe PublicKey) `shouldBe` Nothing++    describe "Test helpers" $ do+      it "customCerealID tells whether a getter reads what a putter writes" $ do+        customCerealID S.getWord8 S.putWord8 7 `shouldBe` True+        customCerealID S.getWord16be S.putWord16le 7 `shouldBe` False+      it "readTestFile reads a JSON file of the data directory" $ do+        value <- readTestFile "sig_strict.json" :: IO Value+        value `shouldSatisfy` \v -> case v of Array _ -> True; _ -> False+        parsed <- readTestFileParser parseJSON "sig_strict.json" :: IO [Value]+        length parsed `shouldSatisfy` (> 0)+      it "readTestFile raises an error for a file that is not there" $+        (readTestFile "no-such-file.json" :: IO Value) `shouldThrow` anyException++type ExceptT' a = ExceptTString a++type ExceptTString a = ExceptT String IO a++-- | integerToBS takes no negative number, so the absolute value is tested.+bytesOfNonNegative :: Integer -> Bool+bytesOfNonNegative i = bsToInteger (integerToBS $ abs i) == abs i++fromToHex :: ByteString -> Bool fromToHex bs = decodeHex (encodeHex bs) == Just bs  testUpdateIndex :: [Int] -> Int -> Int -> Bool testUpdateIndex xs v i =-    updateIndex i xs (const v) == toList (Seq.update i v $ Seq.fromList xs)+  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 +354,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 = do-    fileM <- A.decodeFileStrict $ "data/" <> fp-    maybe (error $ "Could not read test file " <> fp) return fileM-