Merge pull request #461 from bitcoinjs/txb
Add further tests for TransactionBuilder
This commit is contained in:
commit
658f276556
3 changed files with 187 additions and 53 deletions
|
@ -9,7 +9,39 @@ var ECPair = require('./ecpair')
|
||||||
var ECSignature = require('./ecsignature')
|
var ECSignature = require('./ecsignature')
|
||||||
var Transaction = require('./transaction')
|
var Transaction = require('./transaction')
|
||||||
|
|
||||||
function extractInput (txIn) {
|
// re-orders signatures to match pubKeys, fills undefined otherwise
|
||||||
|
function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, hashType, skipPubKey) {
|
||||||
|
// maintain a local copy of unmatched signatures
|
||||||
|
var unmatched = signatures.slice()
|
||||||
|
var cache = {}
|
||||||
|
|
||||||
|
return pubKeys.map(function (pubKey) {
|
||||||
|
// skip optionally provided pubKey
|
||||||
|
if (skipPubKey && bufferutils.equal(skipPubKey, pubKey)) return undefined
|
||||||
|
|
||||||
|
var matched
|
||||||
|
var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)
|
||||||
|
|
||||||
|
// check for a matching signature
|
||||||
|
unmatched.some(function (signature, i) {
|
||||||
|
// skip if undefined || OP_0
|
||||||
|
if (!signature) return false
|
||||||
|
|
||||||
|
var signatureHash = cache[hashType] = cache[hashType] || transaction.hashForSignature(vin, prevOutScript, hashType)
|
||||||
|
if (!keyPair2.verify(signatureHash, signature)) return false
|
||||||
|
|
||||||
|
// remove matched signature from unmatched
|
||||||
|
unmatched[i] = undefined
|
||||||
|
matched = signature
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return matched || undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractInput (transaction, txIn, vin) {
|
||||||
var redeemScript
|
var redeemScript
|
||||||
var scriptSig = txIn.script
|
var scriptSig = txIn.script
|
||||||
var scriptSigChunks = bscript.decompile(scriptSig)
|
var scriptSigChunks = bscript.decompile(scriptSig)
|
||||||
|
@ -63,7 +95,7 @@ function extractInput (txIn) {
|
||||||
|
|
||||||
case 'multisig':
|
case 'multisig':
|
||||||
signatures = scriptSigChunks.slice(1).map(function (chunk) {
|
signatures = scriptSigChunks.slice(1).map(function (chunk) {
|
||||||
if (chunk === ops.OP_0) return chunk
|
if (chunk === ops.OP_0) return undefined
|
||||||
|
|
||||||
var parsed = ECSignature.parseScriptSignature(chunk)
|
var parsed = ECSignature.parseScriptSignature(chunk)
|
||||||
hashType = parsed.hashType
|
hashType = parsed.hashType
|
||||||
|
@ -73,6 +105,10 @@ function extractInput (txIn) {
|
||||||
|
|
||||||
if (redeemScript) {
|
if (redeemScript) {
|
||||||
pubKeys = redeemScriptChunks.slice(1, -2)
|
pubKeys = redeemScriptChunks.slice(1, -2)
|
||||||
|
|
||||||
|
if (pubKeys.length !== signatures.length) {
|
||||||
|
signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, redeemScript, hashType, redeemScript)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -117,7 +153,7 @@ TransactionBuilder.fromTransaction = function (transaction, network) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Extract/add signatures
|
// Extract/add signatures
|
||||||
txb.inputs = transaction.ins.map(function (txIn) {
|
txb.inputs = transaction.ins.map(function (txIn, vin) {
|
||||||
// TODO: verify whether extractInput is sane with coinbase scripts
|
// TODO: verify whether extractInput is sane with coinbase scripts
|
||||||
if (Transaction.isCoinbaseHash(txIn.hash)) {
|
if (Transaction.isCoinbaseHash(txIn.hash)) {
|
||||||
throw new Error('coinbase inputs not supported')
|
throw new Error('coinbase inputs not supported')
|
||||||
|
@ -126,7 +162,7 @@ TransactionBuilder.fromTransaction = function (transaction, network) {
|
||||||
// Ignore empty scripts
|
// Ignore empty scripts
|
||||||
if (txIn.script.length === 0) return {}
|
if (txIn.script.length === 0) return {}
|
||||||
|
|
||||||
return extractInput(txIn)
|
return extractInput(transaction, txIn, vin)
|
||||||
})
|
})
|
||||||
|
|
||||||
return txb
|
return txb
|
||||||
|
@ -154,10 +190,14 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu
|
||||||
switch (prevOutType) {
|
switch (prevOutType) {
|
||||||
case 'multisig':
|
case 'multisig':
|
||||||
input.pubKeys = prevOutScriptChunks.slice(1, -2)
|
input.pubKeys = prevOutScriptChunks.slice(1, -2)
|
||||||
|
input.signatures = input.pubKeys.map(function () { return undefined })
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'pubkey':
|
case 'pubkey':
|
||||||
input.pubKeys = prevOutScriptChunks.slice(0, 1)
|
input.pubKeys = prevOutScriptChunks.slice(0, 1)
|
||||||
|
input.signatures = [undefined]
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,10 +251,10 @@ TransactionBuilder.prototype.buildIncomplete = function () {
|
||||||
return this.__build(true)
|
return this.__build(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var canSignTypes = {
|
var canBuildTypes = {
|
||||||
'pubkeyhash': true,
|
|
||||||
'multisig': true,
|
'multisig': true,
|
||||||
'pubkey': true
|
'pubkey': true,
|
||||||
|
'pubkeyhash': true
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
||||||
|
@ -225,14 +265,16 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
||||||
|
|
||||||
var tx = this.tx.clone()
|
var tx = this.tx.clone()
|
||||||
|
|
||||||
// Create script signatures from signature meta-data
|
// Create script signatures from inputs
|
||||||
this.inputs.forEach(function (input, index) {
|
this.inputs.forEach(function (input, index) {
|
||||||
var scriptType = input.scriptType
|
var scriptType = input.scriptType
|
||||||
var scriptSig
|
var scriptSig
|
||||||
|
|
||||||
if (!allowIncomplete) {
|
if (!allowIncomplete) {
|
||||||
if (!scriptType) throw new Error('Transaction is not complete')
|
if (!scriptType) throw new Error('Transaction is not complete')
|
||||||
if (!canSignTypes[scriptType]) throw new Error(scriptType + ' not supported')
|
if (!canBuildTypes[scriptType]) throw new Error(scriptType + ' not supported')
|
||||||
|
|
||||||
|
// XXX: only relevant to types that need signatures
|
||||||
if (!input.signatures) throw new Error('Transaction is missing signatures')
|
if (!input.signatures) throw new Error('Transaction is missing signatures')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +286,6 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'multisig':
|
case 'multisig':
|
||||||
// Array.prototype.map is sparse-compatible
|
|
||||||
var msSignatures = input.signatures.map(function (signature) {
|
var msSignatures = input.signatures.map(function (signature) {
|
||||||
return signature && signature.toScriptSignature(input.hashType)
|
return signature && signature.toScriptSignature(input.hashType)
|
||||||
})
|
})
|
||||||
|
@ -252,12 +293,11 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
||||||
// fill in blanks with OP_0
|
// fill in blanks with OP_0
|
||||||
if (allowIncomplete) {
|
if (allowIncomplete) {
|
||||||
for (var i = 0; i < msSignatures.length; ++i) {
|
for (var i = 0; i < msSignatures.length; ++i) {
|
||||||
if (msSignatures[i]) continue
|
msSignatures[i] = msSignatures[i] || ops.OP_0
|
||||||
|
|
||||||
msSignatures[i] = ops.OP_0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove blank signatures
|
||||||
} else {
|
} else {
|
||||||
// Array.prototype.filter returns non-sparse array
|
|
||||||
msSignatures = msSignatures.filter(function (x) { return x })
|
msSignatures = msSignatures.filter(function (x) { return x })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,11 +337,12 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash
|
||||||
input.prevOutType &&
|
input.prevOutType &&
|
||||||
input.pubKeys &&
|
input.pubKeys &&
|
||||||
input.scriptType &&
|
input.scriptType &&
|
||||||
input.signatures
|
input.signatures &&
|
||||||
|
input.signatures.length === input.pubKeys.length
|
||||||
|
|
||||||
var kpPubKey = keyPair.getPublicKeyBuffer()
|
var kpPubKey = keyPair.getPublicKeyBuffer()
|
||||||
|
|
||||||
// are we almost ready to sign?
|
// are we ready to sign?
|
||||||
if (canSign) {
|
if (canSign) {
|
||||||
// if redeemScript was provided, enforce consistency
|
// if redeemScript was provided, enforce consistency
|
||||||
if (redeemScript) {
|
if (redeemScript) {
|
||||||
|
@ -323,13 +364,13 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash
|
||||||
}
|
}
|
||||||
|
|
||||||
var scriptType = bscript.classifyOutput(redeemScript)
|
var scriptType = bscript.classifyOutput(redeemScript)
|
||||||
if (!canSignTypes[scriptType]) throw new Error('RedeemScript not supported (' + scriptType + ')')
|
|
||||||
|
|
||||||
var redeemScriptChunks = bscript.decompile(redeemScript)
|
var redeemScriptChunks = bscript.decompile(redeemScript)
|
||||||
var pubKeys = []
|
var pubKeys
|
||||||
|
|
||||||
switch (scriptType) {
|
switch (scriptType) {
|
||||||
case 'multisig':
|
case 'multisig':
|
||||||
pubKeys = redeemScriptChunks.slice(1, -2)
|
pubKeys = redeemScriptChunks.slice(1, -2)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'pubkeyhash':
|
case 'pubkeyhash':
|
||||||
|
@ -338,11 +379,16 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash
|
||||||
|
|
||||||
if (!bufferutils.equal(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')
|
if (!bufferutils.equal(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')
|
||||||
pubKeys = [kpPubKey]
|
pubKeys = [kpPubKey]
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'pubkey':
|
case 'pubkey':
|
||||||
pubKeys = redeemScriptChunks.slice(0, 1)
|
pubKeys = redeemScriptChunks.slice(0, 1)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('RedeemScript not supported (' + scriptType + ')')
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we don't have a prevOutScript, generate a P2SH script
|
// if we don't have a prevOutScript, generate a P2SH script
|
||||||
|
@ -354,55 +400,31 @@ TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hash
|
||||||
input.pubKeys = pubKeys
|
input.pubKeys = pubKeys
|
||||||
input.redeemScript = redeemScript
|
input.redeemScript = redeemScript
|
||||||
input.scriptType = scriptType
|
input.scriptType = scriptType
|
||||||
|
input.signatures = pubKeys.map(function () { return undefined })
|
||||||
// cannot be pay-to-scriptHash
|
|
||||||
} else {
|
} else {
|
||||||
|
// pay-to-scriptHash is not possible without a redeemScript
|
||||||
if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')
|
if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')
|
||||||
|
|
||||||
// can we otherwise sign this?
|
// if we don't have a scriptType, assume pubKeyHash otherwise
|
||||||
if (input.scriptType) {
|
if (!input.scriptType) {
|
||||||
if (!input.pubKeys) throw new Error(input.scriptType + ' not supported')
|
|
||||||
|
|
||||||
// we know nothin' Jon Snow, assume pubKeyHash
|
|
||||||
} else {
|
|
||||||
input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(keyPair.getPublicKeyBuffer()))
|
input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(keyPair.getPublicKeyBuffer()))
|
||||||
input.prevOutType = 'pubkeyhash'
|
input.prevOutType = 'pubkeyhash'
|
||||||
input.pubKeys = [kpPubKey]
|
input.pubKeys = [kpPubKey]
|
||||||
input.scriptType = input.prevOutType
|
input.scriptType = input.prevOutType
|
||||||
|
input.signatures = [undefined]
|
||||||
|
} else {
|
||||||
|
// throw if we can't sign with it
|
||||||
|
if (!input.pubKeys || !input.signatures) throw new Error(input.scriptType + ' not supported')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.hashType = hashType
|
input.hashType = hashType
|
||||||
input.signatures = input.signatures || []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ready to sign?
|
||||||
var signatureScript = input.redeemScript || input.prevOutScript
|
var signatureScript = input.redeemScript || input.prevOutScript
|
||||||
var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)
|
var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)
|
||||||
|
|
||||||
// enforce signature order matches public keys
|
|
||||||
if (input.scriptType === 'multisig' && input.redeemScript && input.signatures.length !== input.pubKeys.length) {
|
|
||||||
// maintain a local copy of unmatched signatures
|
|
||||||
var unmatched = input.signatures.slice()
|
|
||||||
|
|
||||||
input.signatures = input.pubKeys.map(function (pubKey) {
|
|
||||||
var match
|
|
||||||
var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)
|
|
||||||
|
|
||||||
// check for any matching signatures
|
|
||||||
unmatched.some(function (signature, i) {
|
|
||||||
if (!signature || !keyPair2.verify(signatureHash, signature)) return false
|
|
||||||
match = signature
|
|
||||||
|
|
||||||
// remove matched signature from unmatched
|
|
||||||
unmatched.splice(i, 1)
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return match || undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// enforce in order signing of public keys
|
// enforce in order signing of public keys
|
||||||
var valid = input.pubKeys.some(function (pubKey, i) {
|
var valid = input.pubKeys.some(function (pubKey, i) {
|
||||||
if (!bufferutils.equal(kpPubKey, pubKey)) return false
|
if (!bufferutils.equal(kpPubKey, pubKey)) return false
|
||||||
|
|
84
test/fixtures/transaction_builder.json
vendored
84
test/fixtures/transaction_builder.json
vendored
|
@ -118,6 +118,32 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Transaction w/ multisig 2-of-3 -> pubKeyHash",
|
||||||
|
"network": "testnet",
|
||||||
|
"txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000910047304402206b2fc7d3182e2853cab5bcffb85c3ef5470d2d05c496295538c9947af3bfd0ec0220300aa705a985c74f76c26c6d68da9b61b5c4cd5432e8c6a54623f376c8bf8cde01473044022031059c4dd6a97d84e3a4eb1ca21a9870bd1762fbd5db7c1932d75e56da78794502200f22d85be3c5f7035e89a147ee2619a066df19aff14a62e6bb3f649b6da19edf01ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"vout": 0,
|
||||||
|
"prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG",
|
||||||
|
"signs": [
|
||||||
|
{
|
||||||
|
"keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"script": "OP_DUP OP_HASH160 faf1d99bf040ea9c7f8cc9f14ac6733ad75ce246 OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
|
"value": 10000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Transaction w/ multisig 2-of-2 (reverse order) -> pubKeyHash",
|
"description": "Transaction w/ multisig 2-of-2 (reverse order) -> pubKeyHash",
|
||||||
"network": "testnet",
|
"network": "testnet",
|
||||||
|
@ -239,6 +265,64 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"fromTransaction": [
|
||||||
|
{
|
||||||
|
"description": "Transaction w/ scriptHash(multisig 2-of-2) -> OP_RETURN | 1 OP_0, no signatures",
|
||||||
|
"network": "testnet",
|
||||||
|
"incomplete": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": "OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae",
|
||||||
|
"scriptSigAfter": "OP_0 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
|
"value": 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Transaction w/ scriptHash(multisig 2-of-2) -> OP_RETURN | missing OP_0, 1 signature",
|
||||||
|
"network": "testnet",
|
||||||
|
"incomplete": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": "OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae",
|
||||||
|
"scriptSigAfter": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
|
"value": 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Transaction w/ scriptHash(multisig 2-of-2) -> OP_RETURN | no OP_0, 2 signatures",
|
||||||
|
"network": "testnet",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf",
|
||||||
|
"vout": 0,
|
||||||
|
"scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae",
|
||||||
|
"scriptSigAfter": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
|
"value": 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"multisig": [
|
"multisig": [
|
||||||
{
|
{
|
||||||
"description": "P2SH 2-of-2 multisig, signed in correct order",
|
"description": "P2SH 2-of-2 multisig, signed in correct order",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var baddress = require('../src/address')
|
var baddress = require('../src/address')
|
||||||
var bscript = require('../src/script')
|
var bscript = require('../src/script')
|
||||||
|
var bufferutils = require('../src/bufferutils')
|
||||||
var ops = require('../src/opcodes')
|
var ops = require('../src/opcodes')
|
||||||
|
|
||||||
var BigInteger = require('bigi')
|
var BigInteger = require('bigi')
|
||||||
|
@ -76,7 +77,7 @@ describe('TransactionBuilder', function () {
|
||||||
|
|
||||||
describe('fromTransaction', function () {
|
describe('fromTransaction', function () {
|
||||||
fixtures.valid.build.forEach(function (f) {
|
fixtures.valid.build.forEach(function (f) {
|
||||||
it('builds the correct TransactionBuilder for ' + f.description, function () {
|
it('builds TransactionBuilder, with ' + f.description, function () {
|
||||||
var network = NETWORKS[f.network || 'bitcoin']
|
var network = NETWORKS[f.network || 'bitcoin']
|
||||||
var tx = Transaction.fromHex(f.txHex)
|
var tx = Transaction.fromHex(f.txHex)
|
||||||
var txb = TransactionBuilder.fromTransaction(tx, network)
|
var txb = TransactionBuilder.fromTransaction(tx, network)
|
||||||
|
@ -86,6 +87,33 @@ describe('TransactionBuilder', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fixtures.valid.fromTransaction.forEach(function (f) {
|
||||||
|
it('builds TransactionBuilder, with ' + f.description, function () {
|
||||||
|
var tx = new Transaction()
|
||||||
|
|
||||||
|
f.inputs.forEach(function (input) {
|
||||||
|
var txHash = bufferutils.reverse(new Buffer(input.txId, 'hex'))
|
||||||
|
|
||||||
|
tx.addInput(txHash, input.vout, undefined, bscript.fromASM(input.scriptSig))
|
||||||
|
})
|
||||||
|
|
||||||
|
f.outputs.forEach(function (output) {
|
||||||
|
tx.addOutput(bscript.fromASM(output.script), output.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
var txb = TransactionBuilder.fromTransaction(tx)
|
||||||
|
var txAfter = f.incomplete ? txb.buildIncomplete() : txb.build()
|
||||||
|
|
||||||
|
txAfter.ins.forEach(function (input, i) {
|
||||||
|
assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSigAfter)
|
||||||
|
})
|
||||||
|
|
||||||
|
txAfter.outs.forEach(function (output, i) {
|
||||||
|
assert.equal(bscript.toASM(output.script), f.outputs[i].script)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
fixtures.invalid.fromTransaction.forEach(function (f) {
|
fixtures.invalid.fromTransaction.forEach(function (f) {
|
||||||
it('throws on ' + f.exception, function () {
|
it('throws on ' + f.exception, function () {
|
||||||
var tx = Transaction.fromHex(f.txHex)
|
var tx = Transaction.fromHex(f.txHex)
|
||||||
|
|
Loading…
Add table
Reference in a new issue