2014-10-13 09:37:50 +02:00
var ops = require ( './opcodes' )
2015-08-11 09:01:47 +02:00
var typeforce = require ( 'typeforce' )
var types = require ( './types' )
2014-06-24 09:32:23 +02:00
var ecurve = require ( 'ecurve' )
var curve = ecurve . getCurveByName ( 'secp256k1' )
var ECSignature = require ( './ecsignature' )
2014-06-12 13:14:22 +02:00
var Script = require ( './script' )
2015-02-23 00:36:57 +01:00
function isCanonicalPubKey ( buffer ) {
2014-06-24 09:32:23 +02:00
if ( ! Buffer . isBuffer ( buffer ) ) return false
try {
ecurve . Point . decodeFrom ( curve , buffer )
} catch ( e ) {
2015-05-20 16:46:36 +02:00
if ( ! ( e . message . match ( /Invalid sequence (length|tag)/ ) ) ) {
2015-02-23 00:36:57 +01:00
throw e
2015-05-20 16:46:36 +02:00
}
2014-06-24 09:32:23 +02:00
return false
}
return true
}
2015-02-23 00:36:57 +01:00
function isCanonicalSignature ( buffer ) {
2014-06-24 09:32:23 +02:00
if ( ! Buffer . isBuffer ( buffer ) ) return false
try {
ECSignature . parseScriptSignature ( buffer )
2015-02-23 00:36:57 +01:00
} catch ( e ) {
2015-03-02 03:31:03 +01:00
if ( ! ( e . message . match ( /Not a DER sequence|Invalid sequence length|Expected a DER integer|R length is zero|S length is zero|R value excessively padded|S value excessively padded|R value is negative|S value is negative|Invalid hashType/ ) ) ) {
2015-02-23 00:36:57 +01:00
throw e
2015-03-02 03:31:03 +01:00
}
2014-06-24 09:32:23 +02:00
return false
}
return true
}
2015-02-23 00:36:57 +01:00
function isPubKeyHashInput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks . length === 2 &&
isCanonicalSignature ( script . chunks [ 0 ] ) &&
isCanonicalPubKey ( script . chunks [ 1 ] )
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01:00
function isPubKeyHashOutput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks . length === 5 &&
script . chunks [ 0 ] === ops . OP _DUP &&
script . chunks [ 1 ] === ops . OP _HASH160 &&
Buffer . isBuffer ( script . chunks [ 2 ] ) &&
script . chunks [ 2 ] . length === 20 &&
script . chunks [ 3 ] === ops . OP _EQUALVERIFY &&
script . chunks [ 4 ] === ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
}
2015-02-23 00:36:57 +01:00
function isPubKeyInput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks . length === 1 &&
isCanonicalSignature ( script . chunks [ 0 ] )
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01:00
function isPubKeyOutput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks . length === 2 &&
isCanonicalPubKey ( script . chunks [ 0 ] ) &&
script . chunks [ 1 ] === ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
}
2015-02-23 00:36:57 +01:00
function isScriptHashInput ( script , allowIncomplete ) {
2014-10-13 09:46:50 +02:00
if ( script . chunks . length < 2 ) return false
2014-06-24 09:32:23 +02:00
2015-02-04 10:29:58 +01:00
var lastChunk = script . chunks [ script . chunks . length - 1 ]
2014-06-24 09:32:23 +02:00
if ( ! Buffer . isBuffer ( lastChunk ) ) return false
2014-10-13 09:46:50 +02:00
var scriptSig = Script . fromChunks ( script . chunks . slice ( 0 , - 1 ) )
2015-03-04 11:00:07 +01:00
var redeemScript = Script . fromBuffer ( lastChunk )
2015-02-15 16:39:57 +01:00
2015-03-04 11:00:07 +01:00
// is redeemScript a valid script?
if ( redeemScript . chunks . length === 0 ) return false
2014-06-24 09:32:23 +02:00
2015-03-04 11:00:07 +01:00
return classifyInput ( scriptSig , allowIncomplete ) === classifyOutput ( redeemScript )
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01:00
function isScriptHashOutput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks . length === 3 &&
script . chunks [ 0 ] === ops . OP _HASH160 &&
Buffer . isBuffer ( script . chunks [ 1 ] ) &&
script . chunks [ 1 ] . length === 20 &&
script . chunks [ 2 ] === ops . OP _EQUAL
2014-06-24 09:32:23 +02:00
}
2015-02-11 04:01:20 +01:00
// allowIncomplete is to account for combining signatures
// See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197
2015-02-23 00:36:57 +01:00
function isMultisigInput ( script , allowIncomplete ) {
2015-02-11 04:01:20 +01:00
if ( script . chunks . length < 2 ) return false
if ( script . chunks [ 0 ] !== ops . OP _0 ) return false
if ( allowIncomplete ) {
2015-02-23 00:36:57 +01:00
return script . chunks . slice ( 1 ) . every ( function ( chunk ) {
2015-02-11 04:01:20 +01:00
return chunk === ops . OP _0 || isCanonicalSignature ( chunk )
} )
}
return script . chunks . slice ( 1 ) . every ( isCanonicalSignature )
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01:00
function isMultisigOutput ( script ) {
2014-10-13 10:46:20 +02:00
if ( script . chunks . length < 4 ) return false
2014-10-13 09:46:50 +02:00
if ( script . chunks [ script . chunks . length - 1 ] !== ops . OP _CHECKMULTISIG ) return false
2014-06-24 09:32:23 +02:00
2014-10-13 09:46:50 +02:00
var mOp = script . chunks [ 0 ]
2014-10-13 09:37:50 +02:00
if ( mOp === ops . OP _0 ) return false
if ( mOp < ops . OP _1 ) return false
if ( mOp > ops . OP _16 ) return false
2014-06-26 08:22:29 +02:00
2014-10-13 09:46:50 +02:00
var nOp = script . chunks [ script . chunks . length - 2 ]
2014-10-13 09:37:50 +02:00
if ( nOp === ops . OP _0 ) return false
if ( nOp < ops . OP _1 ) return false
if ( nOp > ops . OP _16 ) return false
2014-06-26 08:22:29 +02:00
2014-10-13 09:37:50 +02:00
var m = mOp - ( ops . OP _1 - 1 )
var n = nOp - ( ops . OP _1 - 1 )
2014-06-24 09:32:23 +02:00
if ( n < m ) return false
2014-10-13 09:46:50 +02:00
var pubKeys = script . chunks . slice ( 1 , - 2 )
2014-06-26 08:22:29 +02:00
if ( n < pubKeys . length ) return false
return pubKeys . every ( isCanonicalPubKey )
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01:00
function isNullDataOutput ( script ) {
2014-10-13 09:46:50 +02:00
return script . chunks [ 0 ] === ops . OP _RETURN
2014-06-12 13:14:22 +02:00
}
2015-02-23 00:36:57 +01:00
function classifyOutput ( script ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . Script , script )
2014-10-13 09:38:54 +02:00
2014-10-13 09:46:50 +02:00
if ( isPubKeyHashOutput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'pubkeyhash'
2014-10-13 09:46:50 +02:00
} else if ( isScriptHashOutput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'scripthash'
2014-10-13 09:46:50 +02:00
} else if ( isMultisigOutput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'multisig'
2014-10-13 09:46:50 +02:00
} else if ( isPubKeyOutput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'pubkey'
2014-11-28 02:52:25 +01:00
} else if ( isNullDataOutput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'nulldata'
}
2014-10-13 09:53:07 +02:00
return 'nonstandard'
2014-10-13 09:38:54 +02:00
}
2015-02-23 00:36:57 +01:00
function classifyInput ( script , allowIncomplete ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . Script , script )
2014-10-13 09:38:54 +02:00
2014-10-13 09:46:50 +02:00
if ( isPubKeyHashInput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'pubkeyhash'
2015-02-11 04:01:20 +01:00
} else if ( isMultisigInput ( script , allowIncomplete ) ) {
2014-10-13 09:38:54 +02:00
return 'multisig'
2015-02-11 04:01:20 +01:00
} else if ( isScriptHashInput ( script , allowIncomplete ) ) {
return 'scripthash'
2014-10-13 09:46:50 +02:00
} else if ( isPubKeyInput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'pubkey'
}
2014-10-13 09:53:07 +02:00
return 'nonstandard'
2014-10-13 09:38:54 +02:00
}
2014-06-12 13:14:22 +02:00
// Standard Script Templates
// {pubKey} OP_CHECKSIG
2015-02-23 00:36:57 +01:00
function pubKeyOutput ( pubKey ) {
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [
2015-03-02 06:48:36 +01:00
pubKey ,
2014-10-13 09:37:50 +02:00
ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
] )
}
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
2015-02-23 00:36:57 +01:00
function pubKeyHashOutput ( hash ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . Hash160bit , hash )
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [
2014-10-13 09:37:50 +02:00
ops . OP _DUP ,
ops . OP _HASH160 ,
2014-06-12 13:14:22 +02:00
hash ,
2014-10-13 09:37:50 +02:00
ops . OP _EQUALVERIFY ,
ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
] )
}
// OP_HASH160 {scriptHash} OP_EQUAL
2015-02-23 00:36:57 +01:00
function scriptHashOutput ( hash ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . Hash160bit , hash )
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [
2014-10-13 09:37:50 +02:00
ops . OP _HASH160 ,
2014-06-12 13:14:22 +02:00
hash ,
2014-10-13 09:37:50 +02:00
ops . OP _EQUAL
2014-06-12 13:14:22 +02:00
] )
}
// m [pubKeys ...] n OP_CHECKMULTISIG
2015-02-23 00:36:57 +01:00
function multisigOutput ( m , pubKeys ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . tuple ( types . Number , [ types . Buffer ] ) , arguments )
2014-09-15 06:21:01 +02:00
2014-06-12 13:14:22 +02:00
var n = pubKeys . length
2015-08-11 09:07:13 +02:00
if ( n < m ) throw new Error ( 'Not enough pubKeys provided' )
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [ ] . concat (
2014-10-13 09:37:50 +02:00
( ops . OP _1 - 1 ) + m ,
2015-03-02 06:48:36 +01:00
pubKeys ,
2014-10-13 09:37:50 +02:00
( ops . OP _1 - 1 ) + n ,
ops . OP _CHECKMULTISIG
2014-06-12 13:14:22 +02:00
) )
}
// {signature}
2015-02-23 00:36:57 +01:00
function pubKeyInput ( signature ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . Buffer , signature )
2014-06-12 13:14:22 +02:00
2014-06-13 16:30:01 +02:00
return Script . fromChunks ( [ signature ] )
2014-06-12 13:14:22 +02:00
}
// {signature} {pubKey}
2015-02-23 00:36:57 +01:00
function pubKeyHashInput ( signature , pubKey ) {
2015-08-11 09:01:47 +02:00
typeforce ( types . tuple ( types . Buffer , types . Buffer ) , arguments )
2014-06-12 13:14:22 +02:00
2015-03-02 06:48:36 +01:00
return Script . fromChunks ( [ signature , pubKey ] )
2014-06-12 13:14:22 +02:00
}
// <scriptSig> {serialized scriptPubKey script}
2015-02-23 00:36:57 +01:00
function scriptHashInput ( scriptSig , scriptPubKey ) {
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [ ] . concat (
scriptSig . chunks ,
scriptPubKey . toBuffer ( )
) )
}
// OP_0 [signatures ...]
2015-02-23 00:36:57 +01:00
function multisigInput ( signatures , scriptPubKey ) {
2014-06-12 13:14:22 +02:00
if ( scriptPubKey ) {
2015-08-11 09:07:13 +02:00
if ( ! isMultisigOutput ( scriptPubKey ) ) throw new Error ( 'Expected multisig scriptPubKey' )
2014-06-12 13:14:22 +02:00
2014-08-30 05:02:02 +02:00
var mOp = scriptPubKey . chunks [ 0 ]
var nOp = scriptPubKey . chunks [ scriptPubKey . chunks . length - 2 ]
2014-10-13 09:37:50 +02:00
var m = mOp - ( ops . OP _1 - 1 )
var n = nOp - ( ops . OP _1 - 1 )
2014-08-30 05:02:02 +02:00
2015-08-11 09:07:13 +02:00
if ( signatures . length < m ) throw new Error ( 'Not enough signatures provided' )
if ( signatures . length > n ) throw new Error ( 'Too many signatures provided' )
2014-06-12 13:14:22 +02:00
}
2014-10-13 09:37:50 +02:00
return Script . fromChunks ( [ ] . concat ( ops . OP _0 , signatures ) )
2014-06-12 13:14:22 +02:00
}
2015-02-23 00:36:57 +01:00
function nullDataOutput ( data ) {
2014-10-19 03:29:40 +02:00
return Script . fromChunks ( [ ops . OP _RETURN , data ] )
}
2014-06-12 13:14:22 +02:00
module . exports = {
2014-11-28 02:50:37 +01:00
isCanonicalPubKey : isCanonicalPubKey ,
isCanonicalSignature : isCanonicalSignature ,
isPubKeyHashInput : isPubKeyHashInput ,
isPubKeyHashOutput : isPubKeyHashOutput ,
isPubKeyInput : isPubKeyInput ,
isPubKeyOutput : isPubKeyOutput ,
isScriptHashInput : isScriptHashInput ,
isScriptHashOutput : isScriptHashOutput ,
isMultisigInput : isMultisigInput ,
isMultisigOutput : isMultisigOutput ,
isNullDataOutput : isNullDataOutput ,
2014-06-13 01:58:52 +02:00
classifyOutput : classifyOutput ,
2014-11-28 02:50:37 +01:00
classifyInput : classifyInput ,
pubKeyOutput : pubKeyOutput ,
2014-06-13 01:58:52 +02:00
pubKeyHashOutput : pubKeyHashOutput ,
2014-11-28 02:50:37 +01:00
scriptHashOutput : scriptHashOutput ,
multisigOutput : multisigOutput ,
2014-06-13 01:58:52 +02:00
pubKeyInput : pubKeyInput ,
2014-11-28 02:50:37 +01:00
pubKeyHashInput : pubKeyHashInput ,
2014-06-13 01:58:52 +02:00
scriptHashInput : scriptHashInput ,
2014-11-28 02:50:37 +01:00
multisigInput : multisigInput ,
nullDataOutput : nullDataOutput
2014-06-12 13:14:22 +02:00
}