diff --git a/package.json b/package.json index 777a210..2583afc 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 85 --functions 90 mocha", + "coverage": "nyc --check-coverage --branches 80 --functions 80 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", diff --git a/src/payments/embed.ts b/src/payments/embed.ts index bfb70e5..a28747f 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -1,11 +1,11 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - -function stacksEqual (a, b) { +function stacksEqual (a: Array, b: Array): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -14,7 +14,7 @@ function stacksEqual (a, b) { } // output: OP_RETURN ... -function p2data (a, opts) { +export function p2data (a: Payment, opts: PaymentOpts): Payment { if ( !a.data && !a.output @@ -28,10 +28,7 @@ function p2data (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { - network, - data: undefined - } + const o = { network } lazy.prop(o, 'output', function () { if (!a.data) return @@ -55,6 +52,3 @@ function p2data (a, opts) { return Object.assign(o, a) } - -module.exports = p2data -export {} diff --git a/src/payments/index.ts b/src/payments/index.ts index bb4e0e6..08a8409 100644 --- a/src/payments/index.ts +++ b/src/payments/index.ts @@ -1,13 +1,35 @@ -const embed = require('./embed') -const p2ms = require('./p2ms') -const p2pk = require('./p2pk') -const p2pkh = require('./p2pkh') -const p2sh = require('./p2sh') -const p2wpkh = require('./p2wpkh') -const p2wsh = require('./p2wsh') +import { Network } from '../networks' +import { p2data as embed } from './embed' +import { p2ms } from './p2ms' +import { p2pk } from './p2pk' +import { p2pkh } from './p2pkh' +import { p2sh } from './p2sh' +import { p2wpkh } from './p2wpkh' +import { p2wsh } from './p2wsh' -module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } +export interface Payment { + network?: Network, + output?: Buffer, + data?: Array, + m?: number, + n?: number, + pubkeys?: Array, + input?: Buffer, + signatures?: Array, + pubkey?: Buffer, + signature?: Buffer, + address?: string, + hash?: Buffer, + redeem?: Payment, + witness?: Array, +} + +export interface PaymentOpts { + validate?: boolean, + allowIncomplete?: boolean, +} + +export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO // witness commitment -export {} diff --git a/src/payments/lazy.ts b/src/payments/lazy.ts index 4a9611c..6a4cbe3 100644 --- a/src/payments/lazy.ts +++ b/src/payments/lazy.ts @@ -1,4 +1,4 @@ -function prop (object, name, f) { +export function prop (object: Object, name: string, f: ()=>any): void { Object.defineProperty(object, name, { configurable: true, enumerable: true, @@ -18,14 +18,11 @@ function prop (object, name, f) { }) } -function value (f) { - let value +export function value (f: ()=>T): ()=>T { + let value: T return function () { if (value !== undefined) return value value = f() return value } } - -module.exports = { prop, value } -export {} diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 39b38f1..2142ff1 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -1,13 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 -function stacksEqual (a, b) { +function stacksEqual (a: Array, b: Array): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -17,7 +18,7 @@ function stacksEqual (a, b) { // input: OP_0 [signatures ...] // output: m [pubKeys ...] n OP_CHECKMULTISIG -function p2ms (a, opts) { +export function p2ms (a: Payment, opts: PaymentOpts): Payment { if ( !a.input && !a.output && @@ -26,8 +27,8 @@ function p2ms (a, opts) { ) throw new TypeError('Not enough data') opts = Object.assign({ validate: true }, opts || {}) - function isAcceptableSignature (x) { - return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) + function isAcceptableSignature (x: Buffer | number) { + return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) } typef({ @@ -42,25 +43,17 @@ function p2ms (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { - network, - m: undefined, - n: undefined, - pubkeys: undefined, - output: undefined, - input: undefined, - signatures: undefined, - } + const o: Payment = { network } - let chunks + let chunks: Array let decoded = false - function decode (output) { + function decode (output: Buffer | Array): void { if (decoded) return decoded = true chunks = bscript.decompile(output) - o.m = chunks[0] - OP_INT_BASE - o.n = chunks[chunks.length - 2] - OP_INT_BASE - o.pubkeys = chunks.slice(1, -2) + o.m = chunks[0] - OP_INT_BASE + o.n = chunks[chunks.length - 2] - OP_INT_BASE + o.pubkeys = >chunks.slice(1, -2) } lazy.prop(o, 'output', function () { @@ -137,13 +130,10 @@ function p2ms (a, opts) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures), undefined)) throw new TypeError('Signature mismatch') + if (a.signatures && !stacksEqual(a.signatures, o.signatures)) throw new TypeError('Signature mismatch') if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') } } return Object.assign(o, a) } - -module.exports = p2ms -export {} diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index b810d48..a1ac5e8 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -1,14 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - // input: {signature} // output: {pubKey} OP_CHECKSIG -function p2pk (a, opts) { +export function p2pk (a: Payment, opts: PaymentOpts): Payment { if ( !a.input && !a.output && @@ -30,12 +30,7 @@ function p2pk (a, opts) { const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const network = a.network || BITCOIN_NETWORK - const o = { - network, - input: undefined, - pubkey: undefined, - signature: undefined, - } + const o: Payment = { network } lazy.prop(o, 'output', function () { if (!a.pubkey) return @@ -81,6 +76,3 @@ function p2pk (a, opts) { return Object.assign(o, a) } - -module.exports = p2pk -export {} diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index c322fc7..4324c2a 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -1,16 +1,17 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const bs58check = require('bs58check') // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG -function p2pkh (a, opts) { +export function p2pkh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -90,7 +91,7 @@ function p2pkh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -125,19 +126,16 @@ function p2pkh (a, opts) { if (a.input) { const chunks = _chunks() if (chunks.length !== 2) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') + if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') - if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') - if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') + if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') + if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') - const pkh = bcrypto.hash160(chunks[1]) + const pkh = bcrypto.hash160(chunks[1]) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } return Object.assign(o, a) } - -module.exports = p2pkh -export {} diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index 07bb32e..efd453a 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -1,13 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const bs58check = require('bs58check') -function stacksEqual (a, b) { +function stacksEqual (a: Array, b: Array): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -18,7 +19,7 @@ function stacksEqual (a, b) { // input: [redeemScriptSig ...] {redeemScript} // witness: // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL -function p2sh (a, opts) { +export function p2sh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -64,11 +65,11 @@ function p2sh (a, opts) { return { version, hash } }) const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) - const _redeem = lazy.value(function () { + const _redeem = lazy.value(function (): Payment { const chunks = _chunks() return { network, - output: chunks[chunks.length - 1], + output: chunks[chunks.length - 1], input: bscript.compile(chunks.slice(0, -1)), witness: a.witness || [] } @@ -116,7 +117,7 @@ function p2sh (a, opts) { }) if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -141,7 +142,7 @@ function p2sh (a, opts) { } // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = function (redeem) { + const checkRedeem = function (redeem: Payment): void { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output) @@ -177,7 +178,7 @@ function p2sh (a, opts) { if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') if (a.input) { const redeem = _redeem() - if (a.redeem.output && !a.redeem.output.equals(redeem.output)) throw new TypeError('Redeem.output mismatch') + if (a.redeem.output && !a.redeem.output.equals(redeem.output)) throw new TypeError('Redeem.output mismatch') if (a.redeem.input && !a.redeem.input.equals(redeem.input)) throw new TypeError('Redeem.input mismatch') } @@ -194,6 +195,3 @@ function p2sh (a, opts) { return Object.assign(o, a) } - -module.exports = p2sh -export {} diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 1ab21b5..a25d037 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -1,19 +1,20 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bcrypto = require('../crypto') const bech32 = require('bech32') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const EMPTY_BUFFER = Buffer.alloc(0) // witness: {signature} {pubKey} // input: <> // output: OP_0 {pubKeyHash} -function p2wpkh (a, opts) { +export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -46,12 +47,7 @@ function p2wpkh (a, opts) { }) const network = a.network || BITCOIN_NETWORK - const o = { - network, - hash: undefined, - pubkey: undefined, - witness: undefined, - } + const o: Payment = { network } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -93,7 +89,7 @@ function p2wpkh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -136,6 +132,3 @@ function p2wpkh (a, opts) { return Object.assign(o, a) } - -module.exports = p2wpkh -export {} diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index fe19314..d94d446 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -1,15 +1,16 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const bech32 = require('bech32') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const EMPTY_BUFFER = Buffer.alloc(0) -function stacksEqual (a, b) { +function stacksEqual (a: Array, b: Array): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -20,7 +21,7 @@ function stacksEqual (a, b) { // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} -function p2wsh (a, opts) { +export function p2wsh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -64,12 +65,7 @@ function p2wsh (a, opts) { network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK } - const o = { - network, - hash: undefined, - redeem: undefined, - witness: undefined, - } + const o: Payment = { network } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -126,7 +122,7 @@ function p2wsh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -181,6 +177,3 @@ function p2wsh (a, opts) { return Object.assign(o, a) } - -module.exports = p2wsh -export {} diff --git a/test/payments.js b/test/payments.js index 53e3f16..6617047 100644 --- a/test/payments.js +++ b/test/payments.js @@ -4,7 +4,13 @@ const u = require('./payments.utils') ;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { - const fn = require('../dist/src/payments/' + p) + let fn + let payment = require('../dist/src/payments/' + p) + if (p === 'embed') { + fn = payment.p2data + } else { + fn = payment[p] + } const fixtures = require('./fixtures/' + p) fixtures.valid.forEach(function (f, i) {