Browse Source

Add payments

fixTypes
junderw 6 years ago
parent
commit
867f4b59f9
No known key found for this signature in database GPG Key ID: B256185D3A971908
  1. 2
      package.json
  2. 20
      src/payments/embed.ts
  3. 40
      src/payments/index.ts
  4. 9
      src/payments/lazy.ts
  5. 40
      src/payments/p2ms.ts
  6. 20
      src/payments/p2pk.ts
  7. 24
      src/payments/p2pkh.ts
  8. 26
      src/payments/p2sh.ts
  9. 23
      src/payments/p2wpkh.ts
  10. 25
      src/payments/p2wsh.ts
  11. 8
      test/payments.js

2
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",

20
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<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 {}

40
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<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 {}

9
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 <T>(f: ()=>T): ()=>T {
let value: T
return function () {
if (value !== undefined) return value
value = f()
return value
}
}
module.exports = { prop, value }
export {}

40
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<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 {}

20
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 {}

24
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(<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 {}

26
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<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 {}

23
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 {}

25
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<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 {}

8
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) {

Loading…
Cancel
Save