Skip to content
2 changes: 1 addition & 1 deletion src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ Script.createMultisigOutputScript = function(m, pubKeys) {
}

// {signature} {pubKey}
Script.createInputScript = function(signature, pubKey) {
Script.createPubKeyHashScriptSig = function(signature, pubKey) {
var script = new Script()
script.writeBytes(signature)
script.writeBytes(pubKey.toBuffer())
Expand Down
43 changes: 21 additions & 22 deletions src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,16 @@ Transaction.deserialize = function(buffer) {
*/
Transaction.prototype.sign = function(index, key, type, network) {
assert(key instanceof ECKey)
type = type || SIGHASH_ALL
network = network || Network.bitcoin

var address = key.pub.getAddress(network.pubKeyHash)

// FIXME: Assumed prior TX was pay-to-pubkey-hash
var script = Script.createOutputScript(address, network)
var hash = this.hashTransactionForSignature(script, index, type)
var sig = key.sign(hash).concat([type])
var signature = this.signScriptSig(index, script, key, type)

this.ins[index].script = Script.createInputScript(sig, key.pub)
var scriptSig = Script.createPubKeyHashScriptSig(signature, key.pub)
this.setScriptSig(index, scriptSig)
}

// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Expand Down Expand Up @@ -357,29 +358,27 @@ Transaction.prototype.signWithKeys = function(keys, outputs, type) {
}
}

/**
* Signs a P2SH output at some index with the given key
*/
Transaction.prototype.p2shsign = function(index, script, key, type) {
script = new Script(script)
key = new ECKey(key)
Transaction.prototype.signScriptSig = function(index, script, key, type) {
type = type || SIGHASH_ALL
var hash = this.hashTransactionForSignature(script, index, type),
sig = key.sign(hash).concat([type])
return sig
}

Transaction.prototype.multisign = Transaction.prototype.p2shsign
assert(Number.isFinite(index) && (index >= 0), 'Invalid vin index')
assert(script instanceof Script, 'Invalid Script object')
assert(key instanceof ECKey, 'Invalid private key')
// assert.equal(type & 0x7F, type, 'Invalid type') // TODO

Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) {
this.ins[index].script = Script.createMultiSigInputScript(sigs, script)
var hash = this.hashTransactionForSignature(script, index, type)
return key.sign(hash).concat([type])
}

Transaction.prototype.setScriptSig = function(index, script) {
this.ins[index].script = script
}

Transaction.prototype.validateSig = function(index, script, sig, pub) {
script = new Script(script)
var hash = this.hashTransactionForSignature(script,index,1)
return ecdsa.verify(hash, convert.coerceToBytes(sig),
convert.coerceToBytes(pub))
Transaction.prototype.validateSig = function(index, script, pub, sig, type) {
type = type || SIGHASH_ALL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that this type fallback should be in hashTransactionForSignature. Here type just get passed through. Also it's probably a good idea for hashTransactionForSignature to assert that type after the fallback is among the known types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hashTransactionForSignature uses type, but the resulting signature also needs it for concatenation.
As for the type assertions, it would have to check for SIGHASH_ANYONECANPAY, so that is a little more complicated than just a equality. But not by much.

var hash = this.hashTransactionForSignature(script, index, type)

return pub.verify(hash, sig)
}

Transaction.feePerKb = 20000
Expand Down
55 changes: 42 additions & 13 deletions test/transaction.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
var assert = require('assert')
var convert = require('../src/convert')

var Address = require('../src/address')
var ECKey = require('../src/eckey').ECKey
var T = require('../src/transaction')
var Transaction = T.Transaction
var TransactionOut = T.TransactionOut
var Script = require('../src/script')
var network = require('..').network

var fixtureTxes = require('./fixtures/mainnet_tx')
var fixtureTx1Hex = fixtureTxes.prevTx
var fixtureTx2Hex = fixtureTxes.tx
var fixtureTxBigHex = fixtureTxes.bigTx

function b2h(b) { return new Buffer(b).toString('hex') }
function h2b(h) { return new Buffer(h, 'hex') }

describe('Transaction', function() {
describe('deserialize', function() {
var tx, serializedTx
Expand All @@ -30,8 +33,9 @@ describe('Transaction', function() {

it('returns the original after serialized again', function() {
var actual = tx.serialize()
var expected = convert.hexToBytes(serializedTx)
assert.deepEqual(actual, expected)
var expected = serializedTx

assert.equal(b2h(actual), expected)
})

it('decodes version correctly', function(){
Expand All @@ -51,7 +55,7 @@ describe('Transaction', function() {
assert.equal(input.outpoint.index, 0)
assert.equal(input.outpoint.hash, "69d02fc05c4e0ddc87e796eee42693c244a3112fffe1f762c3fb61ffcb304634")

assert.equal(convert.bytesToHex(input.script.buffer),
assert.equal(b2h(input.script.buffer),
"493046022100ef89701f460e8660c80808a162bbf2d676f40a331a243592c36d6bd1f81d6bdf022100d29c072f1b18e59caba6e1f0b8cadeb373fd33a25feded746832ec179880c23901")
})

Expand All @@ -61,14 +65,14 @@ describe('Transaction', function() {
var output = tx.outs[0]

assert.equal(output.value, 5000000000)
assert.equal(convert.bytesToHex(output.script.toScriptHash()), "dd40dedd8f7e37466624c4dacc6362d8e7be23dd")
assert.equal(b2h(output.script.toScriptHash()), "dd40dedd8f7e37466624c4dacc6362d8e7be23dd")
// assert.equal(output.address.toString(), "n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9")
// TODO: address is wrong because it's a testnet transaction. Transaction needs to support testnet
})

it('assigns hash to deserialized object', function(){
var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c"
assert.deepEqual(tx.hash, convert.hexToBytes(hashHex))
assert.equal(b2h(tx.hash), hashHex)
})

it('decodes large inputs correctly', function() {
Expand Down Expand Up @@ -158,7 +162,7 @@ describe('Transaction', function() {

var output = tx.outs[0]
assert.equal(output.value, 40000)
assert.deepEqual(convert.bytesToHex(output.script.buffer), "76a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac")
assert.equal(b2h(output.script.buffer), "76a9143443bc45c560866cfeabf1d52f50a6ed358c69f288ac")
}
})

Expand All @@ -171,11 +175,10 @@ describe('Transaction', function() {
var key = ECKey.fromWIF('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb')
tx.sign(0, key)

var pub = key.pub.toBuffer()
var script = prevTx.outs[0].script.buffer
var script = prevTx.outs[0].script
var sig = tx.ins[0].script.chunks[0]

assert.equal(tx.validateSig(0, script, sig, pub), true)
assert.equal(tx.validateSig(0, script, key.pub, sig), true)
})
})

Expand All @@ -188,11 +191,10 @@ describe('Transaction', function() {

it('returns true for valid signature', function(){
var key = ECKey.fromWIF('L44f7zxJ5Zw4EK9HZtyAnzCYz2vcZ5wiJf9AuwhJakiV4xVkxBeb')
var pub = key.pub.toBuffer()
var script = prevTx.outs[0].script.buffer
var script = prevTx.outs[0].script
var sig = validTx.ins[0].script.chunks[0]

assert.equal(validTx.validateSig(0, script, sig, pub), true)
assert.equal(validTx.validateSig(0, script, key.pub, sig), true)
})
})

Expand All @@ -219,6 +221,33 @@ describe('Transaction', function() {
})
})

describe('signScriptSig', function() {
var tx = new Transaction()
tx.addInput('deadbeefcafe', 0)
tx.addOutput('mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r', 1, network.testnet)

var privKeys = [
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf',
'5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAvUcVfH'
].map(function(wif) {
return ECKey.fromWIF(wif)
})
var pubKeys = privKeys.map(function(eck) { return eck.pub })
var pubKeyBuffers = pubKeys.map(function(q) { return q.toBuffer() })
var redeemScript = Script.createMultisigOutputScript(2, pubKeyBuffers)

var signatures = privKeys.map(function(privKey) {
return tx.signScriptSig(0, redeemScript, privKey)
})

var scriptSig = Script.createP2SHMultisigScriptSig(signatures, redeemScript)
tx.setScriptSig(0, scriptSig)

var expected = '0100000001fecaefbeadde00000000fd1b0100483045022100a165904d2a3123ae887bd573b685e903a0ce158b1d21faba2ed4a42b3ca6126e02205f4e0e0cb333666d5b6b0b017fe0df0ac15a20f296a3fb8eab4e1572da2b3dea01473044022054e0cb54d62465a4003a2d0876048cde2b43dcab9385ffe173a2886bfa4d04b00220239811a8923887aa147d92987fa5c16f09a7fb7eea1d331c1a1d5303fd81f9c8014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0101000000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000'

assert.equal(b2h(tx.serialize()), expected)
})

describe('TransactionOut', function() {
describe('scriptPubKey', function() {
it('returns hex string', function() {
Expand Down