haskoin-core 0.14.1 → 0.15.0
raw patch · 61 files changed
+2951/−1192 lines, 61 filesdep +lensdep +lens-aesondep ~haskoin-coredep ~secp256k1-haskell
Dependencies added: lens, lens-aeson
Dependency ranges changed: haskoin-core, secp256k1-haskell
Files
- CHANGELOG.md +12/−0
- data/key_io_invalid.json +182/−0
- data/key_io_valid.json +536/−0
- data/rfc6979DERabc.json +102/−0
- data/rfc6979DERcore.json +102/−0
- data/rfc6979abc.json +62/−0
- data/rfc6979core.json +62/−0
- haskoin-core.cabal +14/−6
- src/Haskoin.hs +1/−1
- src/Haskoin/Address.hs +96/−111
- src/Haskoin/Address/Base58.hs +1/−1
- src/Haskoin/Address/Bech32.hs +1/−1
- src/Haskoin/Address/CashAddr.hs +1/−1
- src/Haskoin/Block.hs +1/−1
- src/Haskoin/Block/Common.hs +1/−1
- src/Haskoin/Block/Headers.hs +2/−2
- src/Haskoin/Block/Merkle.hs +1/−1
- src/Haskoin/Constants.hs +1/−1
- src/Haskoin/Crypto.hs +1/−1
- src/Haskoin/Crypto/Hash.hs +1/−1
- src/Haskoin/Crypto/Signature.hs +22/−24
- src/Haskoin/Keys.hs +1/−1
- src/Haskoin/Keys/Common.hs +33/−10
- src/Haskoin/Keys/Extended.hs +8/−8
- src/Haskoin/Keys/Mnemonic.hs +1/−1
- src/Haskoin/Network.hs +1/−1
- src/Haskoin/Network/Bloom.hs +27/−25
- src/Haskoin/Network/Common.hs +1/−2
- src/Haskoin/Network/Message.hs +1/−1
- src/Haskoin/Script.hs +1/−1
- src/Haskoin/Script/Common.hs +30/−194
- src/Haskoin/Script/SigHash.hs +10/−11
- src/Haskoin/Script/Standard.hs +182/−18
- src/Haskoin/Transaction.hs +1/−1
- src/Haskoin/Transaction/Builder.hs +16/−17
- src/Haskoin/Transaction/Builder/Sign.hs +5/−15
- src/Haskoin/Transaction/Common.hs +1/−21
- src/Haskoin/Transaction/Genesis.hs +37/−0
- src/Haskoin/Transaction/Partial.hs +1/−1
- src/Haskoin/Transaction/Segwit.hs +5/−9
- src/Haskoin/Util.hs +1/−1
- src/Haskoin/Util/Arbitrary.hs +1/−1
- src/Haskoin/Util/Arbitrary/Address.hs +1/−1
- src/Haskoin/Util/Arbitrary/Block.hs +1/−1
- src/Haskoin/Util/Arbitrary/Crypto.hs +1/−1
- src/Haskoin/Util/Arbitrary/Keys.hs +1/−1
- src/Haskoin/Util/Arbitrary/Message.hs +1/−1
- src/Haskoin/Util/Arbitrary/Network.hs +1/−1
- src/Haskoin/Util/Arbitrary/Script.hs +3/−3
- src/Haskoin/Util/Arbitrary/Transaction.hs +7/−15
- src/Haskoin/Util/Arbitrary/Util.hs +20/−18
- test/Haskoin/Address/CashAddrSpec.hs +24/−25
- test/Haskoin/AddressSpec.hs +133/−31
- test/Haskoin/Crypto/HashSpec.hs +290/−15
- test/Haskoin/Crypto/SignatureSpec.hs +364/−101
- test/Haskoin/CryptoSpec.hs +0/−165
- test/Haskoin/Keys/ExtendedSpec.hs +146/−91
- test/Haskoin/KeysSpec.hs +205/−9
- test/Haskoin/ScriptSpec.hs +53/−20
- test/Haskoin/TransactionSpec.hs +126/−201
- test/Haskoin/UtilSpec.hs +8/−0
CHANGELOG.md view
@@ -4,6 +4,18 @@ 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.15.0+### Added+- Add more test vectors++### Changed+- stringToAddr renamed to textToAddr+- Move ScriptOutput to Standard.hs+- Move WIF encoding/decoding to Keys.hs+- (breaking) rename `OP_NOP2` and `OP_NOP3` to `OP_CHECKLOCKTIMEVERIFY` and+ `OP_CHECKSEQUENCEVERIFY` resp.+- Update to latest secp256k1 bindings.+ ## 0.14.1 ### Fixed - Correct some Bitcoin Cash Testnet3 seeds.
+ data/key_io_invalid.json view
@@ -0,0 +1,182 @@+[+ [+ ""+ ],+ [+ "x"+ ],+ [+ "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"+ ],+ [+ "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"+ ],+ [+ "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"+ ],+ [+ "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"+ ],+ [+ "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"+ ],+ [+ "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"+ ],+ [+ "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"+ ],+ [+ "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"+ ],+ [+ "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"+ ],+ [+ "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"+ ],+ [+ "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"+ ],+ [+ "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"+ ],+ [+ "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"+ ],+ [+ "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"+ ],+ [+ "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"+ ],+ [+ "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"+ ],+ [+ "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"+ ],+ [+ "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"+ ],+ [+ "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"+ ],+ [+ "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"+ ],+ [+ "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"+ ],+ [+ "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"+ ],+ [+ "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"+ ],+ [+ "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"+ ],+ [+ "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"+ ],+ [+ "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"+ ],+ [+ "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"+ ],+ [+ "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"+ ],+ [+ "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"+ ],+ [+ "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"+ ],+ [+ "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"+ ],+ [+ "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"+ ],+ [+ "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"+ ],+ [+ "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"+ ],+ [+ "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"+ ],+ [+ "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"+ ],+ [+ "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"+ ],+ [+ "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"+ ],+ [+ "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"+ ],+ [+ "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"+ ],+ [+ "EsYbG4tWWWY45G31nox838qNdzksbPySWc"+ ],+ [+ "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"+ ],+ [+ "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"+ ],+ [+ "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"+ ],+ [+ "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"+ ],+ [+ "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"+ ],+ [+ "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"+ ],+ [+ "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"+ ],+ [+ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"+ ],+ [+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"+ ],+ [+ "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"+ ],+ [+ "bc1rw5uspcuh"+ ],+ [+ "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"+ ],+ [+ "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"+ ],+ [+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"+ ],+ [+ "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"+ ],+ [+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"+ ],+ [+ "bc1gmk9yu"+ ]+]
+ data/key_io_valid.json view
@@ -0,0 +1,536 @@+[+ [+ "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",+ "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",+ "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",+ "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",+ "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+ {+ "isPrivkey": false,+ "chain": "regtest"+ }+ ],+ [+ "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",+ "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",+ "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",+ "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",+ "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",+ "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "regtest"+ }+ ],+ [+ "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",+ "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",+ "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "regtest"+ }+ ],+ [+ "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",+ "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",+ "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",+ "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",+ "a914c579342c2c4c9220205e2cdc285617040c924a0a87",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",+ "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",+ "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",+ "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",+ "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",+ "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",+ "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",+ "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",+ "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",+ "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",+ "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",+ "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",+ "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",+ "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",+ "a914188f91a931947eddd7432d6e614387e32b24470987",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",+ "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",+ "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",+ "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",+ "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",+ "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",+ "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "regtest"+ }+ ],+ [+ "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",+ "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",+ "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",+ "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",+ "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",+ "a914e930e1834a4d234702773951d627cce82fbb5d2e87",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",+ "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",+ "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",+ "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",+ "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",+ "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",+ "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",+ "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",+ "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",+ {+ "isPrivkey": false,+ "chain": "test"+ }+ ],+ [+ "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",+ "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",+ "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "main"+ }+ ],+ [+ "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",+ "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",+ {+ "isCompressed": false,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",+ "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",+ {+ "isCompressed": true,+ "isPrivkey": true,+ "chain": "test"+ }+ ],+ [+ "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",+ "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",+ "a9145ece0cadddc415b1980f001785947120acdb36fc87",+ {+ "isPrivkey": false,+ "chain": "main"+ }+ ],+ [+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",+ "0014751e76e8199196d454941c45d1b3a323f1433bd6",+ {+ "isPrivkey": false,+ "chain": "main",+ "tryCaseFlip": true+ }+ ],+ [+ "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",+ "0014751e76e8199196d454941c45d1b3a323f1433bd6",+ {+ "isPrivkey": false,+ "chain": "regtest",+ "tryCaseFlip": true+ }+ ],+ [+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",+ "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+ {+ "isPrivkey": false,+ "chain": "test",+ "tryCaseFlip": true+ }+ ],+ [+ "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",+ "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",+ {+ "isPrivkey": false,+ "chain": "main",+ "tryCaseFlip": true,+ "disabled": true+ }+ ],+ [+ "bc1sw50qa3jx3s",+ "6002751e",+ {+ "isPrivkey": false,+ "chain": "main",+ "tryCaseFlip": true,+ "disabled": true+ }+ ],+ [+ "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",+ "5210751e76e8199196d454941c45d1b3a323",+ {+ "isPrivkey": false,+ "chain": "main",+ "tryCaseFlip": true,+ "disabled": true+ }+ ],+ [+ "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",+ "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+ {+ "isPrivkey": false,+ "chain": "test",+ "tryCaseFlip": true+ }+ ],+ [+ "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",+ "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+ {+ "isPrivkey": false,+ "chain": "regtest",+ "tryCaseFlip": true+ }+ ]+]
+ data/rfc6979DERabc.json view
@@ -0,0 +1,102 @@+[+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Absence makes the heart grow fonder.",+ "304402206c0db71422a66840f238b55635dc87d013a77caebb2057713e14928abe0d0ce602207f687dedcd60e4a8feb3ef31450ad8e42730d909dd19b5237a5eaa4bf50c0741"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000002",+ "Actions speak louder than words.",+ "3045022100912705043b888d052c35a2d9736dde7f29c453cb6b2c355fa1b1a1328a1d23170220021ede2707be213c7d489720af88c43f6eb86c47835e7d6ff61cbe91ebec288a"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000003",+ "All for one and one for all.",+ "3044022055bffc0d939ae7f8209dea0ae77f5b4905818201a4c2ca7f06a302ea56258186022079cfc2f2b5ce08e0a8f3f1082366f509783bceb2c4a05819a715724d68bbe7c0"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000004",+ "All's fair in love and war.",+ "3045022100e1ea024069bd02f1332fafdbd12f50103e0438cbce1e32517c9932c1e043c902022078f4408e6851f25e8fdc91f82a920cf4ce400554f236d810c499886f97c9500b"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000005",+ "All work and no play makes Jack a dull boy.",+ "3045022100824b35db88c1600747a3d9fe3d40d1400c6b8c3527c91d3bdb22b317bdf4dfd60220793025386e2de4289b3f0e135121f20088e1d0e0cb5bbf366561dffc9d17c0d4"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000006",+ "All's well that ends well.",+ "3045022100f3a3f37b80880a5b1ac2a3fcde9a86aae7036e8576f1ce28ab2849155237de63022027ef8c5e61c42a86fe13953d57aea273f797828f22df686bbdbc5601a7cdf49a"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000007",+ "An apple a day keeps the doctor away.",+ "3045022100e8f392a1657ec6fdf747ea8ff5a6dbeec7e932975c0c2f9430b9f6b7e3120018022064dd010aba1522d1ae16219e425225e9508c34a00d3e5d7327336391fc3e0cac"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000008",+ "An apple never falls far from the tree.",+ "3045022100ba82ea8d785a951ce33dcf65379abfd1e3d1affa4557870c1d931bad119e3fde022070d86f0cdb023fd40c365d9caab1351900371166a72f55315e661486ed769753"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000009",+ "An ounce of prevention is worth a pound of cure.",+ "30450221009d898ae395ed916e3b58f3f9e08019c65be8bcdc1634581891c30f7873de2570022048c7c244ceef3c3a6a6cd34119dc07ad54b8f8d50273629ac20fadd580241a92"+ ],+ [+ "000000000000000000000000000000000000000000000000000000000000000a",+ "Appearances can be deceiving.",+ "304402206a75d44757c6ae076d8dce8ea79dfde47765f02659e0ccb6e9e3f675c6d0013602200345ff627949d08d6d02ba1d43d07e89fd74c54d250f212b6d9e1e227887cce2"+ ],+ [+ "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",+ "Absence makes the heart grow fonder.",+ "3044022001e95d38c5d65344800d9d87de5c2d4319674adf6d248bb59113d42d18bdb97502200d5afe847651e64a358e65ba16cca9d1e02eac371cfa8e1206cd3dceb438f9d1"+ ],+ [+ "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986",+ "Actions speak louder than words.",+ "30440220771a29e95d6e9e4c1b05b3cb7853ebc1057a9f68133fcc34190c4ba237e40b99022046067aff0918c18a3b3822c937921905ef22bf10c9f59d806ccfb23f7fcaa1ac"+ ],+ [+ "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5",+ "All for one and one for all.",+ "3045022100822eb70daf001ed5b8a9ccd9e2fa849918148bdd5d2cdf3fb5797be0fa82fec0022060d43b32e6c86804d351dfe863135d8391f10442552dd59b75f2cbc3894f7277"+ ],+ [+ "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71",+ "All's fair in love and war.",+ "30440220264b1220e15eb1256bdac36512687aa37e059bb3b177bbf42f19a75cb95a9710022040928fcc170203596bb449f4d745b49f75b76d013a5c0d70af3f6eefa9af94d6"+ ],+ [+ "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db",+ "All work and no play makes Jack a dull boy.",+ "304402203b96a2ea837df2aba4e94be5167ce9bcb1f00c2a6c6698f4ff340b7a9379dd5f0220051222212a9a4d1886acfac0594ae8f98db354448c8aa626d34ee7d4abb83d4c"+ ],+ [+ "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6",+ "All's well that ends well.",+ "30450221009d6297db8a9446e376d45cc816ea9172d6a6db715680928dc0ead4f2f0fbbdfc02207df1a6c9e7a23c9baee9b2833d8dd18ea6ed33f7f7d653a1a29e75ead07ce143"+ ],+ [+ "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879",+ "An apple a day keeps the doctor away.",+ "304402202e58d297a0d452ae6afc3cc8f3bfc4ff1a10c76c3f71ff1062564f196d4038f3022073f77ea431bd2b95a0e226eccb2fd4680b71a2ee6ba46a6b25d54db5a928f4ce"+ ],+ [+ "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a",+ "An apple never falls far from the tree.",+ "304402204997ddbdb33574552d938e84378f29c3bb24a32e7a32e467315dd841fa278a040220198c27facdf3ac10b59cdc97ab269df337268d5a61527f41966f0ff876ac00df"+ ],+ [+ "2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9",+ "An ounce of prevention is worth a pound of cure.",+ "304402202803b367eacdff2a0ee8727108f43efc864519d0fc972e42a5e4f3f2221b6f880220632003636d7293672003650babc3a589e35be93b98dabf20dead8e70085fa8a8"+ ],+ [+ "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",+ "Appearances can be deceiving.",+ "30440220033aef6b3a2cea67a19d91130ee2736652588a21fdca9520ed778ca2d30e343d02206f0be9db4ebfc474470c4d164a67479e232f20e57f8659bc5b404520b665765d"+ ]+]
+ data/rfc6979DERcore.json view
@@ -0,0 +1,102 @@+[+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Absence makes the heart grow fonder.",+ "3045022100afff580595971b8c1700e77069d73602aef4c2a760dbd697881423dfff845de80220579adb6a1ac03acde461b5821a049ebd39a8a8ebf2506b841b15c27342d2e342"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000002",+ "Actions speak louder than words.",+ "304502210085f28bbc90975b1907a51cbfe7bf0dc1ac74ade49318ee97498dbbde3894a31c0220241d24da8d263e7af7ff49bca6a7a850f0e087faf6fef44f85851b0283c3f026"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000003",+ "All for one and one for all.",+ "30440220502c6ac38e1c68ce68f044f5ab680f2880a6c1cd34e70f2b4f945c6fd30abd03022018ef5c6c3392b9d67ad5109c85476a0e159425d7f6ace2cebeaa65f02f210bbb"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000004",+ "All's fair in love and war.",+ "30440220452d4ab234891cf6e5432cd5472bdca1cfc6fb28563333885f068da02ee216d8022056c368d16a64d29cff92f17203d926e113064527af0480d3bcc1d3fadfde9364"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000005",+ "All work and no play makes Jack a dull boy.",+ "3045022100995025b4880eeb1ecedba945fe8c9b2ddf2b07dbc293c2586c079d7b663ef38a022022fb54ab95014616d014277e05c97a7ed9e22596a0420bbd2d749ca9a2f876fe"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000006",+ "All's well that ends well.",+ "3045022100a9c1593fa6459777b2eba6d7e2a206e3bb119e85b2163973cf28ffaf24ec381c02202f166f13230b3853b928efb649d30375ec6a4b1a64a8d56fbcc0a9d86a0943e9"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000007",+ "An apple a day keeps the doctor away.",+ "304402202fc9c8b749621241c33fd51b57fc5140c1d7fc1594f91b073953e79da2f5e8f60220345e4ea7693b5069c0251771ea476cbe236586ed24b90aeeea7b7c2814edf477"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000008",+ "An apple never falls far from the tree.",+ "3044022052b6e2c49a6f6adbe52fb6bbe744caa3f49364085db118eab8670bc766be160302207d96a42866637ca3d4caf36e597a460eb305adac0220b027410c821a7191a1c4"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000009",+ "An ounce of prevention is worth a pound of cure.",+ "3045022100be53e7c00788e4417083d7511800f18c7c6f5f259de39bc6f8b1bebcd5056bd002201f389e13cfe7d1dbd8d2d1bff18138219f57de166673762009686a28fbc44df6"+ ],+ [+ "000000000000000000000000000000000000000000000000000000000000000a",+ "Appearances can be deceiving.",+ "304402202f2413a1673f642c30ea2e23fcae45776bc77a94f96920aea3c14303b1469428022053ac3e8ea0a488e9159d56e429a51f207bf04e462f8d4ba2c69b1b1635f30217"+ ],+ [+ "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",+ "Absence makes the heart grow fonder.",+ "3045022100996d79fba54b24e9394fc5fab6bf94d173f3752645075de6e32574fe08625f770220345e638b373dcb0ce0c09e5799695ef64ffc5e01dd8367b9a205ce25f28870f6"+ ],+ [+ "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986",+ "Actions speak louder than words.",+ "304502210088164430985a4437471417c2386faa536e1fe8ec91bd0f1f642bc22a776891530220090dc83d6e3b54a1a54dc2e79c693144179a512d9c9e686a6c25e7641a2101a8"+ ],+ [+ "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5",+ "All for one and one for all.",+ "30450221009f1073c9c09b664498d4b216983330b01c29a0fb55dd61aa145b4ebd0579905502204592fb6626f672d4f3ad4bb2d0a1ed6c2a161cc35c6bb77e6f0fd3b63feab36f"+ ],+ [+ "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71",+ "All's fair in love and war.",+ "304502210080eabf24117b492635043886e7229b9705b970cbb6828c4e03a39dae7ac34bda022070e8a32ca1df82add53facbd58b4f2d3984d0a17b6b13c44460238d9ff74e41f"+ ],+ [+ "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db",+ "All work and no play makes Jack a dull boy.",+ "3045022100a43ff5edea7ea0b9716d4359574e990a6859cdaeb9d7d6b4964afd40be11bd35022067f9d82e22fc447a122997335525f117f37b141c3efa9f8c6d77b586753f962f"+ ],+ [+ "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6",+ "All's well that ends well.",+ "3044022053ce16251f4fae7eb87e2ab040a6f334e08687fb445566256cd217ece389e0440220576506a168cbc9ee0dd485d6c418961e7a0861b0f05d22a93401812978d0b215"+ ],+ [+ "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879",+ "An apple a day keeps the doctor away.",+ "3045022100df8744cc06a304b041e88149acfd84a68d8f4a2a4047056644e1ec8357e11ebe02204ba2d5499a26d072c797a86c7851533f287ceb8b818cae2c5d4483c37c62750c"+ ],+ [+ "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a",+ "An apple never falls far from the tree.",+ "3045022100878372d211ed0dbde1273ae3dd85aec577c08a06a55960f2e274f97cc9f2f38f02203f992caa66f472a64f6ccdd8076c0a12202c674155a6a61b8cd23c1ded08aab7"+ ],+ [+ "2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9",+ "An ounce of prevention is worth a pound of cure.",+ "3045022100d5cb4e148c0a29ce37f1542be416e8ef575da522666b19b541960d726c99662b022045c951c1ca938c90dad6c3eede7c5df67fcf0d14f90faf201e8d215f215c5c18"+ ],+ [+ "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",+ "Appearances can be deceiving.",+ "304402203e2f0118062306e2239c873828a7275dd35545a143797e224148c5bbbd59dd08022073a8c9e17be75c66362913b5e05d81fd619b434edda766fae6c352e86987809d"+ ]+]
+ data/rfc6979abc.json view
@@ -0,0 +1,62 @@+[+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Satoshi Nakamoto",+ "cd7a225c6661e35a85f309a66ba9fbcd134487b80ae1d8a3c58d85015ac1f7ed046b573d040c8324ecf796d6b86732fdfd8938888fd8a28309e476732475a743"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "All those moments will be lost in time, like tears in rain. Time to die...",+ "fb073d886dda0444913a3f1fa32d5b2bc35d09420672b6e7e8e36c530422c98f114230b9362ba6985ccb0d87b86c752e597fa3ca46f1c0abe226a9496187588c"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Satoshi Nakamoto",+ "20d0306cd55957786c6dedae03c2dc85cea8030de04ec6ba027cffe8b1be37d15f46f9282a7117c4179f54d00c944b4ce04b51e118cfddcdc73d72d5b5923933"+ ],+ [+ "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",+ "Alan Turing",+ "b27049b4834f874e41a4b6c586ed6aa1d86dba5ef411bd2a149161e39287ab4247ad691e06358323d6bbb16589ac186c63393c694bf75914c4a29ee56675c740"+ ],+ [+ "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",+ "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",+ "08438a0a59cb5c1f3de6a0c3c4852282472b9286ff6427f65dc8af31b6bdd41b300ad6ef64cf7d2f6ce7109d134d1c542c61430d4d3d1e404f4fd2634a0da0ad"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Everything should be made as simple as possible, but not simpler.",+ "53452b2fbb94b30051ab0e7075c1e613685079cc5244c36aa7b9148521a04b87268579ff46388ff7ceb7d5a691fcc995cd62d596603fffff6013eaaef3ec1f65"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",+ "fb904bd574f61510463e4e6593be29aa59de55f1dc31d3075920ac9ca7ef55997afa037dc9192877bf13ecf5458966bb92abd3328247b32425ab21d8b8a0d767"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Not only is the Universe stranger than we think, it is stranger than we can think.",+ "402f863e862bdfabfa9444e1370f4163a8f61a5f3e16b56ce639d3c373d0ad551aea2bdfb410f1f327737f7fa28646338876f4097b86c1204952e8f1733989d7"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "How wonderful that we have met with a paradox. Now we have some hope of making progress.",+ "efdff52e87f77261f1aa07c7938e346547b712d3892a0ef6380cfff46198588b17efea735778bc04bae52cec6617b85537dc913f1423f21fd97860405ad78aee"+ ],+ [+ "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",+ "Computer science is no more about computers than astronomy is about telescopes.",+ "d270a8604b11ec88a4e95cc1e4006f88452d5faf71dfd3f6d561d130a52903f93a6e9662a0fdf9f05ee5da5ba9fc437c97cf894a17844ccfb19d708942b72324"+ ],+ [+ "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",+ "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",+ "c0b51457714f4786e0f1d38219bfa124e06cbfff8b0c74c1a0ab11dff80b3a1b207aefdd3ac67158746b0ffad2be589a8f816296012b394302ab8a514aaeb120"+ ],+ [+ "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",+ "The question of whether computers can think is like the question of whether submarines can swim.",+ "fdcc51599f0b25a48f4b1f3701c153d840d49414b77d582b73bc0a93a3d2fe0c5770d55329857e5e1095545f911881447feccdde067faac1338c7f48a0355407"+ ]+]
+ data/rfc6979core.json view
@@ -0,0 +1,62 @@+[+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Satoshi Nakamoto",+ "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "All those moments will be lost in time, like tears in rain. Time to die...",+ "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Satoshi Nakamoto",+ "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"+ ],+ [+ "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",+ "Alan Turing",+ "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"+ ],+ [+ "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",+ "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",+ "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "Everything should be made as simple as possible, but not simpler.",+ "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",+ "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"+ ],+ [+ "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+ "Not only is the Universe stranger than we think, it is stranger than we can think.",+ "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"+ ],+ [+ "0000000000000000000000000000000000000000000000000000000000000001",+ "How wonderful that we have met with a paradox. Now we have some hope of making progress.",+ "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"+ ],+ [+ "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",+ "Computer science is no more about computers than astronomy is about telescopes.",+ "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"+ ],+ [+ "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",+ "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",+ "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"+ ],+ [+ "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",+ "The question of whether computers can think is like the question of whether submarines can swim.",+ "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"+ ]+]
haskoin-core.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 26dbdf73274ee98d645964af8eced9a7fa589a0e625153f6056626fc0d48a7cd+-- hash: da33a2de954861b59726562ecc494205eab150409d75381e7e3c867546e2d48f name: haskoin-core-version: 0.14.1+version: 0.15.0 synopsis: Bitcoin & Bitcoin Cash library for Haskell description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-core#readme> category: Bitcoin, Finance, Network@@ -23,6 +23,12 @@ extra-source-files: data/forkid_script_tests.json data/forkid_sighash.json+ data/key_io_invalid.json+ data/key_io_valid.json+ data/rfc6979abc.json+ data/rfc6979core.json+ data/rfc6979DERabc.json+ data/rfc6979DERcore.json data/script_tests.json data/sig_nonstrict.json data/sig_strict.json@@ -65,6 +71,7 @@ Haskoin.Transaction.Builder Haskoin.Transaction.Builder.Sign Haskoin.Transaction.Common+ Haskoin.Transaction.Genesis Haskoin.Transaction.Partial Haskoin.Transaction.Segwit Haskoin.Util@@ -103,7 +110,7 @@ , network >=3.1.1.1 , safe >=0.3.18 , scientific >=0.3.6.2- , secp256k1-haskell >=0.3.0+ , secp256k1-haskell >=0.4.0 , split >=0.2.3.3 , string-conversions >=0.4.0.1 , text >=1.2.4.0@@ -123,7 +130,6 @@ Haskoin.BlockSpec Haskoin.Crypto.HashSpec Haskoin.Crypto.SignatureSpec- Haskoin.CryptoSpec Haskoin.Keys.ExtendedSpec Haskoin.Keys.MnemonicSpec Haskoin.KeysSpec@@ -150,15 +156,17 @@ , deepseq >=1.4.4.0 , entropy >=0.4.1.5 , hashable >=1.3.0.0- , haskoin-core ==0.14.1+ , haskoin-core ==0.15.0 , hspec >=2.7.1+ , lens >=4.18.1+ , lens-aeson >=1.1 , memory >=0.15.0 , mtl >=2.2.2 , murmur3 >=1.0.3 , network >=3.1.1.1 , safe >=0.3.18 , scientific >=0.3.6.2- , secp256k1-haskell >=0.3.0+ , secp256k1-haskell >=0.4.0 , split >=0.2.3.3 , string-conversions >=0.4.0.1 , text >=1.2.4.0
src/Haskoin.hs view
@@ -2,7 +2,7 @@ Module : Haskoin Description : Bitcoin (BTC/BCH) Libraries for Haskell Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Address.hs view
@@ -2,11 +2,12 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Haskoin.Address Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -20,8 +21,11 @@ , isScriptAddress , isWitnessPubKeyAddress , isWitnessScriptAddress- , addrToString- , stringToAddr+ , addrToText+ , textToAddr+ , bech32ToAddr+ , cashToAddr+ , base58ToAddr , addrToJSON , addrToEncoding , addrFromJSON@@ -42,15 +46,13 @@ , payToNestedScriptAddress , scriptToAddress , scriptToAddressBS- -- ** Private Key Wallet Import Format (WIF)- , fromWif- , toWif , 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@@ -75,21 +77,25 @@ -- | Address format for Bitcoin and Bitcoin Cash. data Address -- | pay to public key hash (regular)- = PubKeyAddress { getAddrHash160 :: !Hash160- -- ^ RIPEMD160 hash of public key's SHA256 hash- }+ = 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- }+ | 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- }+ | WitnessPubKeyAddress+ { getAddrHash160 :: !Hash160+ -- ^ RIPEMD160 hash of public key's SHA256 hash+ } -- | pay to witness script hash- | WitnessScriptAddress { getAddrHash256 :: !Hash256- -- ^ HASH256 hash of script- }+ | WitnessScriptAddress+ { getAddrHash256 :: !Hash256+ -- ^ HASH256 hash of script+ } deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData) -- | 'Address' pays to a public key hash.@@ -108,94 +114,91 @@ isWitnessPubKeyAddress WitnessPubKeyAddress {} = True isWitnessPubKeyAddress _ = False --- | 'Address' pays to a witness script hash. Only valid for SegWit networks. isWitnessScriptAddress :: Address -> Bool isWitnessScriptAddress WitnessScriptAddress {} = True isWitnessScriptAddress _ = False --- | Deserializer for binary 'Base58' addresses.-base58get :: Network -> Get Address-base58get net = do- pfx <- getWord8- addr <- S.get- f pfx addr- where- f x a- | x == getAddrPrefix net = return $ PubKeyAddress a- | x == getScriptPrefix net = return $ ScriptAddress a- | otherwise = fail "Does not recognize address prefix"---- | Binary serializer for 'Base58' addresses.-base58put :: Network -> Putter Address-base58put net (PubKeyAddress h) = do- putWord8 (getAddrPrefix net)- put h-base58put net (ScriptAddress h) = do- putWord8 (getScriptPrefix net)- put h-base58put _ _ = error "Cannot serialize this address as Base58"- addrToJSON :: Network -> Address -> Value-addrToJSON net a = toJSON (addrToString net a)+addrToJSON net a = toJSON (addrToText net a) addrToEncoding :: Network -> Address -> Encoding-addrToEncoding net = maybe null_ text . addrToString net+addrToEncoding net = maybe null_ text . addrToText net -- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and -- 'Bech32'. addrFromJSON :: Network -> Value -> Parser Address addrFromJSON net = withText "address" $ \t ->- case stringToAddr net t of+ case textToAddr net t of Nothing -> fail "could not decode address"- Just x -> return x+ Just x -> return x -- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or -- 'CashAddr' depending on network.-addrToString :: Network -> Address -> Maybe Text-addrToString net a@PubKeyAddress {getAddrHash160 = h}+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)-addrToString net a@ScriptAddress {getAddrHash160 = h}+addrToText net a@ScriptAddress {getAddrHash160 = h} | isNothing (getCashAddrPrefix net) = Just . encodeBase58Check . runPut $ base58put net a | otherwise = cashAddrEncode net 1 (S.encode h)-addrToString net WitnessPubKeyAddress {getAddrHash160 = h} = do+addrToText net WitnessPubKeyAddress {getAddrHash160 = h} = do hrp <- getBech32Prefix net segwitEncode hrp 0 (B.unpack (S.encode h))-addrToString net WitnessScriptAddress {getAddrHash256 = h} = do+addrToText net WitnessScriptAddress {getAddrHash256 = h} = do hrp <- getBech32Prefix net segwitEncode hrp 0 (B.unpack (S.encode h)) -- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.-stringToAddr :: Network -> Text -> Maybe Address-stringToAddr net bs = cash <|> segwit <|> b58- where- b58 = eitherToMaybe . runGet (base58get net) =<< decodeBase58Check bs- cash = cashAddrDecode net bs >>= \(ver, bs') -> case ver of- 0 -> do- h <- eitherToMaybe (S.decode bs')- return $ PubKeyAddress h- 1 -> do- h <- eitherToMaybe (S.decode bs')- return $ ScriptAddress h+textToAddr :: Network -> Text -> Maybe Address+textToAddr net txt =+ cashToAddr net txt <|> bech32ToAddr net txt <|> base58ToAddr net txt++cashToAddr :: Network -> Text -> Maybe Address+cashToAddr net txt = do+ (ver, bs) <- cashAddrDecode net txt+ case ver of+ 0 -> PubKeyAddress <$> eitherToMaybe (S.decode bs)+ 1 -> ScriptAddress <$> eitherToMaybe (S.decode bs) _ -> Nothing- segwit = do- hrp <- getBech32Prefix net- (ver, bs') <- segwitDecode hrp bs- guard (ver == 0)- let bs'' = B.pack bs'- case B.length bs'' of- 20 -> do- h <- eitherToMaybe (S.decode bs'')- return $ WitnessPubKeyAddress h- 32 -> do- h <- eitherToMaybe (S.decode bs'')- return $ WitnessScriptAddress h- _ -> Nothing +bech32ToAddr :: Network -> Text -> Maybe Address+bech32ToAddr net txt = do+ hrp <- getBech32Prefix net+ (ver, bs) <- second B.pack <$> segwitDecode hrp txt+ guard (ver == 0) -- We only support version 0 for now+ case B.length bs of+ 20 -> WitnessPubKeyAddress <$> eitherToMaybe (S.decode bs)+ 32 -> WitnessScriptAddress <$> eitherToMaybe (S.decode bs)+ _ -> Nothing++base58ToAddr :: Network -> Text -> Maybe Address+base58ToAddr net txt =+ eitherToMaybe . runGet (base58get net) =<< decodeBase58Check txt++base58get :: Network -> Get Address+base58get net = do+ pfx <- getWord8+ addr <- S.get+ f pfx addr+ where+ f x a+ | x == getAddrPrefix net = return $ PubKeyAddress a+ | x == getScriptPrefix net = return $ ScriptAddress a+ | otherwise = fail "Does not recognize address prefix"++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"+ -- | Obtain a standard pay-to-public-key-hash address from a public key. pubKeyAddr :: PubKeyI -> Address pubKeyAddr = PubKeyAddress . addressHash . S.encode@@ -245,10 +248,12 @@ -- | Encode an output script from an address. Will fail if using a -- pay-to-witness address on a non-SegWit network. addressToOutput :: Address -> ScriptOutput-addressToOutput (PubKeyAddress h) = PayPKHash h-addressToOutput (ScriptAddress h) = PayScriptHash h-addressToOutput (WitnessPubKeyAddress h) = PayWitnessPKHash h-addressToOutput (WitnessScriptAddress h) = PayWitnessScriptHash h+addressToOutput =+ \case+ (PubKeyAddress h) -> PayPKHash h+ (ScriptAddress h) -> PayScriptHash h+ (WitnessPubKeyAddress h) -> PayWitnessPKHash h+ (WitnessScriptAddress h) -> PayWitnessScriptHash h -- | Get output script AST for an 'Address'. addressToScript :: Address -> Script@@ -270,39 +275,19 @@ -- | Get the 'Address' of a 'ScriptOutput'. outputAddress :: ScriptOutput -> Maybe Address-outputAddress (PayPKHash h) = Just $ PubKeyAddress h-outputAddress (PayScriptHash h) = Just $ ScriptAddress h-outputAddress (PayPK k) = Just $ pubKeyAddr k-outputAddress (PayWitnessPKHash h) = Just $ WitnessPubKeyAddress h-outputAddress (PayWitnessScriptHash h) = Just $ WitnessScriptAddress h-outputAddress _ = Nothing+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+ _ -> Nothing -- | Infer the 'Address' of a 'ScriptInput'. inputAddress :: ScriptInput -> Maybe Address-inputAddress (RegularInput (SpendPKHash _ key)) = Just $ pubKeyAddr key-inputAddress (ScriptHashInput _ rdm) = Just $ payToScriptAddress rdm-inputAddress _ = Nothing---- | Decode private key from WIF (wallet import format) string.-fromWif :: Network -> Base58 -> Maybe SecKeyI-fromWif net wif = do- bs <- decodeBase58Check wif- -- Check that this is a private key- guard (B.head bs == getSecretPrefix net)- case B.length bs of- -- Uncompressed format- 33 -> wrapSecKey False <$> secKey (B.tail bs)- -- Compressed format- 34 -> do- guard $ B.last bs == 0x01- wrapSecKey True <$> secKey (B.tail $ B.init bs)- -- Bad length- _ -> Nothing---- | Encode private key into a WIF string.-toWif :: Network -> SecKeyI -> Base58-toWif net (SecKeyI k c) =- encodeBase58Check . B.cons (getSecretPrefix net) $- if c- then getSecKey k `B.snoc` 0x01- else getSecKey k+inputAddress =+ \case+ (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr key+ (ScriptHashInput _ rdm) -> Just $ payToScriptAddress rdm+ _ -> Nothing
src/Haskoin/Address/Base58.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Address.Base58 Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Address/Bech32.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Address.Base58 Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Address/CashAddr.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Address.CashAddr Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Block.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Block Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Block/Common.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Block.Common Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Block/Headers.hs view
@@ -7,7 +7,7 @@ {-| Module : Haskoin.Block.Headers Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -90,7 +90,7 @@ import Haskoin.Block.Common import Haskoin.Constants import Haskoin.Crypto-import Haskoin.Transaction.Common+import Haskoin.Transaction.Genesis import Haskoin.Util -- | Short version of the block hash. Uses the good end of the hash (the part
src/Haskoin/Block/Merkle.hs view
@@ -3,7 +3,7 @@ {-| Module : Haskoin.Block.Merkle Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Constants.hs view
@@ -5,7 +5,7 @@ {-| Module : Haskoin.Constants Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Crypto.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Crypto Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Crypto/Hash.hs view
@@ -3,7 +3,7 @@ {-| Module : Haskoin.Crypto.Hash Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Crypto/Signature.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Crypto.Signature Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -21,15 +21,14 @@ , exportSig ) where -import Control.Monad (guard, unless, when)+import Control.Monad (guard, unless, when) import Crypto.Secp256k1-import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import Data.ByteString.Short (toShort)-import Data.Maybe (fromMaybe)-import Data.Serialize as S+import Data.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)+import Numeric (showHex) -- | Convert 256-bit hash into a 'Msg' for signing or verification. hashToMsg :: Hash256 -> Msg@@ -44,28 +43,28 @@ -- | Verify an ECDSA signature for a 256-bit hash. verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool-verifyHashSig h s p =- verifySig p g m+verifyHashSig h s p = verifySig p norm (hashToMsg h) where- (g, _) = normalizeSig s- m = hashToMsg h+ norm = fromMaybe s (normalizeSig s) -- | Deserialize an ECDSA signature as commonly encoded in Bitcoin. getSig :: Get Sig getSig = do- l <- lookAhead $ do+ l <-+ lookAhead $ do t <- getWord8 -- 0x30 is DER sequence type- unless (t == 0x30) $ fail $+ 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"+ bs <- getByteString $ l + 2+ case decodeStrictSig bs of+ Just s -> return s+ Nothing -> fail "Invalid signature" -- | Serialize an ECDSA signature for Bitcoin use. putSig :: Putter Sig@@ -73,18 +72,17 @@ -- | Is canonical half order. isCanonicalHalfOrder :: Sig -> Bool-isCanonicalHalfOrder = not . snd . normalizeSig+isCanonicalHalfOrder = isNothing . normalizeSig -- | Decode signature strictly. decodeStrictSig :: ByteString -> Maybe Sig decodeStrictSig bs = do g <- importSig bs- let compact = exportCompactSig g -- <http://www.secg.org/sec1-v2.pdf Section 4.1.4> -- 4.1.4.1 (r and s can not be zero)- guard $ getCompactSigR compact /= zero- guard $ getCompactSigS compact /= zero+ 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- where- zero = toShort $ BS.replicate 32 0
src/Haskoin/Keys.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Keys Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Keys/Common.hs view
@@ -7,7 +7,7 @@ {-| Module : Haskoin.Keys.Common Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -28,6 +28,9 @@ , tweakSecKey , getSecKey , secKey+ -- ** Private Key Wallet Import Format (WIF)+ , fromWif+ , toWif ) where import Control.Applicative ((<|>))@@ -48,6 +51,8 @@ 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 @@ -107,16 +112,14 @@ , secKeyCompressed :: !Bool } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize SecKey where- get = do- bs <- getByteString 32- maybe (fail "invalid private key") return (secKey bs)- put = putByteString . getSecKey- -- | 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@@ -126,6 +129,26 @@ checkHash = encode $ sha256 $ bs `BS.append` "?" checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00 --- | Tweak a private key.-tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)+-- | 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 view
@@ -6,7 +6,7 @@ {-| Module : Haskoin.Keys.Extended Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -251,10 +251,10 @@ 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)+ 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"+ 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@@ -270,11 +270,11 @@ 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)+ 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"+ 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.
src/Haskoin/Keys/Mnemonic.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Keys.Mnemonic Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Network.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Network Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Network/Bloom.hs view
@@ -3,7 +3,7 @@ {-| Module : Haskoin.Network.Bloom Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -46,8 +46,9 @@ import Data.Word import GHC.Generics (Generic) import Haskoin.Network.Common-import Haskoin.Script.Common+import Haskoin.Script.Standard import Haskoin.Transaction.Common+ -- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% maxBloomSize :: Int maxBloomSize = 36000@@ -212,35 +213,36 @@ bloomRelevantUpdate bfilter tx | isBloomFull bfilter || isBloomEmpty bfilter = Nothing | bloomFlags bfilter == BloomUpdateNone = Nothing- | length matchOuts > 0 = Just $ foldl' (addRelevant) bfilter matchOuts+ | not (null matchOuts) = Just $ foldl' addRelevant bfilter matchOuts | otherwise = Nothing- where -- TxHash if we end up inserting an outpoint- h = txHash tx+ 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+ decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx+ err = error "Error Decoding output script"+ idxOutputScripts = either (const err) (zip [0 ..]) decodedOutputScripts -- Check if any txOuts were contained in the bloom filter- matchFilter = filter (\(_,op) -> bloomContains bfilter $ encodeScriptOut op)- matchOuts = matchFilter idxOutputScripts-- addRelevant :: BloomFilter -> (Word32,ScriptOutput) -> BloomFilter- addRelevant bfilter (id,scriptOut) = case (bloomFlags bfilter,scriptType) of+ 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- (_, True) -> bloomInsert bfilter outpoint- (BloomUpdateAll, _ ) -> bloomInsert bfilter outpoint- _ -> error "Error Updating Bloom Filter with relevant outpoint"- where- outpoint = encode $ OutPoint{ outPointHash = h, outPointIndex = id}- scriptType = (\s -> isPayPK s ||isPayMulSig s) scriptOut-+ 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 getOutputData) = encode getOutputData- encodeScriptOut outputHash = (encode . getOutputHash) outputHash+ encodeScriptOut :: ScriptOutput -> ByteString+ encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig+ encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash+ encodeScriptOut (DataCarrier getOutputDat) = encode getOutputDat+ encodeScriptOut outputHash = (encode . getOutputHash) outputHash -- | Returns True if the filter is empty (all bytes set to 0x00) isBloomEmpty :: BloomFilter -> Bool
src/Haskoin/Network/Common.hs view
@@ -1,11 +1,10 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Haskoin.Network.Common Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Network/Message.hs view
@@ -3,7 +3,7 @@ {-| Module : Haskoin.Network.Message Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Script.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Script Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Script/Common.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Script.Common Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -16,20 +16,6 @@ ScriptOp(..) , Script(..) , PushDataType(..)- , ScriptOutput(..)- , isPayPK- , isPayPKHash- , isPayMulSig- , isPayScriptHash- , isPayWitnessPKHash- , isPayWitnessScriptHash- , isDataCarrier- , encodeOutput- , encodeOutputBS- , decodeOutput- , decodeOutputBS- , toP2SH- , toP2WSH , isPushOp , opPushData , intToScriptOp@@ -38,17 +24,12 @@ import Control.DeepSeq import Control.Monad-import Data.Aeson as A-import Data.Aeson.Encoding (text)-import Data.ByteString (ByteString)-import qualified Data.ByteString as B+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)-import Haskoin.Crypto.Hash-import Haskoin.Keys.Common-import Haskoin.Util+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:@@ -202,8 +183,8 @@ | OP_CHECKMULTISIGVERIFY -- Expansion | OP_NOP1- | OP_NOP2- | OP_NOP3+ | OP_CHECKLOCKTIMEVERIFY+ | OP_CHECKSEQUENCEVERIFY | OP_NOP4 | OP_NOP5 | OP_NOP6@@ -355,8 +336,8 @@ -- More NOPs | op == 0xb0 = return OP_NOP1- | op == 0xb1 = return OP_NOP2- | op == 0xb2 = return OP_NOP3+ | 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@@ -512,31 +493,31 @@ 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_RIPEMD160 -> putWord8 0xa6+ OP_SHA1 -> putWord8 0xa7+ OP_SHA256 -> putWord8 0xa8+ OP_HASH160 -> putWord8 0xa9+ OP_HASH256 -> putWord8 0xaa+ OP_CODESEPARATOR -> putWord8 0xab+ OP_CHECKSIG -> putWord8 0xac+ OP_CHECKSIGVERIFY -> putWord8 0xad+ OP_CHECKMULTISIG -> putWord8 0xae OP_CHECKMULTISIGVERIFY -> putWord8 0xaf -- More NOPs- OP_NOP1 -> putWord8 0xb0- OP_NOP2 -> putWord8 0xb1- OP_NOP3 -> putWord8 0xb2- OP_NOP4 -> putWord8 0xb3- OP_NOP5 -> putWord8 0xb4- OP_NOP6 -> putWord8 0xb5- OP_NOP7 -> putWord8 0xb6- OP_NOP8 -> putWord8 0xb7- OP_NOP9 -> putWord8 0xb8- OP_NOP10 -> putWord8 0xb9+ 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_CHECKDATASIG -> putWord8 0xba OP_CHECKDATASIGVERIFY -> putWord8 0xbb -- Bitcoin Cash May 2020 hard fork@@ -595,148 +576,3 @@ | otherwise = Left $ "scriptOpToInt: invalid opcode " ++ show s where res = fromIntegral (B.head $ S.encode s) - 0x50---- | Data type describing standard transaction output scripts. Output scripts--- provide the conditions that must be fulfilled for someone to spend the funds--- in a transaction output.-data ScriptOutput- -- | pay to public key- = PayPK { getOutputPubKey :: !PubKeyI }- -- | pay to public key hash- | PayPKHash { getOutputHash :: !Hash160 }- -- | multisig- | PayMulSig { getOutputMulSigKeys :: ![PubKeyI]- , getOutputMulSigRequired :: !Int }- -- | pay to a script hash- | PayScriptHash { getOutputHash :: !Hash160 }- -- | pay to witness public key hash- | PayWitnessPKHash { getOutputHash :: !Hash160 }- -- | pay to witness script hash- | PayWitnessScriptHash { getScriptHash :: !Hash256 }- -- | provably unspendable data carrier- | DataCarrier { getOutputData :: !ByteString }- deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance FromJSON ScriptOutput where- parseJSON =- withText "scriptoutput" $ \t ->- either fail return $- maybeToEither "scriptoutput not hex" (decodeHex t) >>=- decodeOutputBS--instance ToJSON ScriptOutput where- toJSON = String . encodeHex . encodeOutputBS- toEncoding = text . encodeHex . encodeOutputBS---- | Is script a pay-to-public-key output?-isPayPK :: ScriptOutput -> Bool-isPayPK (PayPK _) = True-isPayPK _ = False---- | Is script a pay-to-pub-key-hash output?-isPayPKHash :: ScriptOutput -> Bool-isPayPKHash (PayPKHash _) = True-isPayPKHash _ = False---- | Is script a pay-to-multi-sig output?-isPayMulSig :: ScriptOutput -> Bool-isPayMulSig (PayMulSig _ _) = True-isPayMulSig _ = False---- | Is script a pay-to-script-hash output?-isPayScriptHash :: ScriptOutput -> Bool-isPayScriptHash (PayScriptHash _) = True-isPayScriptHash _ = False---- | Is script a pay-to-witness-pub-key-hash output?-isPayWitnessPKHash :: ScriptOutput -> Bool-isPayWitnessPKHash (PayWitnessPKHash _) = True-isPayWitnessPKHash _ = False---- | Is script a pay-to-witness-script-hash output?-isPayWitnessScriptHash :: ScriptOutput -> Bool-isPayWitnessScriptHash (PayWitnessScriptHash _) = True-isPayWitnessScriptHash _ = False---- | Is script a data carrier output?-isDataCarrier :: ScriptOutput -> Bool-isDataCarrier (DataCarrier _) = True-isDataCarrier _ = False---- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the--- script is not recognized as any of the standard output types.-decodeOutput :: Script -> Either String ScriptOutput-decodeOutput s = case scriptOps s of- -- Pay to PubKey- [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs- -- Pay to PubKey Hash- [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->- PayPKHash <$> S.decode bs- -- Pay to Script Hash- [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->- PayScriptHash <$> S.decode bs- -- Pay to Witness- [OP_0, OP_PUSHDATA bs OPCODE]- | B.length bs == 20 -> PayWitnessPKHash <$> S.decode bs- | B.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs- -- Provably unspendable data carrier output- [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs- -- Pay to MultiSig Keys- _ -> matchPayMulSig s---- | Similar to 'decodeOutput' but decodes from a 'ByteString'.-decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< S.decode---- | Computes a 'Script' from a standard 'ScriptOutput'.-encodeOutput :: ScriptOutput -> Script-encodeOutput s = Script $ case s of- -- Pay to PubKey- (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]- -- Pay to PubKey Hash Address- (PayPKHash h) ->- [ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]- -- Pay to MultiSig Keys- (PayMulSig ps r)- | r <= length ps ->- let opM = intToScriptOp r- opN = intToScriptOp $ length ps- keys = map (opPushData . S.encode) ps- in opM : keys ++ [opN, OP_CHECKMULTISIG]- | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"- -- Pay to Script Hash Address- (PayScriptHash h) ->- [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]- -- Pay to Witness PubKey Hash Address- (PayWitnessPKHash h) ->- [ OP_0, opPushData $ S.encode h ]- (PayWitnessScriptHash h) ->- [ OP_0, opPushData $ S.encode h ]- -- Provably unspendable output- (DataCarrier d) -> [OP_RETURN, opPushData d]---- | Similar to 'encodeOutput' but encodes to a ByteString-encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = S.encode . encodeOutput---- | Encode script as pay-to-script-hash script-toP2SH :: Script -> ScriptOutput-toP2SH = PayScriptHash . addressHash . S.encode---- | Encode script as a pay-to-witness-script-hash script-toP2WSH :: Script -> ScriptOutput-toP2WSH = PayWitnessScriptHash . sha256 . S.encode---- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@-matchPayMulSig :: Script -> Either String ScriptOutput-matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of- (m:xs,[n,OP_CHECKMULTISIG]) -> do- (intM,intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)- if intM <= intN && length xs == intN- then liftM2 PayMulSig (go xs) (return intM)- else Left "matchPayMulSig: Invalid M or N parameters"- _ -> Left "matchPayMulSig: script did not match output template"- where- go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)- go [] = return []- go _ = Left "matchPayMulSig: invalid multisig opcode"
src/Haskoin/Script/SigHash.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Script.SigHash Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -13,7 +13,7 @@ -} module Haskoin.Script.SigHash ( -- * Script Signatures- SigHash+ SigHash(..) , SigHashFlag(..) , sigHashAll , sigHashNone@@ -38,10 +38,8 @@ import Control.DeepSeq import Control.Monad-import Crypto.Secp256k1 import qualified Data.Aeson as J import Data.Bits-import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Hashable import Data.Maybe@@ -51,8 +49,8 @@ import GHC.Generics (Generic) import Haskoin.Constants import Haskoin.Crypto.Hash-import Haskoin.Crypto.Signature-import Haskoin.Network+import Haskoin.Network.Common+import Haskoin.Crypto import Haskoin.Script.Common import Haskoin.Transaction.Common import Haskoin.Util@@ -291,22 +289,23 @@ -- is serialized as one byte at the end of an ECDSA 'Sig'. All signatures in -- transaction inputs are of type 'TxSignature'. data TxSignature- = TxSignature { txSignature :: !Sig- , txSignatureSigHash :: !SigHash- }+ = TxSignature+ { txSignature :: !Sig+ , txSignatureSigHash :: !SigHash+ } | TxSignatureEmpty deriving (Eq, Show, Generic) instance NFData TxSignature -- | Serialize a 'TxSignature'.-encodeTxSig :: TxSignature -> ByteString+encodeTxSig :: TxSignature -> BS.ByteString encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature" encodeTxSig (TxSignature sig (SigHash n)) = runPut $ putSig sig >> putWord8 (fromIntegral n) -- | Deserialize a 'TxSignature'.-decodeTxSig :: Network -> ByteString -> Either String TxSignature+decodeTxSig :: Network -> BS.ByteString -> Either String TxSignature decodeTxSig _ bs | BS.null bs = Left "Empty signature candidate" decodeTxSig net bs = case decodeStrictSig $ BS.init bs of
src/Haskoin/Script/Standard.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Script.Standard Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -13,16 +13,30 @@ pay-to-script-hash, pay-to-multisig and corresponding SegWit variants. -} module Haskoin.Script.Standard- ( -- * Standard Scripts- ScriptInput(..)- , SimpleInput(..)+ ( -- * Standard Script Outputs+ ScriptOutput(..) , RedeemScript+ , isPayPK+ , isPayPKHash+ , isPayMulSig+ , isPayScriptHash+ , isPayWitnessPKHash+ , isPayWitnessScriptHash+ , isDataCarrier+ , encodeOutput+ , encodeOutputBS+ , decodeOutput+ , decodeOutputBS+ , toP2SH+ , toP2WSH+ , sortMulSig+ -- * Standard Script Inputs+ , ScriptInput(..)+ , SimpleInput(..) , encodeInput , encodeInputBS , decodeInput , decodeInputBS- , sortMulSig- , scriptOpToInt , isSpendPK , isSpendPKHash , isSpendMulSig@@ -31,19 +45,175 @@ import Control.Applicative ((<|>)) import Control.DeepSeq-import Control.Monad (guard, (<=<))+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.Serialize (decode, encode)+import Data.Serialize as S 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 +-- | Data type describing standard transaction output scripts. Output scripts+-- provide the conditions that must be fulfilled for someone to spend the funds+-- in a transaction output.+data ScriptOutput+ -- | pay to public key+ = PayPK { getOutputPubKey :: !PubKeyI }+ -- | pay to public key hash+ | PayPKHash { getOutputHash :: !Hash160 }+ -- | multisig+ | PayMulSig { getOutputMulSigKeys :: ![PubKeyI]+ , getOutputMulSigRequired :: !Int }+ -- | pay to a script hash+ | PayScriptHash { getOutputHash :: !Hash160 }+ -- | pay to witness public key hash+ | PayWitnessPKHash { getOutputHash :: !Hash160 }+ -- | pay to witness script hash+ | PayWitnessScriptHash { getScriptHash :: !Hash256 }+ -- | provably unspendable data carrier+ | DataCarrier { getOutputData :: !ByteString }+ deriving (Eq, Show, Read, Generic, Hashable, NFData) +instance A.FromJSON ScriptOutput where+ parseJSON =+ A.withText "scriptoutput" $ \t ->+ either fail return $+ maybeToEither "scriptoutput not hex" (decodeHex t) >>=+ decodeOutputBS++instance A.ToJSON ScriptOutput where+ toJSON = A.String . encodeHex . encodeOutputBS+ toEncoding = A.text . encodeHex . encodeOutputBS++-- | Is script a pay-to-public-key output?+isPayPK :: ScriptOutput -> Bool+isPayPK (PayPK _) = True+isPayPK _ = False++-- | Is script a pay-to-pub-key-hash output?+isPayPKHash :: ScriptOutput -> Bool+isPayPKHash (PayPKHash _) = True+isPayPKHash _ = False++-- | Is script a pay-to-multi-sig output?+isPayMulSig :: ScriptOutput -> Bool+isPayMulSig (PayMulSig _ _) = True+isPayMulSig _ = False++-- | Is script a pay-to-script-hash output?+isPayScriptHash :: ScriptOutput -> Bool+isPayScriptHash (PayScriptHash _) = True+isPayScriptHash _ = False++-- | Is script a pay-to-witness-pub-key-hash output?+isPayWitnessPKHash :: ScriptOutput -> Bool+isPayWitnessPKHash (PayWitnessPKHash _) = True+isPayWitnessPKHash _ = False++-- | Is script a pay-to-witness-script-hash output?+isPayWitnessScriptHash :: ScriptOutput -> Bool+isPayWitnessScriptHash (PayWitnessScriptHash _) = True+isPayWitnessScriptHash _ = False++-- | Is script a data carrier output?+isDataCarrier :: ScriptOutput -> Bool+isDataCarrier (DataCarrier _) = True+isDataCarrier _ = False++-- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the+-- script is not recognized as any of the standard output types.+decodeOutput :: Script -> Either String ScriptOutput+decodeOutput s = case scriptOps s of+ -- Pay to PubKey+ [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs+ -- Pay to PubKey Hash+ [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->+ PayPKHash <$> S.decode bs+ -- Pay to Script Hash+ [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->+ PayScriptHash <$> S.decode bs+ -- Pay to Witness+ [OP_0, OP_PUSHDATA bs OPCODE]+ | BS.length bs == 20 -> PayWitnessPKHash <$> S.decode bs+ | BS.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs+ -- Provably unspendable data carrier output+ [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs+ -- Pay to MultiSig Keys+ _ -> matchPayMulSig s++-- | Similar to 'decodeOutput' but decodes from a 'ByteString'.+decodeOutputBS :: ByteString -> Either String ScriptOutput+decodeOutputBS = decodeOutput <=< S.decode++-- | Computes a 'Script' from a standard 'ScriptOutput'.+encodeOutput :: ScriptOutput -> Script+encodeOutput s = Script $ case s of+ -- Pay to PubKey+ (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]+ -- Pay to PubKey Hash Address+ (PayPKHash h) ->+ [ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]+ -- Pay to MultiSig Keys+ (PayMulSig ps r)+ | r <= length ps ->+ let opM = intToScriptOp r+ opN = intToScriptOp $ length ps+ keys = map (opPushData . S.encode) ps+ in opM : keys ++ [opN, OP_CHECKMULTISIG]+ | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"+ -- Pay to Script Hash Address+ (PayScriptHash h) ->+ [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]+ -- Pay to Witness PubKey Hash Address+ (PayWitnessPKHash h) ->+ [ OP_0, opPushData $ S.encode h ]+ (PayWitnessScriptHash h) ->+ [ OP_0, opPushData $ S.encode h ]+ -- Provably unspendable output+ (DataCarrier d) -> [OP_RETURN, opPushData d]++-- | Similar to 'encodeOutput' but encodes to a ByteString+encodeOutputBS :: ScriptOutput -> ByteString+encodeOutputBS = S.encode . encodeOutput++-- | Encode script as pay-to-script-hash script+toP2SH :: Script -> ScriptOutput+toP2SH = PayScriptHash . addressHash . S.encode++-- | Encode script as a pay-to-witness-script-hash script+toP2WSH :: Script -> ScriptOutput+toP2WSH = PayWitnessScriptHash . sha256 . S.encode++-- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@+matchPayMulSig :: Script -> Either String ScriptOutput+matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of+ (m:xs,[n,OP_CHECKMULTISIG]) -> do+ (intM,intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)+ if intM <= intN && length xs == intN+ then liftM2 PayMulSig (go xs) (return intM)+ else Left "matchPayMulSig: Invalid M or N parameters"+ _ -> Left "matchPayMulSig: script did not match output template"+ where+ go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)+ go [] = return []+ go _ = Left "matchPayMulSig: invalid multisig opcode"++-- | 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"+ -- | 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@@ -109,7 +279,7 @@ 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) matchPKHash _ = Nothing@@ -117,10 +287,10 @@ guard $ x == OP_0 SpendMulSig <$> mapM f xs matchMulSig _ = Nothing- f OP_0 = return TxSignatureEmpty+ f OP_0 = return TxSignatureEmpty f (OP_PUSHDATA "" OPCODE) = f OP_0- f (OP_PUSHDATA bs _) = eitherToMaybe $ decodeTxSig net bs- f _ = Nothing+ f (OP_PUSHDATA bs _) = eitherToMaybe $ decodeTxSig net bs+ f _ = Nothing errMsg = "decodeInput: Could not decode script input" -- | Heuristic to decode a 'ScriptInput' from a 'Script'. This function fails if@@ -169,9 +339,3 @@ f TxSignatureEmpty = OP_0 f ts = opPushData $ encodeTxSig ts --- | Sort the public keys of a multisig output in ascending order by comparing--- their compressed serialized representations. Refer to BIP-67.-sortMulSig :: ScriptOutput -> ScriptOutput-sortMulSig out = case out of- PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r- _ -> error "Can only call orderMulSig on PayMulSig scripts"
src/Haskoin/Transaction.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Transaction Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Transaction/Builder.hs view
@@ -1,9 +1,10 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Haskoin.Transaction.Builder Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -247,25 +248,23 @@ -- | Build a transaction by providing a list of outpoints as inputs -- and a list of recipient addresses and amounts as outputs. buildAddrTx :: Network -> [OutPoint] -> [(Text, Word64)] -> Either String Tx-buildAddrTx net xs ys = buildTx xs =<< mapM f ys+buildAddrTx net ops rcps =+ buildTx ops <$> mapM f rcps where- f (s, v) =- maybe (Left ("buildAddrTx: Invalid address " ++ cs s)) Right $ do- a <- stringToAddr net s+ 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)] -> Either String Tx-buildTx xs ys =- mapM fo ys >>= \os -> return $ Tx 1 (map fi xs) os [] 0+buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Tx+buildTx ops rcpts =+ Tx 1 (toIn <$> ops) (toOut <$> rcpts) [] 0 where- fi outPoint = TxIn outPoint B.empty maxBound- fo (o, v)- | v <= 2100000000000000 = return $ TxOut v $ encodeOutputBS o- | otherwise =- Left $ "buildTx: Invalid amount " ++ show v+ 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@@ -346,9 +345,8 @@ -> Tx -> ((ScriptOutput, Word64), Int) -> Either String Tx-mergeTxInput net txs tx ((so, val), i)+mergeTxInput net txs tx ((so, val), i) = do -- Ignore transactions with empty inputs- = do let ins = map (scriptInput . (!! i) . txIn) txs sigRes <- mapM extractSigs $ filter (not . B.null) ins let rdm = snd $ head sigRes@@ -393,7 +391,7 @@ where f (_, _, o) txin = o == prevOutput txin go (Just (so, val, _), i) = verifyStdInput net tx i so val- go _ = False+ go _ = False -- | Verify if a transaction input is valid and standard. verifyStdInput :: Network -> Tx -> Int -> ScriptOutput -> Word64 -> Bool@@ -468,7 +466,8 @@ -> [PubKey] -> [TxSignature] -> Int-countMulSig net tx out val i = countMulSig' h+countMulSig net tx out val i =+ countMulSig' h where h = txSigHash net tx out val i
src/Haskoin/Transaction/Builder/Sign.hs view
@@ -5,7 +5,7 @@ {-| Module : Haskoin.Transaction.Builder.Sign Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -36,26 +36,15 @@ 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.Common (ScriptOutput (..), encodeOutput,- encodeOutputBS, opPushData)-import Haskoin.Script.SigHash (SigHash, TxSignature (..),- decodeTxSig, txSigHash,- txSigHashForkId)-import Haskoin.Script.Standard (RedeemScript, ScriptInput (..),- SimpleInput (..), decodeInputBS,- encodeInputBS)-import Haskoin.Transaction.Common (OutPoint, Tx (..), TxIn (..),- WitnessData)-import Haskoin.Transaction.Segwit (WitnessProgram (..),- calcWitnessProgram, isSegwit,- toWitnessStack)+import Haskoin.Script+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit import Haskoin.Util (matchTemplate, updateIndex) -- | Data type used to specify the signing parameters of a transaction input.@@ -279,3 +268,4 @@ _ -> fromMaybe so rdmM h | isSegwit so = txSigHashForkId | otherwise = txSigHash+
src/Haskoin/Transaction/Common.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Transaction.Common Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -26,7 +26,6 @@ , txHashToHex , nosigTxHash , nullOutPoint- , genesisTx ) where import Control.Applicative ((<|>))@@ -48,7 +47,6 @@ import GHC.Generics (Generic) import Haskoin.Crypto.Hash import Haskoin.Network.Common-import Haskoin.Script.Common import Haskoin.Util import Text.Read as R @@ -344,21 +342,3 @@ "0000000000000000000000000000000000000000000000000000000000000000" , outPointIndex = maxBound }---- | Transaction from Genesis block.-genesisTx :: Tx-genesisTx =- Tx 1 [txin] [txout] [] locktime- where- txin = TxIn outpoint inputBS maxBound- txout = TxOut 5000000000 (encodeOutputBS output)- locktime = 0- outpoint = OutPoint z maxBound- Just inputBS = decodeHex $ fromString $- "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" ++- "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" ++- "757420666f722062616e6b73"- output = PayPK $ fromString $- "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" ++- "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"- z = "0000000000000000000000000000000000000000000000000000000000000000"
+ src/Haskoin/Transaction/Genesis.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haskoin.Transaction.Genesis+Copyright : No rights reserved+License : UNLICENSE+Maintainer : jprupp@protonmail.ch+Stability : experimental+Portability : POSIX++Code related to transactions parsing and serialization.+-}+module Haskoin.Transaction.Genesis+ ( genesisTx+ ) where++import Data.String (fromString)+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util++-- | Transaction from Genesis block.+genesisTx :: Tx+genesisTx =+ Tx 1 [txin] [txout] [] locktime+ where+ txin = TxIn outpoint inputBS maxBound+ txout = TxOut 5000000000 (encodeOutputBS output)+ locktime = 0+ outpoint = OutPoint z maxBound+ Just inputBS = decodeHex $ fromString $+ "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" +++ "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" +++ "757420666f722062616e6b73"+ output = PayPK $ fromString $+ "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" +++ "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"+ z = "0000000000000000000000000000000000000000000000000000000000000000"
src/Haskoin/Transaction/Partial.hs view
@@ -6,7 +6,7 @@ {-| Module : Haskoin.Transaction.Partial Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : matt@bitnomial.com Stability : experimental Portability : POSIX
src/Haskoin/Transaction/Segwit.hs view
@@ -4,7 +4,7 @@ {-| Module : Haskoin.Transaction.Segwit Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -31,14 +31,10 @@ import Data.ByteString (ByteString) import qualified Data.Serialize as S-import Haskoin.Constants (Network)-import Haskoin.Keys.Common (PubKeyI)-import Haskoin.Script.Common (Script (..), ScriptOutput (..),- decodeOutput, encodeOutput)-import Haskoin.Script.SigHash (TxSignature (..), decodeTxSig,- encodeTxSig)-import Haskoin.Script.Standard (ScriptInput (..), SimpleInput (..))-import Haskoin.Transaction.Common (WitnessStack)+import Haskoin.Constants+import Haskoin.Keys.Common+import Haskoin.Script+import Haskoin.Transaction.Common -- | Test if a 'ScriptOutput' is P2WPKH or P2WSH --
src/Haskoin/Util.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Util Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Address.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Test.Address Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Block.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Block Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Crypto.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Crypto Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Keys.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Keys Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Message.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Message Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Network.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Network Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX
src/Haskoin/Util/Arbitrary/Script.hs view
@@ -2,7 +2,7 @@ {-| Module : Haskoin.Test.Script Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -140,8 +140,8 @@ , return OP_CHECKMULTISIGVERIFY -- Expansion , return OP_NOP1- , return OP_NOP2- , return OP_NOP3+ , return OP_CHECKLOCKTIMEVERIFY+ , return OP_CHECKSEQUENCEVERIFY , return OP_NOP4 , return OP_NOP5 , return OP_NOP6
src/Haskoin/Util/Arbitrary/Transaction.hs view
@@ -1,7 +1,7 @@ {-| Module : Haskoin.Test.Transaction Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -71,16 +71,8 @@ -- | Arbitrary witness or legacy transaction. arbitraryWLTx :: Network -> Bool -> Gen Tx arbitraryWLTx net wit = do- ni <- choose (0, 5)- no <-- if wit- then choose (0, 5)- else choose- ( if ni == 0- then 2- else 0- , 5 -- avoid witness case- )+ 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@@ -95,8 +87,8 @@ -- Only compressed public keys are used. arbitraryAddrOnlyTx :: Network -> Gen Tx arbitraryAddrOnlyTx net = do- ni <- choose (0, 5)- no <- choose (0, 5)+ 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@@ -104,8 +96,8 @@ -- | Like 'arbitraryAddrOnlyTx' without empty signatures in the inputs. arbitraryAddrOnlyTxFull :: Network -> Gen Tx arbitraryAddrOnlyTxFull net = do- ni <- choose (0, 5)- no <- choose (0, 5)+ 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
src/Haskoin/Util/Arbitrary/Util.hs view
@@ -3,7 +3,7 @@ {-| Module : Haskoin.Test.Util Copyright : No rights reserved-License : UNLICENSE+License : MIT Maintainer : jprupp@protonmail.ch Stability : experimental Portability : POSIX@@ -17,6 +17,7 @@ , arbitraryBSSn , arbitraryMaybe , arbitraryNetwork+, arbitraryUTCTime , SerialBox(..) , JsonBox(..) , NetBox(..)@@ -31,22 +32,23 @@ ) 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 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 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.Hspec (Spec, describe, shouldBe,+ shouldSatisfy)+import Test.Hspec.QuickCheck (prop) import Test.QuickCheck -- | Arbitrary strict 'ByteString'.@@ -125,9 +127,9 @@ -- | Generate Data.Serialize identity tests testSerial :: (Eq a, Show a, T.Typeable a, S.Serialize a) => Gen a -> Spec-testSerial gen = do+testSerial gen = prop ("Data.Serialize encoding/decoding identity for " <> name) $- forAll gen $ \x -> (S.decode . S.encode) x `shouldBe` Right x+ forAll gen $ \x -> (S.decode . S.encode) x `shouldBe` Right x where name = show $ T.typeRep $ proxy gen proxy :: Gen a -> Proxy a@@ -136,7 +138,7 @@ -- | Generate Read/Show identity tests testRead :: (Eq a, Read a, Show a, T.Typeable a) => Gen a -> Spec-testRead gen = do+testRead gen = prop ("read/show identity for " <> name) $ forAll gen $ \x -> (read . show) x `shouldBe` x where
test/Haskoin/Address/CashAddrSpec.hs view
@@ -7,7 +7,6 @@ import Data.String.Conversions import Data.Text (Text) import Haskoin.Address-import Haskoin.Address.CashAddr import Haskoin.Constants import Haskoin.Util import Test.Hspec@@ -44,80 +43,80 @@ describe "cashaddr to base58 translation test vectors" $ do it "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" $ do let addr =- addrToString bch =<<- stringToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+ addrToText bch =<<+ textToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" addr `shouldBe` Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" it "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" $ do let addr =- addrToString bch =<<- stringToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"+ addrToText bch =<<+ textToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" addr `shouldBe` Just "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" it "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" $ do let addr =- addrToString bch =<<- stringToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"+ addrToText bch =<<+ textToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" addr `shouldBe` Just "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" it "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" $ do let addr =- addrToString bch =<<- stringToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+ addrToText bch =<<+ textToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" addr `shouldBe` Just "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" it "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" $ do let addr =- addrToString bch =<<- stringToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"+ addrToText bch =<<+ textToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" addr `shouldBe` Just "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" it "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" $ do let addr =- addrToString bch =<<- stringToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"+ addrToText bch =<<+ textToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" addr `shouldBe` Just "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" describe "base58 to cashaddr translation test vectors" $ do it "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" addr `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" it "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" addr `shouldBe` Just "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" it "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" addr `shouldBe` Just "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" it "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" addr `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" it "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" addr `shouldBe` Just "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" it "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" $ do let addr =- addrToString btc =<<- stringToAddr+ addrToText btc =<<+ textToAddr bch "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" addr `shouldBe` Just "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"
test/Haskoin/AddressSpec.hs view
@@ -5,9 +5,11 @@ 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 (derivePubKeyI)+import Haskoin.Keys+import Haskoin.Util import Haskoin.Util.Arbitrary import Test.Hspec import Test.Hspec.QuickCheck@@ -34,21 +36,26 @@ prop "encodes and decodes base58 bytestring with checksum" $ forAll arbitraryBS $ \bs -> decodeBase58Check (encodeBase58Check bs) == Just bs- prop "encodes and decodes address" $+ prop "textToAddr . addrToText identity" $ forAll arbitraryNetAddress $ \(net, a) ->- (stringToAddr net =<< addrToString net a) == Just a- describe "Address vectors" . it "base58 btc address vectors" $- mapM_ runVector vectors- describe "Witness address vectors" . it "p2sh(pwpkh)" $- mapM_ testCompatWitness compatWitnessVectors- describe "WIF keys" $- prop "encode and decode wif private keys" $- forAll arbitraryNetwork $ \net ->- forAll arbitraryKeyPair $ \(pk, _) ->- fromWif net (toWif net pk) == Just pk+ (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 -runVector :: (ByteString, Text, Text) -> Assertion-runVector (bs, e, chk) = do+testVector :: (ByteString, Text, Text) -> Assertion+testVector (bs, e, chk) = do assertEqual "encodeBase58" e b58 assertEqual "encodeBase58Check" chk b58Chk assertEqual "decodeBase58" (Just bs) (decodeBase58 b58)@@ -59,29 +66,124 @@ vectors :: [(ByteString, Text, Text)] vectors =- [ ( BS.empty, "", "3QJmnh" )- , ( BS.pack [0], "1", "1Wh4bh" )- , ( BS.pack [0,0,0,0], "1111", "11114bdQda" )- , ( BS.pack [0,0,1,0,0], "11LUw", "113CUwsFVuo" )- , ( BS.pack [255], "5Q", "VrZDWwe" )- , ( BS.pack [0,0,0,0] `BS.append` BS.pack [1..255]- , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"- , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYtFHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5eckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7KeQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZvkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfdUhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcqpJTN9vt"- )+ [ (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") ] -compatWitnessVectors :: [(Network, Text, Text)]-compatWitnessVectors =- [ ( btcTest- , "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q"- , "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw")+-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_encode_decode.json++testBase58Vector :: (Text, Text) -> Assertion+testBase58Vector (a, b) = do+ assertEqual "encodeBase58 match" b (encodeBase58 bsA)+ assertEqual "decodeBase58 match" a (encodeHex bsB)+ assertEqual "bytestring match" bsA bsB+ where+ bsA = fromJust $ decodeHex a+ bsB = fromJust $ decodeBase58 b++base58Vectors :: [(Text, Text)]+base58Vectors =+ [ ("", "")+ , ("61", "2g")+ , ("626262", "a3gV")+ , ("636363", "aPEr")+ , ( "73696d706c792061206c6f6e6720737472696e67"+ , "2cFupjhnEsSn59qHXstmK2ffpLv2")+ , ( "00eb15231dfceb60925886b67d065299925915aeb172c06647"+ , "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L")+ , ("516b6fcd0f", "ABnLTmg")+ , ("bf4f89001e670274dd", "3SEo3LWLoPntC")+ , ("572e4794", "3EFU7m")+ , ("ecac89cad93923c02321", "EJDM8drfXA6uyA")+ , ("10c8511e", "Rt5zm")+ , ("00000000000000000000", "1111111111")+ , ( "000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252a\+ \da93ff480d6dd43dc62a641155a5"+ , "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")+ , ( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c\+ \1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839\+ \3a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556\+ \5758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273\+ \7475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90\+ \9192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacad\+ \aeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9ca\+ \cbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7\+ \e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"+ , "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAn\+ \Ex6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAk\+ \n8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr\+ \4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTc\+ \aB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2\+ \pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY") ] -testCompatWitness :: (Network, Text, Text) -> Assertion-testCompatWitness (net, seckey, addr) = do+-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/base58_tests.cpp++testBase58InvalidVector :: (Text, Maybe Text) -> Assertion+testBase58InvalidVector (a, resM) =+ assertEqual "decodeBase58 invalid match" resM (encodeHex <$> decodeBase58 a)++base58InvalidVectors :: [(Text, Maybe Text)]+base58InvalidVectors =+ [ ("invalid", Nothing)+ , ("\0invalid", Nothing)+ , ("good", Just "768320")+ , ("bad0IOl", Nothing)+ , ("goodbad0IOl", Nothing)+ , ("good\0bad0IOl", Nothing)+ -- Haskoin does not remove white spaces before decoding base58 strings+ -- , (" \t\n\v\f\r skip \r\f\v\n\t a", Nothing)+ -- , (" \t\n\v\f\r skip \r\f\v\n\t ", Just "971a55")+ ]++testBase58ChkInvalidVector :: (Text, Maybe Text) -> Assertion+testBase58ChkInvalidVector (a, resM) =+ assertEqual+ "decodeBase58Check invalid match"+ resM+ (encodeHex <$> decodeBase58Check a)++base58ChkInvalidVectors :: [(Text, Maybe Text)]+base58ChkInvalidVectors =+ [ ("3vQB7B6MrGQZaxCuFg4oh", Just "68656c6c6f20776f726c64")+ , ("3vQB7B6MrGQZaxCuFg4oi", Nothing) + , ("3vQB7B6MrGQZaxCuFg4oh0IOl", Nothing)+ , ("3vQB7B6MrGQZaxCuFg4oh\00IOl", Nothing)+ ]++testCompatWitnessVector :: (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 = addrToString btcTest (pubKeyCompatWitnessAddr pubkey)+ let addrM = addrToText btcTest (pubKeyCompatWitnessAddr pubkey) assertBool "address can be encoded" (isJust addrM) assertEqual "witness address matches" addr (fromJust addrM)++compatWitnessVectors :: [(Network, Text, Text)]+compatWitnessVectors =+ [ ( btcTest+ , "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q"+ , "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw")+ ]
test/Haskoin/Crypto/HashSpec.hs view
@@ -1,19 +1,25 @@ {-# LANGUAGE OverloadedStrings #-} module Haskoin.Crypto.HashSpec (spec) where +import Data.Bits import Data.ByteString (ByteString)-import Data.Map.Strict (singleton)+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 (Assertion, assertBool)+import Test.HUnit import Test.QuickCheck serialVals :: [SerialBox]@@ -36,20 +42,34 @@ spec :: Spec spec =- describe "hash" $ do+ describe "Hash" $ do testIdentity serialVals readVals [] []- prop "join512( split512(h) ) == h" $- forAll arbitraryHash256 $ forAll arbitraryHash256 . joinSplit512- prop "decodeCompact . encodeCompact i == i" decEncCompact- prop "from string 64-byte hash" $- forAll arbitraryHash512 $ \h ->- fromString (cs $ encodeHex $ encode h) == h- prop "from string 32-byte hash" $- forAll arbitraryHash256 $ \h ->- fromString (cs $ encodeHex $ encode h) == h- prop "from string 20-byte hash" $- forAll arbitraryHash160 $ \h ->- fromString (cs $ encodeHex $ encode h) == h+ 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 joinSplit512 :: Hash256 -> Hash256 -> Bool joinSplit512 a b = split512 (join512 (a, b)) == (a, b)@@ -64,3 +84,258 @@ -- 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+testVector f1 f2 (i, res) =+ assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 i))++testHMACVector ::+ (ByteString -> ByteString -> a)+ -> (a -> BSS.ShortByteString)+ -> (Text, Text, Text)+ -> Assertion+testHMACVector f1 f2 (k, m, res) =+ assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 bsK bsM))+ where+ bsK = fromJust $ decodeHex k+ bsM = fromJust $ decodeHex m++longTestString :: ByteString+longTestString =+ BL.toStrict $! toLazyByteString $! go [0 .. 199999]+ where+ go :: [Word32] -> Builder+ go [] = mempty+ go (i:is) =+ let i1 = fromIntegral $! i+ i2 = fromIntegral $! i `shiftR` 4+ i3 = fromIntegral $! i `shiftR` 8+ i4 = fromIntegral $! i `shiftR` 12+ i5 = fromIntegral $! i `shiftR` 16+ in word8 i1 <> word8 i2 <> word8 i3 <> word8 i4 <> word8 i5 <> go is++ripemd160Vectors :: [(ByteString, Text)]+ripemd160Vectors =+ [ ("", "9c1185a5c5e9fc54612808977ee8f548b2258d31")+ , ("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")+ , ("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36")+ , ("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9")+ , ( "RIPEMD160 is considered to be safe"+ , "a7d78608c7af8a8e728778e81576870734122b66")+ , ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"+ , "12a053384a9c0c88e405a06c27dcf49ada62eb2b")+ , ( "For this sample, this 63-byte string will be used as input data"+ , "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11")+ , ( "This is exactly 64 bytes long, not counting the terminating byte"+ , "eda31d51d3a623b81e19eb02e24ff65d27d67b37")+ , (C.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")+ ]++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")+ ]++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")+ ]++-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231+hmacSha256Vectors :: [(Text, Text, Text)]+hmacSha256Vectors =+ [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"+ , "4869205468657265"+ , "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")+ , ( "4a656665"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+ , "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+ \dddddddddddddddddddddddddddddd"+ , "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe")+ , ( "0102030405060708090a0b0c0d0e0f10111213141516171819"+ , "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+ \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"+ , "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+ , "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b\+ \6579202d2048617368204b6579204669727374"+ , "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+ , "5468697320697320612074657374207573696e672061206c6172676572207468616e20\+ \626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c\+ \6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520\+ \686173686564206265666f7265206265696e6720757365642062792074686520484d41\+ \4320616c676f726974686d2e"+ , "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2")+ -- Test case with key length 63 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+ \654a6566654a6566654a6566654a6566654a6566654a6566654a6566"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6")+ -- Test case with key length 64 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+ \654a6566654a6566654a6566654a6566654a6566654a6566654a656665"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd")+ -- Test case with key length 65 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+ \654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483")+ ]++-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231+hmacSha512Vectors :: [(Text, Text, Text)]+hmacSha512Vectors =+ [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"+ , "4869205468657265"+ , "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\+ \daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854")+ , ( "4a656665"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\+ \9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+ , "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+ \dddddddddddddddddddddddddddddddddddd"+ , "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\+ \bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb")+ , ( "0102030405060708090a0b0c0d0e0f10111213141516171819"+ , "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+ \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"+ , "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\+ \a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaa"+ , "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a\+ \65204b6579202d2048617368204b6579204669727374"+ , "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\+ \6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598")+ , ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+ \aaaaaa"+ , "5468697320697320612074657374207573696e672061206c6172676572207468\+ \616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074\+ \68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565\+ \647320746f20626520686173686564206265666f7265206265696e6720757365\+ \642062792074686520484d414320616c676f726974686d2e"+ , "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\+ \b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")+ -- Test case with key length 127 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a\+ \c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e")+ -- Test case with key length 128 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\+ \2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258")+ -- Test case with key length 129 bytes.+ , ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+ \4a"+ , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"+ , "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\+ \fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c")+ ]
test/Haskoin/Crypto/SignatureSpec.hs view
@@ -1,66 +1,62 @@ {-# LANGUAGE OverloadedStrings #-} module Haskoin.Crypto.SignatureSpec (spec) where -import Data.Bits (testBit)-import Data.ByteString (ByteString)-import qualified Data.ByteString as BS (index, length)-import Data.Serialize as S-import Data.Text (Text)-import Haskoin.Crypto (SecKey, Sig, decodeStrictSig,- derivePubKey, exportSig,- importCompactSig, isCanonicalHalfOrder,- sha256, signHash, verifyHashSig)-import Haskoin.Util (decodeHex, eitherToMaybe, lst3)-import Haskoin.Util.Arbitrary (arbitrarySignature)-import Test.Hspec (Spec, describe, it)-import Test.Hspec.QuickCheck (prop)-import Test.HUnit (Assertion, assertBool)-import Test.QuickCheck (forAll)+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 spec :: Spec spec = do- describe "signatures" $ do+ describe "Signature properties" $ do prop "verify signature" $- forAll arbitrarySignature $ \(msg, key, sig) ->- verifyHashSig msg sig (derivePubKey key)+ 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 "encode signature and decode strictly" $+ prop "decodeStrictSig . exportSig identity" $ forAll arbitrarySignature $ (\s -> decodeStrictSig (exportSig s) == Just s) . lst3- prop "encodes and decodes signature" $+ prop "importSig . exportSig identity" $ forAll arbitrarySignature $- (\s -> decodeStrictSig (exportSig s) == Just s) . lst3- describe "trezor rfc6979 test vectors" $ do- it "rfc6979 test vector 1" (testSigning $ head detVec)- it "rfc6979 test vector 2" (testSigning $ detVec !! 1)- it "rfc6979 test vector 3" (testSigning $ detVec !! 2)- it "rfc6979 test vector 4" (testSigning $ detVec !! 3)- it "rfc6979 test vector 5" (testSigning $ detVec !! 4)- it "rfc6979 test vector 6" (testSigning $ detVec !! 5)- it "rfc6979 test vector 7" (testSigning $ detVec !! 6)- it "rfc6979 test vector 8" (testSigning $ detVec !! 7)- it "rfc6979 test vector 9" (testSigning $ detVec !! 8)- it "rfc6979 test vector 10" (testSigning $ detVec !! 9)- it "rfc6979 test vector 11" (testSigning $ detVec !! 10)- it "rfc6979 test vector 12" (testSigning $ detVec !! 11)---- No longer validating that generated signature is exactly equal to provided--- one. Fedora includes libsecp256k1 from Bitcoin ABC that computes--- deterministic signatures using a slightly different nonce generation--- algorithm.-testSigning :: (SecKey, ByteString, Text) -> Assertion-testSigning (prv, msg, str) = do- assertBool "my sig valid" $ verifyHashSig msg' g (derivePubKey prv)- assertBool "valid sig" $ verifyHashSig msg' g' (derivePubKey prv)- where- Just g' = importCompactSig =<< eitherToMaybe . decode =<< decodeHex str- g = signHash prv msg'- msg' = sha256 msg--{- ECDSA Canonical -}+ (\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 -- github.com/bitcoin/bitcoin/blob/master/src/script.cpp -- from function IsCanonicalSignature@@ -80,6 +76,7 @@ (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@@ -106,58 +103,324 @@ rlen = BS.index s 3 slen = BS.index s (fromIntegral rlen + 5) +-- RFC6979 note: Different libraries of libsecp256k1 use different constants+-- to produce a nonce. Thus, their deterministric signatures will be different.+-- We still want to test against fixed signatures so we need a way to switch+-- between implementations. We check the output of signMsg 1 0++data ValidImpl+ = ImplCore+ | ImplABC++implSig :: Text+implSig =+ encodeHex $+ exportSig $+ signMsg+ "0000000000000000000000000000000000000000000000000000000000000001"+ "0000000000000000000000000000000000000000000000000000000000000000"++-- We have test vectors for these cases+validImplMap :: Map Text ValidImpl+validImplMap =+ Map.fromList+ [ ( "3045022100a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231\+ \d9e7e2e1e4022011edc8d307254296264aebfc3dc76cd8b668373a072fd64665\+ \b50000e9fcce52"+ , ImplCore)+ , ( "304402200581361d23e645be9e3efe63a9a2ac2e8dd0c70ba3ac8554c9befe06\+ \0ad0b36202207d8172f1e259395834793d81b17e986f1e6131e4734969d2f4ae\+ \3a9c8bc42965"+ , ImplABC)+ ]++getImpl :: Maybe ValidImpl+getImpl = implSig `Map.lookup` validImplMap++rfc6979files :: ValidImpl -> (FilePath, FilePath)+rfc6979files ImplCore = ("rfc6979core.json", "rfc6979DERcore.json")+rfc6979files ImplABC = ("rfc6979abc.json", "rfc6979DERabc.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"+ {- Trezor RFC 6979 Test Vectors -} -- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py -detVec :: [(SecKey, ByteString, Text)]-detVec =- [- ( "0000000000000000000000000000000000000000000000000000000000000001"- , "Satoshi Nakamoto"- , "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"- )- , ( "0000000000000000000000000000000000000000000000000000000000000001"- , "All those moments will be lost in time, like tears in rain. Time to die..."- , "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"- )- , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"- , "Satoshi Nakamoto"- , "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"- )- , ( "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181"- , "Alan Turing"- , "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"- )- , ( "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2"- , "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!"- , "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"- )- , ( "0000000000000000000000000000000000000000000000000000000000000001"- , "Everything should be made as simple as possible, but not simpler."- , "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"- )- , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"- , "Equations are more important to me, because politics is for the present, but an equation is something for eternity."- , "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"- )- , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"- , "Not only is the Universe stranger than we think, it is stranger than we can think."- , "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"- )- , ( "0000000000000000000000000000000000000000000000000000000000000001"- , "How wonderful that we have met with a paradox. Now we have some hope of making progress."- , "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"- )- , ( "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64"- , "Computer science is no more about computers than astronomy is about telescopes."- , "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"- )- , ( "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637"- , "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough"- , "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"- )- , ( "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3"- , "The question of whether computers can think is like the question of whether submarines can swim."- , "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"- )- ]+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+ where+ h = sha256 m+ s = signHash 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+ where+ h = sha256 m+ s = signHash 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"+ 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"+ unsignedTx =+ "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541d\+ \b4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b\+ \1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb20600000000\+ \1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d0000\+ \00001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"+ Just key0 =+ secHexKey+ "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"+ pubKey0 = toPubKey key0+ Just key1 =+ secHexKey+ "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"+ [op0, op1] = prevOutput <$> txIn unsignedTx+ sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing+ WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1+ sigIn1 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing+ generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]++-- Reproduce the P2SH-P2WPKH example from BIP 143+testBip143p2shp2wpkh :: Assertion+testBip143p2shp2wpkh =+ 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"+ where+ signedTxCore =+ "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+ \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+ \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+ \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+ \a7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a1\+ \0d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d\+ \877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c7\+ \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+ signedTxABC =+ "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+ \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+ \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+ \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+ \a7ad0402e8bd8ad6d77c88ac024730440220091c78fd1e21535f6ddc45515e4c\+ \afca15cdf344765d72c1529fb82d3ada2d1802204a980d5e37d0b04f5e1185a0\+ \f97295c383764e9a4b08d8bd1161b33c6719139a012103ad1d8e89212f0b92c7\+ \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+ unsignedTx =+ "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d\+ \3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7\+ \f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b\+ \1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"+ Just key0 =+ secHexKey+ "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"+ op0 = prevOutput . head $ txIn unsignedTx+ WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key0+ sigIn0 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing+ generatedSignedTx = signNestedWitnessTx btc unsignedTx [sigIn0] [key0]++-- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)+testP2WSHMulsig :: Assertion+testP2WSHMulsig =+ 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"+ 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"+ unsignedTx =+ "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a4\+ \48c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab\+ \583b12fbcb26d1269b4a2c951a33ad88ac00000000"+ op0 = head $ prevOutput <$> txIn unsignedTx+ Just keys =+ traverse+ secHexKey+ [ "3030303030303030303030303030303030303030303030303030303030303031"+ , "3030303030303030303030303030303030303030303030303030303030303032"+ , "3030303030303030303030303030303030303030303030303030303030303033"+ ]+ rdm = PayMulSig (toPubKey <$> keys) 2+ sigIn =+ SigInput+ (toP2WSH $ encodeOutput rdm)+ 100000000+ op0+ sigHashAll+ (Just rdm)+ generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)++-- Reproduce the P2SH-P2WSH multisig example from BIP 143+testBip143p2shp2wpkhMulsig :: Assertion+testBip143p2shp2wpkhMulsig =+ 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"+ 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"+ unsignedTx =+ "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1c\+ \a29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd\+ \9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a3\+ \3f950689af511e6e84c138dbbd3c3ee41588ac00000000"+ op0 = head $ prevOutput <$> txIn unsignedTx+ rawKeys =+ [ "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)+ sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]+ sigHashesB = setAnyoneCanPayFlag <$> sigHashesA+ sigIns = sigIn <$> (sigHashesA <> sigHashesB)+ generatedSignedTx = foldM addSig unsignedTx $ zip sigIns keys+ addSig tx (sigIn', key') = signNestedWitnessTx btc tx [sigIn'] [key']++secHexKey :: Text -> Maybe SecKey+secHexKey = decodeHex >=> secKey++toPubKey :: SecKey -> PubKeyI+toPubKey = derivePubKeyI . wrapSecKey True
− test/Haskoin/CryptoSpec.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Haskoin.CryptoSpec (spec) where--import Control.Monad-import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as C (pack)-import Data.Maybe (fromMaybe, isJust, isNothing)-import Data.Text (Text)-import Haskoin.Address-import Haskoin.Constants-import Haskoin.Crypto-import Haskoin.Keys-import Haskoin.Util-import Test.Hspec-import Test.HUnit (Assertion, assertBool)---- Unit tests copied from bitcoind implementation--- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp--strSecret1 :: Text-strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"--strSecret2 :: Text-strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"--strSecret1C :: Text-strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"--strSecret2C :: Text-strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"--addr1 :: Text-addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"--addr2 :: Text-addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"--addr1C :: Text-addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"--addr2C :: Text-addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"--strAddressBad :: Text-strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"--sigMsg :: [ByteString]-sigMsg =- [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]- | i <- [0..15]- ]--sec1 :: SecKeyI-sec1 =- fromMaybe (error "Could not decode WIF secret 1") (fromWif btc strSecret1)--sec2 :: SecKeyI-sec2 =- fromMaybe (error "Could not decode WIF secret 2") (fromWif btc strSecret2)--sec1C :: SecKeyI-sec1C =- fromMaybe- (error "Could not decode WIF compressed secret 1")- (fromWif btc strSecret1C)--sec2C :: SecKeyI-sec2C =- fromMaybe- (error "Could not decode WIF compressed secret 2")- (fromWif btc strSecret2C)--pub1 :: PubKeyI-pub1 = derivePubKeyI sec1--pub2 :: PubKeyI-pub2 = derivePubKeyI sec2--pub1C :: PubKeyI-pub1C = derivePubKeyI sec1C--pub2C :: PubKeyI-pub2C = derivePubKeyI sec2C--spec :: Spec-spec =- describe "bitcoind /src/test/key_tests.cpp" $ do- it "decode valid wif" checkPrivkey- it "decode invalid wif" checkInvalidKey- it "decode minikey format" checkMiniKey- it "check private key compression" checkPrvKeyCompressed- it "check public key compression" checkKeyCompressed- it "check matching address" checkMatchingAddress- it "check various signatures" sigCheck--sigCheck :: Assertion-sigCheck = forM_ sigMsg $ checkSignatures . doubleSHA256--{- bitcoind /src/test/key_tests.cpp -}--checkPrivkey :: Assertion-checkPrivkey = do- assertBool "Key 1" $ isJust $ fromWif btc strSecret1- assertBool "Key 2" $ isJust $ fromWif btc strSecret2- assertBool "Key 1C" $ isJust $ fromWif btc strSecret1C- assertBool "Key 2C" $ isJust $ fromWif btc strSecret2C--checkInvalidKey :: Assertion-checkInvalidKey =- assertBool "Bad key" $ isNothing $ fromWif btc strAddressBad--checkMiniKey :: Assertion-checkMiniKey =- assertBool "Bad mini key" $- isJust res && fromMiniKey "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy" == res- where- res = do- bs <-- decodeHex- "4C7A9640C72DC2099F23715D0C8A0D8A35F8906E3CAB61DD3F78B67BF887C9AB"- wrapSecKey False <$> secKey bs--checkPrvKeyCompressed :: Assertion-checkPrvKeyCompressed = do- assertBool "Key 1" $ not $ secKeyCompressed sec1- assertBool "Key 2" $ not $ secKeyCompressed sec2- assertBool "Key 1C" $ secKeyCompressed sec1C- assertBool "Key 2C" $ secKeyCompressed sec2C--checkKeyCompressed :: Assertion-checkKeyCompressed = do- assertBool "Key 1" $ not $ pubKeyCompressed pub1- assertBool "Key 2" $ not $ pubKeyCompressed pub2- assertBool "Key 1C" $ pubKeyCompressed pub1C- assertBool "Key 2C" $ pubKeyCompressed pub2C--checkMatchingAddress :: Assertion-checkMatchingAddress = do- assertBool "Key 1" $ Just addr1 == addrToString btc (pubKeyAddr pub1)- assertBool "Key 2" $ Just addr2 == addrToString btc (pubKeyAddr pub2)- assertBool "Key 1C" $ Just addr1C == addrToString btc (pubKeyAddr pub1C)- assertBool "Key 2C" $ Just addr2C == addrToString btc (pubKeyAddr pub2C)--checkSignatures :: Hash256 -> Assertion-checkSignatures h = do- let sign1 = signHash (secKeyData sec1) h- sign2 = signHash (secKeyData sec2) h- sign1C = signHash (secKeyData sec1C) h- sign2C = signHash (secKeyData sec2C) h- assertBool "Key 1, Sign1" $ verifyHashSig h sign1 (pubKeyPoint pub1)- assertBool "Key 1, Sign2" $ not $ verifyHashSig h sign2 (pubKeyPoint pub1)- assertBool "Key 1, Sign1C" $ verifyHashSig h sign1C (pubKeyPoint pub1)- assertBool "Key 1, Sign2C" $ not $ verifyHashSig h sign2C (pubKeyPoint pub1)- assertBool "Key 2, Sign1" $ not $ verifyHashSig h sign1 (pubKeyPoint pub2)- assertBool "Key 2, Sign2" $ verifyHashSig h sign2 (pubKeyPoint pub2)- assertBool "Key 2, Sign1C" $ not $ verifyHashSig h sign1C (pubKeyPoint pub2)- assertBool "Key 2, Sign2C" $ verifyHashSig h sign2C (pubKeyPoint pub2)- assertBool "Key 1C, Sign1" $ verifyHashSig h sign1 (pubKeyPoint pub1C)- assertBool "Key 1C, Sign2" $ not $ verifyHashSig h sign2 (pubKeyPoint pub1C)- assertBool "Key 1C, Sign1C" $ verifyHashSig h sign1C (pubKeyPoint pub1C)- assertBool "Key 1C, Sign2C" $ not $ verifyHashSig h sign2C (pubKeyPoint pub1C)- assertBool "Key 2C, Sign1" $ not $ verifyHashSig h sign1 (pubKeyPoint pub2C)- assertBool "Key 2C, Sign2" $ verifyHashSig h sign2 (pubKeyPoint pub2C)- assertBool "Key 2C, Sign1C" $ not $ verifyHashSig h sign1C (pubKeyPoint pub2C)- assertBool "Key 2C, Sign2C" $ verifyHashSig h sign2C (pubKeyPoint pub2C)
test/Haskoin/Keys/ExtendedSpec.hs view
@@ -1,6 +1,8 @@+{-# 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@@ -74,20 +76,9 @@ forAll arbitraryNetwork $ \net -> forAll arbitraryXPubKey $ customCerealID (getXPubKey net) (putXPubKey net) . snd- describe "bip32 derivation vector 1" $ do- it "chain m" $ runXKeyVec (head xKeyVec)- it "chain m/0'" $ runXKeyVec (xKeyVec !! 1)- it "chain m/0'/1" $ runXKeyVec (xKeyVec !! 2)- it "chain m/0'/1/2'" $ runXKeyVec (xKeyVec !! 3)- it "chain m/0'/1/2'/2" $ runXKeyVec (xKeyVec !! 4)- it "chain m/0'/1/2'/2/1000000000" $ runXKeyVec (xKeyVec !! 5)- describe "bip32 subkey derivation vector 2" $ do- it "chain m" $ runXKeyVec (head xKeyVec2)- it "chain m/0" $ runXKeyVec (xKeyVec2 !! 1)- it "chain m/0/2147483647'" $ runXKeyVec (xKeyVec2 !! 2)- it "chain m/0/2147483647'/1" $ runXKeyVec (xKeyVec2 !! 3)- it "chain m/0/2147483647'/1/2147483646'" $ runXKeyVec (xKeyVec2 !! 4)- it "Chain m/0/2147483647'/1/2147483646'/2" $ runXKeyVec (xKeyVec2 !! 5)+ describe "bip32 subkey derivation 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@@ -113,17 +104,21 @@ toSoft (listToPath $ pathToList p) == Just p describe "Extended Keys" $ do let net = btc- it "computes pubkey of a subkey is subkey of the pubkey" $- property $ forAll arbitraryXPrvKey pubKeyOfSubKeyIsSubKeyOfPubKey- it "exports and imports extended private key" $- property $+ 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- it "exports and imports extended public key" $- property $+ 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@@ -301,59 +296,78 @@ \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB" xpub = deriveXPubKey xprv +-- BIP 0032 Test Vectors+-- https://en.bitcoin.it/wiki/BIP_0032_TestVectors -runXKeyVec :: ([Text], XPrvKey) -> Assertion-runXKeyVec (v, m) = do- assertBool "xPrvID" $ encodeHex (S.encode $ xPrvID m) == head v+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" $- addrToString btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2)- assertBool "prvKey" $ encodeHex (getSecKey $ xPrvKey m) == v !! 3- assertBool "xPrvWIF" $ xPrvWif btc m == v !! 4+ 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 !! 5- assertBool "chain code" $ encodeHex (S.encode $ xPrvChain m) == v !! 6+ 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 !! 7- assertBool "Hex PrvKey" $ encodeHex (runPut (putXPrvKey btc m)) == v !! 8- assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 9- assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 10+ 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 --- BIP 0032 Test Vectors--- https://en.bitcoin.it/wiki/BIP_0032_TestVectors+-- 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)))+ ] -xKeyVec :: [([Text], XPrvKey)]-xKeyVec = zip xKeyResVec $ foldl f [m] der- where f acc d = acc ++ [d $ last acc]- m = makeXPrvKey $ fromJust $ decodeHex m0- der = [ flip hardSubKey 0- , flip prvSubKey 1- , flip hardSubKey 2- , flip prvSubKey 2- , flip prvSubKey 1000000000- ]+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 -xKeyVec2 :: [([Text], XPrvKey)]-xKeyVec2 = zip xKeyResVec2 $ foldl f [m] der- where f acc d = acc ++ [d $ last acc]- m = makeXPrvKey $ fromJust $ decodeHex m1- der = [ flip prvSubKey 0- , flip hardSubKey 2147483647- , flip prvSubKey 1- , flip hardSubKey 2147483646- , flip prvSubKey 2- ]+type TestVector = [Text]+type TestKey = Text -m0 :: Text-m0 = "000102030405060708090a0b0c0d0e0f"+m1 :: TestKey+m1 = "000102030405060708090a0b0c0d0e0f" -xKeyResVec :: [[Text]]-xKeyResVec =+vector1 :: [TestVector]+vector1 = [- -- m- [ "3442193e1bb70916e914552172cd4e2dbc9df811"+ [ "m"+ , "3442193e1bb70916e914552172cd4e2dbc9df811" , "3442193e" , "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"+ , "1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r" , "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35" , "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW" , "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"@@ -363,10 +377,11 @@ , "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" , "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" ]- -- m/0'- , [ "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"+ , [ "m/0'"+ , "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7" , "5c1bd648" , "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"+ , "1DDVw6BRKUv9U8Hzg5rGsia13nDrgJQpBd" , "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea" , "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT" , "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"@@ -376,10 +391,11 @@ , "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw" , "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7" ]- -- m/0'/1- , [ "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"+ , [ "m/0'/1"+ , "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe" , "bef5a2f9" , "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"+ , "1KMg6dRggXSkpz9fFyU76ru83TUSwPePEZ" , "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368" , "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM" , "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"@@ -389,10 +405,11 @@ , "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ" , "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs" ]- -- m/0'/1/2'- , [ "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72"+ , [ "m/0'/1/2'"+ , "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72" , "ee7ab90c" , "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x"+ , "1WykKhR25y7VDT21nZEwUUKSKDz9pENJh" , "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca" , "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU" , "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"@@ -402,10 +419,11 @@ , "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5" , "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM" ]- -- m/0'/1/2'/2- , [ "d880d7d893848509a62d8fb74e32148dac68412f"+ , [ "m/0'/1/2'/2"+ , "d880d7d893848509a62d8fb74e32148dac68412f" , "d880d7d8" , "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt"+ , "1asQ3smHhv2nv5R6hPpiUfkEorJpsdwwx" , "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4" , "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR" , "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"@@ -415,10 +433,11 @@ , "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV" , "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334" ]- -- m/0'/1/2'/2/1000000000- , [ "d69aa102255fed74378278c7812701ea641fdf32"+ , [ "m/0'/1/2'/2/1000000000"+ , "d69aa102255fed74378278c7812701ea641fdf32" , "d69aa102" , "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam"+ , "1HXJog342VFdc68AB9Cb6LwVmCjvcLMiwm" , "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8" , "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs" , "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"@@ -430,16 +449,17 @@ ] ] -m1 :: Text-m1 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"+m2 :: TestKey+m2 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" -xKeyResVec2 :: [[Text]]-xKeyResVec2 =+vector2 :: [TestVector]+vector2 = [- -- m- [ "bd16bee53961a47d6ad888e29545434a89bdfe95"+ [ "m"+ , "bd16bee53961a47d6ad888e29545434a89bdfe95" , "bd16bee5" , "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg"+ , "148CGtv7bwcC933EHtcDfzDQVneur1R8Y1" , "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e" , "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy" , "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"@@ -449,10 +469,11 @@ , "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" , "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U" ]- -- m/0- , [ "5a61ff8eb7aaca3010db97ebda76121610b78096"+ , [ "m/0"+ , "5a61ff8eb7aaca3010db97ebda76121610b78096" , "5a61ff8e" , "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"+ , "1KVyTSpsBGYs7NdyZmArEpVTfWJQSgiDCx" , "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e" , "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj" , "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"@@ -462,10 +483,11 @@ , "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" , "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt" ]- -- m/0/2147483647'- , [ "d8ab493736da02f11ed682f88339e720fb0379d1"+ , [ "m/0/2147483647'"+ , "d8ab493736da02f11ed682f88339e720fb0379d1" , "d8ab4937" , "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk"+ , "14MFLsfx1nc4RKiaH9khqDTNL9CRz3q347" , "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93" , "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr" , "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"@@ -475,10 +497,11 @@ , "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a" , "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9" ]- -- m/0/2147483647'/1- , [ "78412e3a2296a40de124307b6485bd19833e2e34"+ , [ "m/0/2147483647'/1"+ , "78412e3a2296a40de124307b6485bd19833e2e34" , "78412e3a" , "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW"+ , "19ou31MGyGW9VFx7woKBqwLe5JHhQBYaDD" , "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7" , "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2" , "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"@@ -488,10 +511,11 @@ , "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon" , "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef" ]- -- m/0/2147483647'/1/2147483646'- , [ "31a507b815593dfc51ffc7245ae7e5aee304246e"+ , [ "m/0/2147483647'/1/2147483646'"+ , "31a507b815593dfc51ffc7245ae7e5aee304246e" , "31a507b8" , "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R"+ , "18GYmRm4nyjk8ydvoVXFxMWQvxhksEFDZR" , "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d" , "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF" , "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"@@ -501,10 +525,11 @@ , "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL" , "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc" ]- -- m/0/2147483647'/1/2147483646'/2- , [ "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220"+ , [ "m/0/2147483647'/1/2147483646'/2"+ , "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220" , "26132fdb" , "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt"+ , "1758mgwNZhyzpRLe4u7FjqpJtqKpaGhXh7" , "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23" , "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK" , "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"@@ -516,8 +541,38 @@ ] ] -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+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/KeysSpec.hs view
@@ -1,18 +1,28 @@+{-# 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 Data.Map.Strict (singleton)+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.Util.Arbitrary+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]@@ -34,15 +44,36 @@ ] spec :: Spec-spec =- describe "keys" $ do- testIdentity serialVals readVals jsonVals []- it "is public key canonical" $- property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)- it "from string public key" $- property $+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@@ -58,4 +89,169 @@ (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/ScriptSpec.hs view
@@ -5,7 +5,6 @@ import Data.Aeson as A import Data.ByteString (ByteString) import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L import Data.Either import Data.List import Data.Maybe@@ -21,6 +20,7 @@ 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@@ -63,6 +63,8 @@ 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@@ -89,7 +91,7 @@ forAll arbitraryMSOutput $ \out -> map S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs -> xs == sort xs- prop "can decode inputs with empty signatures" $ do+ it "can decode inputs with empty signatures" $ do decodeInput net (Script [OP_0]) `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty)) decodeInput net (Script [opPushData ""]) `shouldBe`@@ -108,7 +110,7 @@ scriptSpec net = when (getNetworkName net == "btc") $ it "can verify standard scripts from script_tests.json file" $ do- xs <- readTestFile "script_tests" :: IO [A.Value]+ xs <- readTestFile "script_tests.json" :: IO [A.Value] let vectorsA = mapMaybe (A.decode . A.encode) xs :: [( String , String@@ -152,7 +154,7 @@ forkIdScriptSpec net = when (isJust (getSigHashForkId net)) $ it "can verify scripts from forkid_script_tests.json file" $ do- xs <- readTestFile "forkid_script_tests" :: IO [A.Value]+ xs <- readTestFile "forkid_script_tests.json" :: IO [A.Value] let vectors = mapMaybe (A.decode . A.encode) xs :: [( [Word64] , String@@ -221,13 +223,13 @@ strictSigSpec net = when (getNetworkName net == "btc") $ do it "can decode strict signatures" $ do- xs <- readTestFile "sig_strict"+ 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"+ xs <- readTestFile "sig_nonstrict.json" let vectors = mapMaybe decodeHex xs length vectors `shouldBe` 17 forM_ vectors $ \sig ->@@ -237,7 +239,7 @@ txSigHashSpec net = when (getNetworkName net == "btc") $ it "can produce valid sighashes from sighash.json test vectors" $ do- xs <- readTestFile "sighash" :: IO [A.Value]+ xs <- readTestFile "sighash.json" :: IO [A.Value] let vectors = mapMaybe (A.decode . A.encode) xs :: [( String , String@@ -260,7 +262,7 @@ txSigHashForkIdSpec net = when (getNetworkName net == "btc") $ it "can produce valid sighashes from forkid_sighash.json test vectors" $ do- xs <- readTestFile "forkid_sighash" :: IO [A.Value]+ xs <- readTestFile "forkid_sighash.json" :: IO [A.Value] let vectors = mapMaybe (A.decode . A.encode) xs :: [( String , String@@ -344,13 +346,6 @@ then setAnyoneCanPayFlag else id -{-- Test Utilities --}--readTestFile :: A.FromJSON a => FilePath -> IO a-readTestFile fp = do- bs <- L.readFile $ "data/" <> fp <> ".json"- maybe (error $ "Could not read test file " <> fp) return $ A.decode bs- {- Parse tests from bitcoin-qt repository -} mapMulSigVector :: ((Text, Text), Int) -> Spec@@ -368,7 +363,7 @@ b = do o <- s d <- eitherToMaybe $ decodeOutput o- addrToString btc $ payToScriptAddress d+ addrToText btc $ payToScriptAddress d sigDecodeMap :: Network -> (Text, Int) -> Spec sigDecodeMap net (_, i) =@@ -390,17 +385,55 @@ mulSigVectors :: [(Text, Text)] mulSigVectors = [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"- , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"+ , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb\+ \129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8\+ \334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253\+ \e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccf\+ \fef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f997\+ \4ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f\+ \322a1863d4621353ae" ) ] scriptSigSignatures :: [Text] scriptSigSignatures =- -- Signature in input of txid 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053- [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e401"+ -- 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.- -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501"+ -- "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+
test/Haskoin/TransactionSpec.hs view
@@ -15,13 +15,11 @@ import Haskoin.Keys import Haskoin.Script import Haskoin.Transaction-import Haskoin.Transaction.Segwit (isSegwit) import Haskoin.Util import Haskoin.Util.Arbitrary-import Safe (readMay) import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit (Assertion, assertBool)+import Test.HUnit import Test.QuickCheck serialVals :: [SerialBox]@@ -57,21 +55,7 @@ spec :: Spec spec = do testIdentity serialVals readVals jsonVals []- describe "transaction unit tests" $ do- it "compute txid from tx" $ mapM_ runTxIDVec txIDVec- it "build pkhash transaction (generated from bitcoind)" $- mapM_ runPKHashVec pkHashVec- it "encode satoshi core script pubkey" tEncodeSatoshiCoreScriptPubKey- --- -- These tests depend on signatures matching exactly those in the spec.- -- Fedora includes a version of libsecp256k1 that computes signatures- -- using a slighlty different deterministic nonce algorithm.- --- -- it "agrees with BIP143 p2wpkh example" testBip143p2wpkh- -- it "agrees with BIP143 p2sh-p2wpkh example" testBip143p2shp2wpkh- -- it "builds a p2wsh multisig transaction" testP2WSHMulsig- -- it "agrees with BIP143 p2sh-p2wsh multisig example" testBip143p2shp2wpkhMulsig- describe "btc transaction" $ do+ describe "Transaction properties" $ do prop "decode and encode txid" $ forAll arbitraryTxHash $ \h -> hexToTxHash (txHashToHex h) == Just h prop "from string transaction id" $@@ -99,191 +83,125 @@ 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 -runTxIDVec :: (Text, Text) -> Assertion-runTxIDVec (tid, tx) = assertBool "txid" $ txHashToHex (txHash txBS) == tid+-- Txid Vectors++testTxidVector :: (Text, Text) -> Assertion+testTxidVector (tid, tx) =+ assertEqual "txid" (Just tid) (txHashToHex . txHash <$> txM) where- txBS = fromJust $ either (const Nothing) return . S.decode =<< decodeHex tx+ txM = eitherToMaybe . S.decode =<< decodeHex tx -txIDVec :: [(Text, Text)]-txIDVec =+txidVectors :: [(Text, Text)]+txidVectors = [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"- , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+ , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa4\+ \3ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75\+ \418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb\+ \60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f0000000000\+ \1976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000" ) , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"- , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"+ , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3\+ \bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a\+ \04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7\+ \d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68\+ \350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b\+ \2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d0000\+ \0000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac000000\+ \00" ) , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"- , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"+ , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2\+ \f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332ad\+ \fca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c\+ \70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f\+ \09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919a\+ \d1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737\+ \864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a473044\+ \0220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa\+ \2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f8\+ \1e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc8\+ \0a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd32273276\+ \94c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df3\+ \2949d4646dfa10a92458cfaa88ac00000000" ) , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"- , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"+ , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c5\+ \9da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678\+ \fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab\+ \486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b\+ \52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbe\+ \a097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045\+ \022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf9506\+ \50802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f\+ \68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e3\+ \77c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e1\+ \8217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a378\+ \8dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972\+ \d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2\+ \e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffff\+ \ff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9c\+ \fdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675\+ \d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa\+ \34b26f62db4a88ac00000000" ) ] -runPKHashVec :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion-runPKHashVec (xs, ys, res) =- assertBool "Build PKHash Tx" $ encodeHex (S.encode tx) == res+-- 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- tx =- fromRight (error "Could not decode transaction") $- buildAddrTx btc (map f xs) ys+ txE = buildAddrTx btc (map f is) os f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix --- These test vectors have been generated from bitcoind raw transaction api--pkHashVec :: [([(Text, Word32)], [(Text, Word64)], Text)]-pkHashVec =- [- ( [("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",14)]- , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",90000000)]- , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"- )- , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)- , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)+pkHashVectors :: [([(Text, Word32)], [(Text, Word64)], Text)]+pkHashVectors =+ [ ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"+ , 14) ]- , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)- , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)+ , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 90000000)]+ , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+ \a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc58\+ \2292b97668bedd91dd5bf9374c88ac00000000")+ , ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"+ , 0)+ , ( "0001000000000000000000000000000000000000000000000000000000000000"+ , 2147483647) ]- , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"- )- , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)- , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)+ , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1)+ , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000) ]+ , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+ \a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000\+ \000000000000000000000100ffffff7f00ffffffff0201000000000000001976a9\+ \1424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976\+ \a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000")+ , ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"+ , 0)+ , ( "0001000000000000000000000000000000000000000000000000000000000000"+ , 2147483647)+ ] , []- , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0000000000"- )+ , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a\+ \1eb29eb0000000000ffffffff000000000000000000000000000000000000000000\+ \0000000000000000000100ffffff7f00ffffffff0000000000") , ( []- , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)- , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)+ , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1)+ , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000) ]- , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"- )+ , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91d\+ \d5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954\+ \993cbca54f88ac00000000") ] -tEncodeSatoshiCoreScriptPubKey :: Assertion-tEncodeSatoshiCoreScriptPubKey = assertBool "tEncodeSatoshiCoreScriptPubKey" $- t1BsOutputScriptPubKey == encodeSatoshiCoreScriptPubKey t1SatoshiCoreJsonScriptPubKey- where- t1BsOutputScriptPubKey :: Text- t1BsOutputScriptPubKey = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"- t1SatoshiCoreJsonScriptPubKey :: String- t1SatoshiCoreJsonScriptPubKey = "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"--encodeSatoshiCoreScriptPubKey :: String -> Text-encodeSatoshiCoreScriptPubKey =- mconcat . map encodeSatoshiCoreScriptPiece . words- where- encodeSatoshiCoreScriptPiece :: String -> Text- encodeSatoshiCoreScriptPiece s = case (readMay ("OP_" ++ s) :: Maybe ScriptOp) of- Just op -> encodeHex . S.encode $ op- Nothing -> case take 2 s of- "OP" -> encodeHex . S.encode . (read :: String -> ScriptOp) $ s- "0x" -> (fromString . drop 2 :: String -> Text) s- _ -> case (readMay s :: Maybe Int) of -- can we get rid of this case now?- Just i -> encodeHex . S.encode . intToScriptOp $ i- Nothing -> error $ "encodeSatoshiCoreScriptPubKey: " ++ s------- Following tests depend on specific secp256k1 deterministic nonce computation.--- Fedora distributes the Bitcoin ABC fork of this library, which computes nonces differently.------- -- Reproduce the P2WPKH example from BIP 143--- testBip143p2wpkh :: Assertion--- testBip143p2wpkh = assertEqual "BIP143 p2wpkh" (Right exampleSignedTx) generatedSignedTx--- where--- exampleSignedTx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"---- unsignedTx = "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"---- Just key0 = secHexKey "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"--- pubKey0 = toPubKey key0---- Just key1 = secHexKey "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"---- [op0, op1] = prevOutput <$> txIn unsignedTx---- sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing---- WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1--- sigIn1 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing---- generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]---- -- Reproduce the P2SH-P2WPKH example from BIP 143--- testBip143p2shp2wpkh :: Assertion--- testBip143p2shp2wpkh = assertEqual "BIP143 p2sh-p2wpkh" (Right exampleSignedTx) generatedSignedTx--- where--- exampleSignedTx = "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"---- unsignedTx = "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"---- Just key0 = secHexKey "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"---- op0 = prevOutput . head $ txIn unsignedTx--- WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key0--- sigIn0 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing---- generatedSignedTx = signNestedWitnessTx btc unsignedTx [sigIn0] [key0]---- -- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)--- testP2WSHMulsig :: Assertion--- testP2WSHMulsig = assertEqual "p2wsh multisig" (Right exampleSignedTx) generatedSignedTx--- where--- exampleSignedTx = "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100fad4fedd2bb4c439c64637eb8e9150d9020a7212808b8dc0578d5ff5b4ad65fe0220714640f261b37eb3106310bf853f4b706e51436fb6b64c2ab00768814eb55b9801473044022100baff4e4ceea4022b9725a2e6f6d77997a554f858165b91ac8c16c9833008bee9021f5f70ebc3f8580dc0a5e96451e3697bdf1f1f5883944f0f33ab0cfb272354040169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea811549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f69ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695e2d8e007a7f26db96c2ee42db15dc953ae00000000"---- unsignedTx = "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac00000000"---- op0 = head $ prevOutput <$> txIn unsignedTx---- Just keys = traverse secHexKey [ "3030303030303030303030303030303030303030303030303030303030303031"--- , "3030303030303030303030303030303030303030303030303030303030303032"--- , "3030303030303030303030303030303030303030303030303030303030303033"--- ]---- rdm = PayMulSig (toPubKey <$> keys) 2--- sigIn = SigInput (toP2WSH $ encodeOutput rdm) 100000000 op0 sigHashAll (Just rdm)--- generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)---- -- Reproduce the P2SH-P2WSH multisig example from BIP 143--- testBip143p2shp2wpkhMulsig :: Assertion--- testBip143p2shp2wpkhMulsig =--- assertEqual "BIP143 p2sh-p2wsh multisig" (Right exampleSignedTx) generatedSignedTx--- where--- exampleSignedTx = "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"---- unsignedTx = "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000"---- op0 = head $ prevOutput <$> txIn unsignedTx---- rawKeys = [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6"--- , "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3"--- , "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661"--- , "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49"--- , "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323"--- , "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"--- ]---- Just keys@[key0, key1, key2, key3, key4, key5] = traverse secHexKey rawKeys---- rdm = PayMulSig (toPubKey <$> keys) 6--- sigIn sh = SigInput (toP2WSH $ encodeOutput rdm) 987654321 op0 sh (Just rdm)---- sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]--- sigHashesB = setAnyoneCanPayFlag <$> sigHashesA--- sigIns = sigIn <$> (sigHashesA <> sigHashesB)---- generatedSignedTx = foldM addSig unsignedTx $ zip sigIns keys--- addSig tx (sigIn, key) = signNestedWitnessTx btc tx [sigIn] [key]---- secHexKey :: Text -> Maybe SecKey--- secHexKey = decodeHex >=> secKey---- toPubKey :: SecKey -> PubKeyI--- toPubKey = derivePubKeyI . wrapSecKey True--{- Building Transactions -}+-- Transaction Properties -- testBuildAddrTx :: Network -> Address -> TestCoin -> Bool testBuildAddrTx net a (TestCoin v)@@ -291,17 +209,17 @@ | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out | otherwise = undefined where- tx = buildAddrTx net [] [(fromJust (addrToString net a), v)]+ 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- -- We compute an upper bound but it should be close enough to the real size- -- We give 2 bytes of slack on every signature (1 on r and 1 on s)- = guess >= len && guess <= len + 2 * delta+testGuessSize net tx =+ guess >= len && guess <= len + 2 * delta where delta = pki + sum (map fst msi) guess = guessTxSize pki msi pkout msout@@ -313,7 +231,7 @@ pki = length $ filter isSpendPKHash ins msi = concatMap shData ins shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]- shData _ = []+ shData _ = [] out = map (fromRight (error "Could not decode transaction output") .@@ -352,9 +270,10 @@ {- Signing Transactions -} testDetSignTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignTx net (tx, sigis, prv) =+testDetSignTx net (tx, sigis, prv) = not (verifyStdTx net tx verData) &&- not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData+ not (verifyStdTx net txSigP verData) &&+ verifyStdTx net txSigC verData where txSigP = fromRight (error "Could not decode transaction") $@@ -367,7 +286,8 @@ 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+ not (verifyStdTx net txSigP verData) &&+ verifyStdTx net txSigC verData where txSigP = fromRight (error "Could not decode transaction") $@@ -381,21 +301,26 @@ | otherwise = (s, v, o) testMergeTx :: Network -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool-testMergeTx net (txs, os) = and- [ isRight mergeRes- , length (txIn mergedTx) == length os- , if enoughSigs then isValid else not isValid- -- Signature count == min (length txs) (sum required signatures)- , sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))- ]+testMergeTx 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"+ 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"
test/Haskoin/UtilSpec.hs view
@@ -1,6 +1,7 @@ module Haskoin.UtilSpec ( spec , customCerealID+ , readTestFile ) where import Data.Aeson (FromJSON, ToJSON)@@ -67,6 +68,13 @@ 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