Skip to content

Commit f947360

Browse files
committed
Merge pull request bitcoinjs#294 from bitcoinjs/scriptsclean
Scripts clean and isMultisigOutput fix
2 parents 8a6a8b1 + ab57630 commit f947360

File tree

3 files changed

+156
-118
lines changed

3 files changed

+156
-118
lines changed

src/scripts.js

Lines changed: 98 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,17 @@
11
var assert = require('assert')
22
var enforceType = require('./types')
3-
var opcodes = require('./opcodes')
4-
5-
// FIXME: use ECPubKey, currently the circular dependency breaks everything.
6-
//
7-
// Solutions:
8-
// * Remove ECPubKey.getAddress
9-
// - Minimal change, but likely unpopular
10-
// * Move all script related functionality out of Address
11-
// - Means a lot of changes to Transaction/Wallet
12-
// * Ignore it (existing solution)
13-
// * Some form of hackery with commonjs
14-
//
3+
var ops = require('./opcodes')
4+
155
var ecurve = require('ecurve')
166
var curve = ecurve.getCurveByName('secp256k1')
177

188
var ECSignature = require('./ecsignature')
199
var Script = require('./script')
2010

21-
function classifyOutput(script) {
22-
enforceType(Script, script)
23-
24-
if (isPubKeyHashOutput.call(script)) {
25-
return 'pubkeyhash'
26-
} else if (isScriptHashOutput.call(script)) {
27-
return 'scripthash'
28-
} else if (isMultisigOutput.call(script)) {
29-
return 'multisig'
30-
} else if (isPubKeyOutput.call(script)) {
31-
return 'pubkey'
32-
} else if (isNulldataOutput.call(script)) {
33-
return 'nulldata'
34-
} else {
35-
return 'nonstandard'
36-
}
37-
}
38-
39-
function classifyInput(script) {
40-
enforceType(Script, script)
41-
42-
if (isPubKeyHashInput.call(script)) {
43-
return 'pubkeyhash'
44-
} else if (isScriptHashInput.call(script)) {
45-
return 'scripthash'
46-
} else if (isMultisigInput.call(script)) {
47-
return 'multisig'
48-
} else if (isPubKeyInput.call(script)) {
49-
return 'pubkey'
50-
} else {
51-
return 'nonstandard'
52-
}
53-
}
54-
5511
function isCanonicalPubKey(buffer) {
5612
if (!Buffer.isBuffer(buffer)) return false
5713

5814
try {
59-
// FIXME: boo
6015
ecurve.Point.decodeFrom(curve, buffer)
6116
} catch (e) {
6217
if (!(e.message.match(/Invalid sequence (length|tag)/))) throw e
@@ -81,92 +36,126 @@ function isCanonicalSignature(buffer) {
8136
return true
8237
}
8338

84-
function isPubKeyHashInput() {
85-
return this.chunks.length === 2 &&
86-
isCanonicalSignature(this.chunks[0]) &&
87-
isCanonicalPubKey(this.chunks[1])
39+
function isPubKeyHashInput(script) {
40+
return script.chunks.length === 2 &&
41+
isCanonicalSignature(script.chunks[0]) &&
42+
isCanonicalPubKey(script.chunks[1])
8843
}
8944

90-
function isPubKeyHashOutput() {
91-
return this.chunks.length === 5 &&
92-
this.chunks[0] === opcodes.OP_DUP &&
93-
this.chunks[1] === opcodes.OP_HASH160 &&
94-
Buffer.isBuffer(this.chunks[2]) &&
95-
this.chunks[2].length === 20 &&
96-
this.chunks[3] === opcodes.OP_EQUALVERIFY &&
97-
this.chunks[4] === opcodes.OP_CHECKSIG
45+
function isPubKeyHashOutput(script) {
46+
return script.chunks.length === 5 &&
47+
script.chunks[0] === ops.OP_DUP &&
48+
script.chunks[1] === ops.OP_HASH160 &&
49+
Buffer.isBuffer(script.chunks[2]) &&
50+
script.chunks[2].length === 20 &&
51+
script.chunks[3] === ops.OP_EQUALVERIFY &&
52+
script.chunks[4] === ops.OP_CHECKSIG
9853
}
9954

100-
function isPubKeyInput() {
101-
return this.chunks.length === 1 &&
102-
isCanonicalSignature(this.chunks[0])
55+
function isPubKeyInput(script) {
56+
return script.chunks.length === 1 &&
57+
isCanonicalSignature(script.chunks[0])
10358
}
10459

105-
function isPubKeyOutput() {
106-
return this.chunks.length === 2 &&
107-
isCanonicalPubKey(this.chunks[0]) &&
108-
this.chunks[1] === opcodes.OP_CHECKSIG
60+
function isPubKeyOutput(script) {
61+
return script.chunks.length === 2 &&
62+
isCanonicalPubKey(script.chunks[0]) &&
63+
script.chunks[1] === ops.OP_CHECKSIG
10964
}
11065

111-
function isScriptHashInput() {
112-
if (this.chunks.length < 2) return false
113-
var lastChunk = this.chunks[this.chunks.length - 1]
66+
function isScriptHashInput(script) {
67+
if (script.chunks.length < 2) return false
68+
var lastChunk = script.chunks[script.chunks.length - 1]
11469

11570
if (!Buffer.isBuffer(lastChunk)) return false
11671

117-
var scriptSig = Script.fromChunks(this.chunks.slice(0, -1))
72+
var scriptSig = Script.fromChunks(script.chunks.slice(0, -1))
11873
var scriptPubKey = Script.fromBuffer(lastChunk)
11974

12075
return classifyInput(scriptSig) === classifyOutput(scriptPubKey)
12176
}
12277

123-
function isScriptHashOutput() {
124-
return this.chunks.length === 3 &&
125-
this.chunks[0] === opcodes.OP_HASH160 &&
126-
Buffer.isBuffer(this.chunks[1]) &&
127-
this.chunks[1].length === 20 &&
128-
this.chunks[2] === opcodes.OP_EQUAL
78+
function isScriptHashOutput(script) {
79+
return script.chunks.length === 3 &&
80+
script.chunks[0] === ops.OP_HASH160 &&
81+
Buffer.isBuffer(script.chunks[1]) &&
82+
script.chunks[1].length === 20 &&
83+
script.chunks[2] === ops.OP_EQUAL
12984
}
13085

131-
function isMultisigInput() {
132-
return this.chunks[0] === opcodes.OP_0 &&
133-
this.chunks.slice(1).every(isCanonicalSignature)
86+
function isMultisigInput(script) {
87+
return script.chunks[0] === ops.OP_0 &&
88+
script.chunks.slice(1).every(isCanonicalSignature)
13489
}
13590

136-
function isMultisigOutput() {
137-
if (this.chunks < 4) return false
138-
if (this.chunks[this.chunks.length - 1] !== opcodes.OP_CHECKMULTISIG) return false
91+
function isMultisigOutput(script) {
92+
if (script.chunks.length < 4) return false
93+
if (script.chunks[script.chunks.length - 1] !== ops.OP_CHECKMULTISIG) return false
13994

140-
var mOp = this.chunks[0]
141-
if (mOp === opcodes.OP_0) return false
142-
if (mOp < opcodes.OP_1) return false
143-
if (mOp > opcodes.OP_16) return false
95+
var mOp = script.chunks[0]
96+
if (mOp === ops.OP_0) return false
97+
if (mOp < ops.OP_1) return false
98+
if (mOp > ops.OP_16) return false
14499

145-
var nOp = this.chunks[this.chunks.length - 2]
146-
if (nOp === opcodes.OP_0) return false
147-
if (nOp < opcodes.OP_1) return false
148-
if (nOp > opcodes.OP_16) return false
100+
var nOp = script.chunks[script.chunks.length - 2]
101+
if (nOp === ops.OP_0) return false
102+
if (nOp < ops.OP_1) return false
103+
if (nOp > ops.OP_16) return false
149104

150-
var m = mOp - (opcodes.OP_1 - 1)
151-
var n = nOp - (opcodes.OP_1 - 1)
105+
var m = mOp - (ops.OP_1 - 1)
106+
var n = nOp - (ops.OP_1 - 1)
152107
if (n < m) return false
153108

154-
var pubKeys = this.chunks.slice(1, -2)
109+
var pubKeys = script.chunks.slice(1, -2)
155110
if (n < pubKeys.length) return false
156111

157112
return pubKeys.every(isCanonicalPubKey)
158113
}
159114

160-
function isNulldataOutput() {
161-
return this.chunks[0] === opcodes.OP_RETURN
115+
function isNulldataOutput(script) {
116+
return script.chunks[0] === ops.OP_RETURN
117+
}
118+
119+
function classifyOutput(script) {
120+
enforceType(Script, script)
121+
122+
if (isPubKeyHashOutput(script)) {
123+
return 'pubkeyhash'
124+
} else if (isScriptHashOutput(script)) {
125+
return 'scripthash'
126+
} else if (isMultisigOutput(script)) {
127+
return 'multisig'
128+
} else if (isPubKeyOutput(script)) {
129+
return 'pubkey'
130+
} else if (isNulldataOutput(script)) {
131+
return 'nulldata'
132+
}
133+
134+
return 'nonstandard'
135+
}
136+
137+
function classifyInput(script) {
138+
enforceType(Script, script)
139+
140+
if (isPubKeyHashInput(script)) {
141+
return 'pubkeyhash'
142+
} else if (isScriptHashInput(script)) {
143+
return 'scripthash'
144+
} else if (isMultisigInput(script)) {
145+
return 'multisig'
146+
} else if (isPubKeyInput(script)) {
147+
return 'pubkey'
148+
}
149+
150+
return 'nonstandard'
162151
}
163152

164153
// Standard Script Templates
165154
// {pubKey} OP_CHECKSIG
166155
function pubKeyOutput(pubKey) {
167156
return Script.fromChunks([
168157
pubKey.toBuffer(),
169-
opcodes.OP_CHECKSIG
158+
ops.OP_CHECKSIG
170159
])
171160
}
172161

@@ -175,11 +164,11 @@ function pubKeyHashOutput(hash) {
175164
enforceType('Buffer', hash)
176165

177166
return Script.fromChunks([
178-
opcodes.OP_DUP,
179-
opcodes.OP_HASH160,
167+
ops.OP_DUP,
168+
ops.OP_HASH160,
180169
hash,
181-
opcodes.OP_EQUALVERIFY,
182-
opcodes.OP_CHECKSIG
170+
ops.OP_EQUALVERIFY,
171+
ops.OP_CHECKSIG
183172
])
184173
}
185174

@@ -188,9 +177,9 @@ function scriptHashOutput(hash) {
188177
enforceType('Buffer', hash)
189178

190179
return Script.fromChunks([
191-
opcodes.OP_HASH160,
180+
ops.OP_HASH160,
192181
hash,
193-
opcodes.OP_EQUAL
182+
ops.OP_EQUAL
194183
])
195184
}
196185

@@ -206,10 +195,10 @@ function multisigOutput(m, pubKeys) {
206195
var n = pubKeys.length
207196

208197
return Script.fromChunks([].concat(
209-
(opcodes.OP_1 - 1) + m,
198+
(ops.OP_1 - 1) + m,
210199
pubKeyBuffers,
211-
(opcodes.OP_1 - 1) + n,
212-
opcodes.OP_CHECKMULTISIG
200+
(ops.OP_1 - 1) + n,
201+
ops.OP_CHECKMULTISIG
213202
))
214203
}
215204

@@ -238,18 +227,18 @@ function scriptHashInput(scriptSig, scriptPubKey) {
238227
// OP_0 [signatures ...]
239228
function multisigInput(signatures, scriptPubKey) {
240229
if (scriptPubKey) {
241-
assert(isMultisigOutput.call(scriptPubKey))
230+
assert(isMultisigOutput(scriptPubKey))
242231

243232
var mOp = scriptPubKey.chunks[0]
244233
var nOp = scriptPubKey.chunks[scriptPubKey.chunks.length - 2]
245-
var m = mOp - (opcodes.OP_1 - 1)
246-
var n = nOp - (opcodes.OP_1 - 1)
234+
var m = mOp - (ops.OP_1 - 1)
235+
var n = nOp - (ops.OP_1 - 1)
247236

248237
assert(signatures.length >= m, 'Not enough signatures provided')
249238
assert(signatures.length <= n, 'Too many signatures provided')
250239
}
251240

252-
return Script.fromChunks([].concat(opcodes.OP_0, signatures))
241+
return Script.fromChunks([].concat(ops.OP_0, signatures))
253242
}
254243

255244
module.exports = {

test/fixtures/scripts.json

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,33 +48,69 @@
4848
"redeemScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501",
4949
"scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae",
5050
"scriptPubKey": "OP_HASH160 722ff0bc2c3f47b35c20df646c395594da24e90e OP_EQUAL"
51+
},
52+
{
53+
"type": "nulldata",
54+
"scriptPubKey": "OP_RETURN ffffffffffffffffffffffffffffffffffffffff"
5155
}
5256
],
5357
"invalid": {
5458
"classify": [
5559
{
56-
"description": "multisig output : m > n",
57-
"scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG"
60+
"description": "multisig output : OP_CHECKMULTISIG not found",
61+
"scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_HASH160"
62+
},
63+
{
64+
"description": "multisig output : less than 4 chunks",
65+
"scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_HASH160"
66+
},
67+
{
68+
"description": "multisig output : m === 0",
69+
"scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG"
70+
},
71+
{
72+
"description": "multisig output : m < OP_1",
73+
"scriptPubKey": "OP_1NEGATE 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG"
74+
},
75+
{
76+
"description": "multisig output : m > OP_16",
77+
"scriptPubKey": "OP_NOP 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG"
5878
},
5979
{
6080
"description": "multisig output : n === 0",
61-
"scriptPubKey": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG"
81+
"scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG"
6282
},
6383
{
64-
"description": "multisig output : not (m <= len(pubKeys) <= n)",
65-
"scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG"
84+
"description": "multisig output : n < OP_1",
85+
"scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1NEGATE OP_CHECKMULTISIG"
86+
},
87+
{
88+
"description": "multisig output : n > OP_16",
89+
"scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_NOP OP_CHECKMULTISIG"
6690
},
6791
{
68-
"description": "multisig output : m not a small int",
69-
"scriptPubKey": "OP_HASH160 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_1 OP_CHECKMULTISIG"
92+
"description": "multisig output : n < m",
93+
"scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG"
7094
},
7195
{
72-
"description": "multisig output : n not a small int",
73-
"scriptPubKey": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_HASH160 OP_CHECKMULTISIG"
96+
"description": "multisig output : n < len(pubKeys)",
97+
"scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG"
7498
},
7599
{
76100
"description": "multisig output : non-canonical pubKey (bad length)",
77101
"scriptPubKey": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG"
102+
},
103+
{
104+
"description": "pubKeyHash input : extraneous data",
105+
"scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 ffffffff"
106+
},
107+
{
108+
"description": "scriptHash input : redeemScript not data",
109+
"scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED"
110+
},
111+
{
112+
"description": "pubKey input : non-canonical signature",
113+
"scriptSig": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf7593ffffffffffffffff"
78114
}
79115
],
80116
"multisig": [

0 commit comments

Comments
 (0)