diff --git a/.gitignore b/.gitignore index a6c0ab8..d6ceb6b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ coverage node_modules .nyc_output npm-debug.log +test/*.js +test/integration/*.js +!test/ts-node-register.js diff --git a/package-lock.json b/package-lock.json index 7f92fcd..5812365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,14 +14,14 @@ } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.5", "jsesc": "^2.5.1", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "source-map": "^0.5.0", "trim-right": "^1.0.1" } @@ -67,9 +67,9 @@ } }, "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", "dev": true }, "@babel/template": { @@ -84,22 +84,31 @@ } }, "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.11" + "lodash": "^4.17.13" }, "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -108,35 +117,65 @@ "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true } } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", "dev": true, "requires": { "esutils": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } }, + "@types/base-x": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/base-x/-/base-x-3.0.0.tgz", + "integrity": "sha512-vnqSlpsv9uFX5/z8GyKWAfWHhLGJDBkrgRRsnxlsX23DHOlNyqP/eHQiv4TwnYcZULzQIxaWA/xRWU9Dyy4qzw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/bs58": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.0.tgz", + "integrity": "sha512-gYX+MHD4G/R+YGYwdhG5gbJj4LsEQGr3Vg6gVDAbe7xC5Bn8dNNG2Lpo6uDX/rT5dE7VBj0rGEFuV8L0AEx4Rg==", + "dev": true, + "requires": { + "@types/base-x": "*" + } + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, "@types/node": { "version": "10.12.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" }, + "@types/proxyquire": { + "version": "1.3.28", + "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", + "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", + "dev": true + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { @@ -163,6 +202,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", + "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -219,16 +264,23 @@ } }, "bip39": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", - "integrity": "sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", "dev": true, "requires": { + "@types/node": "11.11.6", "create-hash": "^1.1.0", "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "unorm": "^1.3.3" + "randombytes": "^2.0.1" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==", + "dev": true + } } }, "bip65": { @@ -300,6 +352,12 @@ "safe-buffer": "^5.1.2" } }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -345,16 +403,22 @@ } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -436,22 +500,25 @@ } }, "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -469,6 +536,15 @@ "strip-bom": "^3.0.0" } }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "dhttp": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz", @@ -522,6 +598,31 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -559,21 +660,6 @@ "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } } }, "file-uri-to-path": { @@ -611,6 +697,15 @@ "locate-path": "^3.0.0" } }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, "foreground-child": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", @@ -619,6 +714,18 @@ "requires": { "cross-spawn": "^4", "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } } }, "fs.realpath": { @@ -627,6 +734,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -663,9 +776,9 @@ "dev": true }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "growl": { @@ -694,12 +807,27 @@ } } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -728,9 +856,9 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "hmac-drbg": { @@ -750,9 +878,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", "dev": true }, "imurmurhash": { @@ -788,6 +916,24 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -800,12 +946,30 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -843,9 +1007,9 @@ }, "dependencies": { "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -894,12 +1058,6 @@ "ms": "^2.1.1" } }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -996,6 +1154,15 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -1016,6 +1183,12 @@ "semver": "^5.6.0" } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -1125,22 +1298,59 @@ } }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", + "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", "dev": true, "requires": { + "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", + "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "find-up": "3.0.0", + "glob": "7.1.3", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "module-not-found-error": { @@ -1150,9 +1360,9 @@ "dev": true }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "nan": { @@ -1178,6 +1388,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1191,9 +1411,9 @@ }, "dependencies": { "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -1210,6 +1430,12 @@ "path-key": "^2.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -1259,6 +1485,34 @@ } } }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1314,9 +1568,9 @@ "dev": true }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -1632,10 +1886,28 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "spawn-wrap": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", - "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", "dev": true, "requires": { "foreground-child": "^1.5.6", @@ -1673,9 +1945,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "sprintf-js": { @@ -1691,23 +1963,22 @@ "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^3.0.0" } }, "strip-bom": { @@ -1722,6 +1993,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -1783,6 +2060,27 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -1831,9 +2129,9 @@ "dev": true }, "uglify-js": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.14.tgz", - "integrity": "sha512-dgyjIw8KFK6AyVl5vm2tEqPewv5TKGEiiVFLI1LbF+oHua/Njd8tZk3lIbF1AWU1rNdEg7scaceADb4zqCcWXg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, "optional": true, "requires": { @@ -1857,16 +2155,10 @@ } } }, - "unorm": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", - "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", - "dev": true - }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, "validate-npm-package-license": { @@ -1902,6 +2194,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -1917,14 +2218,50 @@ "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "wrappy": { @@ -1934,9 +2271,9 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -1957,12 +2294,12 @@ "dev": true }, "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", "dev": true, "requires": { - "cliui": "^5.0.0", + "cliui": "^4.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", "os-locale": "^3.1.0", @@ -1972,18 +2309,107 @@ "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "yargs-parser": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz", - "integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 81ecd1b..700b7fd 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ ], "scripts": { "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", + "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json", "clean": "rimraf src types", + "clean:jstests": "rimraf 'test/**/!(ts-node-register)*.js'", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", @@ -26,12 +28,13 @@ "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", "lint": "tslint -p tsconfig.json -c tslint.json", + "mocha:ts": "mocha --recursive --require test/ts-node-register", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", - "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", - "nobuild:integration": "mocha --timeout 50000 test/integration/", - "nobuild:unit": "mocha", - "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", + "nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha && npm run clean:jstests", + "nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'", + "nobuild:unit": "npm run mocha:ts -- 'test/*.ts'", + "prettier": "prettier 'ts_src/**/*.ts' 'test/**/*.ts' --ignore-path ./.prettierignore", "prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" @@ -63,7 +66,10 @@ "wif": "^2.0.1" }, "devDependencies": { - "bip39": "^2.3.0", + "@types/bs58": "^4.0.0", + "@types/mocha": "^5.2.7", + "@types/proxyquire": "^1.3.28", + "bip39": "^3.0.2", "bip65": "^1.0.1", "bip68": "^1.0.3", "bn.js": "^4.11.8", @@ -71,12 +77,13 @@ "dhttp": "^3.0.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", - "mocha": "^5.2.0", + "mocha": "^6.2.0", "nyc": "^14.1.1", "prettier": "1.16.4", "proxyquire": "^2.0.1", "regtest-client": "0.2.0", "rimraf": "^2.6.3", + "ts-node": "^8.3.0", "tslint": "^5.16.0", "typescript": "3.2.2" }, diff --git a/test/address.spec.ts b/test/address.spec.ts index be16879..b1304c3 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -1,134 +1,148 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') -const fixtures = require('./fixtures/address.json') -const NETWORKS = Object.assign({ - litecoin: { - messagePrefix: '\x19Litecoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as baddress from '../src/address'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/address.json'; + +const NETWORKS = Object.assign( + { + litecoin: { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe, + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0, }, - pubKeyHash: 0x30, - scriptHash: 0x32, - wif: 0xb0 - } -}, require('../src/networks')) + }, + require('../src/networks'), +); describe('address', () => { describe('fromBase58Check', () => { fixtures.standard.forEach(f => { - if (!f.base58check) return + if (!f.base58check) return; it('decodes ' + f.base58check, () => { - const decode = baddress.fromBase58Check(f.base58check) + const decode = baddress.fromBase58Check(f.base58check); - assert.strictEqual(decode.version, f.version) - assert.strictEqual(decode.hash.toString('hex'), f.hash) - }) - }) + assert.strictEqual(decode.version, f.version); + assert.strictEqual(decode.hash.toString('hex'), f.hash); + }); + }); fixtures.invalid.fromBase58Check.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - baddress.fromBase58Check(f.address) - }, new RegExp(f.address + ' ' + f.exception)) - }) - }) - }) + baddress.fromBase58Check(f.address); + }, new RegExp(f.address + ' ' + f.exception)); + }); + }); + }); describe('fromBech32', () => { fixtures.standard.forEach(f => { - if (!f.bech32) return + if (!f.bech32) return; it('decodes ' + f.bech32, () => { - const actual = baddress.fromBech32(f.bech32) + const actual = baddress.fromBech32(f.bech32); - assert.strictEqual(actual.version, f.version) - assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32) - assert.strictEqual(actual.data.toString('hex'), f.data) - }) - }) + assert.strictEqual(actual.version, f.version); + assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32); + assert.strictEqual(actual.data.toString('hex'), f.data); + }); + }); - fixtures.invalid.bech32.forEach((f, i) => { - it('decode fails for ' + f.bech32 + '(' + f.exception + ')', () => { + fixtures.invalid.bech32.forEach(f => { + it('decode fails for ' + f.address + '(' + f.exception + ')', () => { assert.throws(() => { - baddress.fromBech32(f.address) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.fromBech32(f.address); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromOutputScript', () => { fixtures.standard.forEach(f => { it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { - const script = bscript.fromASM(f.script) - const address = baddress.fromOutputScript(script, NETWORKS[f.network]) + const script = bscript.fromASM(f.script); + const address = baddress.fromOutputScript(script, NETWORKS[f.network]); - assert.strictEqual(address, f.base58check || f.bech32.toLowerCase()) - }) - }) + assert.strictEqual(address, f.base58check || f.bech32!.toLowerCase()); + }); + }); fixtures.invalid.fromOutputScript.forEach(f => { it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, () => { - const script = bscript.fromASM(f.script) + const script = bscript.fromASM(f.script); assert.throws(() => { - baddress.fromOutputScript(script) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.fromOutputScript(script); + }, new RegExp(f.exception)); + }); + }); + }); describe('toBase58Check', () => { fixtures.standard.forEach(f => { - if (!f.base58check) return + if (!f.base58check) return; it('encodes ' + f.hash + ' (' + f.network + ')', () => { - const address = baddress.toBase58Check(Buffer.from(f.hash, 'hex'), f.version) + const address = baddress.toBase58Check( + Buffer.from(f.hash, 'hex'), + f.version, + ); - assert.strictEqual(address, f.base58check) - }) - }) - }) + assert.strictEqual(address, f.base58check); + }); + }); + }); describe('toBech32', () => { - fixtures.bech32.forEach((f, i) => { - if (!f.bech32) return - const data = Buffer.from(f.data, 'hex') + fixtures.bech32.forEach(f => { + if (!f.address) return; + const data = Buffer.from(f.data, 'hex'); it('encode ' + f.address, () => { - assert.deepStrictEqual(baddress.toBech32(data, f.version, f.prefix), f.address) - }) - }) + assert.deepStrictEqual( + baddress.toBech32(data, f.version, f.prefix), + f.address.toLowerCase(), + ); + }); + }); - fixtures.invalid.bech32.forEach((f, i) => { - if (!f.prefix || f.version === undefined || f.data === undefined) return + // TODO: These fixtures (according to TypeScript) have none of the data used below + fixtures.invalid.bech32.forEach((f: any) => { + if (!f.prefix || f.version === undefined || f.data === undefined) return; it('encode fails (' + f.exception, () => { assert.throws(() => { - baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix); + }, new RegExp(f.exception)); + }); + }); + }); describe('toOutputScript', () => { fixtures.standard.forEach(f => { it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { - const script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) + const script = baddress.toOutputScript( + (f.base58check || f.bech32)!, + NETWORKS[f.network], + ); - assert.strictEqual(bscript.toASM(script), f.script) - }) - }) + assert.strictEqual(bscript.toASM(script), f.script); + }); + }); fixtures.invalid.toOutputScript.forEach(f => { it('throws when ' + f.exception, () => { assert.throws(() => { - baddress.toOutputScript(f.address, f.network) - }, new RegExp(f.address + ' ' + f.exception)) - }) - }) - }) -}) + baddress.toOutputScript(f.address, f.network as any); + }, new RegExp(f.address + ' ' + f.exception)); + }); + }); + }); +}); diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts index 734c9a9..55b16ac 100644 --- a/test/bitcoin.core.spec.ts +++ b/test/bitcoin.core.spec.ts @@ -1,226 +1,251 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const base58 = require('bs58') -const bitcoin = require('../') - -const base58EncodeDecode = require('./fixtures/core/base58_encode_decode.json') -const base58KeysInvalid = require('./fixtures/core/base58_keys_invalid.json') -const base58KeysValid = require('./fixtures/core/base58_keys_valid.json') -const blocksValid = require('./fixtures/core/blocks.json') -const sigCanonical = require('./fixtures/core/sig_canonical.json') -const sigHash = require('./fixtures/core/sighash.json') -const sigNoncanonical = require('./fixtures/core/sig_noncanonical.json') -const txValid = require('./fixtures/core/tx_valid.json') +import * as assert from 'assert'; +import * as base58 from 'bs58'; +import { describe, it } from 'mocha'; +import * as bitcoin from '..'; +import * as base58EncodeDecode from './fixtures/core/base58_encode_decode.json'; +import * as base58KeysInvalid from './fixtures/core/base58_keys_invalid.json'; +import * as base58KeysValid from './fixtures/core/base58_keys_valid.json'; +import * as blocksValid from './fixtures/core/blocks.json'; +import * as sigCanonical from './fixtures/core/sig_canonical.json'; +import * as sigHash from './fixtures/core/sighash.json'; +import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json'; +import * as txValid from './fixtures/core/tx_valid.json'; describe('Bitcoin-core', () => { // base58EncodeDecode describe('base58', () => { base58EncodeDecode.forEach(f => { - const fhex = f[0] - const fb58 = f[1] + const fhex = f[0]; + const fb58 = f[1]; it('can decode ' + fb58, () => { - const buffer = base58.decode(fb58) - const actual = buffer.toString('hex') + const buffer = base58.decode(fb58); + const actual = buffer.toString('hex'); - assert.strictEqual(actual, fhex) - }) + assert.strictEqual(actual, fhex); + }); it('can encode ' + fhex, () => { - const buffer = Buffer.from(fhex, 'hex') - const actual = base58.encode(buffer) + const buffer = Buffer.from(fhex, 'hex'); + const actual = base58.encode(buffer); - assert.strictEqual(actual, fb58) - }) - }) - }) + assert.strictEqual(actual, fb58); + }); + }); + }); // base58KeysValid describe('address.toBase58Check', () => { - const typeMap = { - 'pubkey': 'pubKeyHash', - 'script': 'scriptHash' - } + const typeMap: any = { + pubkey: 'pubKeyHash', + script: 'scriptHash', + }; base58KeysValid.forEach(f => { - const expected = f[0] - const hash = Buffer.from(f[1], 'hex') - const params = f[2] + const expected = f[0]; + const hash = Buffer.from(f[1] as any, 'hex'); + const params = f[2] as any; - if (params.isPrivkey) return + if (params.isPrivkey) return; - const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - const version = network[typeMap[params.addrType]] + const network: any = params.isTestnet + ? bitcoin.networks.testnet + : bitcoin.networks.bitcoin; + const version = network[typeMap[params.addrType]]; it('can export ' + expected, () => { - assert.strictEqual(bitcoin.address.toBase58Check(hash, version), expected) - }) - }) - }) + assert.strictEqual( + bitcoin.address.toBase58Check(hash, version), + expected, + ); + }); + }); + }); // base58KeysInvalid describe('address.fromBase58Check', () => { const allowedNetworks = [ - bitcoin.networks.bitcoin.pubkeyhash, - bitcoin.networks.bitcoin.scripthash, - bitcoin.networks.testnet.pubkeyhash, - bitcoin.networks.testnet.scripthash - ] + bitcoin.networks.bitcoin.pubKeyHash, + bitcoin.networks.bitcoin.scriptHash, + bitcoin.networks.testnet.pubKeyHash, + bitcoin.networks.testnet.scriptHash, + ]; base58KeysInvalid.forEach(f => { - const string = f[0] + const string = f[0]; it('throws on ' + string, () => { assert.throws(() => { - const address = bitcoin.address.fromBase58Check(string) + const address = bitcoin.address.fromBase58Check(string); - assert.notStrictEqual(allowedNetworks.indexOf(address.version), -1, 'Invalid network') - }, /(Invalid (checksum|network))|(too (short|long))/) - }) - }) - }) + assert.notStrictEqual( + allowedNetworks.indexOf(address.version), + -1, + 'Invalid network', + ); + }, /(Invalid (checksum|network))|(too (short|long))/); + }); + }); + }); // base58KeysValid describe('ECPair', () => { base58KeysValid.forEach(f => { - const string = f[0] - const hex = f[1] - const params = f[2] + const strng = f[0] as string; + const hex = f[1]; + const params = f[2] as any; - if (!params.isPrivkey) return + if (!params.isPrivkey) return; - const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - const keyPair = bitcoin.ECPair.fromWIF(string, network) + const network = params.isTestnet + ? bitcoin.networks.testnet + : bitcoin.networks.bitcoin; + const keyPair = bitcoin.ECPair.fromWIF(strng, network); - it('fromWIF imports ' + string, () => { - assert.strictEqual(keyPair.privateKey.toString('hex'), hex) - assert.strictEqual(keyPair.compressed, params.isCompressed) - }) + it('fromWIF imports ' + strng, () => { + assert.strictEqual(keyPair.privateKey!.toString('hex'), hex); + assert.strictEqual(keyPair.compressed, params.isCompressed); + }); - it('toWIF exports ' + hex + ' to ' + string, () => { - assert.strictEqual(keyPair.toWIF(), string) - }) - }) - }) + it('toWIF exports ' + hex + ' to ' + strng, () => { + assert.strictEqual(keyPair.toWIF(), strng); + }); + }); + }); // base58KeysInvalid describe('ECPair.fromWIF', () => { const allowedNetworks = [ bitcoin.networks.bitcoin, - bitcoin.networks.testnet - ] + bitcoin.networks.testnet, + ]; base58KeysInvalid.forEach(f => { - const string = f[0] + const string = f[0]; it('throws on ' + string, () => { assert.throws(() => { - bitcoin.ECPair.fromWIF(string, allowedNetworks) - }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/) - }) - }) - }) + bitcoin.ECPair.fromWIF(string, allowedNetworks); + }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/); + }); + }); + }); describe('Block.fromHex', () => { blocksValid.forEach(f => { it('can parse ' + f.id, () => { - const block = bitcoin.Block.fromHex(f.hex) + const block = bitcoin.Block.fromHex(f.hex); - assert.strictEqual(block.getId(), f.id) - assert.strictEqual(block.transactions.length, f.transactions) - }) - }) - }) + assert.strictEqual(block.getId(), f.id); + assert.strictEqual(block.transactions!.length, f.transactions); + }); + }); + }); // txValid describe('Transaction.fromHex', () => { txValid.forEach(f => { // Objects that are only a single string are ignored - if (f.length === 1) return + if (f.length === 1) return; - const inputs = f[0] - const fhex = f[1] + const inputs = f[0]; + const fhex = f[1]; // const verifyFlags = f[2] // TODO: do we need to test this? it('can decode ' + fhex, () => { - const transaction = bitcoin.Transaction.fromHex(fhex) + const transaction = bitcoin.Transaction.fromHex(fhex as string); transaction.ins.forEach((txIn, i) => { - const input = inputs[i] + const input = inputs[i]; // reverse because test data is reversed - const prevOutHash = Buffer.from(input[0], 'hex').reverse() - const prevOutIndex = input[1] + const prevOutHash = Buffer.from(input[0] as string, 'hex').reverse(); + const prevOutIndex = input[1]; - assert.deepStrictEqual(txIn.hash, prevOutHash) + assert.deepStrictEqual(txIn.hash, prevOutHash); // we read UInt32, not Int32 - assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex) - }) - }) - }) - }) + assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex); + }); + }); + }); + }); // sighash describe('Transaction', () => { sigHash.forEach(f => { // Objects that are only a single string are ignored - if (f.length === 1) return + if (f.length === 1) return; - const txHex = f[0] - const scriptHex = f[1] - const inIndex = f[2] - const hashType = f[3] - const expectedHash = f[4] + const txHex = f[0] as string; + const scriptHex = f[1] as string; + const inIndex = f[2] as number; + const hashType = f[3] as number; + const expectedHash = f[4]; - const hashTypes = [] - if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE) hashTypes.push('SIGHASH_NONE') - else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE) hashTypes.push('SIGHASH_SINGLE') - else hashTypes.push('SIGHASH_ALL') - if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY) hashTypes.push('SIGHASH_ANYONECANPAY') + const hashTypes = []; + if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE) + hashTypes.push('SIGHASH_NONE'); + else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE) + hashTypes.push('SIGHASH_SINGLE'); + else hashTypes.push('SIGHASH_ALL'); + if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY) + hashTypes.push('SIGHASH_ANYONECANPAY'); - const hashTypeName = hashTypes.join(' | ') + const hashTypeName = hashTypes.join(' | '); - it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', () => { - const transaction = bitcoin.Transaction.fromHex(txHex) - assert.strictEqual(transaction.toHex(), txHex) + it( + 'should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', + () => { + const transaction = bitcoin.Transaction.fromHex(txHex); + assert.strictEqual(transaction.toHex(), txHex); - const script = Buffer.from(scriptHex, 'hex') - const scriptChunks = bitcoin.script.decompile(script) - assert.strictEqual(bitcoin.script.compile(scriptChunks).toString('hex'), scriptHex) + const script = Buffer.from(scriptHex, 'hex'); + const scriptChunks = bitcoin.script.decompile(script); + assert.strictEqual( + bitcoin.script.compile(scriptChunks!).toString('hex'), + scriptHex, + ); - const hash = transaction.hashForSignature(inIndex, script, hashType) + const hash = transaction.hashForSignature(inIndex, script, hashType); - // reverse because test data is reversed - assert.strictEqual(hash.reverse().toString('hex'), expectedHash) - }) - }) - }) + // reverse because test data is reversed + assert.strictEqual( + (hash.reverse() as Buffer).toString('hex'), + expectedHash, + ); + }, + ); + }); + }); describe('script.signature.decode', () => { sigCanonical.forEach(hex => { - const buffer = Buffer.from(hex, 'hex') + const buffer = Buffer.from(hex, 'hex'); it('can parse ' + hex, () => { - const parsed = bitcoin.script.signature.decode(buffer) - const actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) + const parsed = bitcoin.script.signature.decode(buffer); + const actual = bitcoin.script.signature.encode( + parsed.signature, + parsed.hashType, + ); - assert.strictEqual(actual.toString('hex'), hex) - }) - }) + assert.strictEqual(actual.toString('hex'), hex); + }); + }); sigNoncanonical.forEach((hex, i) => { - if (i === 0) return - if (i % 2 !== 0) return + if (i === 0) return; + if (i % 2 !== 0) return; - const description = sigNoncanonical[i - 1].slice(0, -1) - const buffer = Buffer.from(hex, 'hex') + const description = sigNoncanonical[i - 1].slice(0, -1); + const buffer = Buffer.from(hex, 'hex'); it('throws on ' + description, () => { assert.throws(() => { - bitcoin.script.signature.decode(buffer) - }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/) - }) - }) - }) -}) + bitcoin.script.signature.decode(buffer); + }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/); + }); + }); + }); +}); diff --git a/test/block.spec.ts b/test/block.spec.ts index d5e41af..6f8ed03 100644 --- a/test/block.spec.ts +++ b/test/block.spec.ts @@ -1,157 +1,172 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const Block = require('..').Block +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import { Block } from '..'; -const fixtures = require('./fixtures/block') +import * as fixtures from './fixtures/block.json'; describe('Block', () => { describe('version', () => { it('should be interpreted as an int32le', () => { - const blockHex = 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000' - const block = Block.fromHex(blockHex) - assert.strictEqual(-1, block.version) - assert.strictEqual(1, block.timestamp) - }) - }) + const blockHex = + 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000'; + const block = Block.fromHex(blockHex); + assert.strictEqual(-1, block.version); + assert.strictEqual(1, block.timestamp); + }); + }); describe('calculateTarget', () => { fixtures.targets.forEach(f => { it('returns ' + f.expected + ' for 0x' + f.bits, () => { - const bits = parseInt(f.bits, 16) + const bits = parseInt(f.bits, 16); - assert.strictEqual(Block.calculateTarget(bits).toString('hex'), f.expected) - }) - }) - }) + assert.strictEqual( + Block.calculateTarget(bits).toString('hex'), + f.expected, + ); + }); + }); + }); describe('fromBuffer/fromHex', () => { fixtures.valid.forEach(f => { it('imports ' + f.description, () => { - const block = Block.fromHex(f.hex) + const block = Block.fromHex(f.hex); - assert.strictEqual(block.version, f.version) - assert.strictEqual(block.prevHash.toString('hex'), f.prevHash) - assert.strictEqual(block.merkleRoot.toString('hex'), f.merkleRoot) + assert.strictEqual(block.version, f.version); + assert.strictEqual(block.prevHash!.toString('hex'), f.prevHash); + assert.strictEqual(block.merkleRoot!.toString('hex'), f.merkleRoot); if (block.witnessCommit) { - assert.strictEqual(block.witnessCommit.toString('hex'), f.witnessCommit) + assert.strictEqual( + block.witnessCommit.toString('hex'), + f.witnessCommit, + ); } - assert.strictEqual(block.timestamp, f.timestamp) - assert.strictEqual(block.bits, f.bits) - assert.strictEqual(block.nonce, f.nonce) - assert.strictEqual(!block.transactions, f.hex.length === 160) - }) - }) + assert.strictEqual(block.timestamp, f.timestamp); + assert.strictEqual(block.bits, f.bits); + assert.strictEqual(block.nonce, f.nonce); + assert.strictEqual(!block.transactions, f.hex.length === 160); + }); + }); fixtures.invalid.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - Block.fromHex(f.hex) - }, new RegExp(f.exception)) - }) - }) - }) + Block.fromHex(f.hex); + }, new RegExp(f.exception)); + }); + }); + }); describe('toBuffer/toHex', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('exports ' + f.description, () => { - assert.strictEqual(block.toHex(true), f.hex.slice(0, 160)) - assert.strictEqual(block.toHex(), f.hex) - }) - }) - }) + assert.strictEqual(block.toHex(true), f.hex.slice(0, 160)); + assert.strictEqual(block.toHex(), f.hex); + }); + }); + }); describe('getHash/getId', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.id + ' for ' + f.description, () => { - assert.strictEqual(block.getHash().toString('hex'), f.hash) - assert.strictEqual(block.getId(), f.id) - }) - }) - }) + assert.strictEqual(block.getHash().toString('hex'), f.hash); + assert.strictEqual(block.getId(), f.id); + }); + }); + }); describe('getUTCDate', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns UTC date of ' + f.id, () => { - const utcDate = block.getUTCDate().getTime() + const utcDate = block.getUTCDate().getTime(); - assert.strictEqual(utcDate, f.timestamp * 1e3) - }) - }) - }) + assert.strictEqual(utcDate, f.timestamp * 1e3); + }); + }); + }); describe('calculateMerkleRoot', () => { it('should throw on zero-length transaction array', () => { assert.throws(() => { - Block.calculateMerkleRoot([]) - }, /Cannot compute merkle root for zero transactions/) - }) + Block.calculateMerkleRoot([]); + }, /Cannot compute merkle root for zero transactions/); + }); fixtures.valid.forEach(f => { - if (f.hex.length === 160) return + if (f.hex.length === 160) return; - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.merkleRoot + ' for ' + f.id, () => { - assert.strictEqual(Block.calculateMerkleRoot(block.transactions).toString('hex'), f.merkleRoot) - }) + assert.strictEqual( + Block.calculateMerkleRoot(block.transactions!).toString('hex'), + f.merkleRoot, + ); + }); if (f.witnessCommit) { it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, () => { - assert.strictEqual(Block.calculateMerkleRoot(block.transactions, true).toString('hex'), f.witnessCommit) - }) + assert.strictEqual( + Block.calculateMerkleRoot(block.transactions!, true).toString( + 'hex', + ), + f.witnessCommit, + ); + }); } - }) - }) + }); + }); describe('checkTxRoots', () => { fixtures.valid.forEach(f => { - if (f.hex.length === 160) return + if (f.hex.length === 160) return; - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.valid + ' for ' + f.id, () => { - assert.strictEqual(block.checkTxRoots(), true) - }) - }) - }) + assert.strictEqual(block.checkTxRoots(), true); + }); + }); + }); describe('checkProofOfWork', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.valid + ' for ' + f.id, () => { - assert.strictEqual(block.checkProofOfWork(), f.valid) - }) - }) - }) -}) + assert.strictEqual(block.checkProofOfWork(), f.valid); + }); + }); + }); +}); diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 1a38406..4308af9 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -1,49 +1,49 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bufferutils = require('../src/bufferutils') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bufferutils from '../src/bufferutils'; -const fixtures = require('./fixtures/bufferutils.json') +import * as fixtures from './fixtures/bufferutils.json'; describe('bufferutils', () => { describe('readUInt64LE', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { - const buffer = Buffer.from(f.hex, 'hex') - const number = bufferutils.readUInt64LE(buffer, 0) + const buffer = Buffer.from(f.hex, 'hex'); + const number = bufferutils.readUInt64LE(buffer, 0); - assert.strictEqual(number, f.dec) - }) - }) + assert.strictEqual(number, f.dec); + }); + }); fixtures.invalid.readUInt64LE.forEach(f => { it('throws on ' + f.description, () => { - const buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex'); assert.throws(() => { - bufferutils.readUInt64LE(buffer, 0) - }, new RegExp(f.exception)) - }) - }) - }) + bufferutils.readUInt64LE(buffer, 0); + }, new RegExp(f.exception)); + }); + }); + }); describe('writeUInt64LE', () => { fixtures.valid.forEach(f => { it('encodes ' + f.dec, () => { - const buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0); - bufferutils.writeUInt64LE(buffer, f.dec, 0) - assert.strictEqual(buffer.toString('hex'), f.hex) - }) - }) + bufferutils.writeUInt64LE(buffer, f.dec, 0); + assert.strictEqual(buffer.toString('hex'), f.hex); + }); + }); fixtures.invalid.readUInt64LE.forEach(f => { it('throws on ' + f.description, () => { - const buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0); assert.throws(() => { - bufferutils.writeUInt64LE(buffer, f.dec, 0) - }, new RegExp(f.exception)) - }) - }) - }) -}) + bufferutils.writeUInt64LE(buffer, f.dec, 0); + }, new RegExp(f.exception)); + }); + }); + }); +}); diff --git a/test/classify.spec.ts b/test/classify.spec.ts index 86da74d..b2464c0 100644 --- a/test/classify.spec.ts +++ b/test/classify.spec.ts @@ -1,18 +1,18 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const classify = require('../src/classify') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bscript from '../src/script'; +import * as classify from '../src/classify'; -const fixtures = require('./fixtures/templates.json') +import * as fixtures from './fixtures/templates.json'; -const multisig = require('../src/templates/multisig') -const nullData = require('../src/templates/nulldata') -const pubKey = require('../src/templates/pubkey') -const pubKeyHash = require('../src/templates/pubkeyhash') -const scriptHash = require('../src/templates/scripthash') -const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash') -const witnessScriptHash = require('../src/templates/witnessscripthash') -const witnessCommitment = require('../src/templates/witnesscommitment') +import * as multisig from '../src/templates/multisig'; +import * as nullData from '../src/templates/nulldata'; +import * as pubKey from '../src/templates/pubkey'; +import * as pubKeyHash from '../src/templates/pubkeyhash'; +import * as scriptHash from '../src/templates/scripthash'; +import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash'; +import * as witnessScriptHash from '../src/templates/witnessscripthash'; +import * as witnessCommitment from '../src/templates/witnesscommitment'; const tmap = { pubKey, @@ -22,49 +22,48 @@ const tmap = { witnessScriptHash, multisig, nullData, - witnessCommitment -} + witnessCommitment, +}; describe('classify', () => { describe('input', () => { fixtures.valid.forEach(f => { - if (!f.input) return + if (!f.input) return; it('classifies ' + f.input + ' as ' + f.type, () => { - const input = bscript.fromASM(f.input) - const type = classify.input(input) + const input = bscript.fromASM(f.input); + const type = classify.input(input); - assert.strictEqual(type, f.type) - }) - }) + assert.strictEqual(type, f.type); + }); + }); fixtures.valid.forEach(f => { - if (!f.input) return - if (!f.typeIncomplete) return + if (!f.input) return; + if (!f.typeIncomplete) return; it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, () => { - const input = bscript.fromASM(f.input) - const type = classify.input(input, true) + const input = bscript.fromASM(f.input); + const type = classify.input(input, true); - assert.strictEqual(type, f.typeIncomplete) - }) - }) - }) + assert.strictEqual(type, f.typeIncomplete); + }); + }); + }); describe('classifyOutput', () => { fixtures.valid.forEach(f => { - if (!f.output) return + if (!f.output) return; it('classifies ' + f.output + ' as ' + f.type, () => { - const output = bscript.fromASM(f.output) - const type = classify.output(output) + const output = bscript.fromASM(f.output); + const type = classify.output(output); - assert.strictEqual(type, f.type) - }) - }) - }) - - ;[ + assert.strictEqual(type, f.type); + }); + }); + }); + [ 'pubKey', 'pubKeyHash', 'scriptHash', @@ -72,85 +71,110 @@ describe('classify', () => { 'witnessScriptHash', 'multisig', 'nullData', - 'witnessCommitment' + 'witnessCommitment', ].forEach(name => { - const inputType = tmap[name].input - const outputType = tmap[name].output + const inputType = (tmap as any)[name].input; + const outputType = (tmap as any)[name].output; describe(name + '.input.check', () => { fixtures.valid.forEach(f => { - if (name.toLowerCase() === classify.types.P2WPKH) return - if (name.toLowerCase() === classify.types.P2WSH) return - const expected = name.toLowerCase() === f.type.toLowerCase() + if (name.toLowerCase() === classify.types.P2WPKH) return; + if (name.toLowerCase() === classify.types.P2WSH) return; + const expected = name.toLowerCase() === f.type.toLowerCase(); if (inputType && f.input) { - const input = bscript.fromASM(f.input) + const input = bscript.fromASM(f.input); it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual(inputType.check(input), expected) - }) + assert.strictEqual(inputType.check(input), expected); + }); if (f.typeIncomplete) { - const expectedIncomplete = name.toLowerCase() === f.typeIncomplete + const expectedIncomplete = name.toLowerCase() === f.typeIncomplete; it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual(inputType.check(input, true), expectedIncomplete) - }) + assert.strictEqual( + inputType.check(input, true), + expectedIncomplete, + ); + }); } } - }) + }); - if (!(fixtures.invalid[name])) return + if (!(fixtures.invalid as any)[name]) return; - fixtures.invalid[name].inputs.forEach(f => { - if (!f.input && !f.inputHex) return + (fixtures.invalid as any)[name].inputs.forEach((f: any) => { + if (!f.input && !f.inputHex) return; - it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', () => { - let input + it( + 'returns false for ' + + f.description + + ' (' + + (f.input || f.inputHex) + + ')', + () => { + let input; - if (f.input) { - input = bscript.fromASM(f.input) - } else { - input = Buffer.from(f.inputHex, 'hex') - } + if (f.input) { + input = bscript.fromASM(f.input); + } else { + input = Buffer.from(f.inputHex, 'hex'); + } - assert.strictEqual(inputType.check(input), false) - }) - }) - }) + assert.strictEqual(inputType.check(input), false); + }, + ); + }); + }); describe(name + '.output.check', () => { fixtures.valid.forEach(f => { - const expected = name.toLowerCase() === f.type + const expected = name.toLowerCase() === f.type; if (outputType && f.output) { it('returns ' + expected + ' for ' + f.output, () => { - const output = bscript.fromASM(f.output) + const output = bscript.fromASM(f.output); - if (name.toLowerCase() === 'nulldata' && f.type === classify.types.WITNESS_COMMITMENT) return - if (name.toLowerCase() === 'witnesscommitment' && f.type === classify.types.NULLDATA) return - assert.strictEqual(outputType.check(output), expected) - }) + if ( + name.toLowerCase() === 'nulldata' && + f.type === classify.types.WITNESS_COMMITMENT + ) + return; + if ( + name.toLowerCase() === 'witnesscommitment' && + f.type === classify.types.NULLDATA + ) + return; + assert.strictEqual(outputType.check(output), expected); + }); } - }) + }); - if (!(fixtures.invalid[name])) return + if (!(fixtures.invalid as any)[name]) return; - fixtures.invalid[name].outputs.forEach(f => { - if (!f.output && !f.outputHex) return + (fixtures.invalid as any)[name].outputs.forEach((f: any) => { + if (!f.output && !f.outputHex) return; - it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', () => { - let output + it( + 'returns false for ' + + f.description + + ' (' + + (f.output || f.outputHex) + + ')', + () => { + let output; - if (f.output) { - output = bscript.fromASM(f.output) - } else { - output = Buffer.from(f.outputHex, 'hex') - } + if (f.output) { + output = bscript.fromASM(f.output); + } else { + output = Buffer.from(f.outputHex, 'hex'); + } - assert.strictEqual(outputType.check(output), false) - }) - }) - }) - }) -}) + assert.strictEqual(outputType.check(output), false); + }, + ); + }); + }); + }); +}); diff --git a/test/crypto.spec.ts b/test/crypto.spec.ts index 14b4f33..89ffabb 100644 --- a/test/crypto.spec.ts +++ b/test/crypto.spec.ts @@ -1,23 +1,22 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bcrypto = require('../src/crypto') - -const fixtures = require('./fixtures/crypto') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import { crypto as bcrypto } from '..'; +import * as fixtures from './fixtures/crypto.json'; describe('crypto', () => { ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => { describe(algorithm, () => { fixtures.forEach(f => { - const fn = bcrypto[algorithm] - const expected = f[algorithm] + const fn = (bcrypto as any)[algorithm]; + const expected = (f as any)[algorithm]; it('returns ' + expected + ' for ' + f.hex, () => { - const data = Buffer.from(f.hex, 'hex') - const actual = fn(data).toString('hex') + const data = Buffer.from(f.hex, 'hex'); + const actual = fn(data).toString('hex'); - assert.strictEqual(actual, expected) - }) - }) - }) - }) -}) + assert.strictEqual(actual, expected); + }); + }); + }); + }); +}); diff --git a/test/ecpair.spec.ts b/test/ecpair.spec.ts index e067ddd..b9b8538 100644 --- a/test/ecpair.spec.ts +++ b/test/ecpair.spec.ts @@ -1,284 +1,334 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const proxyquire = require('proxyquire') -const hoodwink = require('hoodwink') +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import * as proxyquire from 'proxyquire'; +import { ECPair, ECPairInterface, networks as NETWORKS } from '..'; +import * as fixtures from './fixtures/ecpair.json'; +const hoodwink = require('hoodwink'); +const tinysecp = require('tiny-secp256k1'); -const ECPair = require('../src/ecpair') -const tinysecp = require('tiny-secp256k1') - -const fixtures = require('./fixtures/ecpair.json') - -const NETWORKS = require('../src/networks') -const NETWORKS_LIST = [] // Object.values(NETWORKS) -for (let networkName in NETWORKS) { - NETWORKS_LIST.push(NETWORKS[networkName]) -} - -const ZERO = Buffer.alloc(32, 0) -const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -const GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') -const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') +const NETWORKS_LIST = Object.values(NETWORKS); +const ZERO = Buffer.alloc(32, 0); +const ONE = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', +); +const GROUP_ORDER = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', + 'hex', +); +const GROUP_ORDER_LESS_1 = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', + 'hex', +); describe('ECPair', () => { describe('getPublicKey', () => { - let keyPair + let keyPair: ECPairInterface; beforeEach(() => { - keyPair = ECPair.fromPrivateKey(ONE) - }) + keyPair = ECPair.fromPrivateKey(ONE); + }); - it('calls pointFromScalar lazily', hoodwink(() => { - assert.strictEqual(keyPair.__Q, undefined) + it( + 'calls pointFromScalar lazily', + hoodwink(() => { + assert.strictEqual((keyPair as any).__Q, undefined); - // .publicKey forces the memoization - assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - })) - }) + // .publicKey forces the memoization + assert.strictEqual( + keyPair.publicKey.toString('hex'), + '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + ); + assert.strictEqual( + (keyPair as any).__Q.toString('hex'), + '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + ); + }), + ); + }); describe('fromPrivateKey', () => { it('defaults to compressed', () => { - const keyPair = ECPair.fromPrivateKey(ONE) + const keyPair = ECPair.fromPrivateKey(ONE); - assert.strictEqual(keyPair.compressed, true) - }) + assert.strictEqual(keyPair.compressed, true); + }); it('supports the uncompressed option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { - compressed: false - }) + compressed: false, + }); - assert.strictEqual(keyPair.compressed, false) - }) + assert.strictEqual(keyPair.compressed, false); + }); it('supports the network option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false, - network: NETWORKS.testnet - }) + network: NETWORKS.testnet, + }); - assert.strictEqual(keyPair.network, NETWORKS.testnet) - }) + assert.strictEqual(keyPair.network, NETWORKS.testnet); + }); fixtures.valid.forEach(f => { it('derives public key for ' + f.WIF, () => { - const d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex'); const keyPair = ECPair.fromPrivateKey(d, { - compressed: f.compressed - }) + compressed: f.compressed, + }); - assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q) - }) - }) + assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q); + }); + }); fixtures.invalid.fromPrivateKey.forEach(f => { it('throws ' + f.exception, () => { - const d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex'); assert.throws(() => { - ECPair.fromPrivateKey(d, f.options) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromPrivateKey(d, (f as any).options); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromPublicKey', () => { fixtures.invalid.fromPublicKey.forEach(f => { it('throws ' + f.exception, () => { - const Q = Buffer.from(f.Q, 'hex') + const Q = Buffer.from(f.Q, 'hex'); assert.throws(() => { - ECPair.fromPublicKey(Q, f.options) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromPublicKey(Q, (f as any).options); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromWIF', () => { fixtures.valid.forEach(f => { it('imports ' + f.WIF + ' (' + f.network + ')', () => { - const network = NETWORKS[f.network] - const keyPair = ECPair.fromWIF(f.WIF, network) + const network = (NETWORKS as any)[f.network]; + const keyPair = ECPair.fromWIF(f.WIF, network); - assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) - assert.strictEqual(keyPair.compressed, f.compressed) - assert.strictEqual(keyPair.network, network) - }) - }) + assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); + assert.strictEqual(keyPair.compressed, f.compressed); + assert.strictEqual(keyPair.network, network); + }); + }); fixtures.valid.forEach(f => { it('imports ' + f.WIF + ' (via list of networks)', () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) - assert.strictEqual(keyPair.compressed, f.compressed) - assert.strictEqual(keyPair.network, NETWORKS[f.network]) - }) - }) + assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); + assert.strictEqual(keyPair.compressed, f.compressed); + assert.strictEqual(keyPair.network, (NETWORKS as any)[f.network]); + }); + }); fixtures.invalid.fromWIF.forEach(f => { it('throws on ' + f.WIF, () => { assert.throws(() => { - const networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST + const networks = f.network + ? (NETWORKS as any)[f.network] + : NETWORKS_LIST; - ECPair.fromWIF(f.WIF, networks) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromWIF(f.WIF, networks); + }, new RegExp(f.exception)); + }); + }); + }); describe('toWIF', () => { fixtures.valid.forEach(f => { it('exports ' + f.WIF, () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - const result = keyPair.toWIF() - assert.strictEqual(result, f.WIF) - }) - }) - }) + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); + const result = keyPair.toWIF(); + assert.strictEqual(result, f.WIF); + }); + }); + }); describe('makeRandom', () => { - const d = Buffer.alloc(32, 4) - const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' + const d = Buffer.alloc(32, 4); + const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'; describe('uses randombytes RNG', () => { it('generates a ECPair', () => { - const stub = { randombytes: () => { return d } } - const ProxiedECPair = proxyquire('../src/ecpair', stub) + const stub = { + randombytes: (): Buffer => { + return d; + }, + }; + const ProxiedECPair = proxyquire('../src/ecpair', stub); - const keyPair = ProxiedECPair.makeRandom() - assert.strictEqual(keyPair.toWIF(), exWIF) - }) - }) + const keyPair = ProxiedECPair.makeRandom(); + assert.strictEqual(keyPair.toWIF(), exWIF); + }); + }); it('allows a custom RNG to be used', () => { const keyPair = ECPair.makeRandom({ - rng: size => { return d.slice(0, size) } - }) + rng: (size): Buffer => { + return d.slice(0, size); + }, + }); - assert.strictEqual(keyPair.toWIF(), exWIF) - }) + assert.strictEqual(keyPair.toWIF(), exWIF); + }); it('retains the same defaults as ECPair constructor', () => { - const keyPair = ECPair.makeRandom() + const keyPair = ECPair.makeRandom(); - assert.strictEqual(keyPair.compressed, true) - assert.strictEqual(keyPair.network, NETWORKS.bitcoin) - }) + assert.strictEqual(keyPair.compressed, true); + assert.strictEqual(keyPair.network, NETWORKS.bitcoin); + }); it('supports the options parameter', () => { const keyPair = ECPair.makeRandom({ compressed: false, - network: NETWORKS.testnet - }) + network: NETWORKS.testnet, + }); - assert.strictEqual(keyPair.compressed, false) - assert.strictEqual(keyPair.network, NETWORKS.testnet) - }) + assert.strictEqual(keyPair.compressed, false); + assert.strictEqual(keyPair.network, NETWORKS.testnet); + }); it('throws if d is bad length', () => { - function rng () { - return Buffer.alloc(28) + function rng(): Buffer { + return Buffer.alloc(28); } assert.throws(() => { - ECPair.makeRandom({ rng: rng }) - }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) - }) + ECPair.makeRandom({ rng }); + }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/); + }); - it('loops until d is within interval [1, n) : 1', hoodwink(function () { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO // 0 - return ONE // >0 - }, 2) + it( + 'loops until d is within interval [1, n) : 1', + hoodwink(function(this: any): void { + const rng = this.stub(() => { + if (rng.calls === 0) return ZERO; // 0 + return ONE; // >0 + }, 2); - ECPair.makeRandom({ rng: rng }) - })) + ECPair.makeRandom({ rng }); + }), + ); - it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO // <1 - if (rng.calls === 1) return GROUP_ORDER // >n-1 - return GROUP_ORDER_LESS_1 // n-1 - }, 3) + it( + 'loops until d is within interval [1, n) : n - 1', + hoodwink(function(this: any): void { + const rng = this.stub(() => { + if (rng.calls === 0) return ZERO; // <1 + if (rng.calls === 1) return GROUP_ORDER; // >n-1 + return GROUP_ORDER_LESS_1; // n-1 + }, 3); - ECPair.makeRandom({ rng: rng }) - })) - }) + ECPair.makeRandom({ rng }); + }), + ); + }); describe('.network', () => { fixtures.valid.forEach(f => { it('returns ' + f.network + ' for ' + f.WIF, () => { - const network = NETWORKS[f.network] - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const network = (NETWORKS as any)[f.network]; + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - assert.strictEqual(keyPair.network, network) - }) - }) - }) + assert.strictEqual(keyPair.network, network); + }); + }); + }); describe('tinysecp wrappers', () => { - let keyPair - let hash - let signature + let keyPair: ECPairInterface; + let hash: Buffer; + let signature: Buffer; beforeEach(() => { - keyPair = ECPair.makeRandom() - hash = ZERO - signature = Buffer.alloc(64, 1) - }) + keyPair = ECPair.makeRandom(); + hash = ZERO; + signature = Buffer.alloc(64, 1); + }); describe('signing', () => { - it('wraps tinysecp.sign', hoodwink(function () { - this.mock(tinysecp, 'sign', (h, d) => { - assert.strictEqual(h, hash) - assert.strictEqual(d, keyPair.privateKey) - return signature - }, 1) + it( + 'wraps tinysecp.sign', + hoodwink(function(this: any): void { + this.mock( + tinysecp, + 'sign', + (h: any, d: any) => { + assert.strictEqual(h, hash); + assert.strictEqual(d, keyPair.privateKey); + return signature; + }, + 1, + ); - assert.strictEqual(keyPair.sign(hash), signature) - })) + assert.strictEqual(keyPair.sign(hash), signature); + }), + ); it('throws if no private key is found', () => { - delete keyPair.__D + delete (keyPair as any).__D; assert.throws(() => { - keyPair.sign(hash) - }, /Missing private key/) - }) - }) + keyPair.sign(hash); + }, /Missing private key/); + }); + }); describe('verify', () => { - it('wraps tinysecp.verify', hoodwink(function () { - this.mock(tinysecp, 'verify', (h, q, s) => { - assert.strictEqual(h, hash) - assert.strictEqual(q, keyPair.publicKey) - assert.strictEqual(s, signature) - return true - }, 1) + it( + 'wraps tinysecp.verify', + hoodwink(function(this: any): void { + this.mock( + tinysecp, + 'verify', + (h: any, q: any, s: any) => { + assert.strictEqual(h, hash); + assert.strictEqual(q, keyPair.publicKey); + assert.strictEqual(s, signature); + return true; + }, + 1, + ); - assert.strictEqual(keyPair.verify(hash, signature), true) - })) - }) - }) + assert.strictEqual(keyPair.verify(hash, signature), true); + }), + ); + }); + }); describe('optional low R signing', () => { - const sig = Buffer.from('95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + - 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + - '16b86f5229', 'hex') - const sigLowR = Buffer.from('6a2660c226e8055afad317eeba918a304be79208d505' + - '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + - '95d1fdde963c35', 'hex') - const lowRKeyPair = ECPair.fromWIF('L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + - 'ScpTPiYTxBynfZu') - const dataToSign = Buffer.from('b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + - '2c1ecf8871f5088ec204cfe', 'hex') + const sig = Buffer.from( + '95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + + 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + + '16b86f5229', + 'hex', + ); + const sigLowR = Buffer.from( + '6a2660c226e8055afad317eeba918a304be79208d505' + + '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + + '95d1fdde963c35', + 'hex', + ); + const lowRKeyPair = ECPair.fromWIF( + 'L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + 'ScpTPiYTxBynfZu', + ); + const dataToSign = Buffer.from( + 'b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + '2c1ecf8871f5088ec204cfe', + 'hex', + ); it('signs with normal R by default', () => { - const signed = lowRKeyPair.sign(dataToSign) - assert.deepStrictEqual(sig, signed) - }) + const signed = lowRKeyPair.sign(dataToSign); + assert.deepStrictEqual(sig, signed); + }); it('signs with low R when true is passed', () => { - const signed = lowRKeyPair.sign(dataToSign, true) - assert.deepStrictEqual(sigLowR, signed) - }) - }) -}) + const signed = lowRKeyPair.sign(dataToSign, true); + assert.deepStrictEqual(sigLowR, signed); + }); + }); +}); diff --git a/test/integration/_regtest.spec.ts b/test/integration/_regtest.spec.ts deleted file mode 100644 index c50ef2f..0000000 --- a/test/integration/_regtest.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -const { RegtestUtils } = require('regtest-client') - -const APIPASS = process.env.APIPASS || 'satoshi' -const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1' - -const regtestUtils = new RegtestUtils({ APIPASS, APIURL }) - -module.exports = regtestUtils; diff --git a/test/integration/_regtest.ts b/test/integration/_regtest.ts new file mode 100644 index 0000000..52a6be8 --- /dev/null +++ b/test/integration/_regtest.ts @@ -0,0 +1,6 @@ +import { RegtestUtils } from 'regtest-client'; + +const APIPASS = process.env.APIPASS || 'satoshi'; +const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1'; + +export const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts index 1d28020..49dc578 100644 --- a/test/integration/addresses.spec.ts +++ b/test/integration/addresses.spec.ts @@ -1,117 +1,137 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const dhttp = require('./_regtest').dhttp -const TESTNET = bitcoin.networks.testnet +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const dhttp = regtestUtils.dhttp; +const TESTNET = bitcoin.networks.testnet; describe('bitcoinjs-lib (addresses)', () => { it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async () => { - const keyPair = bitcoin.ECPair.makeRandom() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.makeRandom(); + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); // bitcoin P2PKH addresses start with a '1' - assert.strictEqual(address.startsWith('1'), true) + assert.strictEqual(address!.startsWith('1'), true); const result = await dhttp({ method: 'GET', - url: 'https://blockchain.info/rawaddr/' + address - }) + url: 'https://blockchain.info/rawaddr/' + address, + }); // random private keys [probably!] have no transactions - assert.strictEqual(result.n_tx, 0) - assert.strictEqual(result.total_received, 0) - assert.strictEqual(result.total_sent, 0) - }) + assert.strictEqual((result as any).n_tx, 0); + assert.strictEqual((result as any).total_received, 0); + assert.strictEqual((result as any).total_sent, 0); + }); it('can import an address via WIF', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); - assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH') - }) + assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'); + }); it('can generate a P2SH, pay-to-multisig (2-of-3) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', - '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map((hex) => Buffer.from(hex, 'hex')) + '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) - }) + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }), + }); - assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') - }) + assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7'); + }); it('can generate a SegWit address', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') - const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); + const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }); - assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') - }) + assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'); + }); it('can generate a SegWit address (via P2SH)', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) - }) + redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }), + }); - assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN') - }) + assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN'); + }); it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '023e4740d0ba639e28963f3476157b7cf2fb7c6fdf4254f97099cf8670b505ea59', - '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map((hex) => Buffer.from(hex, 'hex')) + '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2wsh({ - redeem: bitcoin.payments.p2ms({ m: 3, pubkeys }) - }) + redeem: bitcoin.payments.p2ms({ m: 3, pubkeys }), + }); - assert.strictEqual(address, 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul') - }) + assert.strictEqual( + address, + 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul', + ); + }); it('can generate a P2SH(P2WSH(...)), pay-to-multisig (2-of-2) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', - '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9' - ].map((hex) => Buffer.from(hex, 'hex')) + '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wsh({ - redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) - }) - }) + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }), + }), + }); - assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') - }) + assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN'); + }); // examples using other network information it('can generate a Testnet address', () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) + const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }); + const { address } = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: TESTNET, + }); // bitcoin testnet P2PKH addresses start with a 'm' or 'n' - assert.strictEqual(address.startsWith('m') || address.startsWith('n'), true) - }) + assert.strictEqual( + address!.startsWith('m') || address!.startsWith('n'), + true, + ); + }); it('can generate a Litecoin address', () => { // WARNING: although possible, bitcoinjs is NOT necessarily compatible with Litecoin const LITECOIN = { messagePrefix: '\x19Litecoin Signed Message:\n', + bech32: 'ltc', bip32: { public: 0x019da462, - private: 0x019d9cfe + private: 0x019d9cfe, }, pubKeyHash: 0x30, scriptHash: 0x32, - wif: 0xb0 - } + wif: 0xb0, + }; - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }); + const { address } = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: LITECOIN, + }); - assert.strictEqual(address.startsWith('L'), true) - }) -}) + assert.strictEqual(address!.startsWith('L'), true); + }); +}); diff --git a/test/integration/bip32.spec.ts b/test/integration/bip32.spec.ts index 255669c..1279d78 100644 --- a/test/integration/bip32.spec.ts +++ b/test/integration/bip32.spec.ts @@ -1,101 +1,151 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bip32 = require('bip32') -const bip39 = require('bip39') -const bitcoin = require('../../') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bip32 from 'bip32'; +import * as bip39 from 'bip39'; +import * as bitcoin from '../..'; -function getAddress (node, network) { - return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address +function getAddress(node: any, network?: any): string { + return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address!; } describe('bitcoinjs-lib (BIP32)', () => { it('can import a BIP32 testnet xpriv and export to WIF', () => { - const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' - const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) + const xpriv = + 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK'; + const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet); - assert.strictEqual(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') - }) + assert.strictEqual( + node.toWIF(), + 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7', + ); + }); it('can export a BIP32 xpriv, then import it', () => { - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - const seed = bip39.mnemonicToSeed(mnemonic) - const node = bip32.fromSeed(seed) - const string = node.toBase58() - const restored = bip32.fromBase58(string) + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const node = bip32.fromSeed(seed); + const string = node.toBase58(); + const restored = bip32.fromBase58(string); - assert.strictEqual(getAddress(node), getAddress(restored)) // same public key - assert.strictEqual(node.toWIF(), restored.toWIF()) // same private key - }) + assert.strictEqual(getAddress(node), getAddress(restored)); // same public key + assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key + }); it('can export a BIP32 xpub', () => { - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - const seed = bip39.mnemonicToSeed(mnemonic) - const node = bip32.fromSeed(seed) - const string = node.neutered().toBase58() + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const node = bip32.fromSeed(seed); + const string = node.neutered().toBase58(); - assert.strictEqual(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') - }) + assert.strictEqual( + string, + 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n', + ); + }); it('can create a BIP32, bitcoin, account 0, external address', () => { - const path = "m/0'/0/0" - const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const path = "m/0'/0/0"; + const root = bip32.fromSeed( + Buffer.from( + 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex', + ), + ); - const child1 = root.derivePath(path) + const child1 = root.derivePath(path); // option 2, manually - const child1b = root.deriveHardened(0) - .derive(0) + const child1b = root + .deriveHardened(0) .derive(0) + .derive(0); - assert.strictEqual(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - assert.strictEqual(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - }) + assert.strictEqual( + getAddress(child1), + '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7', + ); + assert.strictEqual( + getAddress(child1b), + '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7', + ); + }); it('can create a BIP44, bitcoin, account 0, external address', () => { - const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const root = bip32.fromSeed( + Buffer.from( + 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex', + ), + ); - const child1 = root.derivePath("m/44'/0'/0'/0/0") + const child1 = root.derivePath("m/44'/0'/0'/0/0"); // option 2, manually - const child1b = root.deriveHardened(44) + const child1b = root + .deriveHardened(44) .deriveHardened(0) .deriveHardened(0) .derive(0) - .derive(0) + .derive(0); - assert.strictEqual(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - assert.strictEqual(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - }) + assert.strictEqual( + getAddress(child1), + '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au', + ); + assert.strictEqual( + getAddress(child1b), + '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au', + ); + }); it('can create a BIP49, bitcoin testnet, account 0, external address', () => { - const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' - const seed = bip39.mnemonicToSeed(mnemonic) - const root = bip32.fromSeed(seed) + const mnemonic = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const root = bip32.fromSeed(seed); - const path = "m/49'/1'/0'/0/0" - const child = root.derivePath(path) + const path = "m/49'/1'/0'/0/0"; + const child = root.derivePath(path); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }), - network: bitcoin.networks.testnet - }) - assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') - }) + redeem: bitcoin.payments.p2wpkh({ + pubkey: child.publicKey, + network: bitcoin.networks.testnet, + }), + network: bitcoin.networks.testnet, + }); + assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2'); + }); it('can use BIP39 to generate BIP32 addresses', () => { // var mnemonic = bip39.generateMnemonic() - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - assert(bip39.validateMnemonic(mnemonic)) + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + assert(bip39.validateMnemonic(mnemonic)); - const seed = bip39.mnemonicToSeed(mnemonic) - const root = bip32.fromSeed(seed) + const seed = bip39.mnemonicToSeedSync(mnemonic); + const root = bip32.fromSeed(seed); // receive addresses - assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') - assert.strictEqual(getAddress(root.derivePath("m/0'/0/1")), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') + assert.strictEqual( + getAddress(root.derivePath("m/0'/0/0")), + '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC', + ); + assert.strictEqual( + getAddress(root.derivePath("m/0'/0/1")), + '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL', + ); // change addresses - assert.strictEqual(getAddress(root.derivePath("m/0'/1/0")), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') - assert.strictEqual(getAddress(root.derivePath("m/0'/1/1")), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') - }) -}) + assert.strictEqual( + getAddress(root.derivePath("m/0'/1/0")), + '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn', + ); + assert.strictEqual( + getAddress(root.derivePath("m/0'/1/1")), + '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6', + ); + }); +}); diff --git a/test/integration/blocks.spec.ts b/test/integration/blocks.spec.ts index 044862d..5eed0fc 100644 --- a/test/integration/blocks.spec.ts +++ b/test/integration/blocks.spec.ts @@ -1,22 +1,21 @@ -'use strict' - -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; describe('bitcoinjs-lib (blocks)', () => { it('can extract a height from a CoinBase transaction', () => { // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 - const txHex = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000' - const tx = bitcoin.Transaction.fromHex(txHex) + const txHex = + '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000'; + const tx = bitcoin.Transaction.fromHex(txHex); - assert.strictEqual(tx.ins.length, 1) - const script = tx.ins[0].script + assert.strictEqual(tx.ins.length, 1); + const script = tx.ins[0].script; // bitcoin.script.decompile(script) // returns [] :( - assert.strictEqual(script[0], 0x03) - const heightBuffer = script.slice(1, 4) - const height = bitcoin.script.number.decode(heightBuffer) - assert.strictEqual(height, 498303) - }) -}) + assert.strictEqual(script[0], 0x03); + const heightBuffer = script.slice(1, 4); + const height = bitcoin.script.number.decode(heightBuffer); + assert.strictEqual(height, 498303); + }); +}); diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index e123652..afdcaa5 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -1,198 +1,219 @@ -const { describe, it, before } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -const bip65 = require('bip65') +import * as assert from 'assert'; +import { before, describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +const bip65 = require('bip65'); -const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -console.warn = () => {} // Silence the Deprecation Warning +const alice = bitcoin.ECPair.fromWIF( + 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', + regtest, +); +const bob = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', + regtest, +); +console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // force update MTP before(async () => { - await regtestUtils.mine(11) - }) + await regtestUtils.mine(11); + }); - const hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL; - function cltvCheckSigOutput (aQ, bQ, lockTime) { - return bitcoin.script.compile([ - bitcoin.opcodes.OP_IF, - bitcoin.script.number.encode(lockTime), - bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY, - bitcoin.opcodes.OP_DROP, - - bitcoin.opcodes.OP_ELSE, - bQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_ENDIF, - - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG - ]) + type keyPair = { publicKey: Buffer }; + function cltvCheckSigOutput(aQ: keyPair, bQ: keyPair, lockTime: number) { + return bitcoin.script.fromASM( + ` + OP_IF + ${bitcoin.script.number.encode(lockTime).toString('hex')} + OP_CHECKLOCKTIMEVERIFY + OP_DROP + OP_ELSE + ${bQ.publicKey.toString('hex')} + OP_CHECKSIGVERIFY + OP_ENDIF + ${aQ.publicKey.toString('hex')} + OP_CHECKSIG + ` + .trim() + .replace(/\s+/g, ' '), + ); } - function utcNow () { - return Math.floor(Date.now() / 1000) + function utcNow() { + return Math.floor(Date.now() / 1000); } // expiry past, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async () => { // 3 hours ago - const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 1e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 1e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { - const height = await regtestUtils.height() + const height = await regtestUtils.height(); // 5 blocks from now - const lockTime = bip65.encode({ blocks: height + 5 }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ blocks: height + 5 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 1e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 1e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently // ... // into the future! - await regtestUtils.mine(5) - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.mine(5); + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async () => { // two hours ago - const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 2e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 2e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4); // {Alice's signature} {Bob's signature} OP_FALSE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_FALSE + bitcoin.opcodes.OP_FALSE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 8e4 - }) - }) + value: 8e4, + }); + }); // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { // two hours from now - const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 2e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 2e4); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { - if (err) throw err - }, /Error: non-final \(code 64\)/) - }) - }) -}) + if (err) throw err; + }, /Error: non-final \(code 64\)/); + }); + }); +}); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 2e133f0..0bfb970 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -1,27 +1,41 @@ -const { describe, it, before } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -const bip68 = require('bip68') +import * as assert from 'assert'; +import { before, describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +const bip68 = require('bip68'); -const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -const charles = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest) -const dave = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest) -console.warn = () => {} // Silence the Deprecation Warning +const alice = bitcoin.ECPair.fromWIF( + 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', + regtest, +); +const bob = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', + regtest, +); +const charles = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', + regtest, +); +const dave = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', + regtest, +); +console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CSV)', () => { // force update MTP before(async () => { - await regtestUtils.mine(11) - }) + await regtestUtils.mine(11); + }); - const hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL; + type keyPair = { publicKey: Buffer }; // IF MTP (from when confirmed) > seconds, _alice can redeem - function csvCheckSigOutput (_alice, _bob, sequence) { - return bitcoin.script.fromASM(` + function csvCheckSigOutput(_alice: keyPair, _bob: keyPair, sequence: number) { + return bitcoin.script.fromASM( + ` OP_IF ${bitcoin.script.number.encode(sequence).toString('hex')} OP_CHECKSEQUENCEVERIFY @@ -32,7 +46,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { OP_ENDIF ${_alice.publicKey.toString('hex')} OP_CHECKSIG - `.trim().replace(/\s+/g, ' ')) + ` + .trim() + .replace(/\s+/g, ' '), + ); } // 2 of 3 multisig of _bob, _charles, _dave, @@ -41,8 +58,16 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. - function complexCsvOutput (_alice, _bob, _charles, _dave, sequence1, sequence2) { - return bitcoin.script.fromASM(` + function complexCsvOutput( + _alice: keyPair, + _bob: keyPair, + _charles: keyPair, + _dave: keyPair, + sequence1: number, + sequence2: number, + ) { + return bitcoin.script.fromASM( + ` OP_IF OP_IF OP_2 @@ -66,253 +91,294 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { ${_alice.publicKey.toString('hex')} OP_CHECKSIG OP_ENDIF - `.trim().replace(/\s+/g, ' ')) + ` + .trim() + .replace(/\s+/g, ' '), + ); } // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', async () => { // 5 blocks from now - const sequence = bip68.encode({ blocks: 5 }) + const sequence = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: csvCheckSigOutput(alice, bob, sequence) + output: csvCheckSigOutput(alice, bob, sequence), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently // ... // into the future! - await regtestUtils.mine(10) + await regtestUtils.mine(10); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', async () => { // two hours after confirmation - const sequence = bip68.encode({ seconds: 7168 }) + const sequence = bip68.encode({ seconds: 7168 }); const p2sh = bitcoin.payments.p2sh({ network: regtest, redeem: { - output: csvCheckSigOutput(alice, bob, sequence) - } - }) + output: csvCheckSigOutput(alice, bob, sequence), + }, + }); // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 2e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { - if (err) throw err - }, /Error: non-BIP68-final \(code 64\)/) - }) - }) + if (err) throw err; + }, /Error: non-BIP68-final \(code 64\)/); + }); + }); // Check first combination of complex CSV, 2 of 3 it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.opcodes.OP_0, bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.script.signature.encode(charles.sign(signatureHash), hashType), + bitcoin.script.signature.encode( + charles.sign(signatureHash), + hashType, + ), bitcoin.opcodes.OP_TRUE, - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence1) // Set sequence1 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence1); // Set sequence1 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.opcodes.OP_0, bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_0, - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // Wait 2 blocks - await regtestUtils.mine(2) + await regtestUtils.mine(2); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // Check first combination of complex CSV, mediator after 5 blocks it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence2) // Set sequence2 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence2); // Set sequence2 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice mediator sig} OP_FALSE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_0 - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_0, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // Wait 5 blocks - await regtestUtils.mine(5) + await regtestUtils.mine(5); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) -}) + value: 7e4, + }); + }); +}); diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index 905eda8..6592c2c 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -1,23 +1,27 @@ -const bitcoin = require('../../') - -const { describe, it } = require('mocha') -const regtestUtils = require('./_regtest') -const NETWORK = regtestUtils.network +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const NETWORK = regtestUtils.network; const keyPairs = [ bitcoin.ECPair.makeRandom({ network: NETWORK }), - bitcoin.ECPair.makeRandom({ network: NETWORK }) -] -console.warn = () => {} // Silence the Deprecation Warning + bitcoin.ECPair.makeRandom({ network: NETWORK }), +]; +console.warn = () => {}; // Silence the Deprecation Warning -async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { - const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4) +async function buildAndSign( + depends: any, + prevOutput: any, + redeemScript: any, + witnessScript: any, +) { + const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4); - const txb = new bitcoin.TransactionBuilder(NETWORK) - txb.addInput(unspent.txId, unspent.vout, null, prevOutput) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(NETWORK); + txb.addInput(unspent.txId, unspent.vout, undefined, prevOutput); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - const posType = depends.prevOutScriptType - const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh' + const posType = depends.prevOutScriptType; + const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh'; if (depends.signatures) { keyPairs.forEach(keyPair => { @@ -28,8 +32,8 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { redeemScript, witnessValue: needsValue ? unspent.value : undefined, witnessScript, - }) - }) + }); + }); } else if (depends.signature) { txb.sign({ prevOutScriptType: posType, @@ -38,52 +42,94 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { redeemScript, witnessValue: needsValue ? unspent.value : undefined, witnessScript, - }) + }); } - return regtestUtils.broadcast(txb.build().toHex()) + return regtestUtils.broadcast(txb.build().toHex()); } -;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { - const fixtures = require('../fixtures/' + k) - const { depends } = fixtures.dynamic - const fn = bitcoin.payments[k] +['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { + const fixtures = require('../fixtures/' + k); + const { depends } = fixtures.dynamic; + const fn: any = (bitcoin.payments as any)[k]; - const base = {} - if (depends.pubkey) base.pubkey = keyPairs[0].publicKey - if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey) - if (depends.m) base.m = base.pubkeys.length + const base: any = {}; + if (depends.pubkey) base.pubkey = keyPairs[0].publicKey; + if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey); + if (depends.m) base.m = base.pubkeys.length; - const { output } = fn(base) - if (!output) throw new TypeError('Missing output') + const { output } = fn(base); + if (!output) throw new TypeError('Missing output'); describe('bitcoinjs-lib (payments - ' + k + ')', () => { it('can broadcast as an output, and be spent as an input', async () => { - Object.assign(depends, { prevOutScriptType: k }) - await buildAndSign(depends, output, undefined, undefined) - }) + Object.assign(depends, { prevOutScriptType: k }); + await buildAndSign(depends, output, undefined, undefined); + }); - it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', async () => { - const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) - Object.assign(depends, { prevOutScriptType: 'p2sh-' + k }) - await buildAndSign(depends, p2sh.output, p2sh.redeem.output, undefined) - }) + it( + 'can (as P2SH(' + + k + + ')) broadcast as an output, and be spent as an input', + async () => { + const p2sh = bitcoin.payments.p2sh({ + redeem: { output }, + network: NETWORK, + }); + Object.assign(depends, { prevOutScriptType: 'p2sh-' + k }); + await buildAndSign( + depends, + p2sh.output, + p2sh.redeem!.output, + undefined, + ); + }, + ); // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail - if (k === 'p2wpkh') return + if (k === 'p2wpkh') return; - it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', async () => { - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k }) - await buildAndSign(depends, p2wsh.output, undefined, p2wsh.redeem.output) - }) + it( + 'can (as P2WSH(' + + k + + ')) broadcast as an output, and be spent as an input', + async () => { + const p2wsh = bitcoin.payments.p2wsh({ + redeem: { output }, + network: NETWORK, + }); + Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k }); + await buildAndSign( + depends, + p2wsh.output, + undefined, + p2wsh.redeem!.output, + ); + }, + ); - it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', async () => { - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) + it( + 'can (as P2SH(P2WSH(' + + k + + '))) broadcast as an output, and be spent as an input', + async () => { + const p2wsh = bitcoin.payments.p2wsh({ + redeem: { output }, + network: NETWORK, + }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { output: p2wsh.output }, + network: NETWORK, + }); - Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k }) - await buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output) - }) - }) -}) + Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k }); + await buildAndSign( + depends, + p2sh.output, + p2sh.redeem!.output, + p2wsh.redeem!.output, + ); + }, + ); + }); +}); diff --git a/test/integration/transactions-psbt.spec.ts b/test/integration/transactions-psbt.spec.ts index 9040fc2..a98407d 100644 --- a/test/integration/transactions-psbt.spec.ts +++ b/test/integration/transactions-psbt.spec.ts @@ -1,9 +1,9 @@ -const { describe, it } = require('mocha'); -const assert = require('assert'); -const bitcoin = require('../../'); -const bip32 = require('bip32'); +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +import * as bip32 from 'bip32'; const rng = require('randombytes'); -const regtestUtils = require('./_regtest'); const regtest = regtestUtils.network; // See bottom of file for some helper functions used to make the payment objects needed. @@ -174,7 +174,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData1) .addOutput({ - script: embed.output, + script: embed.output!, value: 1000, }) .addOutput({ @@ -350,7 +350,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // For learning purposes, ignore this test. // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2wpkh = createPayment('p2wpkh'); - const inputData = await getInputData(5e4, p2wpkh.payment, false, 'noredeem'); + const inputData = await getInputData( + 5e4, + p2wpkh.payment, + false, + 'noredeem', + ); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -486,7 +491,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // For learning purposes, ignore this test. // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh-p2wsh'); + const inputData = await getInputData( + 5e4, + p2sh.payment, + false, + 'p2sh-p2wsh', + ); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -528,9 +538,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { masterFingerprint, path, pubkey, - } - ] - } + }, + ], + }; const p2wpkh = createPayment('p2wpkh', [childNode]); const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); { @@ -539,7 +549,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { } // You can add extra attributes for updateData into the addInput(s) object(s) - Object.assign(inputData, updateData) + Object.assign(inputData, updateData); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) @@ -551,7 +561,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInputHD(0, hdRoot); // must sign with root!!! assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual(psbt.validateSignaturesOfInput(0, childNode.publicKey), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, childNode.publicKey), + true, + ); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -568,18 +581,18 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); -function createPayment(_type, myKeys, network) { +function createPayment(_type: string, myKeys?: any[], network?: any) { network = network || regtest; const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; const keys = myKeys || []; - let m; + let m: number | undefined; if (isMultisig) { const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); - m = parseInt(match[1]); - let n = parseInt(match[2]); + m = parseInt(match![1]); + let n = parseInt(match![2]); if (keys.length > 0 && keys.length !== n) { - throw new Error('Need n keys for multisig') + throw new Error('Need n keys for multisig'); } while (!myKeys && n > 1) { keys.push(bitcoin.ECPair.makeRandom({ network })); @@ -588,7 +601,7 @@ function createPayment(_type, myKeys, network) { } if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); - let payment; + let payment: any; splitType.forEach(type => { if (type.slice(0, 4) === 'p2ms') { payment = bitcoin.payments.p2ms({ @@ -597,12 +610,12 @@ function createPayment(_type, myKeys, network) { network, }); } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { - payment = bitcoin.payments[type]({ + payment = (bitcoin.payments as any)[type]({ redeem: payment, network, }); } else { - payment = bitcoin.payments[type]({ + payment = (bitcoin.payments as any)[type]({ pubkey: keys[0].publicKey, network, }); @@ -615,13 +628,18 @@ function createPayment(_type, myKeys, network) { }; } -function getWitnessUtxo(out) { +function getWitnessUtxo(out: any) { delete out.address; out.script = Buffer.from(out.script, 'hex'); return out; } -async function getInputData(amount, payment, isSegwit, redeemType) { +async function getInputData( + amount: number, + payment: any, + isSegwit: boolean, + redeemType: string, +) { const unspent = await regtestUtils.faucetComplex(payment.output, amount); const utx = await regtestUtils.fetch(unspent.txId); // for non segwit inputs, you must pass the full transaction buffer @@ -629,7 +647,7 @@ async function getInputData(amount, payment, isSegwit, redeemType) { // for segwit inputs, you only need the output script and value as an object. const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]); const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo }; - const mixin2 = {}; + const mixin2: any = {}; switch (redeemType) { case 'p2sh': mixin2.redeemScript = payment.redeem.output; diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 173fc4a..8a313f2 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,361 +1,421 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -console.warn = () => {} // Silence the Deprecation Warning +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +console.warn = () => {}; // Silence the Deprecation Warning -function rng () { - return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') +function rng() { + return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64'); } describe('bitcoinjs-lib (transactions)', () => { it('can create a 1-to-1 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') - const txb = new bitcoin.TransactionBuilder() + const alice = bitcoin.ECPair.fromWIF( + 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', + ); + const txb = new bitcoin.TransactionBuilder(); - txb.setVersion(1) - txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis - txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000) + txb.setVersion(1); + txb.addInput( + '61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', + 0, + ); // Alice's previous transaction output, has 15000 satoshis + txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000); // (in)15000 - (out)12000 = (fee)3000, this is the miner fee txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice - }) + keyPair: alice, + }); // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000', + ); + }); it('can create a 2-to-2 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') - const bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') + const alice = bitcoin.ECPair.fromWIF( + 'L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1', + ); + const bob = bitcoin.ECPair.fromWIF( + 'KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z', + ); - const txb = new bitcoin.TransactionBuilder() - txb.setVersion(1) - txb.addInput('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', 6) // Alice's previous transaction output, has 200000 satoshis - txb.addInput('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', 0) // Bob's previous transaction output, has 300000 satoshis - txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000) - txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000) + const txb = new bitcoin.TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', + 6, + ); // Alice's previous transaction output, has 200000 satoshis + txb.addInput( + '7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', + 0, + ); // Bob's previous transaction output, has 300000 satoshis + txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000); + txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000); // (in)(200000 + 300000) - (out)(180000 + 170000) = (fee)150000, this is the miner fee txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, - keyPair: bob - }) // Bob signs his input, which was the second input (1th) + keyPair: bob, + }); // Bob signs his input, which was the second input (1th) txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice - }) // Alice signs her input, which was the first input (0th) + keyPair: alice, + }); // Alice signs her input, which was the first input (0th) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000', + ); + }); it('can create (and broadcast via 3PBP) a typical Transaction', async () => { - const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) - const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) - const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) + const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }); + const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }); + const aliceChange = bitcoin.ECPair.makeRandom({ + network: regtest, + rng: rng, + }); - const alice1pkh = bitcoin.payments.p2pkh({ pubkey: alice1.publicKey, network: regtest }) - const alice2pkh = bitcoin.payments.p2pkh({ pubkey: alice2.publicKey, network: regtest }) - const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest }) + const alice1pkh = bitcoin.payments.p2pkh({ + pubkey: alice1.publicKey, + network: regtest, + }); + const alice2pkh = bitcoin.payments.p2pkh({ + pubkey: alice2.publicKey, + network: regtest, + }); + const aliceCpkh = bitcoin.payments.p2pkh({ + pubkey: aliceChange.publicKey, + network: regtest, + }); // give Alice 2 unspent outputs - const unspent0 = await regtestUtils.faucet(alice1pkh.address, 5e4) + const unspent0 = await regtestUtils.faucet(alice1pkh.address!, 5e4); - const unspent1 = await regtestUtils.faucet(alice2pkh.address, 7e4) + const unspent1 = await regtestUtils.faucet(alice2pkh.address!, 7e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent - txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent - txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" - txb.addOutput(aliceCpkh.address, 1e4) // Alice's change + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent0.txId, unspent0.vout); // alice1 unspent + txb.addInput(unspent1.txId, unspent1.vout); // alice2 unspent + txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4); // the actual "spend" + txb.addOutput(aliceCpkh.address!, 1e4); // Alice's change // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Alice signs each input with the respective private keys txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice1 - }) + keyPair: alice1, + }); txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, - keyPair: alice2 - }) + keyPair: alice2, + }); // build and broadcast our RegTest network - await regtestUtils.broadcast(txb.build().toHex()) + await regtestUtils.broadcast(txb.build().toHex()); // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 - }) + }); it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2pkh = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); - const unspent = await regtestUtils.faucet(p2pkh.address, 2e5) + const unspent = await regtestUtils.faucet(p2pkh.address!, 2e5); - const txb = new bitcoin.TransactionBuilder(regtest) - const data = Buffer.from('bitcoinjs-lib', 'utf8') - const embed = bitcoin.payments.embed({ data: [data] }) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(embed.output, 1000) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) + const txb = new bitcoin.TransactionBuilder(regtest); + const data = Buffer.from('bitcoinjs-lib', 'utf8'); + const embed = bitcoin.payments.embed({ data: [data] }); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(embed.output!, 1000); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // build and broadcast to the RegTest network - await regtestUtils.broadcast(txb.build().toHex()) - }) + await regtestUtils.broadcast(txb.build().toHex()); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }) - ] - const pubkeys = keyPairs.map(x => x.publicKey) - const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }) + bitcoin.ECPair.makeRandom({ network: regtest }), + ]; + const pubkeys = keyPairs.map(x => x.publicKey); + const p2ms = bitcoin.payments.p2ms({ + m: 2, + pubkeys: pubkeys, + network: regtest, + }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 2e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPairs[0], - redeemScript: p2sh.redeem.output, - }) + redeemScript: p2sh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPairs[2], - redeemScript: p2sh.redeem.output, - }) - const tx = txb.build() + redeemScript: p2sh.redeem!.output, + }); + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 1e4 - }) - }) + value: 1e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2wpkh = bitcoin.payments.p2wpkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 5e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 5e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2sh-p2wpkh', vin: 0, keyPair: keyPair, - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - }) + }); - const tx = txb.build() + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2wpkh = bitcoin.payments.p2wpkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); - const unspent = await regtestUtils.faucetComplex(p2wpkh.output, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wpkh.output!, 5e4); // XXX: build the Transaction w/ a P2WPKH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2wpkh.output); // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2wpkh', vin: 0, keyPair: keyPair, witnessValue: unspent.value, - }) // NOTE: no redeem script - const tx = txb.build() + }); // NOTE: no redeem script + const tx = txb.build(); // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2pk = bitcoin.payments.p2pk({ + pubkey: keyPair.publicKey, + network: regtest, + }); + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }); - const unspent = await regtestUtils.faucetComplex(p2wsh.output, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wsh.output!, 5e4); // XXX: build the Transaction w/ a P2WSH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2wsh.output); // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2wsh-p2pk', vin: 0, keyPair: keyPair, witnessValue: 5e4, - witnessScript: p2wsh.redeem.output, - }) // NOTE: provide a witnessScript! - const tx = txb.build() + witnessScript: p2wsh.redeem!.output, + }); // NOTE: provide a witnessScript! + const tx = txb.build(); // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }) - ] - const pubkeys = keyPairs.map(x => x.publicKey) + bitcoin.ECPair.makeRandom({ network: regtest }), + ]; + const pubkeys = keyPairs.map(x => x.publicKey); - const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }) - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }) + const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }); + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 6e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 6e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2sh.output); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[0], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[2], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[3], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); - const tx = txb.build() + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 3e4 - }) - }) + value: 3e4, + }); + }); it('can verify Transaction (P2PKH) signatures', () => { - const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' + const txHex = + '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700'; const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', - '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' - ].map(q => { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) + '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f', + ].map(q => { + return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')); + }); - const tx = bitcoin.Transaction.fromHex(txHex) + const tx = bitcoin.Transaction.fromHex(txHex); tx.ins.forEach((input, i) => { - const keyPair = keyPairs[i] + const keyPair = keyPairs[i]; const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, - input: input.script - }) + input: input.script, + }); - const ss = bitcoin.script.signature.decode(p2pkh.signature) - const hash = tx.hashForSignature(i, p2pkh.output, ss.hashType) + const ss = bitcoin.script.signature.decode(p2pkh.signature!); + const hash = tx.hashForSignature(i, p2pkh.output!, ss.hashType); - assert.strictEqual(keyPair.verify(hash, ss.signature), true) - }) - }) + assert.strictEqual(keyPair.verify(hash, ss.signature), true); + }); + }); it('can verify Transaction (P2SH(P2WPKH)) signatures', () => { const utxos = { 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { - value: 50000 - } - } + value: 50000, + }, + }; - const txHex = '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000' - const tx = bitcoin.Transaction.fromHex(txHex) + const txHex = + '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000'; + const tx = bitcoin.Transaction.fromHex(txHex); tx.ins.forEach((input, i) => { - const txId = Buffer.from(input.hash).reverse().toString('hex') - const utxo = utxos[`${txId}:${i}`] - if (!utxo) throw new Error('Missing utxo') + const txId = (Buffer.from(input.hash).reverse() as Buffer).toString( + 'hex', + ); + const utxo = (utxos as any)[`${txId}:${i}`]; + if (!utxo) throw new Error('Missing utxo'); const p2sh = bitcoin.payments.p2sh({ input: input.script, - witness: input.witness - }) - const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem) - const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }) // because P2WPKH is annoying + witness: input.witness, + }); + const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem!); + const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }); // because P2WPKH is annoying - const ss = bitcoin.script.signature.decode(p2wpkh.signature) - const hash = tx.hashForWitnessV0(i, p2pkh.output, utxo.value, ss.hashType) - const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey) // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk + const ss = bitcoin.script.signature.decode(p2wpkh.signature!); + const hash = tx.hashForWitnessV0( + i, + p2pkh.output!, + utxo.value, + ss.hashType, + ); + const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey!); // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk - assert.strictEqual(keyPair.verify(hash, ss.signature), true) - }) - }) -}) + assert.strictEqual(keyPair.verify(hash, ss.signature), true); + }); + }); +}); diff --git a/test/payments.spec.ts b/test/payments.spec.ts index 1386ea4..051cc04 100644 --- a/test/payments.spec.ts +++ b/test/payments.spec.ts @@ -1,49 +1,55 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const u = require('./payments.utils') - -;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as u from './payments.utils'; +import { PaymentCreator } from '../src/payments'; +['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { describe(p, () => { - let fn - let payment = require('../src/payments/' + p) + let fn: PaymentCreator; + let payment = require('../src/payments/' + p); if (p === 'embed') { - fn = payment.p2data + fn = payment.p2data; } else { - fn = payment[p] + fn = payment[p]; } - const fixtures = require('./fixtures/' + p) + const fixtures = require('./fixtures/' + p); - fixtures.valid.forEach((f, i) => { + fixtures.valid.forEach((f: any) => { it(f.description + ' as expected', () => { - const args = u.preform(f.arguments) - const actual = fn(args, f.options) + const args = u.preform(f.arguments); + const actual = fn(args, f.options); - u.equate(actual, f.expected, f.arguments) - }) + u.equate(actual, f.expected, f.arguments); + }); it(f.description + ' as expected (no validation)', () => { - const args = u.preform(f.arguments) - const actual = fn(args, Object.assign({}, f.options, { - validate: false - })) + const args = u.preform(f.arguments); + const actual = fn( + args, + Object.assign({}, f.options, { + validate: false, + }), + ); - u.equate(actual, f.expected, f.arguments) - }) - }) + u.equate(actual, f.expected, f.arguments); + }); + }); - fixtures.invalid.forEach(f => { - it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), () => { - const args = u.preform(f.arguments) + fixtures.invalid.forEach((f: any) => { + it( + 'throws ' + f.exception + (f.description ? 'for ' + f.description : ''), + () => { + const args = u.preform(f.arguments); - assert.throws(() => { - fn(args, f.options) - }, new RegExp(f.exception)) - }) - }) + assert.throws(() => { + fn(args, f.options); + }, new RegExp(f.exception)); + }, + ); + }); if (p === 'p2sh') { - const p2wsh = require('../src/payments/p2wsh').p2wsh - const p2pk = require('../src/payments/p2pk').p2pk + const p2wsh = require('../src/payments/p2wsh').p2wsh; + const p2pk = require('../src/payments/p2pk').p2pk; it('properly assembles nested p2wsh with names', () => { const actual = fn({ redeem: p2wsh({ @@ -51,42 +57,57 @@ const u = require('./payments.utils') pubkey: Buffer.from( '03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058', 'hex', - ) - }) - }) - }) - assert.strictEqual(actual.address, '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw') - assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk') - assert.strictEqual(actual.redeem.name, 'p2wsh-p2pk') - assert.strictEqual(actual.redeem.redeem.name, 'p2pk') - }) + ), + }), + }), + }); + assert.strictEqual( + actual.address, + '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw', + ); + assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk'); + assert.strictEqual(actual.redeem!.name, 'p2wsh-p2pk'); + assert.strictEqual(actual.redeem!.redeem!.name, 'p2pk'); + }); } // cross-verify dynamically too - if (!fixtures.dynamic) return - const { depends, details } = fixtures.dynamic + if (!fixtures.dynamic) return; + const { depends, details } = fixtures.dynamic; - details.forEach(f => { - const detail = u.preform(f) - const disabled = {} - if (f.disabled) f.disabled.forEach(k => { disabled[k] = true }) + details.forEach((f: any) => { + const detail = u.preform(f); + const disabled: any = {}; + if (f.disabled) + f.disabled.forEach((k: string) => { + disabled[k] = true; + }); for (let key in depends) { - if (key in disabled) continue - const dependencies = depends[key] + if (key in disabled) continue; + const dependencies = depends[key]; - dependencies.forEach(dependency => { - if (!Array.isArray(dependency)) dependency = [dependency] + dependencies.forEach((dependency: any) => { + if (!Array.isArray(dependency)) dependency = [dependency]; - const args = {} - dependency.forEach(d => { u.from(d, detail, args) }) - const expected = u.from(key, detail) + const args = {}; + dependency.forEach((d: any) => { + u.from(d, detail, args); + }); + const expected = u.from(key, detail); - it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), () => { - u.equate(fn(args), expected) - }) - }) + it( + f.description + + ', ' + + key + + ' derives from ' + + JSON.stringify(dependency), + () => { + u.equate(fn(args), expected); + }, + ); + }); } - }) - }) -}) + }); + }); +}); diff --git a/test/payments.utils.spec.ts b/test/payments.utils.spec.ts deleted file mode 100644 index 15414c4..0000000 --- a/test/payments.utils.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -const t = require('assert') -const bscript = require('../src/script') -const BNETWORKS = require('../src/networks') - -function tryHex (x) { - if (Buffer.isBuffer(x)) return x.toString('hex') - if (Array.isArray(x)) return x.map(tryHex) - return x -} - -function fromHex (x) { - if (typeof x === 'string') return Buffer.from(x, 'hex') - if (Array.isArray(x)) return x.map(fromHex) - return x -} -function tryASM (x) { - if (Buffer.isBuffer(x)) return bscript.toASM(x) - return x -} -function asmToBuffer (x) { - if (x === '') return Buffer.alloc(0) - return bscript.fromASM(x) -} -function carryOver (a, b) { - for (let k in b) { - if (k in a && k === 'redeem') { - carryOver(a[k], b[k]) - continue - } - - // don't, the value was specified - if (k in a) continue - - // otherwise, expect match - a[k] = b[k] - } -} - -function equateBase (a, b, context) { - if ('output' in b) t.strictEqual(tryASM(a.output), tryASM(b.output), `Inequal ${context}output`) - if ('input' in b) t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`) - if ('witness' in b) t.deepStrictEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) -} - -function equate (a, b, args) { - b = Object.assign({}, b) - carryOver(b, args) - - // by null, we mean 'undefined', but JSON - if (b.input === null) b.input = undefined - if (b.output === null) b.output = undefined - if (b.witness === null) b.witness = undefined - if (b.redeem) { - if (b.redeem.input === null) b.redeem.input = undefined - if (b.redeem.output === null) b.redeem.output = undefined - if (b.redeem.witness === null) b.redeem.witness = undefined - } - - equateBase(a, b, '') - if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') - if (b.network) t.deepStrictEqual(a.network, BNETWORKS[b.network], 'Inequal *.network') - - // contextual - if (b.signature === null) b.signature = undefined - if (b.signatures === null) b.signatures = undefined - if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address') - if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash') - if ('pubkey' in b) t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey') - if ('signature' in b) t.strictEqual(tryHex(a.signature), tryHex(b.signature), 'Inequal signature') - if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m') - if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') - if ('pubkeys' in b) t.deepStrictEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') - if ('signatures' in b) t.deepStrictEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') - if ('data' in b) t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') -} - -function preform (x) { - x = Object.assign({}, x) - - if (x.network) x.network = BNETWORKS[x.network] - if (typeof x.inputHex === 'string') { - x.input = Buffer.from(x.inputHex, 'hex') - delete x.inputHex - } - if (typeof x.outputHex === 'string') { - x.output = Buffer.from(x.outputHex, 'hex') - delete x.outputHex - } - if (typeof x.output === 'string') x.output = asmToBuffer(x.output) - if (typeof x.input === 'string') x.input = asmToBuffer(x.input) - if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex) - - if (x.data) x.data = x.data.map(fromHex) - if (x.hash) x.hash = Buffer.from(x.hash, 'hex') - if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') - if (x.signature) x.signature = Buffer.from(x.signature, 'hex') - if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) - if (x.signatures) x.signatures = x.signatures.map(y => { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) - if (x.redeem) { - x.redeem = Object.assign({}, x.redeem) - if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) - if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) - if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) - if (x.redeem.network) x.redeem.network = BNETWORKS[x.redeem.network] - } - - return x -} - -function from (path, object, result) { - path = path.split('.') - result = result || {} - - let r = result - path.forEach((k, i) => { - if (i < path.length - 1) { - r[k] = r[k] || {} - - // recurse - r = r[k] - object = object[k] - } else { - r[k] = object[k] - } - }) - - return result -} - -module.exports = { - from, - equate, - preform -} diff --git a/test/payments.utils.ts b/test/payments.utils.ts new file mode 100644 index 0000000..bcf79c7 --- /dev/null +++ b/test/payments.utils.ts @@ -0,0 +1,169 @@ +import * as t from 'assert'; +import * as bscript from '../src/script'; +import * as BNETWORKS from '../src/networks'; + +function tryHex(x: Buffer | Buffer[]): string | string[] { + if (Buffer.isBuffer(x)) return x.toString('hex'); + if (Array.isArray(x)) return x.map(tryHex) as string[]; + return x; +} + +function fromHex(x: string | string[]): Buffer | Buffer[] { + if (typeof x === 'string') return Buffer.from(x, 'hex'); + if (Array.isArray(x)) return x.map(fromHex) as Buffer[]; + return x; +} +function tryASM(x: Buffer): string { + if (Buffer.isBuffer(x)) return bscript.toASM(x); + return x; +} +function asmToBuffer(x: string) { + if (x === '') return Buffer.alloc(0); + return bscript.fromASM(x); +} +function carryOver(a: any, b: any) { + for (let k in b) { + if (k in a && k === 'redeem') { + carryOver(a[k], b[k]); + continue; + } + + // don't, the value was specified + if (k in a) continue; + + // otherwise, expect match + a[k] = b[k]; + } +} + +function equateBase(a: any, b: any, context: string) { + if ('output' in b) + t.strictEqual( + tryASM(a.output), + tryASM(b.output), + `Inequal ${context}output`, + ); + if ('input' in b) + t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`); + if ('witness' in b) + t.deepStrictEqual( + tryHex(a.witness), + tryHex(b.witness), + `Inequal ${context}witness`, + ); +} + +export function equate(a: any, b: any, args?: any) { + b = Object.assign({}, b); + carryOver(b, args); + + // by null, we mean 'undefined', but JSON + if (b.input === null) b.input = undefined; + if (b.output === null) b.output = undefined; + if (b.witness === null) b.witness = undefined; + if (b.redeem) { + if (b.redeem.input === null) b.redeem.input = undefined; + if (b.redeem.output === null) b.redeem.output = undefined; + if (b.redeem.witness === null) b.redeem.witness = undefined; + } + + equateBase(a, b, ''); + if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.'); + if (b.network) + t.deepStrictEqual( + a.network, + (BNETWORKS as any)[b.network], + 'Inequal *.network', + ); + + // contextual + if (b.signature === null) b.signature = undefined; + if (b.signatures === null) b.signatures = undefined; + if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address'); + if ('hash' in b) + t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash'); + if ('pubkey' in b) + t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey'); + if ('signature' in b) + t.strictEqual( + tryHex(a.signature), + tryHex(b.signature), + 'Inequal signature', + ); + if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m'); + if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n'); + if ('pubkeys' in b) + t.deepStrictEqual( + tryHex(a.pubkeys), + tryHex(b.pubkeys), + 'Inequal *.pubkeys', + ); + if ('signatures' in b) + t.deepStrictEqual( + tryHex(a.signatures), + tryHex(b.signatures), + 'Inequal *.signatures', + ); + if ('data' in b) + t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data'); +} + +export function preform(x: any) { + x = Object.assign({}, x); + + if (x.network) x.network = (BNETWORKS as any)[x.network]; + if (typeof x.inputHex === 'string') { + x.input = Buffer.from(x.inputHex, 'hex'); + delete x.inputHex; + } + if (typeof x.outputHex === 'string') { + x.output = Buffer.from(x.outputHex, 'hex'); + delete x.outputHex; + } + if (typeof x.output === 'string') x.output = asmToBuffer(x.output); + if (typeof x.input === 'string') x.input = asmToBuffer(x.input); + if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex); + + if (x.data) x.data = x.data.map(fromHex); + if (x.hash) x.hash = Buffer.from(x.hash, 'hex'); + if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex'); + if (x.signature) x.signature = Buffer.from(x.signature, 'hex'); + if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex); + if (x.signatures) + x.signatures = x.signatures.map((y: any) => { + return Number.isFinite(y) ? y : Buffer.from(y, 'hex'); + }); + if (x.redeem) { + x.redeem = Object.assign({}, x.redeem); + if (typeof x.redeem.input === 'string') + x.redeem.input = asmToBuffer(x.redeem.input); + if (typeof x.redeem.output === 'string') + x.redeem.output = asmToBuffer(x.redeem.output); + if (Array.isArray(x.redeem.witness)) + x.redeem.witness = x.redeem.witness.map(fromHex); + if (x.redeem.network) + x.redeem.network = (BNETWORKS as any)[x.redeem.network]; + } + + return x; +} + +export function from(path: string, object: any, result?: any) { + const paths = path.split('.'); + result = result || {}; + + let r = result; + paths.forEach((k, i) => { + if (i < paths.length - 1) { + r[k] = r[k] || {}; + + // recurse + r = r[k]; + object = object[k]; + } else { + r[k] = object[k]; + } + }); + + return result; +} diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 54eb5e4..02a420d 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,688 +1,713 @@ -const { describe, it } = require('mocha') -const assert = require('assert') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; -const bip32 = require('bip32') -const ECPair = require('../src/ecpair') -const Psbt = require('..').Psbt -const NETWORKS = require('../src/networks') +import { bip32, ECPair, networks as NETWORKS, Psbt } from '..'; -const initBuffers = object => JSON.parse(JSON.stringify(object), (key, value) => { - const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/) - const result = regex.exec(value) - if (!result) return value +import * as preFixtures from './fixtures/psbt.json'; - const data = result[1] - const encoding = result[2] +const initBuffers = (object: any): typeof preFixtures => + JSON.parse(JSON.stringify(object), (_, value) => { + const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/); + const result = regex.exec(value); + if (!result) return value; - return Buffer.from(data, encoding) -}) + const data = result[1]; + const encoding = result[2]; -const fixtures = initBuffers(require('./fixtures/psbt')) + return Buffer.from(data, encoding); + }); -const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase()) +const fixtures = initBuffers(preFixtures); -const b = hex => Buffer.from(hex, 'hex'); +const upperCaseFirstLetter = (str: string) => + str.replace(/^./, s => s.toUpperCase()); + +// const b = (hex: string) => Buffer.from(hex, 'hex'); describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { fixtures.bip174.invalid.forEach(f => { it(`Invalid: ${f.description}`, () => { assert.throws(() => { - Psbt.fromBase64(f.psbt) - }, new RegExp(f.errorMessage)) - }) - }) + Psbt.fromBase64(f.psbt); + }, new RegExp(f.errorMessage)); + }); + }); fixtures.bip174.valid.forEach(f => { it(`Valid: ${f.description}`, () => { assert.doesNotThrow(() => { - Psbt.fromBase64(f.psbt) - }) - }) - }) + Psbt.fromBase64(f.psbt); + }); + }); + }); fixtures.bip174.failSignChecks.forEach(f => { - const keyPair = ECPair.makeRandom() + const keyPair = ECPair.makeRandom(); it(`Fails Signer checks: ${f.description}`, () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); assert.throws(() => { - psbt.signInput(f.inputToCheck, keyPair) - }, new RegExp(f.errorMessage)) - }) - }) + psbt.signInput(f.inputToCheck, keyPair); + }, new RegExp(f.errorMessage)); + }); + }); fixtures.bip174.creator.forEach(f => { it('Creates expected PSBT', () => { - const psbt = new Psbt() + const psbt = new Psbt(); for (const input of f.inputs) { - psbt.addInput(input) + psbt.addInput(input); } for (const output of f.outputs) { const script = Buffer.from(output.script, 'hex'); - psbt.addOutput({...output, script}) + psbt.addOutput({ ...output, script }); } - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.updater.forEach(f => { it('Updates PSBT to the expected result', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); for (const inputOrOutput of ['input', 'output']) { - const fixtureData = f[`${inputOrOutput}Data`] + const fixtureData = (f as any)[`${inputOrOutput}Data`]; if (fixtureData) { for (const [i, data] of fixtureData.entries()) { - const txt = upperCaseFirstLetter(inputOrOutput) - psbt[`update${txt}`](i, data) + const txt = upperCaseFirstLetter(inputOrOutput); + (psbt as any)[`update${txt}`](i, data); } } } - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.signer.forEach(f => { it('Signs PSBT to the expected result', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - f.keys.forEach(({inputToSign, WIF}) => { + f.keys.forEach(({ inputToSign, WIF }) => { const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); psbt.signInput(inputToSign, keyPair); - }) + }); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.combiner.forEach(f => { it('Combines two PSBTs to the expected result', () => { - const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) + const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)); - psbts[0].combine(psbts[1]) + psbts[0].combine(psbts[1]); // Produces a different Base64 string due to implemetation specific key-value ordering. // That means this test will fail: // assert.strictEqual(psbts[0].toBase64(), f.result) // However, if we compare the actual PSBT properties we can see they are logically identical: - assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) - }) - }) + assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)); + }); + }); fixtures.bip174.finalizer.forEach(f => { - it("Finalizes inputs and gives the expected PSBT", () => { - const psbt = Psbt.fromBase64(f.psbt) + it('Finalizes inputs and gives the expected PSBT', () => { + const psbt = Psbt.fromBase64(f.psbt); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.extractor.forEach(f => { it('Extracts the expected transaction from a PSBT', () => { - const psbt1 = Psbt.fromBase64(f.psbt) - const transaction1 = psbt1.extractTransaction(true).toHex() + const psbt1 = Psbt.fromBase64(f.psbt); + const transaction1 = psbt1.extractTransaction(true).toHex(); - const psbt2 = Psbt.fromBase64(f.psbt) - const transaction2 = psbt2.extractTransaction().toHex() + const psbt2 = Psbt.fromBase64(f.psbt); + const transaction2 = psbt2.extractTransaction().toHex(); - assert.strictEqual(transaction1, transaction2) - assert.strictEqual(transaction1, f.transaction) + assert.strictEqual(transaction1, transaction2); + assert.strictEqual(transaction1, f.transaction); - const psbt3 = Psbt.fromBase64(f.psbt) - delete psbt3.data.inputs[0].finalScriptSig - delete psbt3.data.inputs[0].finalScriptWitness + const psbt3 = Psbt.fromBase64(f.psbt); + delete psbt3.data.inputs[0].finalScriptSig; + delete psbt3.data.inputs[0].finalScriptWitness; assert.throws(() => { - psbt3.extractTransaction() - }, new RegExp('Not finalized')) + psbt3.extractTransaction(); + }, new RegExp('Not finalized')); - const psbt4 = Psbt.fromBase64(f.psbt) - psbt4.setMaximumFeeRate(1) + const psbt4 = Psbt.fromBase64(f.psbt); + psbt4.setMaximumFeeRate(1); assert.throws(() => { - psbt4.extractTransaction() - }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) + psbt4.extractTransaction(); + }, new RegExp('Warning: You are paying around [\\d.]+ in fees')); - const psbt5 = Psbt.fromBase64(f.psbt) - psbt5.extractTransaction(true) - const fr1 = psbt5.getFeeRate() - const fr2 = psbt5.getFeeRate() - assert.strictEqual(fr1, fr2) + const psbt5 = Psbt.fromBase64(f.psbt); + psbt5.extractTransaction(true); + const fr1 = psbt5.getFeeRate(); + const fr2 = psbt5.getFeeRate(); + assert.strictEqual(fr1, fr2); - const psbt6 = Psbt.fromBase64(f.psbt) - const f1 = psbt6.getFee() - const f2 = psbt6.getFee() - assert.strictEqual(f1, f2) - }) - }) - }) + const psbt6 = Psbt.fromBase64(f.psbt); + const f1 = psbt6.getFee(); + const f2 = psbt6.getFee(); + assert.strictEqual(f1, f2); + }); + }); + }); describe('signInputAsync', () => { fixtures.signInput.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signInputAsync( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signInputAsync( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.rejects(async () => { - await psbtThatShouldThrow.signInputAsync( + await (psbtThatShouldThrow.signInputAsync as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + ); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signInput( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signInput( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.throws(() => { - psbtThatShouldThrow.signInput( - f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + (psbtThatShouldThrow.signInput as any)(f.shouldThrow.inputToCheck); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsAsync', () => { fixtures.signInput.checks.forEach(f => { - if (f.description === 'checks the input exists') return + if (f.description === 'checks the input exists') return; it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsAsync( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsAsync( ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.rejects(async () => { - await psbtThatShouldThrow.signAllInputsAsync() - }, new RegExp('Need Signer to sign input')) + await (psbtThatShouldThrow.signAllInputsAsync as any)(); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputs', () => { fixtures.signInput.checks.forEach(f => { - if (f.description === 'checks the input exists') return + if (f.description === 'checks the input exists') return; it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signAllInputs( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signAllInputs( ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.throws(() => { - psbtThatShouldThrow.signAllInputs() - }, new RegExp('Need Signer to sign input')) + (psbtThatShouldThrow.signAllInputs as any)(); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInputHDAsync', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signInputHDAsync( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signInputHDAsync( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.rejects(async () => { - await psbtThatShouldThrow.signInputHDAsync( + await (psbtThatShouldThrow.signInputHDAsync as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need HDSigner to sign input')) + ); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInputHD', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signInputHD( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signInputHD( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.throws(() => { - psbtThatShouldThrow.signInputHD( + (psbtThatShouldThrow.signInputHD as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need HDSigner to sign input')) + ); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsHDAsync', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsHDAsync( bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsHDAsync( bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.rejects(async () => { - await psbtThatShouldThrow.signAllInputsHDAsync() - }, new RegExp('Need HDSigner to sign input')) + await (psbtThatShouldThrow.signAllInputsHDAsync as any)(); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsHD', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signAllInputsHD( bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signAllInputsHD( bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.throws(() => { - psbtThatShouldThrow.signAllInputsHD() - }, new RegExp('Need HDSigner to sign input')) + (psbtThatShouldThrow.signAllInputsHD as any)(); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('finalizeAllInputs', () => { fixtures.finalizeAllInputs.forEach(f => { it(`Finalizes inputs of type "${f.type}"`, () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); it('fails if no script found', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 - }) + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + }); assert.throws(() => { - psbt.finalizeAllInputs() - }, new RegExp('No script found for input #0')) + psbt.finalizeAllInputs(); + }, new RegExp('No script found for input #0')); psbt.updateInput(0, { witnessUtxo: { - script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'), - value: 2e5 - } - }) + script: Buffer.from( + '0014d85c2b71d0060b09c9886aeb815e50991dda124d', + 'hex', + ), + value: 2e5, + }, + }); assert.throws(() => { - psbt.finalizeAllInputs() - }, new RegExp('Can not finalize input #0')) - }) - }) + psbt.finalizeAllInputs(); + }, new RegExp('Can not finalize input #0')); + }); + }); describe('addInput', () => { fixtures.addInput.checks.forEach(f => { it(f.description, () => { - const psbt = new Psbt() + const psbt = new Psbt(); if (f.exception) { assert.throws(() => { - psbt.addInput(f.inputData) - }, new RegExp(f.exception)) + psbt.addInput(f.inputData as any); + }, new RegExp(f.exception)); assert.throws(() => { - psbt.addInputs([f.inputData]) - }, new RegExp(f.exception)) + psbt.addInputs([f.inputData as any]); + }, new RegExp(f.exception)); } else { assert.doesNotThrow(() => { - psbt.addInputs([f.inputData]) + psbt.addInputs([f.inputData as any]); if (f.equals) { - assert.strictEqual(psbt.toBase64(), f.equals) + assert.strictEqual(psbt.toBase64(), f.equals); } - }) + }); assert.throws(() => { - psbt.addInput(f.inputData) - }, new RegExp('Duplicate input detected.')) + psbt.addInput(f.inputData as any); + }, new RegExp('Duplicate input detected.')); } - }) - }) - }) + }); + }); + }); describe('addOutput', () => { fixtures.addOutput.checks.forEach(f => { it(f.description, () => { - const psbt = new Psbt() + const psbt = new Psbt(); if (f.exception) { assert.throws(() => { - psbt.addOutput(f.outputData) - }, new RegExp(f.exception)) + psbt.addOutput(f.outputData as any); + }, new RegExp(f.exception)); assert.throws(() => { - psbt.addOutputs([f.outputData]) - }, new RegExp(f.exception)) + psbt.addOutputs([f.outputData as any]); + }, new RegExp(f.exception)); } else { assert.doesNotThrow(() => { - psbt.addOutput(f.outputData) - }) + psbt.addOutput(f.outputData as any); + }); assert.doesNotThrow(() => { - psbt.addOutputs([f.outputData]) - }) + psbt.addOutputs([f.outputData as any]); + }); } - }) - }) - }) + }); + }); + }); describe('setVersion', () => { it('Sets the version value of the unsigned transaction', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.extractTransaction().version, 2) - psbt.setVersion(1) - assert.strictEqual(psbt.extractTransaction().version, 1) - }) - }) + assert.strictEqual(psbt.extractTransaction().version, 2); + psbt.setVersion(1); + assert.strictEqual(psbt.extractTransaction().version, 1); + }); + }); describe('setLocktime', () => { it('Sets the nLockTime value of the unsigned transaction', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.extractTransaction().locktime, 0) - psbt.setLocktime(1) - assert.strictEqual(psbt.extractTransaction().locktime, 1) - }) - }) + assert.strictEqual(psbt.extractTransaction().locktime, 0); + psbt.setLocktime(1); + assert.strictEqual(psbt.extractTransaction().locktime, 1); + }); + }); describe('setInputSequence', () => { it('Sets the sequence number for a given input', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, }); - assert.strictEqual(psbt.inputCount, 1) - assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff) - psbt.setInputSequence(0, 0) - assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0) - }) + assert.strictEqual(psbt.inputCount, 1); + assert.strictEqual( + (psbt as any).__CACHE.__TX.ins[0].sequence, + 0xffffffff, + ); + psbt.setInputSequence(0, 0); + assert.strictEqual((psbt as any).__CACHE.__TX.ins[0].sequence, 0); + }); it('throws if input index is too high', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, }); assert.throws(() => { - psbt.setInputSequence(1, 0) - }, new RegExp('Input index too high')) - }) - }) + psbt.setInputSequence(1, 0); + }, new RegExp('Input index too high')); + }); + }); describe('clone', () => { it('Should clone a psbt exactly with no reference', () => { - const f = fixtures.clone - const psbt = Psbt.fromBase64(f.psbt) - const notAClone = Object.assign(new Psbt(), psbt) // references still active - const clone = psbt.clone() + const f = fixtures.clone; + const psbt = Psbt.fromBase64(f.psbt); + const notAClone = Object.assign(new Psbt(), psbt); // references still active + const clone = psbt.clone(); - assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true) + assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true); - assert.strictEqual(clone.toBase64(), psbt.toBase64()) - assert.strictEqual(clone.toBase64(), notAClone.toBase64()) - assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - psbt.__CACHE.__TX.version |= 0xff0000 - assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) - assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) - assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - }) - }) + assert.strictEqual(clone.toBase64(), psbt.toBase64()); + assert.strictEqual(clone.toBase64(), notAClone.toBase64()); + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()); + (psbt as any).__CACHE.__TX.version |= 0xff0000; + assert.notStrictEqual(clone.toBase64(), psbt.toBase64()); + assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()); + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()); + }); + }); describe('setMaximumFeeRate', () => { it('Sets the maximumFeeRate value', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.opts.maximumFeeRate, 5000) - psbt.setMaximumFeeRate(6000) - assert.strictEqual(psbt.opts.maximumFeeRate, 6000) - }) - }) + assert.strictEqual((psbt as any).opts.maximumFeeRate, 5000); + psbt.setMaximumFeeRate(6000); + assert.strictEqual((psbt as any).opts.maximumFeeRate, 6000); + }); + }); describe('validateSignaturesOfInput', () => { - const f = fixtures.validateSignaturesOfInput + const f = fixtures.validateSignaturesOfInput; it('Correctly validates a signature', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true) + assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true); assert.throws(() => { - psbt.validateSignaturesOfInput(f.nonExistantIndex) - }, new RegExp('No signatures to validate')) - }) + psbt.validateSignaturesOfInput(f.nonExistantIndex); + }, new RegExp('No signatures to validate')); + }); it('Correctly validates a signature against a pubkey', () => { - const psbt = Psbt.fromBase64(f.psbt) - assert.strictEqual(psbt.validateSignaturesOfInput(f.index, f.pubkey), true) + const psbt = Psbt.fromBase64(f.psbt); + assert.strictEqual( + psbt.validateSignaturesOfInput(f.index, f.pubkey as any), + true, + ); assert.throws(() => { - psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey) - }, new RegExp('No signatures for this pubkey')) - }) - }) + psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey as any); + }, new RegExp('No signatures for this pubkey')); + }); + }); describe('getFeeRate', () => { it('Throws error if called before inputs are finalized', () => { - const f = fixtures.getFeeRate - const psbt = Psbt.fromBase64(f.psbt) + const f = fixtures.getFeeRate; + const psbt = Psbt.fromBase64(f.psbt); assert.throws(() => { - psbt.getFeeRate() - }, new RegExp('PSBT must be finalized to calculate fee rate')) + psbt.getFeeRate(); + }, new RegExp('PSBT must be finalized to calculate fee rate')); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.getFeeRate(), f.fee) - psbt.__CACHE.__FEE_RATE = undefined - assert.strictEqual(psbt.getFeeRate(), f.fee) - }) - }) + assert.strictEqual(psbt.getFeeRate(), f.fee); + (psbt as any).__CACHE.__FEE_RATE = undefined; + assert.strictEqual(psbt.getFeeRate(), f.fee); + }); + }); describe('create 1-to-1 transaction', () => { - const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') - const psbt = new Psbt() + const alice = ECPair.fromWIF( + 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', + ); + const psbt = new Psbt(); psbt.addInput({ hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', index: 0, nonWitnessUtxo: Buffer.from( '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + - '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + - 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + - '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + - '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + - 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + + 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', 'hex', ), sighashType: 1, - }) + }); psbt.addOutput({ address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', - value: 80000 - }) - psbt.signInput(0, alice) + value: 80000, + }); + psbt.signInput(0, alice); assert.throws(() => { - psbt.setVersion(3) - }, new RegExp('Can not modify transaction, signatures exist.')) - psbt.validateSignaturesOfInput(0) - psbt.finalizeAllInputs() + psbt.setVersion(3); + }, new RegExp('Can not modify transaction, signatures exist.')); + psbt.validateSignaturesOfInput(0); + psbt.finalizeAllInputs(); assert.throws(() => { - psbt.setVersion(3) - }, new RegExp('Can not modify transaction, signatures exist.')) + psbt.setVersion(3); + }, new RegExp('Can not modify transaction, signatures exist.')); assert.strictEqual( psbt.extractTransaction().toHex(), '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + - 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + - 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + - '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + - 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + - '08a22724efa6f6a07b0ec4c79aa88ac00000000', - ) - }) + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', + ); + }); describe('Method return types', () => { it('fromBuffer returns Psbt type (not base class)', () => { - const psbt = Psbt.fromBuffer(Buffer.from( - '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA - )); + const psbt = Psbt.fromBuffer( + Buffer.from( + '70736274ff01000a01000000000000000000000000', + 'hex', //cHNidP8BAAoBAAAAAAAAAAAAAAAA + ), + ); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) + assert.ok((psbt as any).__CACHE.__TX); + }); it('fromBase64 returns Psbt type (not base class)', () => { const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) + assert.ok((psbt as any).__CACHE.__TX); + }); it('fromHex returns Psbt type (not base class)', () => { const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) - }) + assert.ok((psbt as any).__CACHE.__TX); + }); + }); describe('Cache', () => { it('non-witness UTXOs are cached', () => { const f = fixtures.cache.nonWitnessUtxo; - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); const index = f.inputIndex; // Cache is empty - assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined) + assert.strictEqual( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], + undefined, + ); // Cache is populated - psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo }) - const value = psbt.data.inputs[index].nonWitnessUtxo - assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) - assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) + psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo as any }); + const value = psbt.data.inputs[index].nonWitnessUtxo; + assert.ok( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value), + ); + assert.ok( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals( + f.nonWitnessUtxo, + ), + ); // Cache is rebuilt from internal transaction object when cleared - psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) - psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined - assert.ok(psbt.data.inputs[index].nonWitnessUtxo.equals(value)) - }) - }) -}) + psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1, 2, 3]); + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined; + assert.ok((psbt as any).data.inputs[index].nonWitnessUtxo.equals(value)); + }); + }); +}); diff --git a/test/script.spec.ts b/test/script.spec.ts index d003558..23da986 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -1,157 +1,176 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const minimalData = require('minimaldata') - -const fixtures = require('./fixtures/script.json') -const fixtures2 = require('./fixtures/templates.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/script.json'; +import * as fixtures2 from './fixtures/templates.json'; +const minimalData = require('minimaldata'); describe('script', () => { // TODO describe('isCanonicalPubKey', () => { it('rejects if not provided a Buffer', () => { - assert.strictEqual(false, bscript.isCanonicalPubKey(0)) - }) + assert.strictEqual(false, bscript.isCanonicalPubKey(0 as any)); + }); it('rejects smaller than 33', () => { for (var i = 0; i < 33; i++) { - assert.strictEqual(false, bscript.isCanonicalPubKey(Buffer.from('', i))) + assert.strictEqual( + false, + bscript.isCanonicalPubKey(Buffer.allocUnsafe(i)), + ); } - }) - }) - describe.skip('isCanonicalScriptSignature', () => { - }) + }); + }); + describe.skip('isCanonicalScriptSignature', () => {}); describe('fromASM/toASM', () => { fixtures.valid.forEach(f => { it('encodes/decodes ' + f.asm, () => { - const script = bscript.fromASM(f.asm) - assert.strictEqual(bscript.toASM(script), f.asm) - }) - }) + const script = bscript.fromASM(f.asm); + assert.strictEqual(bscript.toASM(script), f.asm); + }); + }); fixtures.invalid.fromASM.forEach(f => { it('throws ' + f.description, () => { assert.throws(() => { - bscript.fromASM(f.script) - }, new RegExp(f.description)) - }) - }) - }) + bscript.fromASM(f.script); + }, new RegExp(f.description)); + }); + }); + }); describe('fromASM/toASM (templates)', () => { fixtures2.valid.forEach(f => { if (f.inputHex) { - const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')) + const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')); it('encodes/decodes ' + ih, () => { - const script = bscript.fromASM(f.input) - assert.strictEqual(script.toString('hex'), f.inputHex) - assert.strictEqual(bscript.toASM(script), f.input) - }) + const script = bscript.fromASM(f.input); + assert.strictEqual(script.toString('hex'), f.inputHex); + assert.strictEqual(bscript.toASM(script), f.input); + }); } if (f.outputHex) { it('encodes/decodes ' + f.output, () => { - const script = bscript.fromASM(f.output) - assert.strictEqual(script.toString('hex'), f.outputHex) - assert.strictEqual(bscript.toASM(script), f.output) - }) + const script = bscript.fromASM(f.output); + assert.strictEqual(script.toString('hex'), f.outputHex); + assert.strictEqual(bscript.toASM(script), f.output); + }); } - }) - }) + }); + }); describe('isPushOnly', () => { fixtures.valid.forEach(f => { it('returns ' + !!f.stack + ' for ' + f.asm, () => { - const script = bscript.fromASM(f.asm) - const chunks = bscript.decompile(script) + const script = bscript.fromASM(f.asm); + const chunks = bscript.decompile(script); - assert.strictEqual(bscript.isPushOnly(chunks), !!f.stack) - }) - }) - }) + assert.strictEqual(bscript.isPushOnly(chunks!), !!f.stack); + }); + }); + }); describe('toStack', () => { fixtures.valid.forEach(f => { it('returns ' + !!f.stack + ' for ' + f.asm, () => { - if (!f.stack || !f.asm) return + if (!f.stack || !f.asm) return; - const script = bscript.fromASM(f.asm) + const script = bscript.fromASM(f.asm); - const stack = bscript.toStack(script) - assert.deepStrictEqual(stack.map(x => { - return x.toString('hex') - }), f.stack) + const stack = bscript.toStack(script); + assert.deepStrictEqual( + stack.map(x => { + return x.toString('hex'); + }), + f.stack, + ); - assert.strictEqual(bscript.toASM(bscript.compile(stack)), f.asm, 'should rebuild same script from stack') - }) - }) - }) + assert.strictEqual( + bscript.toASM(bscript.compile(stack)), + f.asm, + 'should rebuild same script from stack', + ); + }); + }); + }); describe('compile (via fromASM)', () => { fixtures.valid.forEach(f => { - it('(' + f.type + ') compiles ' + f.asm, () => { - const scriptSig = bscript.fromASM(f.asm) + it('compiles ' + f.asm, () => { + const scriptSig = bscript.fromASM(f.asm); - assert.strictEqual(scriptSig.toString('hex'), f.script) + assert.strictEqual(scriptSig.toString('hex'), f.script); if (f.nonstandard) { - const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig) + const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig); - assert.strictEqual(scriptSigNS.toString('hex'), f.script) + assert.strictEqual(scriptSigNS.toString('hex'), f.script); } - }) - }) - }) + }); + }); + }); describe('decompile', () => { fixtures.valid.forEach(f => { it('decompiles ' + f.asm, () => { - const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')); - assert.strictEqual(bscript.compile(chunks).toString('hex'), f.script) - assert.strictEqual(bscript.toASM(chunks), f.asm) + assert.strictEqual(bscript.compile(chunks!).toString('hex'), f.script); + assert.strictEqual(bscript.toASM(chunks!), f.asm); if (f.nonstandard) { - const chunksNS = bscript.decompile(Buffer.from(f.nonstandard.scriptSigHex, 'hex')) + const chunksNS = bscript.decompile( + Buffer.from(f.nonstandard.scriptSigHex, 'hex'), + ); - assert.strictEqual(bscript.compile(chunksNS).toString('hex'), f.script) + assert.strictEqual( + bscript.compile(chunksNS!).toString('hex'), + f.script, + ); // toASM converts verbatim, only `compile` transforms the script to a minimalpush compliant script - assert.strictEqual(bscript.toASM(chunksNS), f.nonstandard.scriptSig) + assert.strictEqual(bscript.toASM(chunksNS!), f.nonstandard.scriptSig); } - }) - }) + }); + }); fixtures.invalid.decompile.forEach(f => { - it('fails to decompile ' + f.script + ', because "' + f.description + '"', () => { - const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + it( + 'fails to decompile ' + f.script + ', because "' + f.description + '"', + () => { + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')); - assert.strictEqual(chunks, null) - }) - }) - }) + assert.strictEqual(chunks, null); + }, + ); + }); + }); describe('SCRIPT_VERIFY_MINIMALDATA policy', () => { fixtures.valid.forEach(f => { - it('compliant for ' + f.type + ' scriptSig ' + f.asm, () => { - const script = Buffer.from(f.script, 'hex') + it('compliant for scriptSig ' + f.asm, () => { + const script = Buffer.from(f.script, 'hex'); - assert(minimalData(script)) - }) - }) + assert(minimalData(script)); + }); + }); - function testEncodingForSize (i) { + function testEncodingForSize(i: number) { it('compliant for data PUSH of length ' + i, () => { - const buffer = Buffer.alloc(i) - const script = bscript.compile([buffer]) + const buffer = Buffer.alloc(i); + const script = bscript.compile([buffer]); - assert(minimalData(script), 'Failed for ' + i + ' length script: ' + script.toString('hex')) - }) + assert( + minimalData(script), + 'Failed for ' + i + ' length script: ' + script.toString('hex'), + ); + }); } for (var i = 0; i < 520; ++i) { - testEncodingForSize(i) + testEncodingForSize(i); } - }) -}) + }); +}); diff --git a/test/script_number.spec.ts b/test/script_number.spec.ts index d5b97e2..3b05082 100644 --- a/test/script_number.spec.ts +++ b/test/script_number.spec.ts @@ -1,26 +1,26 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const scriptNumber = require('../src/script_number') -const fixtures = require('./fixtures/script_number.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as scriptNumber from '../src/script_number'; +import * as fixtures from './fixtures/script_number.json'; describe('script-number', () => { describe('decode', () => { fixtures.forEach(f => { it(f.hex + ' returns ' + f.number, () => { - const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes) + const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes); - assert.strictEqual(actual, f.number) - }) - }) - }) + assert.strictEqual(actual, f.number); + }); + }); + }); describe('encode', () => { fixtures.forEach(f => { it(f.number + ' returns ' + f.hex, () => { - const actual = scriptNumber.encode(f.number) + const actual = scriptNumber.encode(f.number); - assert.strictEqual(actual.toString('hex'), f.hex) - }) - }) - }) -}) + assert.strictEqual(actual.toString('hex'), f.hex); + }); + }); + }); +}); diff --git a/test/script_signature.spec.ts b/test/script_signature.spec.ts index 6888ca5..3be52ad 100644 --- a/test/script_signature.spec.ts +++ b/test/script_signature.spec.ts @@ -1,64 +1,63 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscriptSig = require('../src/script').signature -const Buffer = require('safe-buffer').Buffer -const fixtures = require('./fixtures/signature.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import { signature as bscriptSig } from '../src/script'; +import * as fixtures from './fixtures/signature.json'; describe('Script Signatures', () => { - function fromRaw (signature) { - return Buffer.concat([ - Buffer.from(signature.r, 'hex'), - Buffer.from(signature.s, 'hex') - ], 64) + function fromRaw(signature: { r: string; s: string }) { + return Buffer.concat( + [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')], + 64, + ); } - function toRaw (signature) { + function toRaw(signature: Buffer) { return { r: signature.slice(0, 32).toString('hex'), - s: signature.slice(32, 64).toString('hex') - } + s: signature.slice(32, 64).toString('hex'), + }; } describe('encode', () => { fixtures.valid.forEach(f => { it('encodes ' + f.hex, () => { - const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) + const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType); - assert.strictEqual(buffer.toString('hex'), f.hex) - }) - }) + assert.strictEqual(buffer.toString('hex'), f.hex); + }); + }); fixtures.invalid.forEach(f => { - if (!f.raw) return + if (!f.raw) return; it('throws ' + f.exception, () => { - const signature = fromRaw(f.raw) + const signature = fromRaw(f.raw); assert.throws(() => { - bscriptSig.encode(signature, f.hashType) - }, new RegExp(f.exception)) - }) - }) - }) + bscriptSig.encode(signature, f.hashType); + }, new RegExp(f.exception)); + }); + }); + }); describe('decode', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { - const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) + const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')); - assert.deepStrictEqual(toRaw(decode.signature), f.raw) - assert.strictEqual(decode.hashType, f.hashType) - }) - }) + assert.deepStrictEqual(toRaw(decode.signature), f.raw); + assert.strictEqual(decode.hashType, f.hashType); + }); + }); fixtures.invalid.forEach(f => { it('throws on ' + f.hex, () => { - const buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex'); assert.throws(() => { - bscriptSig.decode(buffer) - }, new RegExp(f.exception)) - }) - }) - }) -}) + bscriptSig.decode(buffer); + }, new RegExp(f.exception)); + }); + }); + }); +}); diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts index 3fa9243..c87d9e6 100644 --- a/test/transaction.spec.ts +++ b/test/transaction.spec.ts @@ -1,288 +1,338 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const fixtures = require('./fixtures/transaction') -const Transaction = require('..').Transaction +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import { Transaction } from '..'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/transaction.json'; describe('Transaction', () => { - function fromRaw (raw, noWitness) { - const tx = new Transaction() - tx.version = raw.version - tx.locktime = raw.locktime + function fromRaw(raw: any, noWitness?: boolean) { + const tx = new Transaction(); + tx.version = raw.version; + tx.locktime = raw.locktime; - raw.ins.forEach((txIn, i) => { - const txHash = Buffer.from(txIn.hash, 'hex') - let scriptSig + raw.ins.forEach((txIn: any, i: number) => { + const txHash = Buffer.from(txIn.hash, 'hex'); + let scriptSig; if (txIn.data) { - scriptSig = Buffer.from(txIn.data, 'hex') + scriptSig = Buffer.from(txIn.data, 'hex'); } else if (txIn.script) { - scriptSig = bscript.fromASM(txIn.script) + scriptSig = bscript.fromASM(txIn.script); } - tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig) + tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig); if (!noWitness && txIn.witness) { - const witness = txIn.witness.map(x => { - return Buffer.from(x, 'hex') - }) + const witness = txIn.witness.map((x: string) => { + return Buffer.from(x, 'hex'); + }); - tx.setWitness(i, witness) + tx.setWitness(i, witness); } - }) + }); - raw.outs.forEach(txOut => { - let script + raw.outs.forEach((txOut: any) => { + let script: Buffer; if (txOut.data) { - script = Buffer.from(txOut.data, 'hex') + script = Buffer.from(txOut.data, 'hex'); } else if (txOut.script) { - script = bscript.fromASM(txOut.script) + script = bscript.fromASM(txOut.script); } - tx.addOutput(script, txOut.value) - }) + tx.addOutput(script!, txOut.value); + }); - return tx + return tx; } describe('fromBuffer/fromHex', () => { - function importExport (f) { - const id = f.id || f.hash - const txHex = f.hex || f.txHex + function importExport(f: any) { + const id = f.id || f.hash; + const txHex = f.hex || f.txHex; it('imports ' + f.description + ' (' + id + ')', () => { - const actual = Transaction.fromHex(txHex) + const actual = Transaction.fromHex(txHex); - assert.strictEqual(actual.toHex(), txHex) - }) + assert.strictEqual(actual.toHex(), txHex); + }); if (f.whex) { it('imports ' + f.description + ' (' + id + ') as witness', () => { - const actual = Transaction.fromHex(f.whex) + const actual = Transaction.fromHex(f.whex); - assert.strictEqual(actual.toHex(), f.whex) - }) + assert.strictEqual(actual.toHex(), f.whex); + }); } } - fixtures.valid.forEach(importExport) - fixtures.hashForSignature.forEach(importExport) - fixtures.hashForWitnessV0.forEach(importExport) + fixtures.valid.forEach(importExport); + fixtures.hashForSignature.forEach(importExport); + fixtures.hashForWitnessV0.forEach(importExport); fixtures.invalid.fromBuffer.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - Transaction.fromHex(f.hex) - }, new RegExp(f.exception)) - }) - }) + Transaction.fromHex(f.hex); + }, new RegExp(f.exception)); + }); + }); it('.version should be interpreted as an int32le', () => { - const txHex = 'ffffffff0000ffffffff' - const tx = Transaction.fromHex(txHex) - assert.strictEqual(-1, tx.version) - assert.strictEqual(0xffffffff, tx.locktime) - }) - }) + const txHex = 'ffffffff0000ffffffff'; + const tx = Transaction.fromHex(txHex); + assert.strictEqual(-1, tx.version); + assert.strictEqual(0xffffffff, tx.locktime); + }); + }); describe('toBuffer/toHex', () => { fixtures.valid.forEach(f => { it('exports ' + f.description + ' (' + f.id + ')', () => { - const actual = fromRaw(f.raw, true) - assert.strictEqual(actual.toHex(), f.hex) - }) + const actual = fromRaw(f.raw, true); + assert.strictEqual(actual.toHex(), f.hex); + }); if (f.whex) { it('exports ' + f.description + ' (' + f.id + ') as witness', () => { - const wactual = fromRaw(f.raw) - assert.strictEqual(wactual.toHex(), f.whex) - }) + const wactual = fromRaw(f.raw); + assert.strictEqual(wactual.toHex(), f.whex); + }); } - }) + }); it('accepts target Buffer and offset parameters', () => { - const f = fixtures.valid[0] - const actual = fromRaw(f.raw) - const byteLength = actual.byteLength() + const f = fixtures.valid[0]; + const actual = fromRaw(f.raw); + const byteLength = actual.byteLength(); - const target = Buffer.alloc(byteLength * 2) - const a = actual.toBuffer(target, 0) - const b = actual.toBuffer(target, byteLength) + const target = Buffer.alloc(byteLength * 2); + const a = actual.toBuffer(target, 0); + const b = actual.toBuffer(target, byteLength); - assert.strictEqual(a.length, byteLength) - assert.strictEqual(b.length, byteLength) - assert.strictEqual(a.toString('hex'), f.hex) - assert.strictEqual(b.toString('hex'), f.hex) - assert.deepStrictEqual(a, b) - assert.deepStrictEqual(a, target.slice(0, byteLength)) - assert.deepStrictEqual(b, target.slice(byteLength)) - }) - }) + assert.strictEqual(a.length, byteLength); + assert.strictEqual(b.length, byteLength); + assert.strictEqual(a.toString('hex'), f.hex); + assert.strictEqual(b.toString('hex'), f.hex); + assert.deepStrictEqual(a, b); + assert.deepStrictEqual(a, target.slice(0, byteLength)); + assert.deepStrictEqual(b, target.slice(byteLength)); + }); + }); describe('hasWitnesses', () => { fixtures.valid.forEach(f => { - it('detects if the transaction has witnesses: ' + (f.whex ? 'true' : 'false'), () => { - assert.strictEqual(Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), !!f.whex) - }) - }) - }) + it( + 'detects if the transaction has witnesses: ' + + (f.whex ? 'true' : 'false'), + () => { + assert.strictEqual( + Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), + !!f.whex, + ); + }, + ); + }); + }); describe('weight/virtualSize', () => { it('computes virtual size', () => { fixtures.valid.forEach(f => { - const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex); - assert.strictEqual(transaction.virtualSize(), f.virtualSize) - }) - }) + assert.strictEqual(transaction.virtualSize(), f.virtualSize); + }); + }); it('computes weight', () => { fixtures.valid.forEach(f => { - const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex); - assert.strictEqual(transaction.weight(), f.weight) - }) - }) - }) + assert.strictEqual(transaction.weight(), f.weight); + }); + }); + }); describe('addInput', () => { - let prevTxHash + let prevTxHash: Buffer; beforeEach(() => { - prevTxHash = Buffer.from('ffffffff00ffff000000000000000000000000000000000000000000101010ff', 'hex') - }) + prevTxHash = Buffer.from( + 'ffffffff00ffff000000000000000000000000000000000000000000101010ff', + 'hex', + ); + }); it('returns an index', () => { - const tx = new Transaction() - assert.strictEqual(tx.addInput(prevTxHash, 0), 0) - assert.strictEqual(tx.addInput(prevTxHash, 0), 1) - }) + const tx = new Transaction(); + assert.strictEqual(tx.addInput(prevTxHash, 0), 0); + assert.strictEqual(tx.addInput(prevTxHash, 0), 1); + }); it('defaults to empty script, witness and 0xffffffff SEQUENCE number', () => { - const tx = new Transaction() - tx.addInput(prevTxHash, 0) + const tx = new Transaction(); + tx.addInput(prevTxHash, 0); - assert.strictEqual(tx.ins[0].script.length, 0) - assert.strictEqual(tx.ins[0].witness.length, 0) - assert.strictEqual(tx.ins[0].sequence, 0xffffffff) - }) + assert.strictEqual(tx.ins[0].script.length, 0); + assert.strictEqual(tx.ins[0].witness.length, 0); + assert.strictEqual(tx.ins[0].sequence, 0xffffffff); + }); fixtures.invalid.addInput.forEach(f => { it('throws on ' + f.exception, () => { - const tx = new Transaction() - const hash = Buffer.from(f.hash, 'hex') + const tx = new Transaction(); + const hash = Buffer.from(f.hash, 'hex'); assert.throws(() => { - tx.addInput(hash, f.index) - }, new RegExp(f.exception)) - }) - }) - }) + tx.addInput(hash, f.index); + }, new RegExp(f.exception)); + }); + }); + }); describe('addOutput', () => { it('returns an index', () => { - const tx = new Transaction() - assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0) - assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1) - }) - }) + const tx = new Transaction(); + assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0); + assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1); + }); + }); describe('clone', () => { fixtures.valid.forEach(f => { - let actual - let expected + let actual: Transaction; + let expected: Transaction; beforeEach(() => { - expected = Transaction.fromHex(f.hex) - actual = expected.clone() - }) + expected = Transaction.fromHex(f.hex); + actual = expected.clone(); + }); it('should have value equality', () => { - assert.deepStrictEqual(actual, expected) - }) + assert.deepStrictEqual(actual, expected); + }); it('should not have reference equality', () => { - assert.notStrictEqual(actual, expected) - }) - }) - }) + assert.notStrictEqual(actual, expected); + }); + }); + }); describe('getHash/getId', () => { - function verify (f) { + function verify(f: any) { it('should return the id for ' + f.id + '(' + f.description + ')', () => { - const tx = Transaction.fromHex(f.whex || f.hex) + const tx = Transaction.fromHex(f.whex || f.hex); - assert.strictEqual(tx.getHash().toString('hex'), f.hash) - assert.strictEqual(tx.getId(), f.id) - }) + assert.strictEqual(tx.getHash().toString('hex'), f.hash); + assert.strictEqual(tx.getId(), f.id); + }); } - fixtures.valid.forEach(verify) - }) + fixtures.valid.forEach(verify); + }); describe('isCoinbase', () => { - function verify (f) { - it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', () => { - const tx = Transaction.fromHex(f.hex) + function verify(f: any) { + it( + 'should return ' + + f.coinbase + + ' for ' + + f.id + + '(' + + f.description + + ')', + () => { + const tx = Transaction.fromHex(f.hex); - assert.strictEqual(tx.isCoinbase(), f.coinbase) - }) + assert.strictEqual(tx.isCoinbase(), f.coinbase); + }, + ); } - fixtures.valid.forEach(verify) - }) + fixtures.valid.forEach(verify); + }); describe('hashForSignature', () => { it('does not use Witness serialization', () => { - const randScript = Buffer.from('6a', 'hex') + const randScript = Buffer.from('6a', 'hex'); - const tx = new Transaction() - tx.addInput(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 0) - tx.addOutput(randScript, 5000000000) + const tx = new Transaction(); + tx.addInput( + Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex', + ), + 0, + ); + tx.addOutput(randScript, 5000000000); - const original = tx.__toBuffer - tx.__toBuffer = (a, b, c) => { - if (c !== false) throw new Error('hashForSignature MUST pass false') + const original = (tx as any).__toBuffer; + (tx as any).__toBuffer = function( + this: Transaction, + a: any, + b: any, + c: any, + ): any { + if (c !== false) throw new Error('hashForSignature MUST pass false'); - return original.call(this, a, b, c) - } + return original.call(this, a, b, c); + }; assert.throws(() => { - tx.__toBuffer(undefined, undefined, true) - }, /hashForSignature MUST pass false/) + (tx as any).__toBuffer(undefined, undefined, true); + }, /hashForSignature MUST pass false/); // assert hashForSignature does not pass false assert.doesNotThrow(() => { - tx.hashForSignature(0, randScript, 1) - }) - }) + tx.hashForSignature(0, randScript, 1); + }); + }); fixtures.hashForSignature.forEach(f => { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : f.script), () => { - const tx = Transaction.fromHex(f.txHex) - const script = bscript.fromASM(f.script) + it( + 'should return ' + + f.hash + + ' for ' + + (f.description ? 'case "' + f.description + '"' : f.script), + () => { + const tx = Transaction.fromHex(f.txHex); + const script = bscript.fromASM(f.script); - assert.strictEqual(tx.hashForSignature(f.inIndex, script, f.type).toString('hex'), f.hash) - }) - }) - }) + assert.strictEqual( + tx.hashForSignature(f.inIndex, script, f.type).toString('hex'), + f.hash, + ); + }, + ); + }); + }); describe('hashForWitnessV0', () => { fixtures.hashForWitnessV0.forEach(f => { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), () => { - const tx = Transaction.fromHex(f.txHex) - const script = bscript.fromASM(f.script) + it( + 'should return ' + + f.hash + + ' for ' + + (f.description ? 'case "' + f.description + '"' : ''), + () => { + const tx = Transaction.fromHex(f.txHex); + const script = bscript.fromASM(f.script); - assert.strictEqual(tx.hashForWitnessV0(f.inIndex, script, f.value, f.type).toString('hex'), f.hash) - }) - }) - }) + assert.strictEqual( + tx + .hashForWitnessV0(f.inIndex, script, f.value, f.type) + .toString('hex'), + f.hash, + ); + }, + ); + }); + }); describe('setWitness', () => { it('only accepts a a witness stack (Array of Buffers)', () => { assert.throws(() => { - (new Transaction()).setWitness(0, 'foobar') - }, /Expected property "1" of type \[Buffer], got String "foobar"/) - }) - }) -}) + (new Transaction().setWitness as any)(0, 'foobar'); + }, /Expected property "1" of type \[Buffer], got String "foobar"/); + }); + }); +}); diff --git a/test/transaction_builder.spec.ts b/test/transaction_builder.spec.ts index 6374161..0daa103 100644 --- a/test/transaction_builder.spec.ts +++ b/test/transaction_builder.spec.ts @@ -1,45 +1,57 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') -const payments = require('../src/payments') +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import * as baddress from '../src/address'; +import * as bscript from '../src/script'; +import * as payments from '../src/payments'; +import { + ECPair, + networks as NETWORKS, + Transaction, + TransactionBuilder, +} from '..'; -const ECPair = require('../src/ecpair') -const Transaction = require('..').Transaction -const TransactionBuilder = require('..').TransactionBuilder -const NETWORKS = require('../src/networks') +console.warn = () => {}; // Silence the Deprecation Warning -console.warn = () => {} // Silence the Deprecation Warning +import * as fixtures from './fixtures/transaction_builder.json'; -const fixtures = require('./fixtures/transaction_builder') +function constructSign( + f: any, + txb: any, + useOldSignArgs: any, +): TransactionBuilder { + const network = (NETWORKS as any)[f.network]; + const stages = f.stages && f.stages.concat(); -function constructSign (f, txb, useOldSignArgs) { - const network = NETWORKS[f.network] - const stages = f.stages && f.stages.concat() - - f.inputs.forEach((input, index) => { - if (!input.signs) return - input.signs.forEach(sign => { - const keyPair = ECPair.fromWIF(sign.keyPair, network) - let redeemScript - let witnessScript - let witnessValue + f.inputs.forEach((input: any, index: number) => { + if (!input.signs) return; + input.signs.forEach((sign: any) => { + const keyPair = ECPair.fromWIF(sign.keyPair, network); + let redeemScript; + let witnessScript; + let witnessValue; if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) + redeemScript = bscript.fromASM(sign.redeemScript); } if (sign.value) { - witnessValue = sign.value + witnessValue = sign.value; } if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) + witnessScript = bscript.fromASM(sign.witnessScript); } if (useOldSignArgs) { // DEPRECATED: v6 will remove this interface - txb.sign(index, keyPair, redeemScript, sign.hashType, witnessValue, witnessScript) + txb.sign( + index, + keyPair, + redeemScript, + sign.hashType, + witnessValue, + witnessScript, + ); } else { // prevOutScriptType is required, see /ts_src/transaction_builder.ts // The PREVOUT_TYPES constant is a Set with all possible values. @@ -51,64 +63,68 @@ function constructSign (f, txb, useOldSignArgs) { hashType: sign.hashType, witnessValue, witnessScript, - }) + }); } if (sign.stage) { - const tx = txb.buildIncomplete() - assert.strictEqual(tx.toHex(), stages.shift()) - txb = TransactionBuilder.fromTransaction(tx, network) + const tx = txb.buildIncomplete(); + assert.strictEqual(tx.toHex(), stages.shift()); + txb = TransactionBuilder.fromTransaction(tx, network); } - }) - }) + }); + }); - return txb + return txb; } -function construct (f, dontSign, useOldSignArgs) { - const network = NETWORKS[f.network] - const txb = new TransactionBuilder(network) +function construct( + f: any, + dontSign?: any, + useOldSignArgs?: any, +): TransactionBuilder { + const network = (NETWORKS as any)[f.network]; + const txb = new TransactionBuilder(network); - if (Number.isFinite(f.version)) txb.setVersion(f.version) - if (f.locktime !== undefined) txb.setLockTime(f.locktime) + if (Number.isFinite(f.version)) txb.setVersion(f.version); + if (f.locktime !== undefined) txb.setLockTime(f.locktime); - f.inputs.forEach(input => { - let prevTx + f.inputs.forEach((input: any) => { + let prevTx; if (input.txRaw) { - const constructed = construct(input.txRaw) - if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete() - else prevTx = constructed.build() + const constructed = construct(input.txRaw); + if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete(); + else prevTx = constructed.build(); } else if (input.txHex) { - prevTx = Transaction.fromHex(input.txHex) + prevTx = Transaction.fromHex(input.txHex); } else { - prevTx = input.txId + prevTx = input.txId; } - let prevTxScript + let prevTxScript; if (input.prevTxScript) { - prevTxScript = bscript.fromASM(input.prevTxScript) + prevTxScript = bscript.fromASM(input.prevTxScript); } - txb.addInput(prevTx, input.vout, input.sequence, prevTxScript) - }) + txb.addInput(prevTx, input.vout, input.sequence, prevTxScript); + }); - f.outputs.forEach(output => { + f.outputs.forEach((output: any) => { if (output.address) { - txb.addOutput(output.address, output.value) + txb.addOutput(output.address, output.value); } else { - txb.addOutput(bscript.fromASM(output.script), output.value) + txb.addOutput(bscript.fromASM(output.script), output.value); } - }) + }); - if (dontSign) return txb - return constructSign(f, txb, useOldSignArgs) + if (dontSign) return txb; + return constructSign(f, txb, useOldSignArgs); } // TODO: Remove loop in v6 -for (const useOldSignArgs of [ false, true ]) { +for (const useOldSignArgs of [false, true]) { // Search for "useOldSignArgs" // to find the second part of this console.warn replace - let consoleWarn; + let consoleWarn: any; if (useOldSignArgs) { consoleWarn = console.warn; // Silence console.warn during these tests @@ -116,597 +132,699 @@ for (const useOldSignArgs of [ false, true ]) { } describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { // constants - const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) + const keyPair = ECPair.fromPrivateKey( + Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', + ), + ); const scripts = [ '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', - '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' + '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', ].map(x => { - return baddress.toOutputScript(x) - }) - const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') + return baddress.toOutputScript(x); + }); + const txHash = Buffer.from( + '0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', + 'hex', + ); describe('fromTransaction', () => { fixtures.valid.build.forEach(f => { it('returns TransactionBuilder, with ' + f.description, () => { - const network = NETWORKS[f.network || 'bitcoin'] + const network = (NETWORKS as any)[f.network || 'bitcoin']; - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const tx = Transaction.fromHex(f.txHex); + const txb = TransactionBuilder.fromTransaction(tx, network); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); - assert.strictEqual(txAfter.toHex(), f.txHex) - assert.strictEqual(txb.network, network) - }) - }) + assert.strictEqual(txAfter.toHex(), f.txHex); + assert.strictEqual(txb.network, network); + }); + }); fixtures.valid.fromTransaction.forEach(f => { it('returns TransactionBuilder, with ' + f.description, () => { - const tx = new Transaction() + const tx = new Transaction(); f.inputs.forEach(input => { - const txHash2 = Buffer.from(input.txId, 'hex').reverse() + const txHash2 = Buffer.from(input.txId, 'hex').reverse() as Buffer; - tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) - }) + tx.addInput( + txHash2, + input.vout, + undefined, + bscript.fromASM(input.scriptSig), + ); + }); f.outputs.forEach(output => { - tx.addOutput(bscript.fromASM(output.script), output.value) - }) + tx.addOutput(bscript.fromASM(output.script), output.value); + }); - const txb = TransactionBuilder.fromTransaction(tx) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = TransactionBuilder.fromTransaction(tx); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSigAfter, + ); + }); txAfter.outs.forEach((output, i) => { - assert.strictEqual(bscript.toASM(output.script), f.outputs[i].script) - }) - }) - }) + assert.strictEqual( + bscript.toASM(output.script), + f.outputs[i].script, + ); + }); + }); + }); fixtures.valid.fromTransactionSequential.forEach(f => { it('with ' + f.description, () => { - const network = NETWORKS[f.network] - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) + const network = (NETWORKS as any)[f.network]; + const tx = Transaction.fromHex(f.txHex); + const txb = TransactionBuilder.fromTransaction(tx, network); tx.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSig) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSig, + ); + }); - constructSign(f, txb, useOldSignArgs) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + constructSign(f, txb, useOldSignArgs); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSigAfter, + ); + }); - assert.strictEqual(txAfter.toHex(), f.txHexAfter) - }) - }) + assert.strictEqual(txAfter.toHex(), f.txHexAfter); + }); + }); it('classifies transaction inputs', () => { - const tx = Transaction.fromHex(fixtures.valid.classification.hex) - const txb = TransactionBuilder.fromTransaction(tx) + const tx = Transaction.fromHex(fixtures.valid.classification.hex); + const txb = TransactionBuilder.fromTransaction(tx); - txb.__INPUTS.forEach(i => { - assert.strictEqual(i.prevOutType, 'scripthash') - assert.strictEqual(i.redeemScriptType, 'multisig') - }) - }) + (txb as any).__INPUTS.forEach((i: any) => { + assert.strictEqual(i.prevOutType, 'scripthash'); + assert.strictEqual(i.redeemScriptType, 'multisig'); + }); + }); fixtures.invalid.fromTransaction.forEach(f => { it('throws ' + f.exception, () => { - const tx = Transaction.fromHex(f.txHex) + const tx = Transaction.fromHex(f.txHex); assert.throws(() => { - TransactionBuilder.fromTransaction(tx) - }, new RegExp(f.exception)) - }) - }) - }) + TransactionBuilder.fromTransaction(tx); + }, new RegExp(f.exception)); + }); + }); + }); describe('addInput', () => { - let txb + let txb: TransactionBuilder; beforeEach(() => { - txb = new TransactionBuilder() - }) + txb = new TransactionBuilder(); + }); it('accepts a txHash, index [and sequence number]', () => { - const vin = txb.addInput(txHash, 1, 54) - assert.strictEqual(vin, 0) + const vin = txb.addInput(txHash, 1, 54); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.strictEqual(txIn.hash, txHash); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, undefined); + }); it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { - const vin = txb.addInput(txHash, 1, 54, scripts[1]) - assert.strictEqual(vin, 0) + const vin = txb.addInput(txHash, 1, 54, scripts[1]); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.strictEqual(txIn.hash, txHash); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); + }); it('accepts a prevTx, index [and sequence number]', () => { - const prevTx = new Transaction() - prevTx.addOutput(scripts[0], 0) - prevTx.addOutput(scripts[1], 1) + const prevTx = new Transaction(); + prevTx.addOutput(scripts[0], 0); + prevTx.addOutput(scripts[1], 1); - const vin = txb.addInput(prevTx, 1, 54) - assert.strictEqual(vin, 0) + const vin = txb.addInput(prevTx, 1, 54); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.deepStrictEqual(txIn.hash, prevTx.getHash()) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.deepStrictEqual(txIn.hash, prevTx.getHash()); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); + }); it('returns the input index', () => { - assert.strictEqual(txb.addInput(txHash, 0), 0) - assert.strictEqual(txb.addInput(txHash, 1), 1) - }) + assert.strictEqual(txb.addInput(txHash, 0), 0); + assert.strictEqual(txb.addInput(txHash, 1), 1); + }); it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 1000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 1000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.addInput(txHash, 0) - }, /No, this would invalidate signatures/) - }) - }) + txb.addInput(txHash, 0); + }, /No, this would invalidate signatures/); + }); + }); describe('addOutput', () => { - let txb + let txb: TransactionBuilder; beforeEach(() => { - txb = new TransactionBuilder() - }) + txb = new TransactionBuilder(); + }); it('accepts an address string and value', () => { - const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) - const vout = txb.addOutput(address, 1000) - assert.strictEqual(vout, 0) + const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }); + const vout = txb.addOutput(address!, 1000); + assert.strictEqual(vout, 0); - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) + const txout = (txb as any).__TX.outs[0]; + assert.deepStrictEqual(txout.script, scripts[0]); + assert.strictEqual(txout.value, 1000); + }); it('accepts a ScriptPubKey and value', () => { - const vout = txb.addOutput(scripts[0], 1000) - assert.strictEqual(vout, 0) + const vout = txb.addOutput(scripts[0], 1000); + assert.strictEqual(vout, 0); - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) + const txout = (txb as any).__TX.outs[0]; + assert.deepStrictEqual(txout.script, scripts[0]); + assert.strictEqual(txout.value, 1000); + }); it('throws if address is of the wrong network', () => { assert.throws(() => { - txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000) - }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/) - }) + txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000); + }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/); + }); it('add second output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) + }); + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); + }); it('add first output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) + txb.addInput(txHash, 0); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) - }) + }); + assert.strictEqual(txb.addOutput(scripts[0], 2000), 0); + }); it('add second output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_SINGLE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) + }); + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); + }); it('add first output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) + txb.addInput(txHash, 0); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_SINGLE, - }) + }); assert.throws(() => { - txb.addOutput(scripts[0], 2000) - }, /No, this would invalidate signatures/) - }) + txb.addOutput(scripts[0], 2000); + }, /No, this would invalidate signatures/); + }); it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.addOutput(scripts[1], 9000) - }, /No, this would invalidate signatures/) - }) - }) + txb.addOutput(scripts[1], 9000); + }, /No, this would invalidate signatures/); + }); + }); describe('setLockTime', () => { it('throws if if there exist any scriptSigs', () => { - const txb = new TransactionBuilder() - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 100) + const txb = new TransactionBuilder(); + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 100); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.setLockTime(65535) - }, /No, this would invalidate signatures/) - }) - }) + txb.setLockTime(65535); + }, /No, this would invalidate signatures/); + }); + }); describe('sign', () => { it('supports the alternative abstract interface { publicKey, sign }', () => { const keyPair = { - publicKey: ECPair.makeRandom({ rng: () => { return Buffer.alloc(32, 1) } }).publicKey, - sign: hash => { return Buffer.alloc(64, 0x5f) } - } + publicKey: ECPair.makeRandom({ + rng: () => { + return Buffer.alloc(32, 1); + }, + }).publicKey, + sign: () => { + return Buffer.alloc(64, 0x5f); + }, + }; - const txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + const txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') - }) + }); + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); + }); it('supports low R signature signing', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // high R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); - txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.setLowR() + txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); + txb.setLowR(); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // low R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); + }); it('fails when missing required arguments', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); assert.throws(() => { - txb.sign() - }, /TransactionBuilder sign first arg must be TxbSignArg or number/) + (txb as any).sign(); + }, /TransactionBuilder sign first arg must be TxbSignArg or number/); assert.throws(() => { txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, keyPair, - }) - }, /No input at index: 1/) + }); + }, /No input at index: 1/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', keyPair, - }) - }, /sign must include vin parameter as Number \(input index\)/) + }); + }, /sign must include vin parameter as Number \(input index\)/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair: {}, - }) - }, /sign must include keyPair parameter as Signer interface/) + }); + }, /sign must include keyPair parameter as Signer interface/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: 'string', - }) - }, /sign hashType parameter must be a number/) + }); + }, /sign hashType parameter must be a number/); if (useOldSignArgs) { assert.throws(() => { - txb.sign(0) - }, /sign requires keypair/) + txb.sign(0); + }, /sign requires keypair/); } - }) + }); fixtures.invalid.sign.forEach(f => { - it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { - const txb = construct(f, true) + it( + 'throws ' + + f.exception + + (f.description ? ' (' + f.description + ')' : ''), + () => { + const txb = construct(f, true); - let threw = false - f.inputs.forEach((input, index) => { - input.signs.forEach(sign => { - const keyPairNetwork = NETWORKS[sign.network || f.network] - const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) - let redeemScript - let witnessScript + let threw = false; + (f.inputs as any).forEach( + (input: any, index: number): void => { + input.signs.forEach((sign: any) => { + const keyPairNetwork = (NETWORKS as any)[ + sign.network || f.network + ]; + const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork); + let redeemScript: Buffer | undefined; + let witnessScript: Buffer | undefined; - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) - } + if (sign.redeemScript) { + redeemScript = bscript.fromASM(sign.redeemScript); + } - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) - } + if (sign.witnessScript) { + witnessScript = bscript.fromASM(sign.witnessScript); + } - if (sign.throws) { - assert.throws(() => { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }) - }, new RegExp(f.exception)) - threw = true - } else { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }) - } - }) - }) + if (sign.throws) { + assert.throws(() => { + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }); + }, new RegExp(f.exception)); + threw = true; + } else { + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }); + } + }); + }, + ); - assert.strictEqual(threw, true) - }) - }) - }) + assert.strictEqual(threw, true); + }, + ); + }); + }); describe('build', () => { fixtures.valid.build.forEach(f => { it('builds "' + f.description + '"', () => { - const txb = construct(f, undefined, useOldSignArgs) - const tx = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = construct(f, undefined, useOldSignArgs); + const tx = f.incomplete ? txb.buildIncomplete() : txb.build(); - assert.strictEqual(tx.toHex(), f.txHex) - }) - }) + assert.strictEqual(tx.toHex(), f.txHex); + }); + }); // TODO: remove duplicate test code fixtures.invalid.build.forEach(f => { describe('for ' + (f.description || f.exception), () => { it('throws ' + f.exception, () => { assert.throws(() => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.build() - }, new RegExp(f.exception)) - }) + txb.build(); + }, new RegExp(f.exception)); + }); // if throws on incomplete too, enforce that if (f.incomplete) { it('throws ' + f.exception, () => { assert.throws(() => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.buildIncomplete() - }, new RegExp(f.exception)) - }) + txb.buildIncomplete(); + }, new RegExp(f.exception)); + }); } else { it('does not throw if buildIncomplete', () => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); } - }) - }) + }); + }); it('for incomplete with 0 signatures', () => { - const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' - const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' + const randomTxData = + '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; + const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'; - const randomTx = Transaction.fromHex(randomTxData) - let tx = new TransactionBuilder() - tx.addInput(randomTx, 0) - tx.addOutput(randomAddress, 1000) - tx = tx.buildIncomplete() - assert(tx) - }) + const randomTx = Transaction.fromHex(randomTxData); + const txb = new TransactionBuilder(); + txb.addInput(randomTx, 0); + txb.addOutput(randomAddress, 1000); + const tx = txb.buildIncomplete(); + assert(tx); + }); it('for incomplete P2SH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input - const inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', + 'hex', + ); // arbitrary P2SH input + const inpTx = Transaction.fromBuffer(inp); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); it('for incomplete P2WPKH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') - const inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', + 'hex', + ); + const inpTx = Transaction.fromBuffer(inp); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); it('for incomplete P2WSH with 0 signatures', () => { - const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) + const inpTx = Transaction.fromBuffer( + Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', + 'hex', + ), + ); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) - }) + txb.buildIncomplete(); + }); + }); describe('multisig', () => { fixtures.valid.multisig.forEach(f => { it(f.description, () => { - const network = NETWORKS[f.network] - let txb = construct(f, true) - let tx + const network = (NETWORKS as any)[f.network]; + let txb = construct(f, true); + let tx: Transaction; f.inputs.forEach((input, i) => { - const redeemScript = bscript.fromASM(input.redeemScript) + const redeemScript = bscript.fromASM(input.redeemScript); input.signs.forEach(sign => { // rebuild the transaction each-time after the first if (tx) { // manually override the scriptSig? if (sign.scriptSigBefore) { - tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore) + tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore); } // rebuild - txb = TransactionBuilder.fromTransaction(tx, network) + txb = TransactionBuilder.fromTransaction(tx, network); } - const keyPair2 = ECPair.fromWIF(sign.keyPair, network) + const keyPair2 = ECPair.fromWIF(sign.keyPair, network); txb.sign({ prevOutScriptType: sign.prevOutScriptType, vin: i, keyPair: keyPair2, redeemScript, - hashType: sign.hashType, - }) + hashType: (sign as any).hashType, + }); // update the tx - tx = txb.buildIncomplete() + tx = txb.buildIncomplete(); // now verify the serialized scriptSig is as expected - assert.strictEqual(bscript.toASM(tx.ins[i].script), sign.scriptSig) - }) - }) + assert.strictEqual( + bscript.toASM(tx.ins[i].script), + sign.scriptSig, + ); + }); + }); - tx = txb.build() - assert.strictEqual(tx.toHex(), f.txHex) - }) - }) - }) + tx = txb.build(); + assert.strictEqual(tx.toHex(), f.txHex); + }); + }); + }); describe('various edge case', () => { - const network = NETWORKS.testnet + const network = NETWORKS.testnet; it('should warn of high fee for segwit transaction based on VSize, not Size', () => { - const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + - '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + - '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + - '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + - '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + - '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + - '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + - '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + - '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + - 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + - '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + - '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + - 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + - '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + - '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' - const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.__INPUTS[0].value = 241530 - txb.__INPUTS[1].value = 241530 - txb.__INPUTS[2].value = 248920 - txb.__INPUTS[3].value = 248920 + const rawtx = + '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + + '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + + '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + + '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + + '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + + '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + + '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + + '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + + '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + + 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + + '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + + '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + + 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + + '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000'; + const txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(rawtx), + ); + (txb as any).__INPUTS[0].value = 241530; + (txb as any).__INPUTS[1].value = 241530; + (txb as any).__INPUTS[2].value = 248920; + (txb as any).__INPUTS[3].value = 248920; assert.throws(() => { - txb.build() - }, new RegExp('Transaction has absurd fees')) - }) + txb.build(); + }, new RegExp('Transaction has absurd fees')); + }); it('should classify witness inputs with witness = true during multisigning', () => { - const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) - const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') - const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') - const scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') - const txb = new TransactionBuilder(network) - txb.setVersion(1) - txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) - txb.addOutput(scriptPubKey, 99000) + const keyPair = ECPair.fromWIF( + 'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', + network, + ); + const witnessScript = Buffer.from( + '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', + 'hex', + ); + const redeemScript = Buffer.from( + '002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', + 'hex', + ); + const scriptPubKey = Buffer.from( + 'a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', + 'hex', + ); + const txb = new TransactionBuilder(network); + txb.setVersion(1); + txb.addInput( + 'a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', + 1, + 0xffffffff, + scriptPubKey, + ); + txb.addOutput(scriptPubKey, 99000); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, @@ -714,76 +832,116 @@ for (const useOldSignArgs of [ false, true ]) { redeemScript, witnessValue: 100000, witnessScript, - }) + }); // 2-of-2 signed only once - const tx = txb.buildIncomplete() + const tx = txb.buildIncomplete(); // Only input is segwit, so txid should be accurate with the final tx - assert.strictEqual(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + assert.strictEqual( + tx.getId(), + 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821', + ); - const txHex = tx.toHex() - TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) - }) + const txHex = tx.toHex(); + TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)); + }); it('should handle badly pre-filled OP_0s', () => { // OP_0 is used where a signature is missing - const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') + const redeemScripSig = bscript.fromASM( + 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + ); + const redeemScript = bscript.fromASM( + 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG', + ); - const tx = new Transaction() - tx.addInput(Buffer.from('cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', 'hex'), 0, undefined, redeemScripSig) - tx.addOutput(Buffer.from('76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', 'hex'), 1000) + const tx = new Transaction(); + tx.addInput( + Buffer.from( + 'cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', + 'hex', + ), + 0, + undefined, + redeemScripSig, + ); + tx.addOutput( + Buffer.from( + '76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', + 'hex', + ), + 1000, + ); // now import the Transaction - const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) + const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet); - const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) + const keyPair2 = ECPair.fromWIF( + '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', + network, + ); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPair2, redeemScript, - }) + }); - const tx2 = txb.build() - assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') - assert.strictEqual(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - }) + const tx2 = txb.build(); + assert.strictEqual( + tx2.getId(), + 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9', + ); + assert.strictEqual( + bscript.toASM(tx2.ins[0].script), + 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + ); + }); it('should not classify blank scripts as nonstandard', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', + 0, + ); - const incomplete = txb.buildIncomplete().toHex() - const keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + const incomplete = txb.buildIncomplete().toHex(); + const keyPair = ECPair.fromWIF( + 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', + ); // sign, as expected - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - const txId = txb.build().getId() - assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') + }); + const txId = txb.build().getId(); + assert.strictEqual( + txId, + '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3', + ); // and, repeat - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(incomplete), + ); + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - const txId2 = txb.build().getId() - assert.strictEqual(txId, txId2) + }); + const txId2 = txb.build().getId(); + assert.strictEqual(txId, txId2); // TODO: Remove me in v6 if (useOldSignArgs) { console.warn = consoleWarn; } - }) - }) - }) + }); + }); + }); } diff --git a/test/ts-node-register.js b/test/ts-node-register.js new file mode 100644 index 0000000..fef284d --- /dev/null +++ b/test/ts-node-register.js @@ -0,0 +1,5 @@ +// This file is required to run mocha tests on the TS files directly + +require("ts-node").register({ + project: "test/tsconfig.json", +}); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..e29620d --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "commonjs", + "outDir": "../", + "declaration": false, + "rootDir": "../", + "rootDirs": [ + "../src", + "../types" + ], + "types": [ + "node", + "mocha" + ], + "allowJs": false, + "resolveJsonModule": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "esModuleInterop": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "baseUrl": ".", + "paths": { + "../src/*": ["../ts_src/*"] + } + }, + "include": [ + "./**/*.ts" + ], + "exclude": [ + "../ts_src/**/*.ts" + ] +} diff --git a/test/types.spec.ts b/test/types.spec.ts index 8911ca1..363b83c 100644 --- a/test/types.spec.ts +++ b/test/types.spec.ts @@ -1,38 +1,44 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const types = require('../src/types') -const typeforce = require('typeforce') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as types from '../src/types'; +const typeforce = require('typeforce'); describe('types', () => { describe('Buffer Hash160/Hash256', () => { - const buffer20byte = Buffer.alloc(20) - const buffer32byte = Buffer.alloc(32) + const buffer20byte = Buffer.alloc(20); + const buffer32byte = Buffer.alloc(32); it('return true for valid size', () => { - assert(types.Hash160bit(buffer20byte)) - assert(types.Hash256bit(buffer32byte)) - }) + assert(types.Hash160bit(buffer20byte)); + assert(types.Hash256bit(buffer32byte)); + }); it('return true for oneOf', () => { assert.doesNotThrow(() => { - typeforce(types.oneOf(types.Hash160bit, types.Hash256bit), buffer32byte) - }) + typeforce( + types.oneOf(types.Hash160bit, types.Hash256bit), + buffer32byte, + ); + }); assert.doesNotThrow(() => { - typeforce(types.oneOf(types.Hash256bit, types.Hash160bit), buffer32byte) - }) - }) + typeforce( + types.oneOf(types.Hash256bit, types.Hash160bit), + buffer32byte, + ); + }); + }); it('throws for invalid size', () => { assert.throws(() => { - types.Hash160bit(buffer32byte) - }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/) + types.Hash160bit(buffer32byte); + }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/); assert.throws(() => { - types.Hash256bit(buffer20byte) - }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/) - }) - }) + types.Hash256bit(buffer20byte); + }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/); + }); + }); describe('Satoshi', () => { [ @@ -41,11 +47,11 @@ describe('types', () => { { value: 1, result: true }, { value: 20999999 * 1e8, result: true }, { value: 21000000 * 1e8, result: true }, - { value: 21000001 * 1e8, result: false } + { value: 21000001 * 1e8, result: false }, ].forEach(f => { it('returns ' + f.result + ' for valid for ' + f.value, () => { - assert.strictEqual(types.Satoshi(f.value), f.result) - }) - }) - }) -}) + assert.strictEqual(types.Satoshi(f.value), f.result); + }); + }); + }); +}); diff --git a/ts_src/block.ts b/ts_src/block.ts index 820c075..cf4ed51 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -148,7 +148,7 @@ export class Block { return anyTxHasWitness(this.transactions!); } - byteLength(headersOnly: boolean): number { + byteLength(headersOnly?: boolean): number { if (headersOnly || !this.transactions) return 80; return ( @@ -174,7 +174,7 @@ export class Block { } // TODO: buffer, offset compatibility - toBuffer(headersOnly: boolean): Buffer { + toBuffer(headersOnly?: boolean): Buffer { const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); let offset: number = 0; @@ -213,7 +213,7 @@ export class Block { return buffer; } - toHex(headersOnly: boolean): string { + toHex(headersOnly?: boolean): string { return this.toBuffer(headersOnly).toString('hex'); } diff --git a/ts_src/classify.ts b/ts_src/classify.ts index a7ec68c..319e934 100644 --- a/ts_src/classify.ts +++ b/ts_src/classify.ts @@ -38,7 +38,7 @@ function classifyOutput(script: Buffer): string { return types.NONSTANDARD; } -function classifyInput(script: Buffer, allowIncomplete: boolean): string { +function classifyInput(script: Buffer, allowIncomplete?: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script); if (!chunks) throw new TypeError('Invalid script'); @@ -51,7 +51,7 @@ function classifyInput(script: Buffer, allowIncomplete: boolean): string { return types.NONSTANDARD; } -function classifyWitness(script: Buffer[], allowIncomplete: boolean): string { +function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script); if (!chunks) throw new TypeError('Invalid script'); diff --git a/ts_src/index.ts b/ts_src/index.ts index 58c39c7..505407f 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -17,6 +17,12 @@ export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts, Stack, StackElement } from './payments'; +export { + Payment, + PaymentCreator, + PaymentOpts, + Stack, + StackElement, +} from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index c48a6f1..4b7f111 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -25,6 +25,8 @@ export interface Payment { witness?: Buffer[]; } +export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment; + export type PaymentFunction = () => Payment; export interface PaymentOpts { diff --git a/tslint.json b/tslint.json index 1ba998d..8a9cde6 100644 --- a/tslint.json +++ b/tslint.json @@ -16,7 +16,7 @@ "no-bitwise": false, "no-console": false, "no-empty": [true, "allow-empty-catch"], - "no-implicit-dependencies": true, + "no-implicit-dependencies": [true, "dev"], "no-return-await": true, "no-var-requires": false, "no-unused-expression": false, diff --git a/types/block.d.ts b/types/block.d.ts index dd4fc55..0cda4c8 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -16,12 +16,12 @@ export declare class Block { getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; hasWitness(): boolean; - byteLength(headersOnly: boolean): number; + byteLength(headersOnly?: boolean): number; getHash(): Buffer; getId(): string; getUTCDate(): Date; - toBuffer(headersOnly: boolean): Buffer; - toHex(headersOnly: boolean): string; + toBuffer(headersOnly?: boolean): Buffer; + toHex(headersOnly?: boolean): string; checkTxRoots(): boolean; checkProofOfWork(): boolean; private __checkMerkleRoot; diff --git a/types/classify.d.ts b/types/classify.d.ts index 67f913b..d0f07b4 100644 --- a/types/classify.d.ts +++ b/types/classify.d.ts @@ -11,6 +11,6 @@ declare const types: { WITNESS_COMMITMENT: string; }; declare function classifyOutput(script: Buffer): string; -declare function classifyInput(script: Buffer, allowIncomplete: boolean): string; -declare function classifyWitness(script: Buffer[], allowIncomplete: boolean): string; +declare function classifyInput(script: Buffer, allowIncomplete?: boolean): string; +declare function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string; export { classifyInput as input, classifyOutput as output, classifyWitness as witness, types, }; diff --git a/types/index.d.ts b/types/index.d.ts index fc5a932..68da119 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,6 +14,6 @@ export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts, Stack, StackElement } from './payments'; +export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 7f17b79..1edf071 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -24,6 +24,7 @@ export interface Payment { redeem?: Payment; witness?: Buffer[]; } +export declare type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment; export declare type PaymentFunction = () => Payment; export interface PaymentOpts { validate?: boolean;