bitcoinjs-lib/src/scripts.js

299 lines
7.6 KiB
JavaScript
Raw Normal View History

var assert = require('assert')
2014-10-13 09:37:50 +02:00
var ops = require('./opcodes')
var typeForce = require('typeforce')
2014-06-24 09:32:23 +02:00
var ecurve = require('ecurve')
var curve = ecurve.getCurveByName('secp256k1')
var ECSignature = require('./ecsignature')
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-02-23 00:36:57 +01:00
if (!(e.message.match(/Invalid sequence (length|tag)/)))
throw e
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) {
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
2014-06-24 09:32:23 +02:00
return false
}
return true
}
2015-02-23 00:36:57 +01: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
}
2015-02-23 00:36:57 +01: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
}
2015-02-23 00:36:57 +01:00
function isPubKeyInput (script) {
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) {
return script.chunks.length === 2 &&
isCanonicalPubKey(script.chunks[0]) &&
script.chunks[1] === ops.OP_CHECKSIG
}
2015-02-23 00:36:57 +01:00
function isScriptHashInput (script, allowIncomplete) {
if (script.chunks.length < 2) return false
2014-06-24 09:32:23 +02:00
var lastChunk = script.chunks[script.chunks.length - 1]
2014-06-24 09:32:23 +02:00
if (!Buffer.isBuffer(lastChunk)) return false
var scriptSig = Script.fromChunks(script.chunks.slice(0, -1))
var scriptPubKey
try {
scriptPubKey = Script.fromBuffer(lastChunk)
} catch (e) {
return false
}
2014-06-24 09:32:23 +02:00
return classifyInput(scriptSig, allowIncomplete) === classifyOutput(scriptPubKey)
2014-06-24 09:32:23 +02:00
}
2015-02-23 00:36:57 +01: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
}
// 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) {
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) {
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
if (script.chunks[script.chunks.length - 1] !== ops.OP_CHECKMULTISIG) return false
2014-06-24 09:32:23 +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
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-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
var pubKeys = script.chunks.slice(1, -2)
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) {
return script.chunks[0] === ops.OP_RETURN
}
2015-02-23 00:36:57 +01:00
function classifyOutput (script) {
2014-12-23 05:08:20 +01:00
typeForce('Script', script)
2014-10-13 09:38:54 +02:00
if (isPubKeyHashOutput(script)) {
2014-10-13 09:38:54 +02:00
return 'pubkeyhash'
} else if (isScriptHashOutput(script)) {
2014-10-13 09:38:54 +02:00
return 'scripthash'
} else if (isMultisigOutput(script)) {
2014-10-13 09:38:54 +02:00
return 'multisig'
} 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) {
2014-12-23 05:08:20 +01:00
typeForce('Script', script)
2014-10-13 09:38:54 +02:00
if (isPubKeyHashInput(script)) {
2014-10-13 09:38:54 +02:00
return 'pubkeyhash'
} else if (isMultisigInput(script, allowIncomplete)) {
2014-10-13 09:38:54 +02:00
return 'multisig'
} else if (isScriptHashInput(script, allowIncomplete)) {
return 'scripthash'
} 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
}
// Standard Script Templates
// {pubKey} OP_CHECKSIG
2015-02-23 00:36:57 +01:00
function pubKeyOutput (pubKey) {
return Script.fromChunks([
pubKey.toBuffer(),
2014-10-13 09:37:50 +02:00
ops.OP_CHECKSIG
])
}
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
2015-02-23 00:36:57 +01:00
function pubKeyHashOutput (hash) {
2014-12-23 05:08:20 +01:00
typeForce('Buffer', hash)
return Script.fromChunks([
2014-10-13 09:37:50 +02:00
ops.OP_DUP,
ops.OP_HASH160,
hash,
2014-10-13 09:37:50 +02:00
ops.OP_EQUALVERIFY,
ops.OP_CHECKSIG
])
}
// OP_HASH160 {scriptHash} OP_EQUAL
2015-02-23 00:36:57 +01:00
function scriptHashOutput (hash) {
2014-12-23 05:08:20 +01:00
typeForce('Buffer', hash)
return Script.fromChunks([
2014-10-13 09:37:50 +02:00
ops.OP_HASH160,
hash,
2014-10-13 09:37:50 +02:00
ops.OP_EQUAL
])
}
// m [pubKeys ...] n OP_CHECKMULTISIG
2015-02-23 00:36:57 +01:00
function multisigOutput (m, pubKeys) {
typeForce(['ECPubKey'], pubKeys)
assert(pubKeys.length >= m, 'Not enough pubKeys provided')
2015-02-23 00:36:57 +01:00
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,
pubKeyBuffers,
2014-10-13 09:37:50 +02:00
(ops.OP_1 - 1) + n,
ops.OP_CHECKMULTISIG
))
}
// {signature}
2015-02-23 00:36:57 +01:00
function pubKeyInput (signature) {
2014-12-23 05:08:20 +01:00
typeForce('Buffer', signature)
return Script.fromChunks([signature])
}
// {signature} {pubKey}
2015-02-23 00:36:57 +01:00
function pubKeyHashInput (signature, pubKey) {
2014-12-23 05:08:20 +01:00
typeForce('Buffer', signature)
return Script.fromChunks([signature, pubKey.toBuffer()])
}
// <scriptSig> {serialized scriptPubKey script}
2015-02-23 00:36:57 +01:00
function scriptHashInput (scriptSig, scriptPubKey) {
return Script.fromChunks([].concat(
scriptSig.chunks,
scriptPubKey.toBuffer()
))
}
// OP_0 [signatures ...]
2015-02-23 00:36:57 +01:00
function multisigInput (signatures, scriptPubKey) {
if (scriptPubKey) {
assert(isMultisigOutput(scriptPubKey))
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)
var count = 0
2015-02-23 00:36:57 +01:00
signatures.forEach(function (signature) {
count += (signature !== ops.OP_0)
})
assert(count >= m, 'Not enough signatures provided')
assert(count <= n, 'Too many signatures provided')
}
2014-10-13 09:37:50 +02:00
return Script.fromChunks([].concat(ops.OP_0, signatures))
}
2015-02-23 00:36:57 +01:00
function nullDataOutput (data) {
return Script.fromChunks([ops.OP_RETURN, data])
}
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,
2015-02-23 00:36:57 +01:00
dataOutput: function (data) {
2014-11-28 03:47:05 +01:00
console.warn('dataOutput is deprecated, use nullDataOutput by 2.0.0')
return nullDataOutput(data)
},
2014-11-28 02:50:37 +01:00
nullDataOutput: nullDataOutput
}