2014-06-12 13:14:22 +02:00
var assert = require ( 'assert' )
var opcodes = require ( './opcodes' )
2014-06-24 09:32:23 +02:00
// FIXME: use ECPubKey, currently the circular dependency breaks everything.
//
// Solutions:
// * Remove ECPubKey.getAddress
// - Minimal change, but likely unpopular
// * Move all script related functionality out of Address
// - Means a lot of changes to Transaction/Wallet
// * Ignore it (existing solution)
// * Some form of hackery with commonjs
//
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-13 01:58:52 +02:00
function classifyOutput ( script ) {
2014-06-13 08:24:55 +02:00
assert ( script instanceof Script , 'Expected Script, got ' , script )
2014-06-24 09:32:23 +02:00
if ( isPubKeyHashOutput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'pubkeyhash'
2014-06-24 09:32:23 +02:00
} else if ( isScriptHashOutput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'scripthash'
2014-06-24 09:32:23 +02:00
} else if ( isMultisigOutput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'multisig'
2014-06-24 09:32:23 +02:00
} else if ( isPubKeyOutput . call ( script ) ) {
return 'pubkey'
} else if ( isNulldataOutput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'nulldata'
} else {
return 'nonstandard'
}
}
2014-06-24 09:47:16 +02:00
function classifyInput ( script ) {
2014-06-13 08:24:55 +02:00
assert ( script instanceof Script , 'Expected Script, got ' , script )
2014-06-24 09:32:23 +02:00
if ( isPubKeyHashInput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'pubkeyhash'
2014-06-24 09:47:16 +02:00
} else if ( isScriptHashInput . call ( script ) ) {
2014-06-24 09:32:23 +02:00
return 'scripthash'
} else if ( isMultisigInput . call ( script ) ) {
2014-06-12 13:14:22 +02:00
return 'multisig'
2014-06-24 09:32:23 +02:00
} else if ( isPubKeyInput . call ( script ) ) {
return 'pubkey'
2014-06-12 13:14:22 +02:00
} else {
return 'nonstandard'
}
}
2014-06-24 09:32:23 +02:00
function isCanonicalPubKey ( buffer ) {
if ( ! Buffer . isBuffer ( buffer ) ) return false
try {
// FIXME: boo
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 &&
this . chunks [ 0 ] === opcodes . OP _DUP &&
this . chunks [ 1 ] === opcodes . OP _HASH160 &&
2014-06-12 13:14:22 +02:00
Buffer . isBuffer ( this . chunks [ 2 ] ) &&
this . chunks [ 2 ] . length === 20 &&
2014-06-24 09:32:23 +02:00
this . chunks [ 3 ] === opcodes . OP _EQUALVERIFY &&
this . chunks [ 4 ] === opcodes . 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-06-12 13:14:22 +02:00
this . chunks [ 1 ] === opcodes . OP _CHECKSIG
}
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 )
return classifyInput ( scriptSig , false ) === classifyOutput ( scriptPubKey )
}
function isScriptHashOutput ( ) {
return this . chunks . length === 3 &&
this . chunks [ 0 ] === opcodes . OP _HASH160 &&
2014-06-12 13:14:22 +02:00
Buffer . isBuffer ( this . chunks [ 1 ] ) &&
this . chunks [ 1 ] . length === 20 &&
2014-06-24 09:32:23 +02:00
this . chunks [ 2 ] === opcodes . OP _EQUAL
}
function isMultisigInput ( ) {
return this . chunks [ 0 ] === opcodes . OP _0 &&
this . chunks . slice ( 1 ) . every ( isCanonicalSignature )
}
function isMultisigOutput ( ) {
if ( this . chunks < 4 ) return false
if ( this . chunks [ this . chunks . length - 1 ] !== opcodes . OP _CHECKMULTISIG ) return false
2014-06-26 08:22:29 +02:00
var mOp = this . chunks [ 0 ]
if ( mOp === opcodes . OP _0 ) return false
if ( mOp < opcodes . OP _1 ) return false
if ( mOp > opcodes . OP _16 ) return false
var nOp = this . chunks [ this . chunks . length - 2 ]
if ( nOp === opcodes . OP _0 ) return false
if ( nOp < opcodes . OP _1 ) return false
if ( nOp > opcodes . OP _16 ) return false
var m = mOp - ( opcodes . OP _1 - 1 )
var n = nOp - ( opcodes . 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-06-12 13:14:22 +02:00
return this . chunks [ 0 ] === opcodes . OP _RETURN
}
// 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 ( ) ,
opcodes . OP _CHECKSIG
] )
}
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
2014-06-13 01:58:52 +02:00
function pubKeyHashOutput ( hash ) {
2014-06-12 13:14:22 +02:00
assert ( Buffer . isBuffer ( hash ) , 'Expected Buffer, got ' + hash )
return Script . fromChunks ( [
opcodes . OP _DUP ,
opcodes . OP _HASH160 ,
hash ,
opcodes . OP _EQUALVERIFY ,
opcodes . OP _CHECKSIG
] )
}
// OP_HASH160 {scriptHash} OP_EQUAL
2014-06-13 01:58:52 +02:00
function scriptHashOutput ( hash ) {
2014-06-12 13:14:22 +02:00
assert ( Buffer . isBuffer ( hash ) , 'Expected Buffer, got ' + hash )
return Script . fromChunks ( [
opcodes . OP _HASH160 ,
hash ,
opcodes . OP _EQUAL
] )
}
// m [pubKeys ...] n OP_CHECKMULTISIG
2014-06-13 01:58:52 +02:00
function multisigOutput ( m , pubKeys ) {
2014-06-12 13:14:22 +02:00
assert ( Array . isArray ( pubKeys ) , 'Expected Array, got ' + pubKeys )
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 (
( opcodes . OP _1 - 1 ) + m ,
pubKeyBuffers ,
( opcodes . OP _1 - 1 ) + n ,
opcodes . OP _CHECKMULTISIG
) )
}
// {signature}
2014-06-13 01:58:52 +02:00
function pubKeyInput ( signature ) {
2014-06-12 13:14:22 +02:00
assert ( Buffer . isBuffer ( signature ) , 'Expected Buffer, got ' + signature )
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-06-12 13:14:22 +02:00
assert ( Buffer . isBuffer ( signature ) , 'Expected Buffer, got ' + signature )
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
var m = scriptPubKey . chunks [ 0 ]
var k = m - ( opcodes . OP _1 - 1 )
assert ( k <= signatures . length , 'Not enough signatures provided' )
}
return Script . fromChunks ( [ ] . concat ( opcodes . OP _0 , signatures ) )
}
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
}