diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/data/key_io_invalid.json b/data/key_io_invalid.json
new file mode 100644
--- /dev/null
+++ b/data/key_io_invalid.json
@@ -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"
+    ]
+]
diff --git a/data/key_io_valid.json b/data/key_io_valid.json
new file mode 100644
--- /dev/null
+++ b/data/key_io_valid.json
@@ -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
+        }
+    ]
+]
diff --git a/data/rfc6979DERabc.json b/data/rfc6979DERabc.json
new file mode 100644
--- /dev/null
+++ b/data/rfc6979DERabc.json
@@ -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"
+    ]
+]
diff --git a/data/rfc6979DERcore.json b/data/rfc6979DERcore.json
new file mode 100644
--- /dev/null
+++ b/data/rfc6979DERcore.json
@@ -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"
+    ]
+]
diff --git a/data/rfc6979abc.json b/data/rfc6979abc.json
new file mode 100644
--- /dev/null
+++ b/data/rfc6979abc.json
@@ -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"
+    ]
+]
diff --git a/data/rfc6979core.json b/data/rfc6979core.json
new file mode 100644
--- /dev/null
+++ b/data/rfc6979core.json
@@ -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"
+    ]
+]
diff --git a/haskoin-core.cabal b/haskoin-core.cabal
--- a/haskoin-core.cabal
+++ b/haskoin-core.cabal
@@ -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
diff --git a/src/Haskoin.hs b/src/Haskoin.hs
--- a/src/Haskoin.hs
+++ b/src/Haskoin.hs
@@ -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
diff --git a/src/Haskoin/Address.hs b/src/Haskoin/Address.hs
--- a/src/Haskoin/Address.hs
+++ b/src/Haskoin/Address.hs
@@ -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
diff --git a/src/Haskoin/Address/Base58.hs b/src/Haskoin/Address/Base58.hs
--- a/src/Haskoin/Address/Base58.hs
+++ b/src/Haskoin/Address/Base58.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Address.Base58
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Address/Bech32.hs b/src/Haskoin/Address/Bech32.hs
--- a/src/Haskoin/Address/Bech32.hs
+++ b/src/Haskoin/Address/Bech32.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Address.Base58
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Address/CashAddr.hs b/src/Haskoin/Address/CashAddr.hs
--- a/src/Haskoin/Address/CashAddr.hs
+++ b/src/Haskoin/Address/CashAddr.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Address.CashAddr
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Block.hs b/src/Haskoin/Block.hs
--- a/src/Haskoin/Block.hs
+++ b/src/Haskoin/Block.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Block
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Block/Common.hs b/src/Haskoin/Block/Common.hs
--- a/src/Haskoin/Block/Common.hs
+++ b/src/Haskoin/Block/Common.hs
@@ -4,7 +4,7 @@
 {-|
 Module      : Haskoin.Block.Common
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Block/Headers.hs b/src/Haskoin/Block/Headers.hs
--- a/src/Haskoin/Block/Headers.hs
+++ b/src/Haskoin/Block/Headers.hs
@@ -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
diff --git a/src/Haskoin/Block/Merkle.hs b/src/Haskoin/Block/Merkle.hs
--- a/src/Haskoin/Block/Merkle.hs
+++ b/src/Haskoin/Block/Merkle.hs
@@ -3,7 +3,7 @@
 {-|
 Module      : Haskoin.Block.Merkle
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Constants.hs b/src/Haskoin/Constants.hs
--- a/src/Haskoin/Constants.hs
+++ b/src/Haskoin/Constants.hs
@@ -5,7 +5,7 @@
 {-|
 Module      : Haskoin.Constants
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Crypto.hs b/src/Haskoin/Crypto.hs
--- a/src/Haskoin/Crypto.hs
+++ b/src/Haskoin/Crypto.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Crypto
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Crypto/Hash.hs b/src/Haskoin/Crypto/Hash.hs
--- a/src/Haskoin/Crypto/Hash.hs
+++ b/src/Haskoin/Crypto/Hash.hs
@@ -3,7 +3,7 @@
 {-|
 Module      : Haskoin.Crypto.Hash
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Crypto/Signature.hs b/src/Haskoin/Crypto/Signature.hs
--- a/src/Haskoin/Crypto/Signature.hs
+++ b/src/Haskoin/Crypto/Signature.hs
@@ -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
diff --git a/src/Haskoin/Keys.hs b/src/Haskoin/Keys.hs
--- a/src/Haskoin/Keys.hs
+++ b/src/Haskoin/Keys.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Keys
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Keys/Common.hs b/src/Haskoin/Keys/Common.hs
--- a/src/Haskoin/Keys/Common.hs
+++ b/src/Haskoin/Keys/Common.hs
@@ -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
diff --git a/src/Haskoin/Keys/Extended.hs b/src/Haskoin/Keys/Extended.hs
--- a/src/Haskoin/Keys/Extended.hs
+++ b/src/Haskoin/Keys/Extended.hs
@@ -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.
diff --git a/src/Haskoin/Keys/Mnemonic.hs b/src/Haskoin/Keys/Mnemonic.hs
--- a/src/Haskoin/Keys/Mnemonic.hs
+++ b/src/Haskoin/Keys/Mnemonic.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Keys.Mnemonic
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Network.hs b/src/Haskoin/Network.hs
--- a/src/Haskoin/Network.hs
+++ b/src/Haskoin/Network.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Network
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Network/Bloom.hs b/src/Haskoin/Network/Bloom.hs
--- a/src/Haskoin/Network/Bloom.hs
+++ b/src/Haskoin/Network/Bloom.hs
@@ -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
diff --git a/src/Haskoin/Network/Common.hs b/src/Haskoin/Network/Common.hs
--- a/src/Haskoin/Network/Common.hs
+++ b/src/Haskoin/Network/Common.hs
@@ -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
diff --git a/src/Haskoin/Network/Message.hs b/src/Haskoin/Network/Message.hs
--- a/src/Haskoin/Network/Message.hs
+++ b/src/Haskoin/Network/Message.hs
@@ -3,7 +3,7 @@
 {-|
 Module      : Haskoin.Network.Message
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Script.hs b/src/Haskoin/Script.hs
--- a/src/Haskoin/Script.hs
+++ b/src/Haskoin/Script.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Script
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Script/Common.hs b/src/Haskoin/Script/Common.hs
--- a/src/Haskoin/Script/Common.hs
+++ b/src/Haskoin/Script/Common.hs
@@ -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"
diff --git a/src/Haskoin/Script/SigHash.hs b/src/Haskoin/Script/SigHash.hs
--- a/src/Haskoin/Script/SigHash.hs
+++ b/src/Haskoin/Script/SigHash.hs
@@ -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
diff --git a/src/Haskoin/Script/Standard.hs b/src/Haskoin/Script/Standard.hs
--- a/src/Haskoin/Script/Standard.hs
+++ b/src/Haskoin/Script/Standard.hs
@@ -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"
diff --git a/src/Haskoin/Transaction.hs b/src/Haskoin/Transaction.hs
--- a/src/Haskoin/Transaction.hs
+++ b/src/Haskoin/Transaction.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Transaction
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Transaction/Builder.hs b/src/Haskoin/Transaction/Builder.hs
--- a/src/Haskoin/Transaction/Builder.hs
+++ b/src/Haskoin/Transaction/Builder.hs
@@ -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
 
diff --git a/src/Haskoin/Transaction/Builder/Sign.hs b/src/Haskoin/Transaction/Builder/Sign.hs
--- a/src/Haskoin/Transaction/Builder/Sign.hs
+++ b/src/Haskoin/Transaction/Builder/Sign.hs
@@ -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
+
diff --git a/src/Haskoin/Transaction/Common.hs b/src/Haskoin/Transaction/Common.hs
--- a/src/Haskoin/Transaction/Common.hs
+++ b/src/Haskoin/Transaction/Common.hs
@@ -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"
diff --git a/src/Haskoin/Transaction/Genesis.hs b/src/Haskoin/Transaction/Genesis.hs
new file mode 100644
--- /dev/null
+++ b/src/Haskoin/Transaction/Genesis.hs
@@ -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"
diff --git a/src/Haskoin/Transaction/Partial.hs b/src/Haskoin/Transaction/Partial.hs
--- a/src/Haskoin/Transaction/Partial.hs
+++ b/src/Haskoin/Transaction/Partial.hs
@@ -6,7 +6,7 @@
 {-|
 Module      : Haskoin.Transaction.Partial
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : matt@bitnomial.com
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Transaction/Segwit.hs b/src/Haskoin/Transaction/Segwit.hs
--- a/src/Haskoin/Transaction/Segwit.hs
+++ b/src/Haskoin/Transaction/Segwit.hs
@@ -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
 --
diff --git a/src/Haskoin/Util.hs b/src/Haskoin/Util.hs
--- a/src/Haskoin/Util.hs
+++ b/src/Haskoin/Util.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Util
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary.hs b/src/Haskoin/Util/Arbitrary.hs
--- a/src/Haskoin/Util/Arbitrary.hs
+++ b/src/Haskoin/Util/Arbitrary.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Address.hs b/src/Haskoin/Util/Arbitrary/Address.hs
--- a/src/Haskoin/Util/Arbitrary/Address.hs
+++ b/src/Haskoin/Util/Arbitrary/Address.hs
@@ -2,7 +2,7 @@
 {-|
 Module      : Haskoin.Test.Address
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Block.hs b/src/Haskoin/Util/Arbitrary/Block.hs
--- a/src/Haskoin/Util/Arbitrary/Block.hs
+++ b/src/Haskoin/Util/Arbitrary/Block.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test.Block
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Crypto.hs b/src/Haskoin/Util/Arbitrary/Crypto.hs
--- a/src/Haskoin/Util/Arbitrary/Crypto.hs
+++ b/src/Haskoin/Util/Arbitrary/Crypto.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test.Crypto
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Keys.hs b/src/Haskoin/Util/Arbitrary/Keys.hs
--- a/src/Haskoin/Util/Arbitrary/Keys.hs
+++ b/src/Haskoin/Util/Arbitrary/Keys.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test.Keys
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Message.hs b/src/Haskoin/Util/Arbitrary/Message.hs
--- a/src/Haskoin/Util/Arbitrary/Message.hs
+++ b/src/Haskoin/Util/Arbitrary/Message.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test.Message
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Network.hs b/src/Haskoin/Util/Arbitrary/Network.hs
--- a/src/Haskoin/Util/Arbitrary/Network.hs
+++ b/src/Haskoin/Util/Arbitrary/Network.hs
@@ -1,7 +1,7 @@
 {-|
 Module      : Haskoin.Test.Network
 Copyright   : No rights reserved
-License     : UNLICENSE
+License     : MIT
 Maintainer  : jprupp@protonmail.ch
 Stability   : experimental
 Portability : POSIX
diff --git a/src/Haskoin/Util/Arbitrary/Script.hs b/src/Haskoin/Util/Arbitrary/Script.hs
--- a/src/Haskoin/Util/Arbitrary/Script.hs
+++ b/src/Haskoin/Util/Arbitrary/Script.hs
@@ -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
diff --git a/src/Haskoin/Util/Arbitrary/Transaction.hs b/src/Haskoin/Util/Arbitrary/Transaction.hs
--- a/src/Haskoin/Util/Arbitrary/Transaction.hs
+++ b/src/Haskoin/Util/Arbitrary/Transaction.hs
@@ -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
diff --git a/src/Haskoin/Util/Arbitrary/Util.hs b/src/Haskoin/Util/Arbitrary/Util.hs
--- a/src/Haskoin/Util/Arbitrary/Util.hs
+++ b/src/Haskoin/Util/Arbitrary/Util.hs
@@ -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
diff --git a/test/Haskoin/Address/CashAddrSpec.hs b/test/Haskoin/Address/CashAddrSpec.hs
--- a/test/Haskoin/Address/CashAddrSpec.hs
+++ b/test/Haskoin/Address/CashAddrSpec.hs
@@ -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"
diff --git a/test/Haskoin/AddressSpec.hs b/test/Haskoin/AddressSpec.hs
--- a/test/Haskoin/AddressSpec.hs
+++ b/test/Haskoin/AddressSpec.hs
@@ -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")
+    ]
diff --git a/test/Haskoin/Crypto/HashSpec.hs b/test/Haskoin/Crypto/HashSpec.hs
--- a/test/Haskoin/Crypto/HashSpec.hs
+++ b/test/Haskoin/Crypto/HashSpec.hs
@@ -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")
+    ]
diff --git a/test/Haskoin/Crypto/SignatureSpec.hs b/test/Haskoin/Crypto/SignatureSpec.hs
--- a/test/Haskoin/Crypto/SignatureSpec.hs
+++ b/test/Haskoin/Crypto/SignatureSpec.hs
@@ -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
diff --git a/test/Haskoin/CryptoSpec.hs b/test/Haskoin/CryptoSpec.hs
deleted file mode 100644
--- a/test/Haskoin/CryptoSpec.hs
+++ /dev/null
@@ -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)
diff --git a/test/Haskoin/Keys/ExtendedSpec.hs b/test/Haskoin/Keys/ExtendedSpec.hs
--- a/test/Haskoin/Keys/ExtendedSpec.hs
+++ b/test/Haskoin/Keys/ExtendedSpec.hs
@@ -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"
+      ]
+    ]
diff --git a/test/Haskoin/KeysSpec.hs b/test/Haskoin/KeysSpec.hs
--- a/test/Haskoin/KeysSpec.hs
+++ b/test/Haskoin/KeysSpec.hs
@@ -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
 
diff --git a/test/Haskoin/ScriptSpec.hs b/test/Haskoin/ScriptSpec.hs
--- a/test/Haskoin/ScriptSpec.hs
+++ b/test/Haskoin/ScriptSpec.hs
@@ -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
+
diff --git a/test/Haskoin/TransactionSpec.hs b/test/Haskoin/TransactionSpec.hs
--- a/test/Haskoin/TransactionSpec.hs
+++ b/test/Haskoin/TransactionSpec.hs
@@ -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"
diff --git a/test/Haskoin/UtilSpec.hs b/test/Haskoin/UtilSpec.hs
--- a/test/Haskoin/UtilSpec.hs
+++ b/test/Haskoin/UtilSpec.hs
@@ -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
 
