From 05c55381620d9a9f503a73e62ac722b35577725a Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 6 Mar 2014 17:23:00 -0300 Subject: [PATCH] script parser code added and tested --- Script.js | 286 ++++++++++++++++++--------------------- test/test.Script.js | 5 +- test/test.Transaction.js | 19 +-- test/test.util.js | 19 +++ util/util.js | 24 ++++ 5 files changed, 179 insertions(+), 174 deletions(-) diff --git a/Script.js b/Script.js index 2b8bad536..322b4e6b9 100644 --- a/Script.js +++ b/Script.js @@ -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; + 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; - } + case TX_UNKNOWN: + default: + // do nothing + break; + } - return res; + 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 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 = "