|
|
@ -31,7 +31,7 @@ function spec(b) { |
|
|
|
]; |
|
|
|
|
|
|
|
function Script(buffer) { |
|
|
|
if(buffer) { |
|
|
|
if (buffer) { |
|
|
|
this.buffer = buffer; |
|
|
|
} else { |
|
|
|
this.buffer = util.EMPTY_BUFFER; |
|
|
@ -41,13 +41,13 @@ function spec(b) { |
|
|
|
}; |
|
|
|
this.class = Script; |
|
|
|
|
|
|
|
Script.TX_UNKNOWN=TX_UNKNOWN; |
|
|
|
Script.TX_PUBKEY=TX_PUBKEY; |
|
|
|
Script.TX_PUBKEYHASH=TX_PUBKEYHASH; |
|
|
|
Script.TX_MULTISIG=TX_MULTISIG; |
|
|
|
Script.TX_SCRIPTHASH=TX_SCRIPTHASH; |
|
|
|
Script.TX_UNKNOWN = TX_UNKNOWN; |
|
|
|
Script.TX_PUBKEY = TX_PUBKEY; |
|
|
|
Script.TX_PUBKEYHASH = TX_PUBKEYHASH; |
|
|
|
Script.TX_MULTISIG = TX_MULTISIG; |
|
|
|
Script.TX_SCRIPTHASH = TX_SCRIPTHASH; |
|
|
|
|
|
|
|
Script.prototype.parse = function () { |
|
|
|
Script.prototype.parse = function() { |
|
|
|
this.chunks = []; |
|
|
|
|
|
|
|
var parser = new Parser(this.buffer); |
|
|
@ -73,8 +73,7 @@ function spec(b) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isPushOnly = function () |
|
|
|
{ |
|
|
|
Script.prototype.isPushOnly = function() { |
|
|
|
for (var i = 0; i < this.chunks.length; i++) |
|
|
|
if (!Buffer.isBuffer(this.chunks[i])) |
|
|
|
return false; |
|
|
@ -82,8 +81,7 @@ function spec(b) { |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isP2SH = function () |
|
|
|
{ |
|
|
|
Script.prototype.isP2SH = function() { |
|
|
|
return (this.chunks.length == 3 && |
|
|
|
this.chunks[0] == OP_HASH160 && |
|
|
|
Buffer.isBuffer(this.chunks[1]) && |
|
|
@ -91,46 +89,41 @@ function spec(b) { |
|
|
|
this.chunks[2] == OP_EQUAL); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isPubkey = function () |
|
|
|
{ |
|
|
|
Script.prototype.isPubkey = function() { |
|
|
|
return (this.chunks.length == 2 && |
|
|
|
Buffer.isBuffer(this.chunks[0]) && |
|
|
|
this.chunks[1] == OP_CHECKSIG); |
|
|
|
Buffer.isBuffer(this.chunks[0]) && |
|
|
|
this.chunks[1] == OP_CHECKSIG); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isPubkeyHash = function () |
|
|
|
{ |
|
|
|
Script.prototype.isPubkeyHash = function() { |
|
|
|
return (this.chunks.length == 5 && |
|
|
|
this.chunks[0] == OP_DUP && |
|
|
|
this.chunks[1] == OP_HASH160 && |
|
|
|
this.chunks[0] == OP_DUP && |
|
|
|
this.chunks[1] == OP_HASH160 && |
|
|
|
Buffer.isBuffer(this.chunks[2]) && |
|
|
|
this.chunks[2].length == 20 && |
|
|
|
this.chunks[3] == OP_EQUALVERIFY && |
|
|
|
this.chunks[4] == OP_CHECKSIG); |
|
|
|
this.chunks[3] == OP_EQUALVERIFY && |
|
|
|
this.chunks[4] == OP_CHECKSIG); |
|
|
|
}; |
|
|
|
|
|
|
|
function isSmallIntOp(opcode) |
|
|
|
{ |
|
|
|
function isSmallIntOp(opcode) { |
|
|
|
return ((opcode == OP_0) || |
|
|
|
((opcode >= OP_1) && (opcode <= OP_16))); |
|
|
|
((opcode >= OP_1) && (opcode <= OP_16))); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.isMultiSig = function () |
|
|
|
{ |
|
|
|
Script.prototype.isMultiSig = function() { |
|
|
|
return (this.chunks.length > 3 && |
|
|
|
isSmallIntOp(this.chunks[0]) && |
|
|
|
isSmallIntOp(this.chunks[this.chunks.length-2]) && |
|
|
|
this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); |
|
|
|
isSmallIntOp(this.chunks[0]) && |
|
|
|
isSmallIntOp(this.chunks[this.chunks.length - 2]) && |
|
|
|
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.finishedMultiSig = function() |
|
|
|
{ |
|
|
|
Script.prototype.finishedMultiSig = function() { |
|
|
|
var nsigs = 0; |
|
|
|
for (var i = 0; i < this.chunks.length-1; i++) |
|
|
|
for (var i = 0; i < this.chunks.length - 1; i++) |
|
|
|
if (this.chunks[i] !== 0) |
|
|
|
nsigs++; |
|
|
|
|
|
|
|
var serializedScript = this.chunks[this.chunks.length-1]; |
|
|
|
var serializedScript = this.chunks[this.chunks.length - 1]; |
|
|
|
var script = new Script(serializedScript); |
|
|
|
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
|
|
|
|
|
|
|
@ -140,11 +133,9 @@ function spec(b) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
Script.prototype.removePlaceHolders = function() |
|
|
|
{ |
|
|
|
Script.prototype.removePlaceHolders = function() { |
|
|
|
var chunks = []; |
|
|
|
for (var i in this.chunks) |
|
|
|
{ |
|
|
|
for (var i in this.chunks) { |
|
|
|
if (this.chunks.hasOwnProperty(i)) { |
|
|
|
var chunk = this.chunks[i]; |
|
|
|
if (chunk != 0) |
|
|
@ -156,8 +147,7 @@ function spec(b) { |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
Script.prototype.prependOp0 = function() |
|
|
|
{ |
|
|
|
Script.prototype.prependOp0 = function() { |
|
|
|
var chunks = [0]; |
|
|
|
for (i in this.chunks) { |
|
|
|
if (this.chunks.hasOwnProperty(i)) { |
|
|
@ -170,62 +160,61 @@ function spec(b) { |
|
|
|
} |
|
|
|
|
|
|
|
// is this a script form we know?
|
|
|
|
Script.prototype.classify = function () |
|
|
|
{ |
|
|
|
Script.prototype.classify = function() { |
|
|
|
if (this.isPubkeyHash()) |
|
|
|
return TX_PUBKEYHASH; |
|
|
|
if (this.isP2SH()) |
|
|
|
return TX_SCRIPTHASH; |
|
|
|
if (this.isMultiSig()) |
|
|
|
return TX_MULTISIG; |
|
|
|
if (this.isPubkey()) |
|
|
|
return TX_PUBKEY; |
|
|
|
return TX_UNKNOWN; |
|
|
|
return TX_PUBKEYHASH; |
|
|
|
if (this.isP2SH()) |
|
|
|
return TX_SCRIPTHASH; |
|
|
|
if (this.isMultiSig()) |
|
|
|
return TX_MULTISIG; |
|
|
|
if (this.isPubkey()) |
|
|
|
return TX_PUBKEY; |
|
|
|
return TX_UNKNOWN; |
|
|
|
}; |
|
|
|
|
|
|
|
// extract useful data items from known scripts
|
|
|
|
Script.prototype.capture = function () |
|
|
|
{ |
|
|
|
Script.prototype.capture = function() { |
|
|
|
var txType = this.classify(); |
|
|
|
var res = []; |
|
|
|
switch (txType) { |
|
|
|
case TX_PUBKEY: |
|
|
|
res.push(this.chunks[0]); |
|
|
|
break; |
|
|
|
case TX_PUBKEYHASH: |
|
|
|
res.push(this.chunks[2]); |
|
|
|
break; |
|
|
|
case TX_MULTISIG: |
|
|
|
for (var i = 1; i < (this.chunks.length - 2); i++) |
|
|
|
res.push(this.chunks[i]); |
|
|
|
break; |
|
|
|
case TX_SCRIPTHASH: |
|
|
|
res.push(this.chunks[1]); |
|
|
|
break; |
|
|
|
|
|
|
|
case TX_UNKNOWN: |
|
|
|
default: |
|
|
|
// do nothing
|
|
|
|
break; |
|
|
|
} |
|
|
|
var res = []; |
|
|
|
switch (txType) { |
|
|
|
case TX_PUBKEY: |
|
|
|
res.push(this.chunks[0]); |
|
|
|
break; |
|
|
|
case TX_PUBKEYHASH: |
|
|
|
res.push(this.chunks[2]); |
|
|
|
break; |
|
|
|
case TX_MULTISIG: |
|
|
|
for (var i = 1; i < (this.chunks.length - 2); i++) |
|
|
|
res.push(this.chunks[i]); |
|
|
|
break; |
|
|
|
case TX_SCRIPTHASH: |
|
|
|
res.push(this.chunks[1]); |
|
|
|
break; |
|
|
|
|
|
|
|
return res; |
|
|
|
case TX_UNKNOWN: |
|
|
|
default: |
|
|
|
// do nothing
|
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return res; |
|
|
|
}; |
|
|
|
|
|
|
|
// return first extracted data item from script
|
|
|
|
Script.prototype.captureOne = function () |
|
|
|
{ |
|
|
|
Script.prototype.captureOne = function() { |
|
|
|
var arr = this.capture(); |
|
|
|
return arr[0]; |
|
|
|
return arr[0]; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.getOutType = function () |
|
|
|
{ |
|
|
|
Script.prototype.getOutType = function() { |
|
|
|
var txType = this.classify(); |
|
|
|
switch (txType) { |
|
|
|
case TX_PUBKEY: return 'Pubkey'; |
|
|
|
case TX_PUBKEYHASH: return 'Address'; |
|
|
|
default: return 'Strange'; |
|
|
|
case TX_PUBKEY: |
|
|
|
return 'Pubkey'; |
|
|
|
case TX_PUBKEYHASH: |
|
|
|
return 'Address'; |
|
|
|
default: |
|
|
|
return 'Strange'; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -233,84 +222,79 @@ function spec(b) { |
|
|
|
return TX_TYPES[this.classify()]; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.simpleOutHash = function () |
|
|
|
{ |
|
|
|
Script.prototype.simpleOutHash = function() { |
|
|
|
switch (this.getOutType()) { |
|
|
|
case 'Address': |
|
|
|
return this.chunks[2]; |
|
|
|
case 'Pubkey': |
|
|
|
return util.sha256ripe160(this.chunks[0]); |
|
|
|
default: |
|
|
|
log.debug("Encountered non-standard scriptPubKey"); |
|
|
|
log.debug("Strange script was: " + this.toString()); |
|
|
|
return null; |
|
|
|
case 'Address': |
|
|
|
return this.chunks[2]; |
|
|
|
case 'Pubkey': |
|
|
|
return util.sha256ripe160(this.chunks[0]); |
|
|
|
default: |
|
|
|
log.debug("Encountered non-standard scriptPubKey"); |
|
|
|
log.debug("Strange script was: " + this.toString()); |
|
|
|
return null; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.getInType = function () |
|
|
|
{ |
|
|
|
Script.prototype.getInType = function() { |
|
|
|
if (this.chunks.length == 1) { |
|
|
|
// Direct IP to IP transactions only have the public key in their scriptSig.
|
|
|
|
return 'Pubkey'; |
|
|
|
} else if (this.chunks.length == 2 && |
|
|
|
Buffer.isBuffer(this.chunks[0]) && |
|
|
|
Buffer.isBuffer(this.chunks[1])) { |
|
|
|
Buffer.isBuffer(this.chunks[0]) && |
|
|
|
Buffer.isBuffer(this.chunks[1])) { |
|
|
|
return 'Address'; |
|
|
|
} else { |
|
|
|
return 'Strange'; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.simpleInPubKey = function () |
|
|
|
{ |
|
|
|
Script.prototype.simpleInPubKey = function() { |
|
|
|
switch (this.getInType()) { |
|
|
|
case 'Address': |
|
|
|
return this.chunks[1]; |
|
|
|
case 'Pubkey': |
|
|
|
return null; |
|
|
|
default: |
|
|
|
log.debug("Encountered non-standard scriptSig"); |
|
|
|
log.debug("Strange script was: " + this.toString()); |
|
|
|
return null; |
|
|
|
case 'Address': |
|
|
|
return this.chunks[1]; |
|
|
|
case 'Pubkey': |
|
|
|
return null; |
|
|
|
default: |
|
|
|
log.debug("Encountered non-standard scriptSig"); |
|
|
|
log.debug("Strange script was: " + this.toString()); |
|
|
|
return null; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.getBuffer = function () |
|
|
|
{ |
|
|
|
Script.prototype.getBuffer = function() { |
|
|
|
return this.buffer; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.fromStringContent = function(s) { |
|
|
|
var chunks = []; |
|
|
|
var split = s.split(' '); |
|
|
|
console.log(split); |
|
|
|
for (var i=0; i<split.length; i++) { |
|
|
|
var word = split[i]; |
|
|
|
if (word.length > 2 && word.substring(0,2) === '0x') { |
|
|
|
chunks.push(new Buffer(word.substring(2,word.length), 'hex')); |
|
|
|
} else { |
|
|
|
var integer = parseInt(word); |
|
|
|
if (isNaN(integer)) { |
|
|
|
chunks.push(Opcode.map['OP_'+word]); |
|
|
|
} else { |
|
|
|
var hexi = integer.toString(16); |
|
|
|
if (hexi.length %2 === 1) hexi = '0'+hexi; |
|
|
|
console.log(hexi); |
|
|
|
chunks.push(new Buffer(hexi,'hex')); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (var i = 0; i < split.length; i++) { |
|
|
|
var word = split[i]; |
|
|
|
if (word.length > 2 && word.substring(0, 2) === '0x') { |
|
|
|
chunks.push(new Buffer(word.substring(2, word.length), 'hex')); |
|
|
|
} else { |
|
|
|
var opcode = Opcode.map['OP_' + word]; |
|
|
|
if (opcode) { |
|
|
|
chunks.push(opcode); |
|
|
|
} else { |
|
|
|
var integer = parseInt(word); |
|
|
|
if (!isNaN(integer)) { |
|
|
|
//console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length);
|
|
|
|
var data = util.intToBuffer(integer); |
|
|
|
chunks.push(data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return Script.fromChunks(chunks); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.getStringContent = function (truncate, maxEl) |
|
|
|
{ |
|
|
|
Script.prototype.getStringContent = function(truncate, maxEl) { |
|
|
|
if (truncate === null) { |
|
|
|
truncate = true; |
|
|
|
} |
|
|
|
|
|
|
|
if ("undefined" === typeof maxEl) { |
|
|
|
if ('undefined' === typeof maxEl) { |
|
|
|
maxEl = 15; |
|
|
|
} |
|
|
|
|
|
|
@ -323,7 +307,7 @@ function spec(b) { |
|
|
|
} |
|
|
|
|
|
|
|
if (Buffer.isBuffer(chunk)) { |
|
|
|
s += '0x'+util.formatBuffer(chunk, truncate ? null : 0); |
|
|
|
s += '0x' + util.formatBuffer(chunk, truncate ? null : 0); |
|
|
|
} else { |
|
|
|
s += Opcode.reverseMap[chunk]; |
|
|
|
} |
|
|
@ -336,8 +320,7 @@ function spec(b) { |
|
|
|
return s; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.toString = function (truncate, maxEl) |
|
|
|
{ |
|
|
|
Script.prototype.toString = function(truncate, maxEl) { |
|
|
|
var script = "<Script "; |
|
|
|
script += this.getStringContent(truncate, maxEl); |
|
|
|
script += ">"; |
|
|
@ -345,8 +328,7 @@ function spec(b) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Script.prototype.writeOp = function (opcode) |
|
|
|
{ |
|
|
|
Script.prototype.writeOp = function(opcode) { |
|
|
|
var buf = Buffer(this.buffer.length + 1); |
|
|
|
this.buffer.copy(buf); |
|
|
|
buf.writeUInt8(opcode, this.buffer.length); |
|
|
@ -356,8 +338,7 @@ function spec(b) { |
|
|
|
this.chunks.push(opcode); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.writeN = function (n) |
|
|
|
{ |
|
|
|
Script.prototype.writeN = function(n) { |
|
|
|
if (n < 0 || n > 16) |
|
|
|
throw new Error("writeN: out of range value " + n); |
|
|
|
|
|
|
@ -367,8 +348,7 @@ function spec(b) { |
|
|
|
this.writeOp(OP_1 + n - 1); |
|
|
|
}; |
|
|
|
|
|
|
|
function prefixSize(data_length) |
|
|
|
{ |
|
|
|
function prefixSize(data_length) { |
|
|
|
if (data_length < OP_PUSHDATA1) { |
|
|
|
return 1; |
|
|
|
} else if (data_length <= 0xff) { |
|
|
@ -385,21 +365,15 @@ function spec(b) { |
|
|
|
if (data_length < OP_PUSHDATA1) { |
|
|
|
buf = new Buffer(1); |
|
|
|
buf.writeUInt8(data_length, 0); |
|
|
|
} |
|
|
|
|
|
|
|
else if (data_length <= 0xff) { |
|
|
|
} else if (data_length <= 0xff) { |
|
|
|
buf = new Buffer(1 + 1); |
|
|
|
buf.writeUInt8(OP_PUSHDATA1, 0); |
|
|
|
buf.writeUInt8(data_length, 1); |
|
|
|
} |
|
|
|
|
|
|
|
else if (data_length <= 0xffff) { |
|
|
|
} else if (data_length <= 0xffff) { |
|
|
|
buf = new Buffer(1 + 2); |
|
|
|
buf.writeUInt8(OP_PUSHDATA2, 0); |
|
|
|
buf.writeUInt16LE(data_length, 1); |
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
} else { |
|
|
|
buf = new Buffer(1 + 4); |
|
|
|
buf.writeUInt8(OP_PUSHDATA4, 0); |
|
|
|
buf.writeUInt32LE(data_length, 1); |
|
|
@ -408,25 +382,22 @@ function spec(b) { |
|
|
|
return buf; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.writeBytes = function (data) |
|
|
|
{ |
|
|
|
Script.prototype.writeBytes = function(data) { |
|
|
|
var newSize = this.buffer.length + prefixSize(data.length) + data.length; |
|
|
|
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]); |
|
|
|
this.chunks.push(data); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.updateBuffer = function () |
|
|
|
{ |
|
|
|
Script.prototype.updateBuffer = function() { |
|
|
|
this.buffer = Script.chunksToBuffer(this.chunks); |
|
|
|
}; |
|
|
|
|
|
|
|
Script.prototype.findAndDelete = function (chunk) |
|
|
|
{ |
|
|
|
Script.prototype.findAndDelete = function(chunk) { |
|
|
|
var dirty = false; |
|
|
|
if (Buffer.isBuffer(chunk)) { |
|
|
|
for (var i = 0, l = this.chunks.length; i < l; i++) { |
|
|
|
if (Buffer.isBuffer(this.chunks[i]) && |
|
|
|
buffertools.compare(this.chunks[i], chunk) === 0) { |
|
|
|
buffertools.compare(this.chunks[i], chunk) === 0) { |
|
|
|
this.chunks.splice(i, 1); |
|
|
|
dirty = true; |
|
|
|
} |
|
|
@ -452,7 +423,7 @@ function spec(b) { |
|
|
|
* These are used for coinbase transactions and at some point were used for |
|
|
|
* IP-based transactions as well. |
|
|
|
*/ |
|
|
|
Script.createPubKeyOut = function (pubkey) { |
|
|
|
Script.createPubKeyOut = function(pubkey) { |
|
|
|
var script = new Script(); |
|
|
|
script.writeBytes(pubkey); |
|
|
|
script.writeOp(OP_CHECKSIG); |
|
|
@ -462,7 +433,7 @@ function spec(b) { |
|
|
|
/** |
|
|
|
* Creates a standard txout script. |
|
|
|
*/ |
|
|
|
Script.createPubKeyHashOut = function (pubKeyHash) { |
|
|
|
Script.createPubKeyHashOut = function(pubKeyHash) { |
|
|
|
var script = new Script(); |
|
|
|
script.writeOp(OP_DUP); |
|
|
|
script.writeOp(OP_HASH160); |
|
|
@ -491,8 +462,8 @@ function spec(b) { |
|
|
|
return script; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.fromTestData = function (testData) { |
|
|
|
testData = testData.map(function (chunk) { |
|
|
|
Script.fromTestData = function(testData) { |
|
|
|
testData = testData.map(function(chunk) { |
|
|
|
if ("string" === typeof chunk) { |
|
|
|
return new Buffer(chunk, 'hex'); |
|
|
|
} else { |
|
|
@ -506,14 +477,14 @@ function spec(b) { |
|
|
|
return script; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.fromChunks = function (chunks) { |
|
|
|
Script.fromChunks = function(chunks) { |
|
|
|
var script = new Script(); |
|
|
|
script.chunks = chunks; |
|
|
|
script.updateBuffer(); |
|
|
|
return script; |
|
|
|
}; |
|
|
|
|
|
|
|
Script.chunksToBuffer = function (chunks) { |
|
|
|
Script.chunksToBuffer = function(chunks) { |
|
|
|
var buf = new Put(); |
|
|
|
|
|
|
|
for (var i = 0, l = chunks.length; i < l; i++) { |
|
|
@ -544,4 +515,3 @@ function spec(b) { |
|
|
|
return Script; |
|
|
|
}; |
|
|
|
module.defineClass(spec); |
|
|
|
|
|
|
|