2014-06-12 13:14:22 +02:00
var assert = require ( 'assert' )
2014-09-15 06:21:01 +02:00
var enforceType = require ( './types' )
2014-10-13 09:37:50 +02:00
var ops = require ( './opcodes' )
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' )
2014-06-24 09:32:23 +02:00
function isCanonicalPubKey ( buffer ) {
if ( ! Buffer . isBuffer ( buffer ) ) return false
try {
ecurve . Point . decodeFrom ( curve , buffer )
} catch ( e ) {
if ( ! ( e . message . match ( /Invalid sequence (length|tag)/ ) ) ) throw e
return false
}
return true
}
function isCanonicalSignature ( buffer ) {
if ( ! Buffer . isBuffer ( buffer ) ) return false
try {
ECSignature . parseScriptSignature ( buffer )
} catch ( e ) {
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/ ) ) ) throw e
return false
}
return true
}
2014-10-13 09:46:50 +02:00
function isPubKeyHashInput ( script ) {
return script . chunks . length === 2 &&
isCanonicalSignature ( script . chunks [ 0 ] ) &&
isCanonicalPubKey ( script . chunks [ 1 ] )
2014-06-24 09:32:23 +02:00
}
2014-10-13 09:46:50 +02:00
function isPubKeyHashOutput ( script ) {
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
}
2014-10-13 09:46:50 +02:00
function isPubKeyInput ( script ) {
return script . chunks . length === 1 &&
isCanonicalSignature ( script . chunks [ 0 ] )
2014-06-24 09:32:23 +02:00
}
2014-10-13 09:46:50 +02:00
function isPubKeyOutput ( script ) {
return script . chunks . length === 2 &&
isCanonicalPubKey ( script . chunks [ 0 ] ) &&
script . chunks [ 1 ] === ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
}
2014-10-13 09:46:50 +02:00
function isScriptHashInput ( script ) {
if ( script . chunks . length < 2 ) return false
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 ) )
2014-06-24 09:32:23 +02:00
var scriptPubKey = Script . fromBuffer ( lastChunk )
2014-06-29 17:06:43 +02:00
return classifyInput ( scriptSig ) === classifyOutput ( scriptPubKey )
2014-06-24 09:32:23 +02:00
}
2014-10-13 09:46:50 +02:00
function isScriptHashOutput ( script ) {
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
}
2014-10-13 09:46:50 +02:00
function isMultisigInput ( script ) {
return script . chunks [ 0 ] === ops . OP _0 &&
script . chunks . slice ( 1 ) . every ( isCanonicalSignature )
2014-06-24 09:32:23 +02:00
}
2014-10-13 09:46:50 +02: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
}
2014-10-13 09:46:50 +02:00
function isNulldataOutput ( script ) {
return script . chunks [ 0 ] === ops . OP _RETURN
2014-06-12 13:14:22 +02:00
}
2014-10-13 09:38:54 +02:00
function classifyOutput ( script ) {
enforceType ( Script , script )
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-10-13 09:46:50 +02: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
}
function classifyInput ( script ) {
enforceType ( Script , script )
2014-10-13 09:46:50 +02:00
if ( isPubKeyHashInput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'pubkeyhash'
2014-10-13 09:46:50 +02:00
} else if ( isScriptHashInput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'scripthash'
2014-10-13 09:46:50 +02:00
} else if ( isMultisigInput ( script ) ) {
2014-10-13 09:38:54 +02:00
return 'multisig'
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
2014-06-13 01:58:52 +02:00
function pubKeyOutput ( pubKey ) {
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [
pubKey . toBuffer ( ) ,
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
2014-06-13 01:58:52 +02:00
function pubKeyHashOutput ( hash ) {
2014-09-15 06:21:01 +02:00
enforceType ( 'Buffer' , 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
2014-06-13 01:58:52 +02:00
function scriptHashOutput ( hash ) {
2014-09-15 06:21:01 +02:00
enforceType ( 'Buffer' , 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
2014-06-13 01:58:52 +02:00
function multisigOutput ( m , pubKeys ) {
2014-09-15 06:21:01 +02:00
enforceType ( 'Array' , pubKeys )
2014-06-12 13:14:22 +02:00
assert ( pubKeys . length >= m , 'Not enough pubKeys provided' )
var pubKeyBuffers = pubKeys . map ( function ( pubKey ) {
return pubKey . toBuffer ( )
} )
var n = pubKeys . length
return Script . fromChunks ( [ ] . concat (
2014-10-13 09:37:50 +02:00
( ops . OP _1 - 1 ) + m ,
2014-06-12 13:14:22 +02:00
pubKeyBuffers ,
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}
2014-06-13 01:58:52 +02:00
function pubKeyInput ( signature ) {
2014-09-15 06:21:01 +02:00
enforceType ( '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}
2014-06-13 01:58:52 +02:00
function pubKeyHashInput ( signature , pubKey ) {
2014-09-15 06:21:01 +02:00
enforceType ( 'Buffer' , signature )
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [ signature , pubKey . toBuffer ( ) ] )
}
// <scriptSig> {serialized scriptPubKey script}
2014-06-13 01:58:52 +02:00
function scriptHashInput ( scriptSig , scriptPubKey ) {
2014-06-12 13:14:22 +02:00
return Script . fromChunks ( [ ] . concat (
scriptSig . chunks ,
scriptPubKey . toBuffer ( )
) )
}
// OP_0 [signatures ...]
2014-06-13 01:58:52 +02:00
function multisigInput ( signatures , scriptPubKey ) {
2014-06-12 13:14:22 +02:00
if ( scriptPubKey ) {
2014-10-13 09:46:50 +02:00
assert ( isMultisigOutput ( 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
assert ( signatures . length >= m , 'Not enough signatures provided' )
assert ( signatures . length <= n , '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
}
module . exports = {
2014-06-13 01:58:52 +02:00
classifyInput : classifyInput ,
classifyOutput : classifyOutput ,
multisigInput : multisigInput ,
multisigOutput : multisigOutput ,
pubKeyHashInput : pubKeyHashInput ,
pubKeyHashOutput : pubKeyHashOutput ,
pubKeyInput : pubKeyInput ,
pubKeyOutput : pubKeyOutput ,
scriptHashInput : scriptHashInput ,
scriptHashOutput : scriptHashOutput
2014-06-12 13:14:22 +02:00
}