2018-12-29 22:49:35 +09:00
import { Payment , PaymentOpts } from './index' // eslint-disable-line
2018-12-29 00:00:52 +09:00
import * as bscript from '../script'
import * as lazy from './lazy'
import { bitcoin as BITCOIN_NETWORK } from '../networks'
2018-12-29 22:49:35 +09:00
const OPS = bscript . OPS
2018-07-03 22:33:48 +10:00
const typef = require ( 'typeforce' )
const ecc = require ( 'tiny-secp256k1' )
2018-06-05 17:24:47 +10:00
2018-07-03 22:33:48 +10:00
const OP_INT_BASE = OPS . OP_RESERVED // OP_1 - 1
2018-06-05 17:24:47 +10:00
2018-12-29 00:00:52 +09:00
function stacksEqual ( a : Array < Buffer > , b : Array < Buffer > ) : boolean {
2018-06-05 17:24:47 +10:00
if ( a . length !== b . length ) return false
return a . every ( function ( x , i ) {
return x . equals ( b [ i ] )
} )
}
// input: OP_0 [signatures ...]
// output: m [pubKeys ...] n OP_CHECKMULTISIG
2018-12-29 16:10:36 +09:00
export function p2ms ( a : Payment , opts? : PaymentOpts ) : Payment {
2018-06-05 17:24:47 +10:00
if (
2018-06-27 17:29:18 +10:00
! a . input &&
2018-06-05 17:24:47 +10:00
! a . output &&
2018-06-27 11:22:35 +10:00
! ( a . pubkeys && a . m !== undefined ) &&
! a . signatures
2018-06-05 17:24:47 +10:00
) throw new TypeError ( 'Not enough data' )
2018-08-28 14:21:18 +09:00
opts = Object . assign ( { validate : true } , opts || { } )
2018-06-05 17:24:47 +10:00
2018-12-29 00:00:52 +09:00
function isAcceptableSignature ( x : Buffer | number ) {
2018-12-29 22:49:35 +09:00
return bscript . isCanonicalScriptSignature ( < Buffer > x ) ||
( ( < PaymentOpts > opts ) . allowIncomplete &&
( < number > x === OPS . OP_0 ) ) !== undefined // eslint-disable-line
2018-06-05 17:24:47 +10:00
}
typef ( {
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 )
} , a )
2018-07-03 22:33:48 +10:00
const network = a . network || BITCOIN_NETWORK
2018-12-29 00:00:52 +09:00
const o : Payment = { network }
2018-06-05 17:24:47 +10:00
2018-12-29 01:55:07 +09:00
let chunks : Array < Buffer | number > = [ ]
2018-06-05 17:24:47 +10:00
let decoded = false
2018-12-29 00:00:52 +09:00
function decode ( output : Buffer | Array < Buffer | number > ) : void {
2018-06-05 17:24:47 +10:00
if ( decoded ) return
decoded = true
2018-12-29 01:55:07 +09:00
chunks = < Array < Buffer | number > > bscript . decompile ( output )
2018-12-29 22:49:35 +09:00
o . m = < number > chunks [ 0 ] - OP_INT_BASE // eslint-disable-line
o . n = < number > chunks [ chunks . length - 2 ] - OP_INT_BASE // eslint-disable-line
2018-12-29 00:00:52 +09:00
o . pubkeys = < Array < Buffer > > chunks . slice ( 1 , - 2 )
2018-06-05 17:24:47 +10:00
}
lazy . prop ( o , 'output' , function ( ) {
if ( ! a . m ) return
if ( ! o . n ) return
if ( ! a . pubkeys ) return
2018-12-29 01:55:07 +09:00
return bscript . compile ( ( < Array < Buffer | number > > [ ] ) . concat (
2018-06-05 17:24:47 +10:00
OP_INT_BASE + a . m ,
a . pubkeys ,
OP_INT_BASE + o . n ,
OPS . OP_CHECKMULTISIG
) )
} )
lazy . prop ( o , 'm' , function ( ) {
if ( ! o . output ) return
decode ( o . output )
return o . m
} )
lazy . prop ( o , 'n' , function ( ) {
if ( ! o . pubkeys ) return
return o . pubkeys . length
} )
lazy . prop ( o , 'pubkeys' , function ( ) {
if ( ! a . output ) return
decode ( a . output )
return o . pubkeys
} )
lazy . prop ( o , 'signatures' , function ( ) {
if ( ! a . input ) return
2018-12-29 01:55:07 +09:00
return ( < Array < Buffer | number > > bscript . decompile ( a . input ) ) . slice ( 1 )
2018-06-05 17:24:47 +10:00
} )
lazy . prop ( o , 'input' , function ( ) {
if ( ! a . signatures ) return
2018-12-29 21:39:19 +09:00
return bscript . compile ( ( < Array < Buffer | number > > [ OPS . OP_0 ] ) . concat ( a . signatures ) )
2018-06-05 17:24:47 +10:00
} )
lazy . prop ( o , 'witness' , function ( ) {
if ( ! o . input ) return
return [ ]
} )
// extended validation
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 ] ) ) throw new TypeError ( 'Output is invalid' )
if ( chunks [ chunks . length - 1 ] !== OPS . OP_CHECKMULTISIG ) throw new TypeError ( 'Output is invalid' )
if (
2018-12-29 22:49:35 +09:00
< number > ( < Payment > o ) . m <= 0 || // eslint-disable-line
< number > ( < Payment > o ) . n > 16 || // eslint-disable-line
< number > ( < Payment > o ) . m > < number > ( < Payment > o ) . n || // eslint-disable-line
2018-06-05 17:24:47 +10:00
o . n !== chunks . length - 3 ) throw new TypeError ( 'Output is invalid' )
2018-12-29 01:55:07 +09:00
if ( ! ( < Array < Buffer > > o . pubkeys ) . every ( x = > ecc . isPoint ( x ) ) ) throw new TypeError ( 'Output is invalid' )
2018-06-05 17:24:47 +10:00
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' )
2018-12-29 01:55:07 +09:00
if ( a . pubkeys && ! stacksEqual ( a . pubkeys , ( < Array < Buffer > > o . pubkeys ) ) ) throw new TypeError ( 'Pubkeys mismatch' )
2018-06-05 17:24:47 +10:00
}
if ( a . pubkeys ) {
if ( a . n !== undefined && a . n !== a . pubkeys . length ) throw new TypeError ( 'Pubkey count mismatch' )
o . n = a . pubkeys . length
2018-12-29 01:55:07 +09:00
if ( o . n < < number > ( < Payment > o ) . m ) throw new TypeError ( 'Pubkey count cannot be less than m' )
2018-06-05 17:24:47 +10:00
}
if ( a . signatures ) {
2018-12-29 01:55:07 +09:00
if ( a . signatures . length < < number > ( < Payment > o ) . m ) throw new TypeError ( 'Not enough signatures provided' )
if ( a . signatures . length > < number > ( < Payment > o ) . m ) throw new TypeError ( 'Too many signatures provided' )
2018-06-05 17:24:47 +10:00
}
if ( a . input ) {
if ( a . input [ 0 ] !== OPS . OP_0 ) throw new TypeError ( 'Input is invalid' )
2018-12-29 01:55:07 +09:00
if ( ( < Array < Buffer > > o . signatures ) . length === 0 || ! ( < Array < Buffer > > o . signatures ) . every ( isAcceptableSignature ) ) throw new TypeError ( 'Input has invalid signature(s)' )
2018-06-05 17:24:47 +10:00
2018-12-29 01:55:07 +09:00
if ( a . signatures && ! stacksEqual ( a . signatures , ( < Array < Buffer > > o . signatures ) ) ) throw new TypeError ( 'Signature mismatch' )
if ( a . m !== undefined && a . m !== ( < Array < Buffer > > a . signatures ) . length ) throw new TypeError ( 'Signature count mismatch' )
2018-06-05 17:24:47 +10:00
}
}
return Object . assign ( o , a )
}