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
}
function isPubKeyHashInput ( ) {
return this . chunks . length === 2 &&
isCanonicalSignature ( this . chunks [ 0 ] ) &&
isCanonicalPubKey ( this . chunks [ 1 ] )
}
function isPubKeyHashOutput ( ) {
return this . chunks . length === 5 &&
2014-10-13 09:37:50 +02:00
this . chunks [ 0 ] === ops . OP _DUP &&
this . chunks [ 1 ] === ops . OP _HASH160 &&
2014-06-12 13:14:22 +02:00
Buffer . isBuffer ( this . chunks [ 2 ] ) &&
this . chunks [ 2 ] . length === 20 &&
2014-10-13 09:37:50 +02:00
this . chunks [ 3 ] === ops . OP _EQUALVERIFY &&
this . chunks [ 4 ] === ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
}
2014-06-24 09:32:23 +02:00
function isPubKeyInput ( ) {
return this . chunks . length === 1 &&
isCanonicalSignature ( this . chunks [ 0 ] )
}
function isPubKeyOutput ( ) {
2014-06-12 13:14:22 +02:00
return this . chunks . length === 2 &&
2014-06-24 09:32:23 +02:00
isCanonicalPubKey ( this . chunks [ 0 ] ) &&
2014-10-13 09:37:50 +02:00
this . chunks [ 1 ] === ops . OP _CHECKSIG
2014-06-12 13:14:22 +02:00
}
2014-06-24 09:32:23 +02:00
function isScriptHashInput ( ) {
if ( this . chunks . length < 2 ) return false
var lastChunk = this . chunks [ this . chunks . length - 1 ]
if ( ! Buffer . isBuffer ( lastChunk ) ) return false
var scriptSig = Script . fromChunks ( this . chunks . slice ( 0 , - 1 ) )
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
}
function isScriptHashOutput ( ) {
return this . chunks . length === 3 &&
2014-10-13 09:37:50 +02:00
this . chunks [ 0 ] === ops . OP _HASH160 &&
2014-06-12 13:14:22 +02:00
Buffer . isBuffer ( this . chunks [ 1 ] ) &&
this . chunks [ 1 ] . length === 20 &&
2014-10-13 09:37:50 +02:00
this . chunks [ 2 ] === ops . OP _EQUAL
2014-06-24 09:32:23 +02:00
}
function isMultisigInput ( ) {
2014-10-13 09:37:50 +02:00
return this . chunks [ 0 ] === ops . OP _0 &&
2014-06-24 09:32:23 +02:00
this . chunks . slice ( 1 ) . every ( isCanonicalSignature )
}
function isMultisigOutput ( ) {
if ( this . chunks < 4 ) return false
2014-10-13 09:37:50 +02:00
if ( this . chunks [ this . chunks . length - 1 ] !== ops . OP _CHECKMULTISIG ) return false
2014-06-24 09:32:23 +02:00
2014-06-26 08:22:29 +02:00
var mOp = this . 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
var nOp = this . chunks [ this . 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-06-26 08:22:29 +02:00
var pubKeys = this . chunks . slice ( 1 , - 2 )
if ( n < pubKeys . length ) return false
return pubKeys . every ( isCanonicalPubKey )
2014-06-24 09:32:23 +02:00
}
function isNulldataOutput ( ) {
2014-10-13 09:37:50 +02:00
return this . 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 )
if ( isPubKeyHashOutput . call ( script ) ) {
return 'pubkeyhash'
} else if ( isScriptHashOutput . call ( script ) ) {
return 'scripthash'
} else if ( isMultisigOutput . call ( script ) ) {
return 'multisig'
} else if ( isPubKeyOutput . call ( script ) ) {
return 'pubkey'
} else if ( isNulldataOutput . call ( script ) ) {
return 'nulldata'
} else {
return 'nonstandard'
}
}
function classifyInput ( script ) {
enforceType ( Script , script )
if ( isPubKeyHashInput . call ( script ) ) {
return 'pubkeyhash'
} else if ( isScriptHashInput . call ( script ) ) {
return 'scripthash'
} else if ( isMultisigInput . call ( script ) ) {
return 'multisig'
} else if ( isPubKeyInput . call ( script ) ) {
return 'pubkey'
} else {
return 'nonstandard'
}
}
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-06-24 09:32:23 +02:00
assert ( isMultisigOutput . call ( 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
}