Skip to content

Commit 21aa517

Browse files
committed
Merge pull request bitcoinjs#143 from dcousens/multisigfix
Multi-sig ScriptSig Support
2 parents 4689abb + f5ce83b commit 21aa517

File tree

2 files changed

+57
-49
lines changed

2 files changed

+57
-49
lines changed

src/script.js

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -402,52 +402,60 @@ Script.prototype.extractPubkeys = function() {
402402
})
403403
}
404404

405-
/**
406-
* Create an m-of-n output script
407-
*/
408-
Script.createMultiSigOutputScript = function(m, pubkeys) {
405+
// m [pubKeys ...] n OP_CHECKMULTISIG
406+
Script.createMultisigOutputScript = function(m, pubKeys) {
409407
var script = new Script()
410-
pubkeys = pubkeys.sort()
408+
pubKeys = pubKeys.sort()
411409

412410
script.writeOp(Opcode.map.OP_1 + m - 1)
413-
for (var i = 0; i < pubkeys.length; ++i) {
414-
script.writeBytes(pubkeys[i])
411+
for (var i = 0; i < pubKeys.length; ++i) {
412+
script.writeBytes(pubKeys[i])
415413
}
416-
script.writeOp(Opcode.map.OP_1 + pubkeys.length - 1)
414+
script.writeOp(Opcode.map.OP_1 + pubKeys.length - 1)
417415
script.writeOp(Opcode.map.OP_CHECKMULTISIG)
418416

419417
return script
420418
}
421419

422-
/**
423-
* Create a standard payToPubKeyHash input.
424-
*/
420+
// {signature} {pubKey}
425421
Script.createInputScript = function(signature, pubKey) {
426422
var script = new Script()
427423
script.writeBytes(signature)
428424
script.writeBytes(pubKey)
429425
return script
430426
}
431427

432-
/**
433-
* Create a multisig input
434-
*/
435-
Script.createMultiSigInputScript = function(signatures, script) {
436-
script = new Script(script)
437-
var k = script.chunks[0][0]
438-
439-
//Not enough sigs
440-
if (signatures.length < k) return false;
441-
428+
// OP_0 [signatures ...]
429+
Script.createMultisigScriptSig = function(signatures) {
442430
var inScript = new Script()
431+
443432
inScript.writeOp(Opcode.map.OP_0)
444433
signatures.map(function(sig) {
445434
inScript.writeBytes(sig)
446435
})
447-
inScript.writeBytes(script.buffer)
436+
437+
return inScript
438+
}
439+
440+
// <scriptSig> {serialized scriptPubKey script}
441+
Script.createP2SHScriptSig = function(scriptSig, scriptPubKey) {
442+
var inScript = new Script(scriptSig.buffer)
443+
inScript.writeBytes(scriptPubKey.buffer)
448444
return inScript
449445
}
450446

447+
// [signatures ...] {m [pubKeys ...] n OP_CHECKSIG}
448+
Script.createP2SHMultisigScriptSig = function(signatures, scriptPubKey) {
449+
assert(isMultisig.call(scriptPubKey))
450+
451+
var m = scriptPubKey.chunks[0]
452+
var k = m - (Opcode.map.OP_1 - 1)
453+
assert(k <= signatures.length, 'Not enough signatures provided')
454+
455+
var scriptSig = Script.createMultisigScriptSig(signatures)
456+
return Script.createP2SHScriptSig(scriptSig, scriptPubKey)
457+
}
458+
451459
Script.prototype.clone = function() {
452460
return new Script(this.buffer)
453461
}

test/script.js

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,42 +102,42 @@ describe('Script', function() {
102102
})
103103

104104
describe('2-of-3 Multi-Signature', function() {
105-
var compressedPubKeys = []
106-
var numSigs, script, multisig
105+
var pubKeys
107106

108107
beforeEach(function() {
109-
compressedPubKeys = ['02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f',
108+
pubKeys = [
109+
'02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f',
110110
'02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f',
111-
'036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19'].map(h2b)
112-
numSigs = 2
111+
'036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19'
112+
].map(h2b)
113113
})
114114

115-
it('should create valid multi-sig address', function() {
116-
script = Script.createMultiSigOutputScript(numSigs, compressedPubKeys)
117-
multisig = crypto.hash160(script.buffer)
118-
var multisigAddress = new Address(multisig, network.bitcoin.scriptHash)
115+
it('should create valid redeemScript', function() {
116+
var redeemScript = Script.createMultisigOutputScript(2, pubKeys)
117+
118+
var hash160 = crypto.hash160(redeemScript.buffer)
119+
var multisigAddress = new Address(hash160, network.bitcoin.scriptHash)
119120

120-
assert.equal(multisigAddress.version, network.bitcoin.scriptHash)
121121
assert.equal(multisigAddress.toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v')
122122
})
123+
})
123124

124-
it('should create valid redeemScript', function() {
125-
var redeemScript = script.buffer
126-
var deserialized = new Script(redeemScript)
127-
var numOfSignatures = deserialized.chunks[deserialized.chunks.length - 2] - 80
128-
var signaturesRequired = deserialized.chunks[0] - 80
129-
var sigs = [
130-
b2h(deserialized.chunks[1]),
131-
b2h(deserialized.chunks[2]),
132-
b2h(deserialized.chunks[3])
133-
]
134-
135-
assert.equal(numOfSignatures, 3)
136-
assert.equal(signaturesRequired, 2)
137-
assert.equal(sigs[0], '02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f')
138-
assert.equal(sigs[1], '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f')
139-
assert.equal(sigs[2], '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19')
140-
assert.equal(new Address(crypto.hash160(redeemScript), network.bitcoin.scriptHash).toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v')
125+
describe('2-of-2 Multisig scriptSig', function() {
126+
var pubKeys = [
127+
'02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1',
128+
'0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a'
129+
].map(h2b)
130+
var signatures = [
131+
'304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801',
132+
'3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501'
133+
].map(h2b)
134+
var expected = '0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae'
135+
136+
it('should create a valid P2SH multisig scriptSig', function() {
137+
var redeemScript = Script.createMultisigOutputScript(2, pubKeys)
138+
var actual = Script.createP2SHMultisigScriptSig(signatures, redeemScript)
139+
140+
assert.equal(b2h(actual.buffer), expected)
141141
})
142142
})
143143
})

0 commit comments

Comments
 (0)