Add payments
This commit is contained in:
parent
5c34b4ce22
commit
867f4b59f9
11 changed files with 110 additions and 127 deletions
|
@ -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",
|
||||
|
|
|
@ -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<Buffer>, b: Array<Buffer>): 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 = <Payment>{ 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 {}
|
||||
|
|
|
@ -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<Buffer>,
|
||||
m?: number,
|
||||
n?: number,
|
||||
pubkeys?: Array<Buffer>,
|
||||
input?: Buffer,
|
||||
signatures?: Array<Buffer>,
|
||||
pubkey?: Buffer,
|
||||
signature?: Buffer,
|
||||
address?: string,
|
||||
hash?: Buffer,
|
||||
redeem?: Payment,
|
||||
witness?: Array<Buffer>,
|
||||
}
|
||||
|
||||
export interface PaymentOpts {
|
||||
validate?: boolean,
|
||||
allowIncomplete?: boolean,
|
||||
}
|
||||
|
||||
export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }
|
||||
|
||||
// TODO
|
||||
// witness commitment
|
||||
export {}
|
||||
|
|
|
@ -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 <T>(f: ()=>T): ()=>T {
|
||||
let value: T
|
||||
return function () {
|
||||
if (value !== undefined) return value
|
||||
value = f()
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { prop, value }
|
||||
export {}
|
||||
|
|
|
@ -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<Buffer>, b: Array<Buffer>): 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(<Buffer>x) || (opts.allowIncomplete && (<number>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<Buffer | number>
|
||||
let decoded = false
|
||||
function decode (output) {
|
||||
function decode (output: Buffer | Array<Buffer | number>): 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 = <number>chunks[0] - OP_INT_BASE
|
||||
o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE
|
||||
o.pubkeys = <Array<Buffer>>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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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(<Buffer>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(<Buffer>chunks[0])) throw new TypeError('Signature mismatch')
|
||||
if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) throw new TypeError('Pubkey mismatch')
|
||||
|
||||
const pkh = bcrypto.hash160(chunks[1])
|
||||
const pkh = bcrypto.hash160(<Buffer>chunks[1])
|
||||
if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch')
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign(o, a)
|
||||
}
|
||||
|
||||
module.exports = p2pkh
|
||||
export {}
|
||||
|
|
|
@ -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<Buffer>, b: Array<Buffer>): 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: <Buffer>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(<Buffer>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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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<Buffer>, b: Array<Buffer>): 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 {}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue