|
|
@ -4,22 +4,19 @@ var BufferReader = require('./encoding/bufferreader'); |
|
|
|
var BufferWriter = require('./encoding/bufferwriter'); |
|
|
|
var Opcode = require('./opcode'); |
|
|
|
|
|
|
|
var Script = function Script(buf) { |
|
|
|
if (!(this instanceof Script)) |
|
|
|
return new Script(buf); |
|
|
|
|
|
|
|
var Script = function Script(from) { |
|
|
|
if (!(this instanceof Script)) { |
|
|
|
return new Script(from); |
|
|
|
} |
|
|
|
|
|
|
|
this.chunks = []; |
|
|
|
|
|
|
|
if (Buffer.isBuffer(buf)) { |
|
|
|
this.fromBuffer(buf); |
|
|
|
} |
|
|
|
else if (typeof buf === 'string') { |
|
|
|
var str = buf; |
|
|
|
this.fromString(str); |
|
|
|
} |
|
|
|
else if (typeof buf !== 'undefined') { |
|
|
|
var obj = buf; |
|
|
|
this.set(obj); |
|
|
|
if (Buffer.isBuffer(from)) { |
|
|
|
this.fromBuffer(from); |
|
|
|
} else if (typeof from === 'string') { |
|
|
|
this.fromString(from); |
|
|
|
} else if (typeof from !== 'undefined') { |
|
|
|
this.set(from); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -28,33 +25,26 @@ Script.prototype.set = function(obj) { |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.fromJSON = function(json) { |
|
|
|
return this.fromString(json); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.toJSON = function() { |
|
|
|
return this.toString(); |
|
|
|
}; |
|
|
|
Script.fromBuffer = function(buffer) { |
|
|
|
var script = new Script(); |
|
|
|
script.chunks = []; |
|
|
|
|
|
|
|
Script.prototype.fromBuffer = function(buf) { |
|
|
|
this.chunks = []; |
|
|
|
|
|
|
|
var br = new BufferReader(buf); |
|
|
|
var br = new BufferReader(buffer); |
|
|
|
while (!br.eof()) { |
|
|
|
var opcodenum = br.readUInt8(); |
|
|
|
|
|
|
|
var len, buf; |
|
|
|
if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { |
|
|
|
len = opcodenum; |
|
|
|
this.chunks.push({ |
|
|
|
script.chunks.push({ |
|
|
|
buf: br.read(len), |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1) { |
|
|
|
len = br.readUInt8(); |
|
|
|
var buf = br.read(len); |
|
|
|
this.chunks.push({ |
|
|
|
buf = br.read(len); |
|
|
|
script.chunks.push({ |
|
|
|
buf: buf, |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
@ -62,7 +52,7 @@ Script.prototype.fromBuffer = function(buf) { |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA2) { |
|
|
|
len = br.readUInt16LE(); |
|
|
|
buf = br.read(len); |
|
|
|
this.chunks.push({ |
|
|
|
script.chunks.push({ |
|
|
|
buf: buf, |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
@ -70,17 +60,17 @@ Script.prototype.fromBuffer = function(buf) { |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
len = br.readUInt32LE(); |
|
|
|
buf = br.read(len); |
|
|
|
this.chunks.push({ |
|
|
|
script.chunks.push({ |
|
|
|
buf: buf, |
|
|
|
len: len, |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
} else { |
|
|
|
this.chunks.push(opcodenum); |
|
|
|
script.chunks.push(opcodenum); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return this; |
|
|
|
return script; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.toBuffer = function() { |
|
|
@ -88,24 +78,22 @@ Script.prototype.toBuffer = function() { |
|
|
|
|
|
|
|
for (var i = 0; i < this.chunks.length; i++) { |
|
|
|
var chunk = this.chunks[i]; |
|
|
|
var opcodenum; |
|
|
|
if (typeof chunk === 'number') { |
|
|
|
var opcodenum = chunk; |
|
|
|
opcodenum = chunk; |
|
|
|
bw.writeUInt8(opcodenum); |
|
|
|
} else { |
|
|
|
var opcodenum = chunk.opcodenum; |
|
|
|
opcodenum = chunk.opcodenum; |
|
|
|
bw.writeUInt8(chunk.opcodenum); |
|
|
|
if (opcodenum < Opcode.map.OP_PUSHDATA1) { |
|
|
|
bw.write(chunk.buf); |
|
|
|
} |
|
|
|
else if (opcodenum === Opcode.map.OP_PUSHDATA1) { |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1) { |
|
|
|
bw.writeUInt8(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} |
|
|
|
else if (opcodenum === Opcode.map.OP_PUSHDATA2) { |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA2) { |
|
|
|
bw.writeUInt16LE(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} |
|
|
|
else if (opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
bw.writeUInt32LE(chunk.len); |
|
|
|
bw.write(chunk.buf); |
|
|
|
} |
|
|
@ -134,13 +122,13 @@ Script.prototype.fromString = function(str) { |
|
|
|
opcodenum: opcodenum |
|
|
|
}); |
|
|
|
i = i + 2; |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
throw new Error('Invalid script'); |
|
|
|
} |
|
|
|
} else if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4) { |
|
|
|
if (tokens[i + 2].slice(0, 2) != '0x') |
|
|
|
if (tokens[i + 2].slice(0, 2) !== '0x') { |
|
|
|
throw new Error('Pushdata data must start with 0x'); |
|
|
|
} |
|
|
|
this.chunks.push({ |
|
|
|
buf: new Buffer(tokens[i + 2].slice(2), 'hex'), |
|
|
|
len: parseInt(tokens[i + 1]), |
|
|
@ -156,19 +144,23 @@ Script.prototype.fromString = function(str) { |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.toString = function() { |
|
|
|
var str = ""; |
|
|
|
var str = ''; |
|
|
|
|
|
|
|
for (var i = 0; i < this.chunks.length; i++) { |
|
|
|
var chunk = this.chunks[i]; |
|
|
|
var opcodenum; |
|
|
|
if (typeof chunk === 'number') { |
|
|
|
var opcodenum = chunk; |
|
|
|
str = str + Opcode(opcodenum).toString() + " "; |
|
|
|
opcodenum = chunk; |
|
|
|
str = str + Opcode(opcodenum).toString() + ' '; |
|
|
|
} else { |
|
|
|
var 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 + chunk.len + " " ; |
|
|
|
str = str + "0x" + chunk.buf.toString('hex') + " "; |
|
|
|
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 + chunk.len + ' '; |
|
|
|
str = str + '0x' + chunk.buf.toString('hex') + ' '; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -176,14 +168,9 @@ Script.prototype.toString = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isOpReturn = function() { |
|
|
|
if (this.chunks[0] === Opcode('OP_RETURN').toNumber() |
|
|
|
&& |
|
|
|
(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))) { |
|
|
|
if (this.chunks[0] === Opcode('OP_RETURN').toNumber() && |
|
|
|
(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))) { |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
@ -191,11 +178,7 @@ Script.prototype.isOpReturn = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isPublicKeyHashOut = function() { |
|
|
|
if (this.chunks[0] === Opcode('OP_DUP').toNumber() |
|
|
|
&& this.chunks[1] === Opcode('OP_HASH160').toNumber() |
|
|
|
&& this.chunks[2].buf |
|
|
|
&& this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() |
|
|
|
&& this.chunks[4] === Opcode('OP_CHECKSIG').toNumber()) { |
|
|
|
if (this.chunks[0] === Opcode('OP_DUP').toNumber() && this.chunks[1] === Opcode('OP_HASH160').toNumber() && this.chunks[2].buf && this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() && this.chunks[4] === Opcode('OP_CHECKSIG').toNumber()) { |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
@ -203,9 +186,7 @@ Script.prototype.isPublicKeyHashOut = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isPublicKeyHashIn = function() { |
|
|
|
if (this.chunks.length === 2 |
|
|
|
&& this.chunks[0].buf |
|
|
|
&& this.chunks[1].buf) { |
|
|
|
if (this.chunks.length === 2 && this.chunks[0].buf && this.chunks[1].buf) { |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
@ -213,11 +194,7 @@ Script.prototype.isPublicKeyHashIn = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isScriptHashOut = function() { |
|
|
|
if (this.chunks.length === 3 |
|
|
|
&& this.chunks[0] === Opcode('OP_HASH160').toNumber() |
|
|
|
&& this.chunks[1].buf |
|
|
|
&& this.chunks[1].buf.length === 20 |
|
|
|
&& this.chunks[2] === Opcode('OP_EQUAL').toNumber()) { |
|
|
|
if (this.chunks.length === 3 && this.chunks[0] === Opcode('OP_HASH160').toNumber() && this.chunks[1].buf && this.chunks[1].buf.length === 20 && this.chunks[2] === Opcode('OP_EQUAL').toNumber()) { |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
@ -236,29 +213,35 @@ Script.prototype.isScriptHashIn = function() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.write = function(obj) { |
|
|
|
if (typeof obj === 'string') |
|
|
|
this.writeOp(obj); |
|
|
|
else if (typeof obj === 'number') |
|
|
|
this.writeOp(obj); |
|
|
|
else if (Buffer.isBuffer(obj)) |
|
|
|
this.writeBuffer(obj); |
|
|
|
else if (typeof obj === 'object') |
|
|
|
Script.prototype.add = function(obj) { |
|
|
|
if (typeof obj === 'string') { |
|
|
|
this._addOpcode(obj); |
|
|
|
} else if (typeof obj === 'number') { |
|
|
|
this._addOpcode(obj); |
|
|
|
} else if (obj.constructor && obj.constructor.name && obj.constructor.name === 'Opcode') { |
|
|
|
this._addOpcode(obj); |
|
|
|
} else if (Buffer.isBuffer(obj)) { |
|
|
|
this._addBuffer(obj); |
|
|
|
} else if (typeof obj === 'object') { |
|
|
|
this.chunks.push(obj); |
|
|
|
else |
|
|
|
} else { |
|
|
|
throw new Error('Invalid script chunk'); |
|
|
|
} |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.writeOp = function(str) { |
|
|
|
if (typeof str === 'number') |
|
|
|
this.chunks.push(str); |
|
|
|
else |
|
|
|
this.chunks.push(Opcode(str).toNumber()); |
|
|
|
Script.prototype._addOpcode = function(opcode) { |
|
|
|
if (typeof opcode === 'number') { |
|
|
|
this.chunks.push(opcode); |
|
|
|
} else if (opcode.constructor && opcode.constructor.name && opcode.constructor.name === 'Opcode') { |
|
|
|
this.chunks.push(opcode.toNumber()); |
|
|
|
} else { |
|
|
|
this.chunks.push(Opcode(opcode).toNumber()); |
|
|
|
} |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.writeBuffer = function(buf) { |
|
|
|
Script.prototype._addBuffer = function(buf) { |
|
|
|
var opcodenum; |
|
|
|
var len = buf.length; |
|
|
|
if (buf.length > 0 && buf.length < Opcode.map.OP_PUSHDATA1) { |
|
|
@ -270,7 +253,7 @@ Script.prototype.writeBuffer = function(buf) { |
|
|
|
} else if (buf.length < Math.pow(2, 32)) { |
|
|
|
opcodenum = Opcode.map.OP_PUSHDATA4; |
|
|
|
} else { |
|
|
|
throw new Error("You can't push that much data"); |
|
|
|
throw new Error('You can\'t push that much data'); |
|
|
|
} |
|
|
|
this.chunks.push({ |
|
|
|
buf: buf, |
|
|
|