Merge pull request #1734 from bitcoinjs/feat/modular

Refactor: Remove all require statements, remove ECPair, remove tiny-secp256k1 dep
This commit is contained in:
Jonathan Underwood 2021-10-21 14:42:23 +09:00 committed by GitHub
commit 5137976e7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 1449 additions and 1155 deletions

View file

@ -35,10 +35,18 @@ You can find a [Web UI](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html) t
## Installation
``` bash
npm install bitcoinjs-lib
# optionally, install a key derivation library as well
npm install ecpair bip32
# ecpair is the ECPair class for single keys
# bip32 is for generating HD keys
```
Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release).
If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by our continuous integration tests.
Previous versions of the library included classes for key management (ECPair, HDNode(->"bip32")) but now these have been separated into different libraries. This lowers the bundle size significantly if you don't need to perform any crypto functions (converting private to public keys and deriving HD keys).
Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release). TypeScript target will be set
to the ECMAScript version in which all features are fully supported by current Active Node LTS.
However, depending on adoption among other environments (browsers etc.) we may keep the target back a year or two.
If in doubt, see the [main_ci.yml](.github/workflows/main_ci.yml) for what versions are used by our continuous integration tests.
**WARNING**: We presently don't provide any tooling to verify that the release on `npm` matches GitHub. As such, you should verify anything downloaded by `npm` against your own verified copy.

200
package-lock.json generated
View file

@ -5,12 +5,12 @@
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
"version": "7.15.8",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz",
"integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.8.3"
"@babel/highlight": "^7.14.5"
}
},
"@babel/core": {
@ -196,14 +196,22 @@
}
},
"@babel/highlight": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
"integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
"integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.14.5",
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
},
"dependencies": {
"@babel/helper-validator-identifier": {
"version": "7.15.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"dev": true
}
}
},
"@babel/parser": {
@ -383,6 +391,24 @@
"@types/base-x": "*"
}
},
"@types/bs58check": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz",
"integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/create-hash": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.2.tgz",
"integrity": "sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
@ -390,9 +416,9 @@
"dev": true
},
"@types/node": {
"version": "12.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz",
"integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==",
"version": "16.11.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==",
"dev": true
},
"@types/proxyquire": {
@ -401,6 +427,24 @@
"integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==",
"dev": true
},
"@types/randombytes": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz",
"integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/wif": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.2.tgz",
"integrity": "sha512-IiIuBeJzlh4LWJ7kVTrX0nwB60OG0vvGTaWC/SgSbVFw7uYUTF6gEuvDZ1goWkeirekJDD58Y8g7NljQh2fNkA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@ -501,6 +545,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
@ -511,15 +556,16 @@
"integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ=="
},
"bip32": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.4.tgz",
"integrity": "sha512-ioPytarPDIrWckWMuK4RNUtvwhvWEc2fvuhnO0WEwu732k5OLjUXv4rXi2c/KJHw9ZMNQMkYRJrBw81RujShGQ==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
"integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
"dev": true,
"requires": {
"@types/node": "10.12.18",
"bs58check": "^2.1.1",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
"tiny-secp256k1": "^1.1.0",
"tiny-secp256k1": "^1.1.3",
"typeforce": "^1.11.5",
"wif": "^2.0.6"
},
@ -527,7 +573,8 @@
"@types/node": {
"version": "10.12.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
"dev": true
}
}
},
@ -557,14 +604,6 @@
"integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==",
"dev": true
},
"bip66": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"bip68": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bip68/-/bip68-1.0.4.tgz",
@ -574,12 +613,14 @@
"bitcoin-ops": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow=="
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==",
"dev": true
},
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
@ -603,7 +644,8 @@
"brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
"dev": true
},
"browser-stdout": {
"version": "1.3.1",
@ -809,6 +851,7 @@
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
"cipher-base": "^1.0.3",
"create-hash": "^1.1.0",
@ -888,18 +931,45 @@
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"elliptic": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"ecpair": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ecpair/-/ecpair-1.0.0.tgz",
"integrity": "sha512-1L+P/ivLC3eKHgqcX1M9tFYQWXDoqwJ3zQnN7zDaTtLpiCQKpFTaAZvnsPC5PkWB4q3EPFAHffCLvjfCqRjuwQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"randombytes": "^2.0.1",
"tiny-secp256k1": "^1.1.6",
"typeforce": "^1.11.3",
"wif": "^2.0.1"
}
},
"elliptic": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"dev": true,
"requires": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
},
"dependencies": {
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
}
}
},
"emoji-regex": {
@ -956,16 +1026,11 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true
},
"fill-keys": {
"version": "1.0.2",
@ -1143,6 +1208,7 @@
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
@ -1168,6 +1234,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
"dev": true,
"requires": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
@ -1549,11 +1616,6 @@
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
"dev": true
},
"merkle-lib": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz",
"integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY="
},
"minimaldata": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz",
@ -1567,12 +1629,14 @@
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
"dev": true
},
"minimalistic-crypto-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
"dev": true
},
"minimatch": {
"version": "3.0.4",
@ -1645,7 +1709,8 @@
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"dev": true
},
"node-environment-flags": {
"version": "1.0.6",
@ -2091,6 +2156,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz",
"integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=",
"dev": true,
"requires": {
"bitcoin-ops": "^1.3.0"
}
@ -2099,6 +2165,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.0"
}
@ -2401,6 +2468,7 @@
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
"dev": true,
"requires": {
"bindings": "^1.3.0",
"bn.js": "^4.11.8",
@ -2446,15 +2514,15 @@
}
},
"tslib": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tslint": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
"integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
"integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
@ -2465,10 +2533,10 @@
"glob": "^7.1.1",
"js-yaml": "^3.13.1",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"mkdirp": "^0.5.3",
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.8.0",
"tslib": "^1.13.0",
"tsutils": "^2.29.0"
},
"dependencies": {
@ -2510,9 +2578,9 @@
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
},
"typescript": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
"integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==",
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
"dev": true
},
"uuid": {
@ -2522,9 +2590,9 @@
"dev": true
},
"varuint-bitcoin": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz",
"integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
"integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
"requires": {
"safe-buffer": "^5.1.1"
}

View file

@ -3,7 +3,7 @@
"version": "5.2.0",
"description": "Client-side Bitcoin JavaScript library",
"main": "./src/index.js",
"types": "./types/index.d.ts",
"types": "./src/index.d.ts",
"engines": {
"node": ">=8.0.0"
},
@ -18,7 +18,7 @@
"audit": "NPM_AUDIT_IGNORE_DEV=1 NPM_AUDIT_IGNORE_LEVEL=low npm-audit-whitelister .npm-audit-whitelister.json",
"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": "rimraf src",
"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",
@ -46,37 +46,34 @@
"url": "https://github.com/bitcoinjs/bitcoinjs-lib.git"
},
"files": [
"src",
"types"
"src"
],
"dependencies": {
"bech32": "^2.0.0",
"bip174": "^2.0.1",
"bip32": "^2.0.4",
"bip66": "^1.1.0",
"bitcoin-ops": "^1.4.0",
"bs58check": "^2.0.0",
"bs58check": "^2.1.2",
"create-hash": "^1.1.0",
"create-hmac": "^1.1.3",
"merkle-lib": "^2.0.10",
"pushdata-bitcoin": "^1.0.1",
"randombytes": "^2.0.1",
"tiny-secp256k1": "^1.1.6",
"typeforce": "^1.11.3",
"varuint-bitcoin": "^1.0.4",
"varuint-bitcoin": "^1.1.2",
"wif": "^2.0.1"
},
"devDependencies": {
"@types/bs58": "^4.0.0",
"@types/bs58check": "^2.1.0",
"@types/create-hash": "^1.2.2",
"@types/mocha": "^5.2.7",
"@types/node": "12.7.5",
"@types/node": "^16.11.1",
"@types/proxyquire": "^1.3.28",
"@types/randombytes": "^2.0.0",
"@types/wif": "^2.0.2",
"bip32": "^2.0.6",
"bip39": "^3.0.2",
"bip65": "^1.0.1",
"bip68": "^1.0.3",
"bn.js": "^4.11.8",
"bs58": "^4.0.0",
"dhttp": "^3.0.0",
"ecpair": "^1.0.0",
"hoodwink": "^2.0.0",
"minimaldata": "^1.0.2",
"mocha": "^7.1.1",
@ -84,11 +81,12 @@
"nyc": "^15.1.0",
"prettier": "1.16.4",
"proxyquire": "^2.0.1",
"randombytes": "^2.1.0",
"regtest-client": "0.2.0",
"rimraf": "^2.6.3",
"ts-node": "^8.3.0",
"tslint": "^5.20.1",
"typescript": "3.2.2"
"tslint": "^6.1.3",
"typescript": "^4.4.4"
},
"license": "MIT"
}

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
import { Network } from './networks';
export interface Base58CheckResult {
hash: Buffer;

View file

@ -1,12 +1,13 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.toOutputScript = exports.fromOutputScript = exports.toBech32 = exports.toBase58Check = exports.fromBech32 = exports.fromBase58Check = void 0;
const networks = require('./networks');
const payments = require('./payments');
const bscript = require('./script');
const types = require('./types');
const { bech32, bech32m } = require('bech32');
const bech32_1 = require('bech32');
const bs58check = require('bs58check');
const typeforce = require('typeforce');
const { typeforce } = types;
const FUTURE_SEGWIT_MAX_SIZE = 40;
const FUTURE_SEGWIT_MIN_SIZE = 2;
const FUTURE_SEGWIT_MAX_VERSION = 16;
@ -43,17 +44,17 @@ function fromBech32(address) {
let result;
let version;
try {
result = bech32.decode(address);
result = bech32_1.bech32.decode(address);
} catch (e) {}
if (result) {
version = result.words[0];
if (version !== 0) throw new TypeError(address + ' uses wrong encoding');
} else {
result = bech32m.decode(address);
result = bech32_1.bech32m.decode(address);
version = result.words[0];
if (version === 0) throw new TypeError(address + ' uses wrong encoding');
}
const data = bech32.fromWords(result.words.slice(1));
const data = bech32_1.bech32.fromWords(result.words.slice(1));
return {
version,
prefix: result.prefix,
@ -70,11 +71,11 @@ function toBase58Check(hash, version) {
}
exports.toBase58Check = toBase58Check;
function toBech32(data, version, prefix) {
const words = bech32.toWords(data);
const words = bech32_1.bech32.toWords(data);
words.unshift(version);
return version === 0
? bech32.encode(prefix, words)
: bech32m.encode(prefix, words);
? bech32_1.bech32.encode(prefix, words)
: bech32_1.bech32m.encode(prefix, words);
}
exports.toBech32 = toBech32;
function fromOutputScript(output, network) {

7
src/bip66.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
/// <reference types="node" />
export declare function check(buffer: Buffer): boolean;
export declare function decode(buffer: Buffer): {
r: Buffer;
s: Buffer;
};
export declare function encode(r: Buffer, s: Buffer): Buffer;

102
src/bip66.js Normal file
View file

@ -0,0 +1,102 @@
'use strict';
// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
// NOTE: SIGHASH byte ignored AND restricted, truncate before use
Object.defineProperty(exports, '__esModule', { value: true });
exports.encode = exports.decode = exports.check = void 0;
function check(buffer) {
if (buffer.length < 8) return false;
if (buffer.length > 72) return false;
if (buffer[0] !== 0x30) return false;
if (buffer[1] !== buffer.length - 2) return false;
if (buffer[2] !== 0x02) return false;
const lenR = buffer[3];
if (lenR === 0) return false;
if (5 + lenR >= buffer.length) return false;
if (buffer[4 + lenR] !== 0x02) return false;
const lenS = buffer[5 + lenR];
if (lenS === 0) return false;
if (6 + lenR + lenS !== buffer.length) return false;
if (buffer[4] & 0x80) return false;
if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false;
if (buffer[lenR + 6] & 0x80) return false;
if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
return false;
return true;
}
exports.check = check;
function decode(buffer) {
if (buffer.length < 8) throw new Error('DER sequence length is too short');
if (buffer.length > 72) throw new Error('DER sequence length is too long');
if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
if (buffer[1] !== buffer.length - 2)
throw new Error('DER sequence length is invalid');
if (buffer[2] !== 0x02) throw new Error('Expected DER integer');
const lenR = buffer[3];
if (lenR === 0) throw new Error('R length is zero');
if (5 + lenR >= buffer.length) throw new Error('R length is too long');
if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)');
const lenS = buffer[5 + lenR];
if (lenS === 0) throw new Error('S length is zero');
if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid');
if (buffer[4] & 0x80) throw new Error('R value is negative');
if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80))
throw new Error('R value excessively padded');
if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative');
if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
throw new Error('S value excessively padded');
// non-BIP66 - extract R, S values
return {
r: buffer.slice(4, 4 + lenR),
s: buffer.slice(6 + lenR),
};
}
exports.decode = decode;
/*
* Expects r and s to be positive DER integers.
*
* The DER format uses the most significant bit as a sign bit (& 0x80).
* If the significant bit is set AND the integer is positive, a 0x00 is prepended.
*
* Examples:
*
* 0 => 0x00
* 1 => 0x01
* -1 => 0xff
* 127 => 0x7f
* -127 => 0x81
* 128 => 0x0080
* -128 => 0x80
* 255 => 0x00ff
* -255 => 0xff01
* 16300 => 0x3fac
* -16300 => 0xc054
* 62300 => 0x00f35c
* -62300 => 0xff0ca4
*/
function encode(r, s) {
const lenR = r.length;
const lenS = s.length;
if (lenR === 0) throw new Error('R length is zero');
if (lenS === 0) throw new Error('S length is zero');
if (lenR > 33) throw new Error('R length is too long');
if (lenS > 33) throw new Error('S length is too long');
if (r[0] & 0x80) throw new Error('R value is negative');
if (s[0] & 0x80) throw new Error('S value is negative');
if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80))
throw new Error('R value excessively padded');
if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
throw new Error('S value excessively padded');
const signature = Buffer.allocUnsafe(6 + lenR + lenS);
// 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
signature[0] = 0x30;
signature[1] = signature.length - 2;
signature[2] = 0x02;
signature[3] = r.length;
r.copy(signature, 4);
signature[4 + lenR] = 0x02;
signature[5 + lenR] = s.length;
s.copy(signature, 6 + lenR);
return signature;
}
exports.encode = encode;

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
import { Transaction } from './transaction';
export declare class Block {
static fromBuffer(buffer: Buffer): Block;

View file

@ -1,12 +1,12 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.Block = void 0;
const bufferutils_1 = require('./bufferutils');
const bcrypto = require('./crypto');
const merkle_1 = require('./merkle');
const transaction_1 = require('./transaction');
const types = require('./types');
const fastMerkleRoot = require('merkle-lib/fastRoot');
const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin');
const { typeforce } = types;
const errorMerkleNoTxes = new TypeError(
'Cannot compute merkle root for zero transactions',
);
@ -72,7 +72,7 @@ class Block {
const hashes = transactions.map(transaction =>
transaction.getHash(forWitness),
);
const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
const rootHash = (0, merkle_1.fastMerkleRoot)(hashes, bcrypto.hash256);
return forWitness
? bcrypto.hash256(
Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
@ -117,7 +117,7 @@ class Block {
if (headersOnly || !this.transactions) return 80;
return (
80 +
varuint.encodingLength(this.transactions.length) +
bufferutils_1.varuint.encodingLength(this.transactions.length) +
this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0)
);
}
@ -125,7 +125,7 @@ class Block {
return bcrypto.hash256(this.toBuffer(true));
}
getId() {
return bufferutils_1.reverseBuffer(this.getHash()).toString('hex');
return (0, bufferutils_1.reverseBuffer)(this.getHash()).toString('hex');
}
getUTCDate() {
const date = new Date(0); // epoch
@ -143,8 +143,12 @@ class Block {
bufferWriter.writeUInt32(this.bits);
bufferWriter.writeUInt32(this.nonce);
if (headersOnly || !this.transactions) return buffer;
varuint.encode(this.transactions.length, buffer, bufferWriter.offset);
bufferWriter.offset += varuint.encode.bytes;
bufferutils_1.varuint.encode(
this.transactions.length,
buffer,
bufferWriter.offset,
);
bufferWriter.offset += bufferutils_1.varuint.encode.bytes;
this.transactions.forEach(tx => {
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
tx.toBuffer(buffer, bufferWriter.offset);
@ -166,7 +170,7 @@ class Block {
);
}
checkProofOfWork() {
const hash = bufferutils_1.reverseBuffer(this.getHash());
const hash = (0, bufferutils_1.reverseBuffer)(this.getHash());
const target = Block.calculateTarget(this.bits);
return hash.compare(target) <= 0;
}

View file

@ -1,3 +1,6 @@
/// <reference types="node" />
import * as varuint from 'varuint-bitcoin';
export { varuint };
export declare function readUInt64LE(buffer: Buffer, offset: number): number;
export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number;
export declare function reverseBuffer(buffer: Buffer): Buffer;

View file

@ -1,8 +1,10 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.BufferReader = exports.BufferWriter = exports.cloneBuffer = exports.reverseBuffer = exports.writeUInt64LE = exports.readUInt64LE = exports.varuint = void 0;
const types = require('./types');
const typeforce = require('typeforce');
const { typeforce } = types;
const varuint = require('varuint-bitcoin');
exports.varuint = varuint;
// https://github.com/feross/buffer/blob/master/index.js#L1127
function verifuint(value, max) {
if (typeof value !== 'number')

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
export declare function ripemd160(buffer: Buffer): Buffer;
export declare function sha1(buffer: Buffer): Buffer;
export declare function sha256(buffer: Buffer): Buffer;

View file

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = void 0;
const createHash = require('create-hash');
function ripemd160(buffer) {
try {

View file

@ -1,107 +0,0 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const NETWORKS = require('./networks');
const types = require('./types');
const ecc = require('tiny-secp256k1');
const randomBytes = require('randombytes');
const typeforce = require('typeforce');
const wif = require('wif');
const isOptions = typeforce.maybe(
typeforce.compile({
compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network),
}),
);
class ECPair {
constructor(__D, __Q, options) {
this.__D = __D;
this.__Q = __Q;
this.lowR = false;
if (options === undefined) options = {};
this.compressed =
options.compressed === undefined ? true : options.compressed;
this.network = options.network || NETWORKS.bitcoin;
if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed);
}
get privateKey() {
return this.__D;
}
get publicKey() {
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed);
return this.__Q;
}
toWIF() {
if (!this.__D) throw new Error('Missing private key');
return wif.encode(this.network.wif, this.__D, this.compressed);
}
sign(hash, lowR) {
if (!this.__D) throw new Error('Missing private key');
if (lowR === undefined) lowR = this.lowR;
if (lowR === false) {
return ecc.sign(hash, this.__D);
} else {
let sig = ecc.sign(hash, this.__D);
const extraData = Buffer.alloc(32, 0);
let counter = 0;
// if first try is lowR, skip the loop
// for second try and on, add extra entropy counting up
while (sig[0] > 0x7f) {
counter++;
extraData.writeUIntLE(counter, 0, 6);
sig = ecc.signWithEntropy(hash, this.__D, extraData);
}
return sig;
}
}
verify(hash, signature) {
return ecc.verify(hash, this.publicKey, signature);
}
}
function fromPrivateKey(buffer, options) {
typeforce(types.Buffer256bit, buffer);
if (!ecc.isPrivate(buffer))
throw new TypeError('Private key not in range [1, n)');
typeforce(isOptions, options);
return new ECPair(buffer, undefined, options);
}
exports.fromPrivateKey = fromPrivateKey;
function fromPublicKey(buffer, options) {
typeforce(ecc.isPoint, buffer);
typeforce(isOptions, options);
return new ECPair(undefined, buffer, options);
}
exports.fromPublicKey = fromPublicKey;
function fromWIF(wifString, network) {
const decoded = wif.decode(wifString);
const version = decoded.version;
// list of networks?
if (types.Array(network)) {
network = network
.filter(x => {
return version === x.wif;
})
.pop();
if (!network) throw new Error('Unknown network version');
// otherwise, assume a network object (or default to bitcoin)
} else {
network = network || NETWORKS.bitcoin;
if (version !== network.wif) throw new Error('Invalid network version');
}
return fromPrivateKey(decoded.privateKey, {
compressed: decoded.compressed,
network: network,
});
}
exports.fromWIF = fromWIF;
function makeRandom(options) {
typeforce(isOptions, options);
if (options === undefined) options = {};
const rng = options.rng || randomBytes;
let d;
do {
d = rng(32);
typeforce(types.Buffer256bit, d);
} while (!ecc.isPrivate(d));
return fromPrivateKey(d, options);
}
exports.makeRandom = makeRandom;

View file

@ -1,18 +1,13 @@
import * as bip32 from 'bip32';
import * as address from './address';
import * as crypto from './crypto';
import * as ECPair from './ecpair';
import * as networks from './networks';
import * as payments from './payments';
import * as script from './script';
export { ECPair, address, bip32, crypto, networks, payments, script };
export { address, crypto, networks, payments, script };
export { Block } from './block';
export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt';
export { OPS as opcodes } from './script';
export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt';
export { OPS as opcodes } from './ops';
export { Transaction } from './transaction';
export { BIP32Interface } from 'bip32';
export { ECPairInterface, Signer, SignerAsync } from './ecpair';
export { Network } from './networks';
export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments';
export { OpCode } from './script';
export { Input as TxInput, Output as TxOutput } from './transaction';

View file

@ -1,13 +1,10 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const bip32 = require('bip32');
exports.bip32 = bip32;
exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0;
const address = require('./address');
exports.address = address;
const crypto = require('./crypto');
exports.crypto = crypto;
const ECPair = require('./ecpair');
exports.ECPair = ECPair;
const networks = require('./networks');
exports.networks = networks;
const payments = require('./payments');
@ -15,10 +12,30 @@ exports.payments = payments;
const script = require('./script');
exports.script = script;
var block_1 = require('./block');
exports.Block = block_1.Block;
Object.defineProperty(exports, 'Block', {
enumerable: true,
get: function() {
return block_1.Block;
},
});
var psbt_1 = require('./psbt');
exports.Psbt = psbt_1.Psbt;
var script_1 = require('./script');
exports.opcodes = script_1.OPS;
Object.defineProperty(exports, 'Psbt', {
enumerable: true,
get: function() {
return psbt_1.Psbt;
},
});
var ops_1 = require('./ops');
Object.defineProperty(exports, 'opcodes', {
enumerable: true,
get: function() {
return ops_1.OPS;
},
});
var transaction_1 = require('./transaction');
exports.Transaction = transaction_1.Transaction;
Object.defineProperty(exports, 'Transaction', {
enumerable: true,
get: function() {
return transaction_1.Transaction;
},
});

2
src/merkle.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference types="node" />
export declare function fastMerkleRoot(values: Buffer[], digestFn: (b: Buffer) => Buffer): Buffer;

22
src/merkle.js Normal file
View file

@ -0,0 +1,22 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.fastMerkleRoot = void 0;
function fastMerkleRoot(values, digestFn) {
if (!Array.isArray(values)) throw TypeError('Expected values Array');
if (typeof digestFn !== 'function')
throw TypeError('Expected digest Function');
let length = values.length;
const results = values.concat();
while (length > 1) {
let j = 0;
for (let i = 0; i < length; i += 2, ++j) {
const left = results[i];
const right = i + 1 === length ? left : results[i + 1];
const data = Buffer.concat([left, right]);
results[j] = digestFn(data);
}
length = j;
}
return results[0];
}
exports.fastMerkleRoot = fastMerkleRoot;

View file

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.testnet = exports.regtest = exports.bitcoin = void 0;
exports.bitcoin = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bc',

7
src/ops.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
declare const OPS: {
[key: string]: number;
};
declare const REVERSE_OPS: {
[key: number]: string;
};
export { OPS, REVERSE_OPS };

130
src/ops.js Normal file
View file

@ -0,0 +1,130 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.REVERSE_OPS = exports.OPS = void 0;
const OPS = {
OP_FALSE: 0,
OP_0: 0,
OP_PUSHDATA1: 76,
OP_PUSHDATA2: 77,
OP_PUSHDATA4: 78,
OP_1NEGATE: 79,
OP_RESERVED: 80,
OP_TRUE: 81,
OP_1: 81,
OP_2: 82,
OP_3: 83,
OP_4: 84,
OP_5: 85,
OP_6: 86,
OP_7: 87,
OP_8: 88,
OP_9: 89,
OP_10: 90,
OP_11: 91,
OP_12: 92,
OP_13: 93,
OP_14: 94,
OP_15: 95,
OP_16: 96,
OP_NOP: 97,
OP_VER: 98,
OP_IF: 99,
OP_NOTIF: 100,
OP_VERIF: 101,
OP_VERNOTIF: 102,
OP_ELSE: 103,
OP_ENDIF: 104,
OP_VERIFY: 105,
OP_RETURN: 106,
OP_TOALTSTACK: 107,
OP_FROMALTSTACK: 108,
OP_2DROP: 109,
OP_2DUP: 110,
OP_3DUP: 111,
OP_2OVER: 112,
OP_2ROT: 113,
OP_2SWAP: 114,
OP_IFDUP: 115,
OP_DEPTH: 116,
OP_DROP: 117,
OP_DUP: 118,
OP_NIP: 119,
OP_OVER: 120,
OP_PICK: 121,
OP_ROLL: 122,
OP_ROT: 123,
OP_SWAP: 124,
OP_TUCK: 125,
OP_CAT: 126,
OP_SUBSTR: 127,
OP_LEFT: 128,
OP_RIGHT: 129,
OP_SIZE: 130,
OP_INVERT: 131,
OP_AND: 132,
OP_OR: 133,
OP_XOR: 134,
OP_EQUAL: 135,
OP_EQUALVERIFY: 136,
OP_RESERVED1: 137,
OP_RESERVED2: 138,
OP_1ADD: 139,
OP_1SUB: 140,
OP_2MUL: 141,
OP_2DIV: 142,
OP_NEGATE: 143,
OP_ABS: 144,
OP_NOT: 145,
OP_0NOTEQUAL: 146,
OP_ADD: 147,
OP_SUB: 148,
OP_MUL: 149,
OP_DIV: 150,
OP_MOD: 151,
OP_LSHIFT: 152,
OP_RSHIFT: 153,
OP_BOOLAND: 154,
OP_BOOLOR: 155,
OP_NUMEQUAL: 156,
OP_NUMEQUALVERIFY: 157,
OP_NUMNOTEQUAL: 158,
OP_LESSTHAN: 159,
OP_GREATERTHAN: 160,
OP_LESSTHANOREQUAL: 161,
OP_GREATERTHANOREQUAL: 162,
OP_MIN: 163,
OP_MAX: 164,
OP_WITHIN: 165,
OP_RIPEMD160: 166,
OP_SHA1: 167,
OP_SHA256: 168,
OP_HASH160: 169,
OP_HASH256: 170,
OP_CODESEPARATOR: 171,
OP_CHECKSIG: 172,
OP_CHECKSIGVERIFY: 173,
OP_CHECKMULTISIG: 174,
OP_CHECKMULTISIGVERIFY: 175,
OP_NOP1: 176,
OP_NOP2: 177,
OP_CHECKLOCKTIMEVERIFY: 177,
OP_NOP3: 178,
OP_CHECKSEQUENCEVERIFY: 178,
OP_NOP4: 179,
OP_NOP5: 180,
OP_NOP6: 181,
OP_NOP7: 182,
OP_NOP8: 183,
OP_NOP9: 184,
OP_NOP10: 185,
OP_PUBKEYHASH: 253,
OP_PUBKEY: 254,
OP_INVALIDOPCODE: 255,
};
exports.OPS = OPS;
const REVERSE_OPS = {};
exports.REVERSE_OPS = REVERSE_OPS;
for (const op of Object.keys(OPS)) {
const code = OPS[op];
REVERSE_OPS[code] = op;
}

View file

@ -1,9 +1,10 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2data = void 0;
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
function stacksEqual(a, b) {
if (a.length !== b.length) return false;
@ -15,11 +16,13 @@ function stacksEqual(a, b) {
function p2data(a, opts) {
if (!a.data && !a.output) throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
data: typef.maybe(typef.arrayOf(typef.Buffer)),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
data: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
},
a,
);
@ -38,7 +41,7 @@ function p2data(a, opts) {
if (a.output) {
const chunks = bscript.decompile(a.output);
if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid');
if (!chunks.slice(1).every(typef.Buffer))
if (!chunks.slice(1).every(types_1.typeforce.Buffer))
throw new TypeError('Output is invalid');
if (a.data && !stacksEqual(a.data, o.data))
throw new TypeError('Data mismatch');

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
import { Network } from '../networks';
import { p2data as embed } from './embed';
import { p2ms } from './p2ms';

View file

@ -1,18 +1,54 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0;
const embed_1 = require('./embed');
exports.embed = embed_1.p2data;
Object.defineProperty(exports, 'embed', {
enumerable: true,
get: function() {
return embed_1.p2data;
},
});
const p2ms_1 = require('./p2ms');
exports.p2ms = p2ms_1.p2ms;
Object.defineProperty(exports, 'p2ms', {
enumerable: true,
get: function() {
return p2ms_1.p2ms;
},
});
const p2pk_1 = require('./p2pk');
exports.p2pk = p2pk_1.p2pk;
Object.defineProperty(exports, 'p2pk', {
enumerable: true,
get: function() {
return p2pk_1.p2pk;
},
});
const p2pkh_1 = require('./p2pkh');
exports.p2pkh = p2pkh_1.p2pkh;
Object.defineProperty(exports, 'p2pkh', {
enumerable: true,
get: function() {
return p2pkh_1.p2pkh;
},
});
const p2sh_1 = require('./p2sh');
exports.p2sh = p2sh_1.p2sh;
Object.defineProperty(exports, 'p2sh', {
enumerable: true,
get: function() {
return p2sh_1.p2sh;
},
});
const p2wpkh_1 = require('./p2wpkh');
exports.p2wpkh = p2wpkh_1.p2wpkh;
Object.defineProperty(exports, 'p2wpkh', {
enumerable: true,
get: function() {
return p2wpkh_1.p2wpkh;
},
});
const p2wsh_1 = require('./p2wsh');
exports.p2wsh = p2wsh_1.p2wsh;
Object.defineProperty(exports, 'p2wsh', {
enumerable: true,
get: function() {
return p2wsh_1.p2wsh;
},
});
// TODO
// witness commitment

View file

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.value = exports.prop = void 0;
function prop(object, name, f) {
Object.defineProperty(object, name, {
configurable: true,

View file

@ -1,11 +1,11 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2ms = void 0;
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const OPS = bscript.OPS;
const typef = require('typeforce');
const ecc = require('tiny-secp256k1');
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
function stacksEqual(a, b) {
if (a.length !== b.length) return false;
@ -30,15 +30,19 @@ function p2ms(a, opts) {
(opts.allowIncomplete && x === OPS.OP_0) !== undefined
);
}
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
m: typef.maybe(typef.Number),
n: typef.maybe(typef.Number),
output: typef.maybe(typef.Buffer),
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
input: typef.maybe(typef.Buffer),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
m: types_1.typeforce.maybe(types_1.typeforce.Number),
n: types_1.typeforce.maybe(types_1.typeforce.Number),
output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
pubkeys: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.isPoint),
),
signatures: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(isAcceptableSignature),
),
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
},
a,
);
@ -101,14 +105,15 @@ function p2ms(a, opts) {
if (opts.validate) {
if (a.output) {
decode(a.output);
if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid');
if (!typef.Number(chunks[chunks.length - 2]))
if (!types_1.typeforce.Number(chunks[0]))
throw new TypeError('Output is invalid');
if (!types_1.typeforce.Number(chunks[chunks.length - 2]))
throw new TypeError('Output is invalid');
if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG)
throw new TypeError('Output is invalid');
if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3)
throw new TypeError('Output is invalid');
if (!o.pubkeys.every(x => ecc.isPoint(x)))
if (!o.pubkeys.every(x => (0, types_1.isPoint)(x)))
throw new TypeError('Output is invalid');
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');
if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch');

View file

@ -1,24 +1,24 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2pk = void 0;
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
// input: {signature}
// output: {pubKey} OP_CHECKSIG
function p2pk(a, opts) {
if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
pubkey: typef.maybe(ecc.isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
input: typef.maybe(typef.Buffer),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
pubkey: types_1.typeforce.maybe(types_1.isPoint),
signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
},
a,
);
@ -52,7 +52,7 @@ function p2pk(a, opts) {
if (a.output) {
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
throw new TypeError('Output is invalid');
if (!ecc.isPoint(o.pubkey))
if (!(0, types_1.isPoint)(o.pubkey))
throw new TypeError('Output pubkey is invalid');
if (a.pubkey && !a.pubkey.equals(o.pubkey))
throw new TypeError('Pubkey mismatch');

View file

@ -1,28 +1,28 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2pkh = void 0;
const bcrypto = require('../crypto');
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const bs58check = require('bs58check');
const OPS = bscript.OPS;
// input: {signature} {pubkey}
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
function p2pkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
address: typef.maybe(typef.String),
hash: typef.maybe(typef.BufferN(20)),
output: typef.maybe(typef.BufferN(25)),
pubkey: typef.maybe(ecc.isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
input: typef.maybe(typef.Buffer),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
address: types_1.typeforce.maybe(types_1.typeforce.String),
hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
output: types_1.typeforce.maybe(types_1.typeforce.BufferN(25)),
pubkey: types_1.typeforce.maybe(types_1.isPoint),
signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
},
a,
);
@ -116,7 +116,7 @@ function p2pkh(a, opts) {
if (chunks.length !== 2) throw new TypeError('Input is invalid');
if (!bscript.isCanonicalScriptSignature(chunks[0]))
throw new TypeError('Input has invalid signature');
if (!ecc.isPoint(chunks[1]))
if (!(0, types_1.isPoint)(chunks[1]))
throw new TypeError('Input has invalid pubkey');
if (a.signature && !a.signature.equals(chunks[0]))
throw new TypeError('Signature mismatch');

View file

@ -1,12 +1,13 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2sh = void 0;
const bcrypto = require('../crypto');
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
const bs58check = require('bs58check');
const OPS = bscript.OPS;
function stacksEqual(a, b) {
if (a.length !== b.length) return false;
return a.every((x, i) => {
@ -20,20 +21,24 @@ function p2sh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.input)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
address: typef.maybe(typef.String),
hash: typef.maybe(typef.BufferN(20)),
output: typef.maybe(typef.BufferN(23)),
redeem: typef.maybe({
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
input: typef.maybe(typef.Buffer),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
address: types_1.typeforce.maybe(types_1.typeforce.String),
hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
output: types_1.typeforce.maybe(types_1.typeforce.BufferN(23)),
redeem: types_1.typeforce.maybe({
network: types_1.typeforce.maybe(types_1.typeforce.Object),
output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
witness: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
}),
input: typef.maybe(typef.Buffer),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
witness: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
},
a,
);

View file

@ -1,13 +1,13 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2wpkh = void 0;
const bcrypto = require('../crypto');
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const bech32_1 = require('bech32');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const { bech32 } = require('bech32');
const EMPTY_BUFFER = Buffer.alloc(0);
// witness: {signature} {pubKey}
// input: <>
@ -16,23 +16,25 @@ function p2wpkh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
address: typef.maybe(typef.String),
hash: typef.maybe(typef.BufferN(20)),
input: typef.maybe(typef.BufferN(0)),
network: typef.maybe(typef.Object),
output: typef.maybe(typef.BufferN(22)),
pubkey: typef.maybe(ecc.isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
address: types_1.typeforce.maybe(types_1.typeforce.String),
hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)),
input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
output: types_1.typeforce.maybe(types_1.typeforce.BufferN(22)),
pubkey: types_1.typeforce.maybe(types_1.isPoint),
signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature),
witness: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
},
a,
);
const _address = lazy.value(() => {
const result = bech32.decode(a.address);
const result = bech32_1.bech32.decode(a.address);
const version = result.words.shift();
const data = bech32.fromWords(result.words);
const data = bech32_1.bech32.fromWords(result.words);
return {
version,
prefix: result.prefix,
@ -43,9 +45,9 @@ function p2wpkh(a, opts) {
const o = { name: 'p2wpkh', network };
lazy.prop(o, 'address', () => {
if (!o.hash) return;
const words = bech32.toWords(o.hash);
const words = bech32_1.bech32.toWords(o.hash);
words.unshift(0x00);
return bech32.encode(network.bech32, words);
return bech32_1.bech32.encode(network.bech32, words);
});
lazy.prop(o, 'hash', () => {
if (a.output) return a.output.slice(2, 22);
@ -107,14 +109,14 @@ function p2wpkh(a, opts) {
if (hash.length > 0 && !hash.equals(pkh))
throw new TypeError('Hash mismatch');
else hash = pkh;
if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33)
if (!(0, types_1.isPoint)(a.pubkey) || a.pubkey.length !== 33)
throw new TypeError('Invalid pubkey for p2wpkh');
}
if (a.witness) {
if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
if (!bscript.isCanonicalScriptSignature(a.witness[0]))
throw new TypeError('Witness has invalid signature');
if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33)
if (!(0, types_1.isPoint)(a.witness[1]) || a.witness[1].length !== 33)
throw new TypeError('Witness has invalid pubkey');
if (a.signature && !a.signature.equals(a.witness[0]))
throw new TypeError('Signature mismatch');

View file

@ -1,13 +1,13 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2wsh = void 0;
const bcrypto = require('../crypto');
const networks_1 = require('../networks');
const bscript = require('../script');
const types_1 = require('../types');
const lazy = require('./lazy');
const typef = require('typeforce');
const bech32_1 = require('bech32');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const { bech32 } = require('bech32');
const EMPTY_BUFFER = Buffer.alloc(0);
function stacksEqual(a, b) {
if (a.length !== b.length) return false;
@ -20,7 +20,7 @@ function chunkHasUncompressedPubkey(chunk) {
Buffer.isBuffer(chunk) &&
chunk.length === 65 &&
chunk[0] === 0x04 &&
ecc.isPoint(chunk)
(0, types_1.isPoint)(chunk)
) {
return true;
} else {
@ -34,27 +34,31 @@ function p2wsh(a, opts) {
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
typef(
(0, types_1.typeforce)(
{
network: typef.maybe(typef.Object),
address: typef.maybe(typef.String),
hash: typef.maybe(typef.BufferN(32)),
output: typef.maybe(typef.BufferN(34)),
redeem: typef.maybe({
input: typef.maybe(typef.Buffer),
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
address: types_1.typeforce.maybe(types_1.typeforce.String),
hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)),
output: types_1.typeforce.maybe(types_1.typeforce.BufferN(34)),
redeem: types_1.typeforce.maybe({
input: types_1.typeforce.maybe(types_1.typeforce.Buffer),
network: types_1.typeforce.maybe(types_1.typeforce.Object),
output: types_1.typeforce.maybe(types_1.typeforce.Buffer),
witness: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
}),
input: typef.maybe(typef.BufferN(0)),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)),
witness: types_1.typeforce.maybe(
types_1.typeforce.arrayOf(types_1.typeforce.Buffer),
),
},
a,
);
const _address = lazy.value(() => {
const result = bech32.decode(a.address);
const result = bech32_1.bech32.decode(a.address);
const version = result.words.shift();
const data = bech32.fromWords(result.words);
const data = bech32_1.bech32.fromWords(result.words);
return {
version,
prefix: result.prefix,
@ -71,9 +75,9 @@ function p2wsh(a, opts) {
const o = { network };
lazy.prop(o, 'address', () => {
if (!o.hash) return;
const words = bech32.toWords(o.hash);
const words = bech32_1.bech32.toWords(o.hash);
words.unshift(0x00);
return bech32.encode(network.bech32, words);
return bech32_1.bech32.encode(network.bech32, words);
});
lazy.prop(o, 'hash', () => {
if (a.output) return a.output.slice(2);

View file

@ -1,6 +1,6 @@
/// <reference types="node" />
import { Psbt as PsbtBase } from 'bip174';
import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate } from 'bip174/src/lib/interfaces';
import { Signer, SignerAsync } from './ecpair';
import { Network } from './networks';
import { Transaction } from './transaction';
export interface TransactionInput {
@ -18,6 +18,7 @@ export interface TransactionOutput {
export interface PsbtTxOutput extends TransactionOutput {
address: string | undefined;
}
export declare type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, signature: Buffer) => boolean;
/**
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
* There are 6 roles that this class fulfills. (Explained in BIP174)
@ -58,11 +59,13 @@ export declare class Psbt {
private __CACHE;
private opts;
constructor(opts?: PsbtOptsOptional, data?: PsbtBase);
readonly inputCount: number;
version: number;
locktime: number;
readonly txInputs: PsbtTxInput[];
readonly txOutputs: PsbtTxOutput[];
get inputCount(): number;
get version(): number;
set version(version: number);
get locktime(): number;
set locktime(locktime: number);
get txInputs(): PsbtTxInput[];
get txOutputs(): PsbtTxOutput[];
combine(...those: Psbt[]): this;
clone(): Psbt;
setMaximumFeeRate(satoshiPerByte: number): void;
@ -83,8 +86,8 @@ export declare class Psbt {
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;
outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean;
outputHasHDKey(outputIndex: number, root: HDSigner): boolean;
validateSignaturesOfAllInputs(): boolean;
validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean;
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean;
validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Buffer): boolean;
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
@ -129,7 +132,7 @@ interface HDSignerBase {
*/
fingerprint: Buffer;
}
interface HDSigner extends HDSignerBase {
export interface HDSigner extends HDSignerBase {
/**
* The path string must match /^m(\/\d+'?)+$/
* ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
@ -144,10 +147,22 @@ interface HDSigner extends HDSignerBase {
/**
* Same as above but with async sign method
*/
interface HDSignerAsync extends HDSignerBase {
export interface HDSignerAsync extends HDSignerBase {
derivePath(path: string): HDSignerAsync;
sign(hash: Buffer): Promise<Buffer>;
}
export interface Signer {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Buffer;
getPublicKey?(): Buffer;
}
export interface SignerAsync {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
getPublicKey?(): Buffer;
}
/**
* This function must do two things:
* 1. Check if the `input` can be finalized. If it can not be finalized, throw.

View file

@ -1,12 +1,12 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.Psbt = void 0;
const bip174_1 = require('bip174');
const varuint = require('bip174/src/lib/converter/varint');
const utils_1 = require('bip174/src/lib/utils');
const address_1 = require('./address');
const bufferutils_1 = require('./bufferutils');
const crypto_1 = require('./crypto');
const ecpair_1 = require('./ecpair');
const networks_1 = require('./networks');
const payments = require('./payments');
const bscript = require('./script');
@ -25,7 +25,7 @@ const DEFAULT_OPTS = {
* THIS IS NOT TO BE RELIED ON.
* It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
*/
maximumFeeRate: 5000,
maximumFeeRate: 5000, // satoshi per byte
};
/**
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
@ -69,6 +69,8 @@ class Psbt {
__NON_WITNESS_UTXO_BUF_CACHE: [],
__TX_IN_CACHE: {},
__TX: this.data.globalMap.unsignedTx.tx,
// Psbt's predecesor (TransactionBuilder - now removed) behavior
// was to not confirm input values before signing.
// Even though we highly encourage people to get
// the full parent transaction to verify values, the ability to
// sign non-segwit inputs without the full transaction was often
@ -118,7 +120,7 @@ class Psbt {
}
get txInputs() {
return this.__CACHE.__TX.ins.map(input => ({
hash: bufferutils_1.cloneBuffer(input.hash),
hash: (0, bufferutils_1.cloneBuffer)(input.hash),
index: input.index,
sequence: input.sequence,
}));
@ -127,10 +129,13 @@ class Psbt {
return this.__CACHE.__TX.outs.map(output => {
let address;
try {
address = address_1.fromOutputScript(output.script, this.opts.network);
address = (0, address_1.fromOutputScript)(
output.script,
this.opts.network,
);
} catch (_) {}
return {
script: bufferutils_1.cloneBuffer(output.script),
script: (0, bufferutils_1.cloneBuffer)(output.script),
value: output.value,
address,
};
@ -229,7 +234,7 @@ class Psbt {
const { address } = outputData;
if (typeof address === 'string') {
const { network } = this.opts;
const script = address_1.toOutputScript(address, network);
const script = (0, address_1.toOutputScript)(address, network);
outputData = Object.assign(outputData, { script });
}
const c = this.__CACHE;
@ -262,12 +267,12 @@ class Psbt {
return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE);
}
finalizeAllInputs() {
utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
(0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one
range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
return this;
}
finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex,
input,
@ -292,7 +297,7 @@ class Psbt {
return this;
}
getInputType(inputIndex) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
const result = getMeaningfulScript(
script,
@ -307,39 +312,41 @@ class Psbt {
return type + mainType;
}
inputHasPubkey(inputIndex, pubkey) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
}
inputHasHDKey(inputIndex, root) {
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
);
}
outputHasPubkey(outputIndex, pubkey) {
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex);
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
}
outputHasHDKey(outputIndex, root) {
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex);
const derivationIsMine = bip32DerivationIsMine(root);
return (
!!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
);
}
validateSignaturesOfAllInputs() {
utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
validateSignaturesOfAllInputs(validator) {
(0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one
const results = range(this.data.inputs.length).map(idx =>
this.validateSignaturesOfInput(idx),
this.validateSignaturesOfInput(idx, validator),
);
return results.reduce((final, res) => res === true && final, true);
}
validateSignaturesOfInput(inputIndex, pubkey) {
validateSignaturesOfInput(inputIndex, validator, pubkey) {
const input = this.data.inputs[inputIndex];
const partialSig = (input || {}).partialSig;
if (!input || !partialSig || partialSig.length < 1)
throw new Error('No signatures to validate');
if (typeof validator !== 'function')
throw new Error('Need validator function to validate signatures');
const mySigs = pubkey
? partialSig.filter(sig => sig.pubkey.equals(pubkey))
: partialSig;
@ -363,8 +370,7 @@ class Psbt {
hashCache = hash;
scriptCache = script;
checkScriptForPubkey(pSig.pubkey, script, 'verify');
const keypair = ecpair_1.fromPublicKey(pSig.pubkey);
results.push(keypair.verify(hash, sig.signature));
results.push(validator(pSig.pubkey, hash, sig.signature));
}
return results.every(res => res === true);
}
@ -641,7 +647,7 @@ class PsbtTransaction {
}
const hash =
typeof input.hash === 'string'
? bufferutils_1.reverseBuffer(Buffer.from(input.hash, 'hex'))
? (0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash, 'hex'))
: input.hash;
this.tx.addInput(hash, input.index, input.sequence);
}
@ -684,8 +690,7 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
if (pubkeys) {
sigs = pubkeys
.map(pkey => {
const pubkey = ecpair_1.fromPublicKey(pkey, { compressed: true })
.publicKey;
const pubkey = compressPubkey(pkey);
return partialSig.find(pSig => pSig.pubkey.equals(pubkey));
})
.filter(v => !!v);
@ -816,7 +821,7 @@ function checkTxForDupeIns(tx, cache) {
}
function checkTxInputCache(cache, input) {
const key =
bufferutils_1.reverseBuffer(Buffer.from(input.hash)).toString('hex') +
(0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash)).toString('hex') +
':' +
input.index;
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
@ -911,7 +916,7 @@ function getHashAndSighashType(
cache,
sighashTypes,
) {
const input = utils_1.checkForInput(inputs, inputIndex);
const input = (0, utils_1.checkForInput)(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(
inputIndex,
input,
@ -997,7 +1002,8 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
console.warn(
'Warning: Signing non-segwit inputs without the full parent transaction ' +
'means there is a chance that a miner could feed you incorrect information ' +
'to trick you into paying large fees. You are not ' +
"to trick you into paying large fees. This behavior is the same as Psbt's predecesor " +
'(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
'*********************',
@ -1094,7 +1100,7 @@ function getScriptFromInput(inputIndex, input, cache) {
return res;
}
function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
const input = utils_1.checkForInput(inputs, inputIndex);
const input = (0, utils_1.checkForInput)(inputs, inputIndex);
if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
throw new Error('Need bip32Derivation to sign with HD');
}
@ -1321,6 +1327,15 @@ function redeemFromFinalWitnessScript(finalScript) {
if (!sDecomp) return;
return lastItem;
}
function compressPubkey(pubkey) {
if (pubkey.length === 65) {
const parity = pubkey[64] & 1;
const newKey = pubkey.slice(0, 33);
newKey[0] = 2 | parity;
return newKey;
}
return pubkey.slice();
}
function isPubkeyLike(buf) {
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
}
@ -1376,7 +1391,7 @@ function checkInvalidP2WSH(script) {
}
}
function pubkeyInScript(pubkey, script) {
const pubkeyHash = crypto_1.hash160(pubkey);
const pubkeyHash = (0, crypto_1.hash160)(pubkey);
const decompiled = bscript.decompile(script);
if (decompiled === null) throw new Error('Unknown script error');
return decompiled.some(element => {

8
src/push_data.d.ts vendored Normal file
View file

@ -0,0 +1,8 @@
/// <reference types="node" />
export declare function encodingLength(i: number): number;
export declare function encode(buffer: Buffer, num: number, offset: number): number;
export declare function decode(buffer: Buffer, offset: number): {
opcode: number;
number: number;
size: number;
} | null;

61
src/push_data.js Normal file
View file

@ -0,0 +1,61 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.decode = exports.encode = exports.encodingLength = void 0;
const ops_1 = require('./ops');
function encodingLength(i) {
return i < ops_1.OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
}
exports.encodingLength = encodingLength;
function encode(buffer, num, offset) {
const size = encodingLength(num);
// ~6 bit
if (size === 1) {
buffer.writeUInt8(num, offset);
// 8 bit
} else if (size === 2) {
buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA1, offset);
buffer.writeUInt8(num, offset + 1);
// 16 bit
} else if (size === 3) {
buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA2, offset);
buffer.writeUInt16LE(num, offset + 1);
// 32 bit
} else {
buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA4, offset);
buffer.writeUInt32LE(num, offset + 1);
}
return size;
}
exports.encode = encode;
function decode(buffer, offset) {
const opcode = buffer.readUInt8(offset);
let num;
let size;
// ~6 bit
if (opcode < ops_1.OPS.OP_PUSHDATA1) {
num = opcode;
size = 1;
// 8 bit
} else if (opcode === ops_1.OPS.OP_PUSHDATA1) {
if (offset + 2 > buffer.length) return null;
num = buffer.readUInt8(offset + 1);
size = 2;
// 16 bit
} else if (opcode === ops_1.OPS.OP_PUSHDATA2) {
if (offset + 3 > buffer.length) return null;
num = buffer.readUInt16LE(offset + 1);
size = 3;
// 32 bit
} else {
if (offset + 5 > buffer.length) return null;
if (opcode !== ops_1.OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
num = buffer.readUInt32LE(offset + 1);
size = 5;
}
return {
opcode,
number: num,
size,
};
}
exports.decode = decode;

View file

@ -1,10 +1,9 @@
/// <reference types="node" />
import { OPS } from './ops';
import { Stack } from './payments';
import * as scriptNumber from './script_number';
import * as scriptSignature from './script_signature';
export declare type OpCode = number;
export declare const OPS: {
[index: string]: number;
};
export { OPS };
export declare function isPushOnly(value: Stack): boolean;
export declare function compile(chunks: Buffer | Stack): Buffer;
export declare function decompile(buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null;

View file

@ -1,21 +1,26 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.signature = exports.number = exports.isCanonicalScriptSignature = exports.isDefinedHashType = exports.isCanonicalPubKey = exports.toStack = exports.fromASM = exports.toASM = exports.decompile = exports.compile = exports.isPushOnly = exports.OPS = void 0;
const bip66 = require('./bip66');
const ops_1 = require('./ops');
Object.defineProperty(exports, 'OPS', {
enumerable: true,
get: function() {
return ops_1.OPS;
},
});
const pushdata = require('./push_data');
const scriptNumber = require('./script_number');
const scriptSignature = require('./script_signature');
const types = require('./types');
const bip66 = require('bip66');
const ecc = require('tiny-secp256k1');
const pushdata = require('pushdata-bitcoin');
const typeforce = require('typeforce');
exports.OPS = require('bitcoin-ops');
const REVERSE_OPS = require('bitcoin-ops/map');
const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1
const { typeforce } = types;
const OP_INT_BASE = ops_1.OPS.OP_RESERVED; // OP_1 - 1
function isOPInt(value) {
return (
types.Number(value) &&
(value === exports.OPS.OP_0 ||
(value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) ||
value === exports.OPS.OP_1NEGATE)
(value === ops_1.OPS.OP_0 ||
(value >= ops_1.OPS.OP_1 && value <= ops_1.OPS.OP_16) ||
value === ops_1.OPS.OP_1NEGATE)
);
}
function isPushOnlyChunk(value) {
@ -26,10 +31,10 @@ function isPushOnly(value) {
}
exports.isPushOnly = isPushOnly;
function asMinimalOP(buffer) {
if (buffer.length === 0) return exports.OPS.OP_0;
if (buffer.length === 0) return ops_1.OPS.OP_0;
if (buffer.length !== 1) return;
if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE;
if (buffer[0] === 0x81) return ops_1.OPS.OP_1NEGATE;
}
function chunksIsBuffer(buf) {
return Buffer.isBuffer(buf);
@ -90,7 +95,7 @@ function decompile(buffer) {
while (i < buffer.length) {
const opcode = buffer[i];
// data chunk
if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) {
if (opcode > ops_1.OPS.OP_0 && opcode <= ops_1.OPS.OP_PUSHDATA4) {
const d = pushdata.decode(buffer, i);
// did reading a pushDataInt fail?
if (d === null) return null;
@ -128,7 +133,7 @@ function toASM(chunks) {
chunk = op;
}
// opcode!
return REVERSE_OPS[chunk];
return ops_1.REVERSE_OPS[chunk];
})
.join(' ');
}
@ -138,7 +143,7 @@ function fromASM(asm) {
return compile(
asm.split(' ').map(chunkStr => {
// opcode?
if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr];
if (ops_1.OPS[chunkStr] !== undefined) return ops_1.OPS[chunkStr];
typeforce(types.Hex, chunkStr);
// data!
return Buffer.from(chunkStr, 'hex');
@ -151,13 +156,13 @@ function toStack(chunks) {
typeforce(isPushOnly, chunks);
return chunks.map(op => {
if (singleChunkIsBuffer(op)) return op;
if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0);
if (op === ops_1.OPS.OP_0) return Buffer.allocUnsafe(0);
return scriptNumber.encode(op - OP_INT_BASE);
});
}
exports.toStack = toStack;
function isCanonicalPubKey(buffer) {
return ecc.isPoint(buffer);
return types.isPoint(buffer);
}
exports.isCanonicalPubKey = isCanonicalPubKey;
function isDefinedHashType(hashType) {

View file

@ -1,2 +1,3 @@
/// <reference types="node" />
export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number;
export declare function encode(_number: number): Buffer;

View file

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.encode = exports.decode = void 0;
function decode(buffer, maxLength, minimal) {
maxLength = maxLength || 4;
minimal = minimal === undefined ? true : minimal;

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
interface ScriptSignature {
signature: Buffer;
hashType: number;

View file

@ -1,8 +1,9 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.encode = exports.decode = void 0;
const bip66 = require('./bip66');
const types = require('./types');
const bip66 = require('bip66');
const typeforce = require('typeforce');
const { typeforce } = types;
const ZERO = Buffer.alloc(1, 0);
function toDER(x) {
let i = 0;

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
export interface Output {
script: Buffer;
value: number;

View file

@ -1,20 +1,20 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.Transaction = void 0;
const bufferutils_1 = require('./bufferutils');
const bcrypto = require('./crypto');
const bscript = require('./script');
const script_1 = require('./script');
const types = require('./types');
const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin');
const { typeforce } = types;
function varSliceSize(someScript) {
const length = someScript.length;
return varuint.encodingLength(length) + length;
return bufferutils_1.varuint.encodingLength(length) + length;
}
function vectorSize(someVector) {
const length = someVector.length;
return (
varuint.encodingLength(length) +
bufferutils_1.varuint.encodingLength(length) +
someVector.reduce((sum, witness) => {
return sum + varSliceSize(witness);
}, 0)
@ -157,8 +157,8 @@ class Transaction {
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
return (
(hasWitnesses ? 10 : 8) +
varuint.encodingLength(this.ins.length) +
varuint.encodingLength(this.outs.length) +
bufferutils_1.varuint.encodingLength(this.ins.length) +
bufferutils_1.varuint.encodingLength(this.outs.length) +
this.ins.reduce((sum, input) => {
return sum + 40 + varSliceSize(input.script);
}, 0) +
@ -336,7 +336,9 @@ class Transaction {
}
getId() {
// transaction hash's are displayed in reverse order
return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex');
return (0, bufferutils_1.reverseBuffer)(this.getHash(false)).toString(
'hex',
);
}
toBuffer(buffer, initialOffset) {
return this.__toBuffer(buffer, initialOffset, true);
@ -392,6 +394,7 @@ class Transaction {
return buffer;
}
}
exports.Transaction = Transaction;
Transaction.DEFAULT_SEQUENCE = 0xffffffff;
Transaction.SIGHASH_ALL = 0x01;
Transaction.SIGHASH_NONE = 0x02;
@ -399,4 +402,3 @@ Transaction.SIGHASH_SINGLE = 0x03;
Transaction.SIGHASH_ANYONECANPAY = 0x80;
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00;
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01;
exports.Transaction = Transaction;

View file

@ -1,3 +1,6 @@
/// <reference types="node" />
export declare const typeforce: any;
export declare function isPoint(p: Buffer | number | undefined | null): boolean;
export declare function UInt31(value: number): boolean;
export declare function BIP32Path(value: string): boolean;
export declare namespace BIP32Path {

View file

@ -1,13 +1,39 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const typeforce = require('typeforce');
exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0;
const buffer_1 = require('buffer');
exports.typeforce = require('typeforce');
const ZERO32 = buffer_1.Buffer.alloc(32, 0);
const EC_P = buffer_1.Buffer.from(
'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
'hex',
);
function isPoint(p) {
if (!buffer_1.Buffer.isBuffer(p)) return false;
if (p.length < 33) return false;
const t = p[0];
const x = p.slice(1, 33);
if (x.compare(ZERO32) === 0) return false;
if (x.compare(EC_P) >= 0) return false;
if ((t === 0x02 || t === 0x03) && p.length === 33) {
return true;
}
const y = p.slice(33);
if (y.compare(ZERO32) === 0) return false;
if (y.compare(EC_P) >= 0) return false;
if (t === 0x04 && p.length === 65) return true;
return false;
}
exports.isPoint = isPoint;
const UINT31_MAX = Math.pow(2, 31) - 1;
function UInt31(value) {
return typeforce.UInt32(value) && value <= UINT31_MAX;
return exports.typeforce.UInt32(value) && value <= UINT31_MAX;
}
exports.UInt31 = UInt31;
function BIP32Path(value) {
return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/);
return (
exports.typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/)
);
}
exports.BIP32Path = BIP32Path;
BIP32Path.toJSON = () => {
@ -15,7 +41,7 @@ BIP32Path.toJSON = () => {
};
function Signer(obj) {
return (
(typeforce.Buffer(obj.publicKey) ||
(exports.typeforce.Buffer(obj.publicKey) ||
typeof obj.getPublicKey === 'function') &&
typeof obj.sign === 'function'
);
@ -23,36 +49,39 @@ function Signer(obj) {
exports.Signer = Signer;
const SATOSHI_MAX = 21 * 1e14;
function Satoshi(value) {
return typeforce.UInt53(value) && value <= SATOSHI_MAX;
return exports.typeforce.UInt53(value) && value <= SATOSHI_MAX;
}
exports.Satoshi = Satoshi;
// external dependent types
exports.ECPoint = typeforce.quacksLike('Point');
exports.ECPoint = exports.typeforce.quacksLike('Point');
// exposed, external API
exports.Network = typeforce.compile({
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
exports.Network = exports.typeforce.compile({
messagePrefix: exports.typeforce.oneOf(
exports.typeforce.Buffer,
exports.typeforce.String,
),
bip32: {
public: typeforce.UInt32,
private: typeforce.UInt32,
public: exports.typeforce.UInt32,
private: exports.typeforce.UInt32,
},
pubKeyHash: typeforce.UInt8,
scriptHash: typeforce.UInt8,
wif: typeforce.UInt8,
pubKeyHash: exports.typeforce.UInt8,
scriptHash: exports.typeforce.UInt8,
wif: exports.typeforce.UInt8,
});
exports.Buffer256bit = typeforce.BufferN(32);
exports.Hash160bit = typeforce.BufferN(20);
exports.Hash256bit = typeforce.BufferN(32);
exports.Number = typeforce.Number; // tslint:disable-line variable-name
exports.Array = typeforce.Array;
exports.Boolean = typeforce.Boolean; // tslint:disable-line variable-name
exports.String = typeforce.String; // tslint:disable-line variable-name
exports.Buffer = typeforce.Buffer;
exports.Hex = typeforce.Hex;
exports.maybe = typeforce.maybe;
exports.tuple = typeforce.tuple;
exports.UInt8 = typeforce.UInt8;
exports.UInt32 = typeforce.UInt32;
exports.Function = typeforce.Function;
exports.BufferN = typeforce.BufferN;
exports.Null = typeforce.Null;
exports.oneOf = typeforce.oneOf;
exports.Buffer256bit = exports.typeforce.BufferN(32);
exports.Hash160bit = exports.typeforce.BufferN(20);
exports.Hash256bit = exports.typeforce.BufferN(32);
exports.Number = exports.typeforce.Number; // tslint:disable-line variable-name
exports.Array = exports.typeforce.Array;
exports.Boolean = exports.typeforce.Boolean; // tslint:disable-line variable-name
exports.String = exports.typeforce.String; // tslint:disable-line variable-name
exports.Buffer = exports.typeforce.Buffer;
exports.Hex = exports.typeforce.Hex;
exports.maybe = exports.typeforce.maybe;
exports.tuple = exports.typeforce.tuple;
exports.UInt8 = exports.typeforce.UInt8;
exports.UInt32 = exports.typeforce.UInt32;
exports.Function = exports.typeforce.Function;
exports.BufferN = exports.typeforce.BufferN;
exports.Null = exports.typeforce.Null;
exports.oneOf = exports.typeforce.oneOf;

View file

@ -88,49 +88,6 @@ describe('Bitcoin-core', () => {
});
});
// base58KeysValid
describe('ECPair', () => {
base58KeysValid.forEach(f => {
const strng = f[0] as string;
const hex = f[1];
const params = f[2] as any;
if (!params.isPrivkey) return;
const network = params.isTestnet
? bitcoin.networks.testnet
: bitcoin.networks.bitcoin;
const keyPair = bitcoin.ECPair.fromWIF(strng, network);
it('fromWIF imports ' + strng, () => {
assert.strictEqual(keyPair.privateKey!.toString('hex'), hex);
assert.strictEqual(keyPair.compressed, params.isCompressed);
});
it('toWIF exports ' + hex + ' to ' + strng, () => {
assert.strictEqual(keyPair.toWIF(), strng);
});
});
});
// base58KeysInvalid
describe('ECPair.fromWIF', () => {
const allowedNetworks = [
bitcoin.networks.bitcoin,
bitcoin.networks.testnet,
];
base58KeysInvalid.forEach(f => {
const strng = f[0];
it('throws on ' + strng, () => {
assert.throws(() => {
bitcoin.ECPair.fromWIF(strng, allowedNetworks);
}, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/);
});
});
});
describe('Block.fromHex', () => {
blocksValid.forEach(f => {
it('can parse ' + f.id, () => {

View file

@ -1,341 +0,0 @@
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 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: ECPairInterface;
beforeEach(() => {
keyPair = ECPair.fromPrivateKey(ONE);
});
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 as any).__Q.toString('hex'),
'0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
);
}),
);
});
describe('fromPrivateKey', () => {
it('defaults to compressed', () => {
const keyPair = ECPair.fromPrivateKey(ONE);
assert.strictEqual(keyPair.compressed, true);
});
it('supports the uncompressed option', () => {
const keyPair = ECPair.fromPrivateKey(ONE, {
compressed: false,
});
assert.strictEqual(keyPair.compressed, false);
});
it('supports the network option', () => {
const keyPair = ECPair.fromPrivateKey(ONE, {
compressed: false,
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 keyPair = ECPair.fromPrivateKey(d, {
compressed: f.compressed,
});
assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q);
});
});
fixtures.invalid.fromPrivateKey.forEach(f => {
it('throws ' + f.exception, () => {
const d = Buffer.from(f.d, 'hex');
assert.throws(() => {
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');
assert.throws(() => {
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 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);
});
});
fixtures.valid.forEach(f => {
it('imports ' + f.WIF + ' (via list of networks)', () => {
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 as any)[f.network]);
});
});
fixtures.invalid.fromWIF.forEach(f => {
it('throws on ' + f.WIF, () => {
assert.throws(() => {
const networks = f.network
? (NETWORKS as any)[f.network]
: NETWORKS_LIST;
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);
});
});
it('throws if no private key is found', () => {
assert.throws(() => {
const keyPair = ECPair.makeRandom();
delete (keyPair as any).__D;
keyPair.toWIF();
}, /Missing private key/);
});
});
describe('makeRandom', () => {
const d = Buffer.alloc(32, 4);
const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv';
describe('uses randombytes RNG', () => {
it('generates a ECPair', () => {
const stub = {
randombytes: (): Buffer => {
return d;
},
};
const ProxiedECPair = proxyquire('../src/ecpair', stub);
const keyPair = ProxiedECPair.makeRandom();
assert.strictEqual(keyPair.toWIF(), exWIF);
});
});
it('allows a custom RNG to be used', () => {
const keyPair = ECPair.makeRandom({
rng: (size): Buffer => {
return d.slice(0, size);
},
});
assert.strictEqual(keyPair.toWIF(), exWIF);
});
it('retains the same defaults as ECPair constructor', () => {
const keyPair = ECPair.makeRandom();
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,
});
assert.strictEqual(keyPair.compressed, false);
assert.strictEqual(keyPair.network, NETWORKS.testnet);
});
it('throws if d is bad length', () => {
function rng(): Buffer {
return Buffer.alloc(28);
}
assert.throws(() => {
ECPair.makeRandom({ rng });
}, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/);
});
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 });
}),
);
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 });
}),
);
});
describe('.network', () => {
fixtures.valid.forEach(f => {
it('returns ' + f.network + ' for ' + f.WIF, () => {
const network = (NETWORKS as any)[f.network];
const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST);
assert.strictEqual(keyPair.network, network);
});
});
});
describe('tinysecp wrappers', () => {
let keyPair: ECPairInterface;
let hash: Buffer;
let signature: Buffer;
beforeEach(() => {
keyPair = ECPair.makeRandom();
hash = ZERO;
signature = Buffer.alloc(64, 1);
});
describe('signing', () => {
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);
}),
);
it('throws if no private key is found', () => {
delete (keyPair as any).__D;
assert.throws(() => {
keyPair.sign(hash);
}, /Missing private key/);
});
});
describe('verify', () => {
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);
}),
);
});
});
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',
);
it('signs with normal R by default', () => {
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);
});
});
});

View file

@ -1,4 +1,5 @@
import * as assert from 'assert';
import { ECPair } from 'ecpair';
import { describe, it } from 'mocha';
import * as bitcoin from '../..';
import { regtestUtils } from './_regtest';
@ -10,7 +11,7 @@ describe('bitcoinjs-lib (addresses)', () => {
'can generate a random address [and support the retrieval of ' +
'transactions for that address (via 3PBP)]',
async () => {
const keyPair = bitcoin.ECPair.makeRandom();
const keyPair = ECPair.makeRandom();
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
// bitcoin P2PKH addresses start with a '1'
@ -29,7 +30,7 @@ describe('bitcoinjs-lib (addresses)', () => {
);
it('can import an address via WIF', () => {
const keyPair = bitcoin.ECPair.fromWIF(
const keyPair = ECPair.fromWIF(
'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn',
);
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
@ -51,7 +52,7 @@ describe('bitcoinjs-lib (addresses)', () => {
});
it('can generate a SegWit address', () => {
const keyPair = bitcoin.ECPair.fromWIF(
const keyPair = ECPair.fromWIF(
'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn',
);
const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey });
@ -60,7 +61,7 @@ describe('bitcoinjs-lib (addresses)', () => {
});
it('can generate a SegWit address (via P2SH)', () => {
const keyPair = bitcoin.ECPair.fromWIF(
const keyPair = ECPair.fromWIF(
'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn',
);
const { address } = bitcoin.payments.p2sh({
@ -103,7 +104,7 @@ describe('bitcoinjs-lib (addresses)', () => {
// examples using other network information
it('can generate a Testnet address', () => {
const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET });
const keyPair = ECPair.makeRandom({ network: TESTNET });
const { address } = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
network: TESTNET,
@ -130,7 +131,7 @@ describe('bitcoinjs-lib (addresses)', () => {
wif: 0xb0,
};
const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN });
const keyPair = ECPair.makeRandom({ network: LITECOIN });
const { address } = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
network: LITECOIN,

View file

@ -1,4 +1,5 @@
import * as assert from 'assert';
import { ECPair } from 'ecpair';
import { before, describe, it } from 'mocha';
import * as bitcoin from '../..';
import { regtestUtils } from './_regtest';
@ -13,11 +14,11 @@ function idToHash(txid: string): Buffer {
return Buffer.from(txid, 'hex').reverse();
}
const alice = bitcoin.ECPair.fromWIF(
const alice = ECPair.fromWIF(
'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe',
regtest,
);
const bob = bitcoin.ECPair.fromWIF(
const bob = ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x',
regtest,
);

View file

@ -1,5 +1,6 @@
import * as assert from 'assert';
import { PsbtInput } from 'bip174/src/lib/interfaces';
import { ECPair } from 'ecpair';
import { before, describe, it } from 'mocha';
import * as bitcoin from '../..';
import { regtestUtils } from './_regtest';
@ -15,19 +16,19 @@ function idToHash(txid: string): Buffer {
return Buffer.from(txid, 'hex').reverse();
}
const alice = bitcoin.ECPair.fromWIF(
const alice = ECPair.fromWIF(
'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe',
regtest,
);
const bob = bitcoin.ECPair.fromWIF(
const bob = ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x',
regtest,
);
const charles = bitcoin.ECPair.fromWIF(
const charles = ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf',
regtest,
);
const dave = bitcoin.ECPair.fromWIF(
const dave = ECPair.fromWIF(
'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx',
regtest,
);

View file

@ -1,10 +1,11 @@
import { ECPair } from 'ecpair';
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 }),
ECPair.makeRandom({ network: NETWORK }),
ECPair.makeRandom({ network: NETWORK }),
];
async function buildAndSign(

View file

@ -1,16 +1,23 @@
import * as assert from 'assert';
import * as bip32 from 'bip32';
import { ECPair } from 'ecpair';
import { describe, it } from 'mocha';
import * as bitcoin from '../..';
import { regtestUtils } from './_regtest';
const rng = require('randombytes');
const regtest = regtestUtils.network;
const validator = (
pubkey: Buffer,
msghash: Buffer,
signature: Buffer,
): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature);
// See bottom of file for some helper functions used to make the payment objects needed.
describe('bitcoinjs-lib (transactions with psbt)', () => {
it('can create a 1-to-1 Transaction', () => {
const alice = bitcoin.ECPair.fromWIF(
const alice = ECPair.fromWIF(
'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr',
);
const psbt = new bitcoin.Psbt();
@ -59,7 +66,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
value: 80000,
});
psbt.signInput(0, alice);
psbt.validateSignaturesOfInput(0);
psbt.validateSignaturesOfInput(0, validator);
psbt.finalizeAllInputs();
assert.strictEqual(
psbt.extractTransaction().toHex(),
@ -147,8 +154,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
// Finalizer wants to check all signatures are valid before finalizing.
// If the finalizer wants to check for specific pubkeys, the second arg
// can be passed. See the first multisig example below.
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(1), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
assert.strictEqual(psbt.validateSignaturesOfInput(1, validator), true);
// This step it new. Since we separate the signing operation and
// the creation of the scriptSig and witness stack, we are able to
@ -183,7 +190,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
})
.signInput(0, alice1.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
psbt.finalizeAllInputs();
// build and broadcast to the RegTest network
@ -215,13 +222,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.signInput(0, multisig.keys[0])
.signInput(0, multisig.keys[2]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey),
psbt.validateSignaturesOfInput(0, validator, multisig.keys[0].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey);
psbt.validateSignaturesOfInput(0, validator, multisig.keys[3].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
@ -330,7 +337,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
})
.signInput(0, p2wpkh.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
@ -398,7 +405,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
})
.signInput(0, p2wsh.keys[0]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
@ -472,13 +479,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
.signInput(0, p2sh.keys[2])
.signInput(0, p2sh.keys[3]);
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey),
psbt.validateSignaturesOfInput(0, validator, p2sh.keys[3].publicKey),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey);
psbt.validateSignaturesOfInput(0, validator, p2sh.keys[1].publicKey);
}, new RegExp('No signatures for this pubkey'));
psbt.finalizeAllInputs();
@ -534,10 +541,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
'P2SH(P2MS(2 of 2)) input with nonWitnessUtxo',
async () => {
const myKey = bitcoin.ECPair.makeRandom({ network: regtest });
const myKey = ECPair.makeRandom({ network: regtest });
const myKeys = [
myKey,
bitcoin.ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }),
ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }),
];
const p2sh = createPayment('p2sh-p2ms(2 of 2)', myKeys);
const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh');
@ -603,9 +610,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
})
.signInputHD(0, hdRoot); // must sign with root!!!
assert.strictEqual(psbt.validateSignaturesOfInput(0), true);
assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(0, childNode.publicKey),
psbt.validateSignaturesOfInput(0, validator, childNode.publicKey),
true,
);
psbt.finalizeAllInputs();
@ -638,11 +645,11 @@ function createPayment(_type: string, myKeys?: any[], network?: any): any {
throw new Error('Need n keys for multisig');
}
while (!myKeys && n > 1) {
keys.push(bitcoin.ECPair.makeRandom({ network }));
keys.push(ECPair.makeRandom({ network }));
n--;
}
}
if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }));
if (!myKeys) keys.push(ECPair.makeRandom({ network }));
let payment: any;
splitType.forEach(type => {

View file

@ -1,19 +1,19 @@
import * as assert from 'assert';
import * as bip32 from 'bip32';
import * as crypto from 'crypto';
import { ECPair } from 'ecpair';
import { describe, it } from 'mocha';
import {
bip32,
ECPair,
networks as NETWORKS,
payments,
Psbt,
Signer,
SignerAsync,
} from '..';
import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..';
import * as preFixtures from './fixtures/psbt.json';
const validator = (
pubkey: Buffer,
msghash: Buffer,
signature: Buffer,
): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature);
const initBuffers = (object: any): typeof preFixtures =>
JSON.parse(JSON.stringify(object), (_, value) => {
const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/);
@ -896,7 +896,7 @@ describe(`Psbt`, () => {
const notAClone = Object.assign(new Psbt(), psbt); // references still active
const clone = psbt.clone();
assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true);
assert.strictEqual(psbt.validateSignaturesOfAllInputs(validator), true);
assert.strictEqual(clone.toBase64(), psbt.toBase64());
assert.strictEqual(clone.toBase64(), notAClone.toBase64());
@ -923,20 +923,27 @@ describe(`Psbt`, () => {
it('Correctly validates a signature', () => {
const psbt = Psbt.fromBase64(f.psbt);
assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true);
assert.strictEqual(
psbt.validateSignaturesOfInput(f.index, validator),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(f.nonExistantIndex);
psbt.validateSignaturesOfInput(f.nonExistantIndex, validator);
}, 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 as any),
psbt.validateSignaturesOfInput(f.index, validator, f.pubkey as any),
true,
);
assert.throws(() => {
psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey as any);
psbt.validateSignaturesOfInput(
f.index,
validator,
f.incorrectPubkey as any,
);
}, new RegExp('No signatures for this pubkey'));
});
});
@ -985,7 +992,7 @@ describe(`Psbt`, () => {
assert.throws(() => {
psbt.setVersion(3);
}, new RegExp('Can not modify transaction, signatures exist.'));
psbt.validateSignaturesOfInput(0);
psbt.validateSignaturesOfInput(0, validator);
psbt.finalizeAllInputs();
assert.throws(() => {
psbt.setVersion(3);

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2017",
"target": "ES2020",
"module": "commonjs",
"outDir": "../",
"declaration": false,

View file

@ -3,10 +3,9 @@ import * as networks from './networks';
import * as payments from './payments';
import * as bscript from './script';
import * as types from './types';
const { bech32, bech32m } = require('bech32');
const bs58check = require('bs58check');
const typeforce = require('typeforce');
import { bech32, bech32m } from 'bech32';
import * as bs58check from 'bs58check';
const { typeforce } = types;
export interface Base58CheckResult {
hash: Buffer;

111
ts_src/bip66.ts Normal file
View file

@ -0,0 +1,111 @@
// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
// NOTE: SIGHASH byte ignored AND restricted, truncate before use
export function check(buffer: Buffer): boolean {
if (buffer.length < 8) return false;
if (buffer.length > 72) return false;
if (buffer[0] !== 0x30) return false;
if (buffer[1] !== buffer.length - 2) return false;
if (buffer[2] !== 0x02) return false;
const lenR = buffer[3];
if (lenR === 0) return false;
if (5 + lenR >= buffer.length) return false;
if (buffer[4 + lenR] !== 0x02) return false;
const lenS = buffer[5 + lenR];
if (lenS === 0) return false;
if (6 + lenR + lenS !== buffer.length) return false;
if (buffer[4] & 0x80) return false;
if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false;
if (buffer[lenR + 6] & 0x80) return false;
if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
return false;
return true;
}
export function decode(buffer: Buffer): { r: Buffer; s: Buffer } {
if (buffer.length < 8) throw new Error('DER sequence length is too short');
if (buffer.length > 72) throw new Error('DER sequence length is too long');
if (buffer[0] !== 0x30) throw new Error('Expected DER sequence');
if (buffer[1] !== buffer.length - 2)
throw new Error('DER sequence length is invalid');
if (buffer[2] !== 0x02) throw new Error('Expected DER integer');
const lenR = buffer[3];
if (lenR === 0) throw new Error('R length is zero');
if (5 + lenR >= buffer.length) throw new Error('R length is too long');
if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)');
const lenS = buffer[5 + lenR];
if (lenS === 0) throw new Error('S length is zero');
if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid');
if (buffer[4] & 0x80) throw new Error('R value is negative');
if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80))
throw new Error('R value excessively padded');
if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative');
if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
throw new Error('S value excessively padded');
// non-BIP66 - extract R, S values
return {
r: buffer.slice(4, 4 + lenR),
s: buffer.slice(6 + lenR),
};
}
/*
* Expects r and s to be positive DER integers.
*
* The DER format uses the most significant bit as a sign bit (& 0x80).
* If the significant bit is set AND the integer is positive, a 0x00 is prepended.
*
* Examples:
*
* 0 => 0x00
* 1 => 0x01
* -1 => 0xff
* 127 => 0x7f
* -127 => 0x81
* 128 => 0x0080
* -128 => 0x80
* 255 => 0x00ff
* -255 => 0xff01
* 16300 => 0x3fac
* -16300 => 0xc054
* 62300 => 0x00f35c
* -62300 => 0xff0ca4
*/
export function encode(r: Buffer, s: Buffer): Buffer {
const lenR = r.length;
const lenS = s.length;
if (lenR === 0) throw new Error('R length is zero');
if (lenS === 0) throw new Error('S length is zero');
if (lenR > 33) throw new Error('R length is too long');
if (lenS > 33) throw new Error('S length is too long');
if (r[0] & 0x80) throw new Error('R value is negative');
if (s[0] & 0x80) throw new Error('S value is negative');
if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80))
throw new Error('R value excessively padded');
if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80))
throw new Error('S value excessively padded');
const signature = Buffer.allocUnsafe(6 + lenR + lenS);
// 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
signature[0] = 0x30;
signature[1] = signature.length - 2;
signature[2] = 0x02;
signature[3] = r.length;
r.copy(signature, 4);
signature[4 + lenR] = 0x02;
signature[5 + lenR] = s.length;
s.copy(signature, 6 + lenR);
return signature;
}

View file

@ -1,11 +1,14 @@
import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils';
import {
BufferReader,
BufferWriter,
reverseBuffer,
varuint,
} from './bufferutils';
import * as bcrypto from './crypto';
import { fastMerkleRoot } from './merkle';
import { Transaction } from './transaction';
import * as types from './types';
const fastMerkleRoot = require('merkle-lib/fastRoot');
const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin');
const { typeforce } = types;
const errorMerkleNoTxes = new TypeError(
'Cannot compute merkle root for zero transactions',

View file

@ -1,7 +1,7 @@
import * as types from './types';
const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin');
const { typeforce } = types;
import * as varuint from 'varuint-bitcoin';
export { varuint };
// https://github.com/feross/buffer/blob/master/index.js#L1127
function verifuint(value: number, max: number): void {

View file

@ -1,4 +1,4 @@
const createHash = require('create-hash');
import * as createHash from 'create-hash';
export function ripemd160(buffer: Buffer): Buffer {
try {

View file

@ -1,161 +0,0 @@
import { Network } from './networks';
import * as NETWORKS from './networks';
import * as types from './types';
const ecc = require('tiny-secp256k1');
const randomBytes = require('randombytes');
const typeforce = require('typeforce');
const wif = require('wif');
const isOptions = typeforce.maybe(
typeforce.compile({
compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network),
}),
);
interface ECPairOptions {
compressed?: boolean;
network?: Network;
rng?(arg0: number): Buffer;
}
export interface Signer {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Buffer;
getPublicKey?(): Buffer;
}
export interface SignerAsync {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
getPublicKey?(): Buffer;
}
export interface ECPairInterface extends Signer {
compressed: boolean;
network: Network;
lowR: boolean;
privateKey?: Buffer;
toWIF(): string;
verify(hash: Buffer, signature: Buffer): boolean;
}
class ECPair implements ECPairInterface {
compressed: boolean;
network: Network;
lowR: boolean;
constructor(
private __D?: Buffer,
private __Q?: Buffer,
options?: ECPairOptions,
) {
this.lowR = false;
if (options === undefined) options = {};
this.compressed =
options.compressed === undefined ? true : options.compressed;
this.network = options.network || NETWORKS.bitcoin;
if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed);
}
get privateKey(): Buffer | undefined {
return this.__D;
}
get publicKey(): Buffer {
if (!this.__Q)
this.__Q = ecc.pointFromScalar(this.__D, this.compressed) as Buffer;
return this.__Q;
}
toWIF(): string {
if (!this.__D) throw new Error('Missing private key');
return wif.encode(this.network.wif, this.__D, this.compressed);
}
sign(hash: Buffer, lowR?: boolean): Buffer {
if (!this.__D) throw new Error('Missing private key');
if (lowR === undefined) lowR = this.lowR;
if (lowR === false) {
return ecc.sign(hash, this.__D);
} else {
let sig = ecc.sign(hash, this.__D);
const extraData = Buffer.alloc(32, 0);
let counter = 0;
// if first try is lowR, skip the loop
// for second try and on, add extra entropy counting up
while (sig[0] > 0x7f) {
counter++;
extraData.writeUIntLE(counter, 0, 6);
sig = ecc.signWithEntropy(hash, this.__D, extraData);
}
return sig;
}
}
verify(hash: Buffer, signature: Buffer): boolean {
return ecc.verify(hash, this.publicKey, signature);
}
}
function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair {
typeforce(types.Buffer256bit, buffer);
if (!ecc.isPrivate(buffer))
throw new TypeError('Private key not in range [1, n)');
typeforce(isOptions, options);
return new ECPair(buffer, undefined, options);
}
function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair {
typeforce(ecc.isPoint, buffer);
typeforce(isOptions, options);
return new ECPair(undefined, buffer, options);
}
function fromWIF(wifString: string, network?: Network | Network[]): ECPair {
const decoded = wif.decode(wifString);
const version = decoded.version;
// list of networks?
if (types.Array(network)) {
network = (network as Network[])
.filter((x: Network) => {
return version === x.wif;
})
.pop() as Network;
if (!network) throw new Error('Unknown network version');
// otherwise, assume a network object (or default to bitcoin)
} else {
network = network || NETWORKS.bitcoin;
if (version !== (network as Network).wif)
throw new Error('Invalid network version');
}
return fromPrivateKey(decoded.privateKey, {
compressed: decoded.compressed,
network: network as Network,
});
}
function makeRandom(options?: ECPairOptions): ECPair {
typeforce(isOptions, options);
if (options === undefined) options = {};
const rng = options.rng || randomBytes;
let d;
do {
d = rng(32);
typeforce(types.Buffer256bit, d);
} while (!ecc.isPrivate(d));
return fromPrivateKey(d, options);
}
export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF };

View file

@ -1,20 +1,24 @@
import * as bip32 from 'bip32';
import * as address from './address';
import * as crypto from './crypto';
import * as ECPair from './ecpair';
import * as networks from './networks';
import * as payments from './payments';
import * as script from './script';
export { ECPair, address, bip32, crypto, networks, payments, script };
export { address, crypto, networks, payments, script };
export { Block } from './block';
export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt';
export { OPS as opcodes } from './script';
export {
Psbt,
PsbtTxInput,
PsbtTxOutput,
Signer,
SignerAsync,
HDSigner,
HDSignerAsync,
} from './psbt';
export { OPS as opcodes } from './ops';
export { Transaction } from './transaction';
export { BIP32Interface } from 'bip32';
export { ECPairInterface, Signer, SignerAsync } from './ecpair';
export { Network } from './networks';
export {
Payment,
@ -23,5 +27,4 @@ export {
Stack,
StackElement,
} from './payments';
export { OpCode } from './script';
export { Input as TxInput, Output as TxOutput } from './transaction';

27
ts_src/merkle.ts Normal file
View file

@ -0,0 +1,27 @@
export function fastMerkleRoot(
values: Buffer[],
digestFn: (b: Buffer) => Buffer,
): Buffer {
if (!Array.isArray(values)) throw TypeError('Expected values Array');
if (typeof digestFn !== 'function')
throw TypeError('Expected digest Function');
let length = values.length;
const results = values.concat();
while (length > 1) {
let j = 0;
for (let i = 0; i < length; i += 2, ++j) {
const left = results[i];
const right = i + 1 === length ? left : results[i + 1];
const data = Buffer.concat([left, right]);
results[j] = digestFn(data);
}
length = j;
}
return results[0];
}

141
ts_src/ops.ts Normal file
View file

@ -0,0 +1,141 @@
const OPS: { [key: string]: number } = {
OP_FALSE: 0,
OP_0: 0,
OP_PUSHDATA1: 76,
OP_PUSHDATA2: 77,
OP_PUSHDATA4: 78,
OP_1NEGATE: 79,
OP_RESERVED: 80,
OP_TRUE: 81,
OP_1: 81,
OP_2: 82,
OP_3: 83,
OP_4: 84,
OP_5: 85,
OP_6: 86,
OP_7: 87,
OP_8: 88,
OP_9: 89,
OP_10: 90,
OP_11: 91,
OP_12: 92,
OP_13: 93,
OP_14: 94,
OP_15: 95,
OP_16: 96,
OP_NOP: 97,
OP_VER: 98,
OP_IF: 99,
OP_NOTIF: 100,
OP_VERIF: 101,
OP_VERNOTIF: 102,
OP_ELSE: 103,
OP_ENDIF: 104,
OP_VERIFY: 105,
OP_RETURN: 106,
OP_TOALTSTACK: 107,
OP_FROMALTSTACK: 108,
OP_2DROP: 109,
OP_2DUP: 110,
OP_3DUP: 111,
OP_2OVER: 112,
OP_2ROT: 113,
OP_2SWAP: 114,
OP_IFDUP: 115,
OP_DEPTH: 116,
OP_DROP: 117,
OP_DUP: 118,
OP_NIP: 119,
OP_OVER: 120,
OP_PICK: 121,
OP_ROLL: 122,
OP_ROT: 123,
OP_SWAP: 124,
OP_TUCK: 125,
OP_CAT: 126,
OP_SUBSTR: 127,
OP_LEFT: 128,
OP_RIGHT: 129,
OP_SIZE: 130,
OP_INVERT: 131,
OP_AND: 132,
OP_OR: 133,
OP_XOR: 134,
OP_EQUAL: 135,
OP_EQUALVERIFY: 136,
OP_RESERVED1: 137,
OP_RESERVED2: 138,
OP_1ADD: 139,
OP_1SUB: 140,
OP_2MUL: 141,
OP_2DIV: 142,
OP_NEGATE: 143,
OP_ABS: 144,
OP_NOT: 145,
OP_0NOTEQUAL: 146,
OP_ADD: 147,
OP_SUB: 148,
OP_MUL: 149,
OP_DIV: 150,
OP_MOD: 151,
OP_LSHIFT: 152,
OP_RSHIFT: 153,
OP_BOOLAND: 154,
OP_BOOLOR: 155,
OP_NUMEQUAL: 156,
OP_NUMEQUALVERIFY: 157,
OP_NUMNOTEQUAL: 158,
OP_LESSTHAN: 159,
OP_GREATERTHAN: 160,
OP_LESSTHANOREQUAL: 161,
OP_GREATERTHANOREQUAL: 162,
OP_MIN: 163,
OP_MAX: 164,
OP_WITHIN: 165,
OP_RIPEMD160: 166,
OP_SHA1: 167,
OP_SHA256: 168,
OP_HASH160: 169,
OP_HASH256: 170,
OP_CODESEPARATOR: 171,
OP_CHECKSIG: 172,
OP_CHECKSIGVERIFY: 173,
OP_CHECKMULTISIG: 174,
OP_CHECKMULTISIGVERIFY: 175,
OP_NOP1: 176,
OP_NOP2: 177,
OP_CHECKLOCKTIMEVERIFY: 177,
OP_NOP3: 178,
OP_CHECKSEQUENCEVERIFY: 178,
OP_NOP4: 179,
OP_NOP5: 180,
OP_NOP6: 181,
OP_NOP7: 182,
OP_NOP8: 183,
OP_NOP9: 184,
OP_NOP10: 185,
OP_PUBKEYHASH: 253,
OP_PUBKEY: 254,
OP_INVALIDOPCODE: 255,
};
const REVERSE_OPS: { [key: number]: string } = {};
for (const op of Object.keys(OPS)) {
const code = OPS[op];
REVERSE_OPS[code] = op;
}
export { OPS, REVERSE_OPS };

View file

@ -1,9 +1,9 @@
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { typeforce as typef } from '../types';
import { Payment, PaymentOpts, Stack } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
const OPS = bscript.OPS;
function stacksEqual(a: Buffer[], b: Buffer[]): boolean {

View file

@ -1,10 +1,9 @@
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { isPoint, typeforce as typef } from '../types';
import { Payment, PaymentOpts, Stack } from './index';
import * as lazy from './lazy';
const OPS = bscript.OPS;
const typef = require('typeforce');
const ecc = require('tiny-secp256k1');
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
@ -41,7 +40,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
m: typef.maybe(typef.Number),
n: typef.maybe(typef.Number),
output: typef.maybe(typef.Buffer),
pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)),
pubkeys: typef.maybe(typef.arrayOf(isPoint)),
signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)),
input: typef.maybe(typef.Buffer),
@ -119,7 +118,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment {
if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3)
throw new TypeError('Output is invalid');
if (!o.pubkeys!.every(x => ecc.isPoint(x)))
if (!o.pubkeys!.every(x => isPoint(x)))
throw new TypeError('Output is invalid');
if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch');

View file

@ -1,10 +1,9 @@
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { isPoint, typeforce as typef } from '../types';
import { Payment, PaymentOpts, StackFunction } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
// input: {signature}
// output: {pubKey} OP_CHECKSIG
@ -17,7 +16,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
{
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
pubkey: typef.maybe(ecc.isPoint),
pubkey: typef.maybe(isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
input: typef.maybe(typef.Buffer),
@ -58,8 +57,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment {
if (a.output) {
if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG)
throw new TypeError('Output is invalid');
if (!ecc.isPoint(o.pubkey))
throw new TypeError('Output pubkey is invalid');
if (!isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid');
if (a.pubkey && !a.pubkey.equals(o.pubkey!))
throw new TypeError('Pubkey mismatch');
}

View file

@ -1,13 +1,11 @@
import * as bcrypto from '../crypto';
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { isPoint, typeforce as typef } from '../types';
import { Payment, PaymentOpts, StackFunction } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
import * as bs58check from 'bs58check';
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const bs58check = require('bs58check');
// input: {signature} {pubkey}
// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG
@ -23,7 +21,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
hash: typef.maybe(typef.BufferN(20)),
output: typef.maybe(typef.BufferN(25)),
pubkey: typef.maybe(ecc.isPoint),
pubkey: typef.maybe(isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
input: typef.maybe(typef.Buffer),
},
@ -31,7 +29,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
);
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address);
const payload = bs58check.decode(a.address!);
const version = payload.readUInt8(0);
const hash = payload.slice(1);
return { version, hash };
@ -129,8 +127,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment {
if (chunks.length !== 2) throw new TypeError('Input is invalid');
if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer))
throw new TypeError('Input has invalid signature');
if (!ecc.isPoint(chunks[1]))
throw new TypeError('Input has invalid pubkey');
if (!isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey');
if (a.signature && !a.signature.equals(chunks[0] as Buffer))
throw new TypeError('Signature mismatch');

View file

@ -1,6 +1,7 @@
import * as bcrypto from '../crypto';
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { typeforce as typef } from '../types';
import {
Payment,
PaymentFunction,
@ -9,11 +10,9 @@ import {
StackFunction,
} from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
import * as bs58check from 'bs58check';
const OPS = bscript.OPS;
const bs58check = require('bs58check');
function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
if (a.length !== b.length) return false;
@ -58,7 +57,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment {
const o: Payment = { network };
const _address = lazy.value(() => {
const payload = bs58check.decode(a.address);
const payload = bs58check.decode(a.address!);
const version = payload.readUInt8(0);
const hash = payload.slice(1);
return { version, hash };

View file

@ -1,13 +1,11 @@
import * as bcrypto from '../crypto';
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { isPoint, typeforce as typef } from '../types';
import { Payment, PaymentOpts } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
import { bech32 } from 'bech32';
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const { bech32 } = require('bech32');
const EMPTY_BUFFER = Buffer.alloc(0);
@ -26,7 +24,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
input: typef.maybe(typef.BufferN(0)),
network: typef.maybe(typef.Object),
output: typef.maybe(typef.BufferN(22)),
pubkey: typef.maybe(ecc.isPoint),
pubkey: typef.maybe(isPoint),
signature: typef.maybe(bscript.isCanonicalScriptSignature),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
},
@ -34,7 +32,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
);
const _address = lazy.value(() => {
const result = bech32.decode(a.address);
const result = bech32.decode(a.address!);
const version = result.words.shift();
const data = bech32.fromWords(result.words);
return {
@ -118,7 +116,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
if (hash.length > 0 && !hash.equals(pkh))
throw new TypeError('Hash mismatch');
else hash = pkh;
if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33)
if (!isPoint(a.pubkey) || a.pubkey.length !== 33)
throw new TypeError('Invalid pubkey for p2wpkh');
}
@ -126,7 +124,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
if (!bscript.isCanonicalScriptSignature(a.witness[0]))
throw new TypeError('Witness has invalid signature');
if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33)
if (!isPoint(a.witness[1]) || a.witness[1].length !== 33)
throw new TypeError('Witness has invalid pubkey');
if (a.signature && !a.signature.equals(a.witness[0]))

View file

@ -1,13 +1,11 @@
import * as bcrypto from '../crypto';
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { isPoint, typeforce as typef } from '../types';
import { Payment, PaymentOpts, StackElement, StackFunction } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
import { bech32 } from 'bech32';
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const { bech32 } = require('bech32');
const EMPTY_BUFFER = Buffer.alloc(0);
@ -24,7 +22,7 @@ function chunkHasUncompressedPubkey(chunk: StackElement): boolean {
Buffer.isBuffer(chunk) &&
chunk.length === 65 &&
chunk[0] === 0x04 &&
ecc.isPoint(chunk)
isPoint(chunk)
) {
return true;
} else {
@ -61,7 +59,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
);
const _address = lazy.value(() => {
const result = bech32.decode(a.address);
const result = bech32.decode(a.address!);
const version = result.words.shift();
const data = bech32.fromWords(result.words);
return {

View file

@ -16,11 +16,6 @@ import { checkForInput, checkForOutput } from 'bip174/src/lib/utils';
import { fromOutputScript, toOutputScript } from './address';
import { cloneBuffer, reverseBuffer } from './bufferutils';
import { hash160 } from './crypto';
import {
fromPublicKey as ecPairFromPublicKey,
Signer,
SignerAsync,
} from './ecpair';
import { bitcoin as btcNetwork, Network } from './networks';
import * as payments from './payments';
import * as bscript from './script';
@ -45,6 +40,13 @@ export interface PsbtTxOutput extends TransactionOutput {
address: string | undefined;
}
// msghash is 32 byte hash of preimage, signature is 64 byte compact signature (r,s 32 bytes each)
export type ValidateSigFunction = (
pubkey: Buffer,
msghash: Buffer,
signature: Buffer,
) => boolean;
/**
* These are the default arguments for a Psbt instance.
*/
@ -416,19 +418,25 @@ export class Psbt {
);
}
validateSignaturesOfAllInputs(): boolean {
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
checkForInput(this.data.inputs, 0); // making sure we have at least one
const results = range(this.data.inputs.length).map(idx =>
this.validateSignaturesOfInput(idx),
this.validateSignaturesOfInput(idx, validator),
);
return results.reduce((final, res) => res === true && final, true);
}
validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean {
validateSignaturesOfInput(
inputIndex: number,
validator: ValidateSigFunction,
pubkey?: Buffer,
): boolean {
const input = this.data.inputs[inputIndex];
const partialSig = (input || {}).partialSig;
if (!input || !partialSig || partialSig.length < 1)
throw new Error('No signatures to validate');
if (typeof validator !== 'function')
throw new Error('Need validator function to validate signatures');
const mySigs = pubkey
? partialSig.filter(sig => sig.pubkey.equals(pubkey))
: partialSig;
@ -452,8 +460,7 @@ export class Psbt {
hashCache = hash;
scriptCache = script;
checkScriptForPubkey(pSig.pubkey, script, 'verify');
const keypair = ecPairFromPublicKey(pSig.pubkey);
results.push(keypair.verify(hash, sig.signature));
results.push(validator(pSig.pubkey, hash, sig.signature));
}
return results.every(res => res === true);
}
@ -780,7 +787,7 @@ interface HDSignerBase {
fingerprint: Buffer;
}
interface HDSigner extends HDSignerBase {
export interface HDSigner extends HDSignerBase {
/**
* The path string must match /^m(\/\d+'?)+$/
* ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
@ -796,11 +803,25 @@ interface HDSigner extends HDSignerBase {
/**
* Same as above but with async sign method
*/
interface HDSignerAsync extends HDSignerBase {
export interface HDSignerAsync extends HDSignerBase {
derivePath(path: string): HDSignerAsync;
sign(hash: Buffer): Promise<Buffer>;
}
export interface Signer {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Buffer;
getPublicKey?(): Buffer;
}
export interface SignerAsync {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
getPublicKey?(): Buffer;
}
/**
* This function is needed to pass to the bip174 base class's fromBuffer.
* It takes the "transaction buffer" portion of the psbt buffer and returns a
@ -903,8 +924,7 @@ function hasSigs(
if (pubkeys) {
sigs = pubkeys
.map(pkey => {
const pubkey = ecPairFromPublicKey(pkey, { compressed: true })
.publicKey;
const pubkey = compressPubkey(pkey);
return partialSig.find(pSig => pSig.pubkey.equals(pubkey));
})
.filter(v => !!v);
@ -1305,7 +1325,7 @@ function getHashForSig(
console.warn(
'Warning: Signing non-segwit inputs without the full parent transaction ' +
'means there is a chance that a miner could feed you incorrect information ' +
'to trick you into paying large fees. This behavior is the same as Psbt\'s predecesor ' +
"to trick you into paying large fees. This behavior is the same as Psbt's predecesor " +
'(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
@ -1714,6 +1734,16 @@ function redeemFromFinalWitnessScript(
return lastItem;
}
function compressPubkey(pubkey: Buffer): Buffer {
if (pubkey.length === 65) {
const parity = pubkey[64] & 1;
const newKey = pubkey.slice(0, 33);
newKey[0] = 2 | parity;
return newKey;
}
return pubkey.slice();
}
function isPubkeyLike(buf: Buffer): boolean {
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
}

76
ts_src/push_data.ts Normal file
View file

@ -0,0 +1,76 @@
import { OPS } from './ops';
export function encodingLength(i: number): number {
return i < OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5;
}
export function encode(buffer: Buffer, num: number, offset: number): number {
const size = encodingLength(num);
// ~6 bit
if (size === 1) {
buffer.writeUInt8(num, offset);
// 8 bit
} else if (size === 2) {
buffer.writeUInt8(OPS.OP_PUSHDATA1, offset);
buffer.writeUInt8(num, offset + 1);
// 16 bit
} else if (size === 3) {
buffer.writeUInt8(OPS.OP_PUSHDATA2, offset);
buffer.writeUInt16LE(num, offset + 1);
// 32 bit
} else {
buffer.writeUInt8(OPS.OP_PUSHDATA4, offset);
buffer.writeUInt32LE(num, offset + 1);
}
return size;
}
export function decode(
buffer: Buffer,
offset: number,
): {
opcode: number;
number: number;
size: number;
} | null {
const opcode = buffer.readUInt8(offset);
let num: number;
let size: number;
// ~6 bit
if (opcode < OPS.OP_PUSHDATA1) {
num = opcode;
size = 1;
// 8 bit
} else if (opcode === OPS.OP_PUSHDATA1) {
if (offset + 2 > buffer.length) return null;
num = buffer.readUInt8(offset + 1);
size = 2;
// 16 bit
} else if (opcode === OPS.OP_PUSHDATA2) {
if (offset + 3 > buffer.length) return null;
num = buffer.readUInt16LE(offset + 1);
size = 3;
// 32 bit
} else {
if (offset + 5 > buffer.length) return null;
if (opcode !== OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode');
num = buffer.readUInt32LE(offset + 1);
size = 5;
}
return {
opcode,
number: num,
size,
};
}

View file

@ -1,17 +1,14 @@
import * as bip66 from './bip66';
import { OPS, REVERSE_OPS } from './ops';
import { Stack } from './payments';
import * as pushdata from './push_data';
import * as scriptNumber from './script_number';
import * as scriptSignature from './script_signature';
import * as types from './types';
const bip66 = require('bip66');
const ecc = require('tiny-secp256k1');
const pushdata = require('pushdata-bitcoin');
const typeforce = require('typeforce');
const { typeforce } = types;
export type OpCode = number;
export const OPS = require('bitcoin-ops') as { [index: string]: OpCode };
const REVERSE_OPS = require('bitcoin-ops/map') as { [index: number]: string };
const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1
export { OPS };
function isOPInt(value: number): boolean {
return (
@ -194,7 +191,7 @@ export function toStack(chunks: Buffer | Array<number | Buffer>): Buffer[] {
}
export function isCanonicalPubKey(buffer: Buffer): boolean {
return ecc.isPoint(buffer);
return types.isPoint(buffer);
}
export function isDefinedHashType(hashType: number): boolean {

View file

@ -1,7 +1,6 @@
import * as bip66 from './bip66';
import * as types from './types';
const bip66 = require('bip66');
const typeforce = require('typeforce');
const { typeforce } = types;
const ZERO = Buffer.alloc(1, 0);
function toDER(x: Buffer): Buffer {

View file

@ -1,11 +1,14 @@
import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils';
import {
BufferReader,
BufferWriter,
reverseBuffer,
varuint,
} from './bufferutils';
import * as bcrypto from './crypto';
import * as bscript from './script';
import { OPS as opcodes } from './script';
import * as types from './types';
const typeforce = require('typeforce');
const varuint = require('varuint-bitcoin');
const { typeforce } = types;
function varSliceSize(someScript: Buffer): number {
const length = someScript.length;

View file

@ -1,4 +1,29 @@
const typeforce = require('typeforce');
import { Buffer as NBuffer } from 'buffer';
export const typeforce = require('typeforce');
const ZERO32 = NBuffer.alloc(32, 0);
const EC_P = NBuffer.from(
'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
'hex',
);
export function isPoint(p: Buffer | number | undefined | null): boolean {
if (!NBuffer.isBuffer(p)) return false;
if (p.length < 33) return false;
const t = p[0];
const x = p.slice(1, 33);
if (x.compare(ZERO32) === 0) return false;
if (x.compare(EC_P) >= 0) return false;
if ((t === 0x02 || t === 0x03) && p.length === 33) {
return true;
}
const y = p.slice(33);
if (y.compare(ZERO32) === 0) return false;
if (y.compare(EC_P) >= 0) return false;
if (t === 0x04 && p.length === 65) return true;
return false;
}
const UINT31_MAX: number = Math.pow(2, 31) - 1;
export function UInt31(value: number): boolean {

View file

@ -1,10 +1,9 @@
{
"compilerOptions": {
"target": "ES2015",
"target": "ES2020",
"module": "commonjs",
"outDir": "./src",
"declaration": true,
"declarationDir": "./types",
"rootDir": "./ts_src",
"types": [
"node"

View file

@ -2,7 +2,8 @@
"defaultSeverity": "error",
"extends": ["tslint:recommended"],
"rules": {
"arrow-parens": [true, "ban-single-arg-parens"],
"array-type": false,
"arrow-parens": false,
"curly": false,
"indent": [
true,
@ -25,7 +26,6 @@
"typedef": [
true,
"call-signature",
"arrow-call-signature",
"property-declaration"
],
"variable-name": [

44
types/ecpair.d.ts vendored
View file

@ -1,44 +0,0 @@
import { Network } from './networks';
interface ECPairOptions {
compressed?: boolean;
network?: Network;
rng?(arg0: number): Buffer;
}
export interface Signer {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Buffer;
getPublicKey?(): Buffer;
}
export interface SignerAsync {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
getPublicKey?(): Buffer;
}
export interface ECPairInterface extends Signer {
compressed: boolean;
network: Network;
lowR: boolean;
privateKey?: Buffer;
toWIF(): string;
verify(hash: Buffer, signature: Buffer): boolean;
}
declare class ECPair implements ECPairInterface {
private __D?;
private __Q?;
compressed: boolean;
network: Network;
lowR: boolean;
constructor(__D?: Buffer | undefined, __Q?: Buffer | undefined, options?: ECPairOptions);
readonly privateKey: Buffer | undefined;
readonly publicKey: Buffer;
toWIF(): string;
sign(hash: Buffer, lowR?: boolean): Buffer;
verify(hash: Buffer, signature: Buffer): boolean;
}
declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair;
declare function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair;
declare function fromWIF(wifString: string, network?: Network | Network[]): ECPair;
declare function makeRandom(options?: ECPairOptions): ECPair;
export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF };