|
|
@ -55,14 +55,14 @@ Script.fromBuffer = function(buffer) { |
|
|
|
var opcodenum = br.readUInt8(); |
|
|
|
|
|
|
|
var len, buf; |
|
|
|
if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { |
|
|
|
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) { |
|
|
|
len = opcodenum; |
|
|
|
script.chunks.push({ |
|
|
|
buf: br.read(len), |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA1) { |
|
|
|
len = br.readUInt8(); |
|
|
|
buf = br.read(len); |
|
|
|
script.chunks.push({ |
|
|
@ -70,7 +70,7 @@ Script.fromBuffer = function(buffer) { |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA2) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA2) { |
|
|
|
len = br.readUInt16LE(); |
|
|
|
buf = br.read(len); |
|
|
|
script.chunks.push({ |
|
|
@ -78,7 +78,7 @@ Script.fromBuffer = function(buffer) { |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA4) { |
|
|
|
len = br.readUInt32LE(); |
|
|
|
buf = br.read(len); |
|
|
|
script.chunks.push({ |
|
|
@ -87,7 +87,9 @@ Script.fromBuffer = function(buffer) { |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else { |
|
|
|
script.chunks.push(opcodenum); |
|
|
|
script.chunks.push({ |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -99,22 +101,18 @@ Script.prototype.toBuffer = function() { |
|
|
|
|
|
|
|
for (var i = 0; i < this.chunks.length; i++) { |
|
|
|
var chunk = this.chunks[i]; |
|
|
|
var opcodenum; |
|
|
|
if (typeof chunk === 'number') { |
|
|
|
opcodenum = chunk; |
|
|
|
bw.writeUInt8(opcodenum); |
|
|
|
} else { |
|
|
|
opcodenum = chunk.opcodenum; |
|
|
|
var opcodenum = chunk.opcodenum; |
|
|
|
bw.writeUInt8(chunk.opcodenum); |
|
|
|
if (opcodenum < Opcode.map.OP_PUSHDATA1) { |
|
|
|
if (chunk.buf) { |
|
|
|
if (opcodenum < Opcode.OP_PUSHDATA1) { |
|
|
|
bw.write(chunk.buf); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA1) { |
|
|
|
bw.writeUInt8(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA2) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA2) { |
|
|
|
bw.writeUInt16LE(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA4) { |
|
|
|
bw.writeUInt32LE(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} |
|
|
@ -140,7 +138,7 @@ Script.fromString = function(str) { |
|
|
|
|
|
|
|
if (typeof opcodenum === 'undefined') { |
|
|
|
opcodenum = parseInt(token); |
|
|
|
if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { |
|
|
|
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) { |
|
|
|
script.chunks.push({ |
|
|
|
buf: new Buffer(tokens[i + 1].slice(2), 'hex'), |
|
|
|
len: opcodenum, |
|
|
@ -150,9 +148,9 @@ Script.fromString = function(str) { |
|
|
|
} else { |
|
|
|
throw new Error('Invalid script: ' + JSON.stringify(str)); |
|
|
|
} |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1 || |
|
|
|
opcodenum === Opcode.map.OP_PUSHDATA2 || |
|
|
|
opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
} else if (opcodenum === Opcode.OP_PUSHDATA1 || |
|
|
|
opcodenum === Opcode.OP_PUSHDATA2 || |
|
|
|
opcodenum === Opcode.OP_PUSHDATA4) { |
|
|
|
if (tokens[i + 2].slice(0, 2) !== '0x') { |
|
|
|
throw new Error('Pushdata data must start with 0x'); |
|
|
|
} |
|
|
@ -163,7 +161,9 @@ Script.fromString = function(str) { |
|
|
|
}); |
|
|
|
i = i + 3; |
|
|
|
} else { |
|
|
|
script.chunks.push(opcodenum); |
|
|
|
script.chunks.push({ |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
i = i + 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -175,23 +175,25 @@ Script.prototype.toString = function() { |
|
|
|
|
|
|
|
for (var i = 0; i < this.chunks.length; i++) { |
|
|
|
var chunk = this.chunks[i]; |
|
|
|
var opcodenum; |
|
|
|
if (typeof chunk === 'number') { |
|
|
|
opcodenum = chunk; |
|
|
|
str = str + Opcode(opcodenum).toString() + ' '; |
|
|
|
var opcodenum = chunk.opcodenum; |
|
|
|
if (!chunk.buf) { |
|
|
|
if (typeof Opcode.reverseMap[opcodenum] !== 'undefined') { |
|
|
|
str = str + ' ' + Opcode(opcodenum).toString(); |
|
|
|
} else { |
|
|
|
opcodenum = chunk.opcodenum; |
|
|
|
if (opcodenum === Opcode.map.OP_PUSHDATA1 || |
|
|
|
opcodenum === Opcode.map.OP_PUSHDATA2 || |
|
|
|
opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
str = str + Opcode(opcodenum).toString() + ' '; |
|
|
|
str = str + ' ' + '0x' + opcodenum.toString(16); |
|
|
|
} |
|
|
|
str = str + chunk.len + ' '; |
|
|
|
str = str + '0x' + chunk.buf.toString('hex') + ' '; |
|
|
|
} else { |
|
|
|
if (opcodenum === Opcode.OP_PUSHDATA1 || |
|
|
|
opcodenum === Opcode.OP_PUSHDATA2 || |
|
|
|
opcodenum === Opcode.OP_PUSHDATA4) { |
|
|
|
str = str + ' ' + Opcode(opcodenum).toString(); |
|
|
|
} |
|
|
|
str = str + ' ' + chunk.len; |
|
|
|
str = str + ' ' + '0x' + chunk.buf.toString('hex'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return str.substr(0, str.length - 1); |
|
|
|
return str.substr(1); |
|
|
|
}; |
|
|
|
|
|
|
|
// script classification methods
|
|
|
@ -200,23 +202,23 @@ Script.prototype.toString = function() { |
|
|
|
* @returns true if this is a pay to pubkey hash output script |
|
|
|
*/ |
|
|
|
Script.prototype.isPublicKeyHashOut = function() { |
|
|
|
return this.chunks[0] === Opcode('OP_DUP').toNumber() && |
|
|
|
this.chunks[1] === Opcode('OP_HASH160').toNumber() && |
|
|
|
return !!(this.chunks.length === 5 && |
|
|
|
this.chunks[0].opcodenum === Opcode.OP_DUP && |
|
|
|
this.chunks[1].opcodenum === Opcode.OP_HASH160 && |
|
|
|
this.chunks[2].buf && |
|
|
|
this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() && |
|
|
|
this.chunks[4] === Opcode('OP_CHECKSIG').toNumber(); |
|
|
|
this.chunks[3].opcodenum === Opcode.OP_EQUALVERIFY && |
|
|
|
this.chunks[4].opcodenum === Opcode.OP_CHECKSIG); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* @returns true if this is a pay to public key hash input script |
|
|
|
*/ |
|
|
|
Script.prototype.isPublicKeyHashIn = function() { |
|
|
|
return !!(this.chunks.length === 2 && |
|
|
|
return this.chunks.length === 2 && |
|
|
|
this.chunks[0].buf && |
|
|
|
this.chunks[0].buf.length >= 0x47 && |
|
|
|
this.chunks[0].buf.length <= 0x49 && |
|
|
|
this.chunks[1].buf && |
|
|
|
PublicKey.isValid(this.chunks[1].buf)); |
|
|
|
PublicKey.isValid(this.chunks[1].buf); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -226,7 +228,7 @@ Script.prototype.isPublicKeyOut = function() { |
|
|
|
return this.chunks.length === 2 && |
|
|
|
bufferUtil.isBuffer(this.chunks[0].buf) && |
|
|
|
PublicKey.isValid(this.chunks[0].buf) && |
|
|
|
this.chunks[1] === Opcode('OP_CHECKSIG').toNumber(); |
|
|
|
this.chunks[1].opcodenum === Opcode.OP_CHECKSIG; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -244,10 +246,10 @@ Script.prototype.isPublicKeyIn = function() { |
|
|
|
*/ |
|
|
|
Script.prototype.isScriptHashOut = function() { |
|
|
|
return this.chunks.length === 3 && |
|
|
|
this.chunks[0] === Opcode('OP_HASH160').toNumber() && |
|
|
|
this.chunks[0].opcodenum === Opcode.OP_HASH160 && |
|
|
|
this.chunks[1].buf && |
|
|
|
this.chunks[1].buf.length === 20 && |
|
|
|
this.chunks[2] === Opcode('OP_EQUAL').toNumber(); |
|
|
|
this.chunks[2].opcodenum === Opcode.OP_EQUAL; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -276,20 +278,21 @@ Script.prototype.isScriptHashIn = function() { |
|
|
|
*/ |
|
|
|
Script.prototype.isMultisigOut = function() { |
|
|
|
return (this.chunks.length > 3 && |
|
|
|
Opcode.isSmallIntOp(this.chunks[0]) && |
|
|
|
Opcode.isSmallIntOp(this.chunks[0].opcodenum) && |
|
|
|
this.chunks.slice(1, this.chunks.length - 2).every(function(obj) { |
|
|
|
return obj.buf && bufferUtil.isBuffer(obj.buf); |
|
|
|
}) && |
|
|
|
Opcode.isSmallIntOp(this.chunks[this.chunks.length - 2]) && |
|
|
|
this.chunks[this.chunks.length - 1] === Opcode.map.OP_CHECKMULTISIG); |
|
|
|
Opcode.isSmallIntOp(this.chunks[this.chunks.length - 2].opcodenum) && |
|
|
|
this.chunks[this.chunks.length - 1].opcodenum === Opcode.OP_CHECKMULTISIG); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* @returns true if this is a mutlsig input script |
|
|
|
* @returns true if this is a multisig input script |
|
|
|
*/ |
|
|
|
Script.prototype.isMultisigIn = function() { |
|
|
|
return this.chunks[0] === 0 && |
|
|
|
return this.chunks.length >= 2 && |
|
|
|
this.chunks[0].opcodenum === 0 && |
|
|
|
this.chunks.slice(1, this.chunks.length).every(function(obj) { |
|
|
|
return obj.buf && |
|
|
|
bufferUtil.isBuffer(obj.buf) && |
|
|
@ -301,12 +304,13 @@ Script.prototype.isMultisigIn = function() { |
|
|
|
* @returns true if this is an OP_RETURN data script |
|
|
|
*/ |
|
|
|
Script.prototype.isDataOut = function() { |
|
|
|
return (this.chunks[0] === Opcode('OP_RETURN').toNumber() && |
|
|
|
return this.chunks.length >= 1 && |
|
|
|
this.chunks[0].opcodenum === Opcode.OP_RETURN && |
|
|
|
(this.chunks.length === 1 || |
|
|
|
(this.chunks.length === 2 && |
|
|
|
this.chunks[1].buf && |
|
|
|
this.chunks[1].buf.length <= 40 && |
|
|
|
this.chunks[1].length === this.chunks.len))); |
|
|
|
this.chunks[1].length === this.chunks.len)); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -315,8 +319,7 @@ Script.prototype.isDataOut = function() { |
|
|
|
*/ |
|
|
|
Script.prototype.isPushOnly = function() { |
|
|
|
return _.every(this.chunks, function(chunk) { |
|
|
|
var opcodenum = chunk.opcodenum; |
|
|
|
return !_.isUndefined(opcodenum) || chunk <= Opcode.map.OP_16; |
|
|
|
return chunk.opcodenum <= Opcode.OP_16; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
@ -424,7 +427,9 @@ Script.prototype._addOpcode = function(opcode, prepend) { |
|
|
|
} else { |
|
|
|
op = Opcode(opcode).toNumber(); |
|
|
|
} |
|
|
|
this._insertAtPosition(op, prepend); |
|
|
|
this._insertAtPosition({ |
|
|
|
opcodenum: op |
|
|
|
}, prepend); |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
@ -433,14 +438,14 @@ Script.prototype._addBuffer = function(buf, prepend) { |
|
|
|
var len = buf.length; |
|
|
|
if (len === 0) { |
|
|
|
return; |
|
|
|
} else if (len > 0 && len < Opcode.map.OP_PUSHDATA1) { |
|
|
|
} else if (len > 0 && len < Opcode.OP_PUSHDATA1) { |
|
|
|
opcodenum = len; |
|
|
|
} else if (len < Math.pow(2, 8)) { |
|
|
|
opcodenum = Opcode.map.OP_PUSHDATA1; |
|
|
|
opcodenum = Opcode.OP_PUSHDATA1; |
|
|
|
} else if (len < Math.pow(2, 16)) { |
|
|
|
opcodenum = Opcode.map.OP_PUSHDATA2; |
|
|
|
opcodenum = Opcode.OP_PUSHDATA2; |
|
|
|
} else if (len < Math.pow(2, 32)) { |
|
|
|
opcodenum = Opcode.map.OP_PUSHDATA4; |
|
|
|
opcodenum = Opcode.OP_PUSHDATA4; |
|
|
|
} else { |
|
|
|
throw new Error('You can\'t push that much data'); |
|
|
|
} |
|
|
@ -455,7 +460,7 @@ Script.prototype._addBuffer = function(buf, prepend) { |
|
|
|
Script.prototype.removeCodeseparators = function() { |
|
|
|
var chunks = []; |
|
|
|
for (var i = 0; i < this.chunks.length; i++) { |
|
|
|
if (this.chunks[i] !== Opcode.OP_CODESEPARATOR) { |
|
|
|
if (this.chunks[i].opcodenum !== Opcode.OP_CODESEPARATOR) { |
|
|
|
chunks.push(this.chunks[i]); |
|
|
|
} |
|
|
|
} |
|
|
@ -489,7 +494,7 @@ Script.buildMultisigOut = function(pubkeys, m, opts) { |
|
|
|
s.add(pubkey.toBuffer()); |
|
|
|
} |
|
|
|
s.add(Opcode.smallInt(pubkeys.length)); |
|
|
|
s.add(Opcode('OP_CHECKMULTISIG')); |
|
|
|
s.add(Opcode.OP_CHECKMULTISIG); |
|
|
|
return s; |
|
|
|
}; |
|
|
|
|
|
|
@ -505,11 +510,11 @@ Script.buildPublicKeyHashOut = function(to) { |
|
|
|
to = new Address(to); |
|
|
|
} |
|
|
|
var s = new Script(); |
|
|
|
s.add(Opcode('OP_DUP')) |
|
|
|
.add(Opcode('OP_HASH160')) |
|
|
|
s.add(Opcode.OP_DUP) |
|
|
|
.add(Opcode.OP_HASH160) |
|
|
|
.add(to.hashBuffer) |
|
|
|
.add(Opcode('OP_EQUALVERIFY')) |
|
|
|
.add(Opcode('OP_CHECKSIG')); |
|
|
|
.add(Opcode.OP_EQUALVERIFY) |
|
|
|
.add(Opcode.OP_CHECKSIG); |
|
|
|
return s; |
|
|
|
}; |
|
|
|
|
|
|
@ -520,7 +525,7 @@ Script.buildPublicKeyHashOut = function(to) { |
|
|
|
Script.buildPublicKeyOut = function(pubkey) { |
|
|
|
var s = new Script(); |
|
|
|
s.add(pubkey.toBuffer()) |
|
|
|
.add(Opcode('OP_CHECKSIG')); |
|
|
|
.add(Opcode.OP_CHECKSIG); |
|
|
|
return s; |
|
|
|
}; |
|
|
|
|
|
|
@ -533,7 +538,7 @@ Script.buildDataOut = function(data) { |
|
|
|
data = new Buffer(data); |
|
|
|
} |
|
|
|
var s = new Script(); |
|
|
|
s.add(Opcode('OP_RETURN')) |
|
|
|
s.add(Opcode.OP_RETURN) |
|
|
|
.add(data); |
|
|
|
return s; |
|
|
|
}; |
|
|
@ -544,9 +549,9 @@ Script.buildDataOut = function(data) { |
|
|
|
*/ |
|
|
|
Script.buildScriptHashOut = function(script) { |
|
|
|
var s = new Script(); |
|
|
|
s.add(Opcode('OP_HASH160')) |
|
|
|
s.add(Opcode.OP_HASH160) |
|
|
|
.add(Hash.sha256ripemd160(script.toBuffer())) |
|
|
|
.add(Opcode('OP_EQUAL')); |
|
|
|
.add(Opcode.OP_EQUAL); |
|
|
|
return s; |
|
|
|
}; |
|
|
|
|
|
|
|