script parser code added and tested
This commit is contained in:
parent
40ee699453
commit
05c5538162
120
Script.js
120
Script.js
|
@ -73,8 +73,7 @@ function spec(b) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.isPushOnly = function ()
|
Script.prototype.isPushOnly = function() {
|
||||||
{
|
|
||||||
for (var i = 0; i < this.chunks.length; i++)
|
for (var i = 0; i < this.chunks.length; i++)
|
||||||
if (!Buffer.isBuffer(this.chunks[i]))
|
if (!Buffer.isBuffer(this.chunks[i]))
|
||||||
return false;
|
return false;
|
||||||
|
@ -82,8 +81,7 @@ function spec(b) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.isP2SH = function ()
|
Script.prototype.isP2SH = function() {
|
||||||
{
|
|
||||||
return (this.chunks.length == 3 &&
|
return (this.chunks.length == 3 &&
|
||||||
this.chunks[0] == OP_HASH160 &&
|
this.chunks[0] == OP_HASH160 &&
|
||||||
Buffer.isBuffer(this.chunks[1]) &&
|
Buffer.isBuffer(this.chunks[1]) &&
|
||||||
|
@ -91,15 +89,13 @@ function spec(b) {
|
||||||
this.chunks[2] == OP_EQUAL);
|
this.chunks[2] == OP_EQUAL);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.isPubkey = function ()
|
Script.prototype.isPubkey = function() {
|
||||||
{
|
|
||||||
return (this.chunks.length == 2 &&
|
return (this.chunks.length == 2 &&
|
||||||
Buffer.isBuffer(this.chunks[0]) &&
|
Buffer.isBuffer(this.chunks[0]) &&
|
||||||
this.chunks[1] == OP_CHECKSIG);
|
this.chunks[1] == OP_CHECKSIG);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.isPubkeyHash = function ()
|
Script.prototype.isPubkeyHash = function() {
|
||||||
{
|
|
||||||
return (this.chunks.length == 5 &&
|
return (this.chunks.length == 5 &&
|
||||||
this.chunks[0] == OP_DUP &&
|
this.chunks[0] == OP_DUP &&
|
||||||
this.chunks[1] == OP_HASH160 &&
|
this.chunks[1] == OP_HASH160 &&
|
||||||
|
@ -109,22 +105,19 @@ function spec(b) {
|
||||||
this.chunks[4] == OP_CHECKSIG);
|
this.chunks[4] == OP_CHECKSIG);
|
||||||
};
|
};
|
||||||
|
|
||||||
function isSmallIntOp(opcode)
|
function isSmallIntOp(opcode) {
|
||||||
{
|
|
||||||
return ((opcode == OP_0) ||
|
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 &&
|
return (this.chunks.length > 3 &&
|
||||||
isSmallIntOp(this.chunks[0]) &&
|
isSmallIntOp(this.chunks[0]) &&
|
||||||
isSmallIntOp(this.chunks[this.chunks.length - 2]) &&
|
isSmallIntOp(this.chunks[this.chunks.length - 2]) &&
|
||||||
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG);
|
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.finishedMultiSig = function()
|
Script.prototype.finishedMultiSig = function() {
|
||||||
{
|
|
||||||
var nsigs = 0;
|
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)
|
if (this.chunks[i] !== 0)
|
||||||
|
@ -140,11 +133,9 @@ function spec(b) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.prototype.removePlaceHolders = function()
|
Script.prototype.removePlaceHolders = function() {
|
||||||
{
|
|
||||||
var chunks = [];
|
var chunks = [];
|
||||||
for (var i in this.chunks)
|
for (var i in this.chunks) {
|
||||||
{
|
|
||||||
if (this.chunks.hasOwnProperty(i)) {
|
if (this.chunks.hasOwnProperty(i)) {
|
||||||
var chunk = this.chunks[i];
|
var chunk = this.chunks[i];
|
||||||
if (chunk != 0)
|
if (chunk != 0)
|
||||||
|
@ -156,8 +147,7 @@ function spec(b) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.prototype.prependOp0 = function()
|
Script.prototype.prependOp0 = function() {
|
||||||
{
|
|
||||||
var chunks = [0];
|
var chunks = [0];
|
||||||
for (i in this.chunks) {
|
for (i in this.chunks) {
|
||||||
if (this.chunks.hasOwnProperty(i)) {
|
if (this.chunks.hasOwnProperty(i)) {
|
||||||
|
@ -170,8 +160,7 @@ function spec(b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this a script form we know?
|
// is this a script form we know?
|
||||||
Script.prototype.classify = function ()
|
Script.prototype.classify = function() {
|
||||||
{
|
|
||||||
if (this.isPubkeyHash())
|
if (this.isPubkeyHash())
|
||||||
return TX_PUBKEYHASH;
|
return TX_PUBKEYHASH;
|
||||||
if (this.isP2SH())
|
if (this.isP2SH())
|
||||||
|
@ -184,8 +173,7 @@ function spec(b) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// extract useful data items from known scripts
|
// extract useful data items from known scripts
|
||||||
Script.prototype.capture = function ()
|
Script.prototype.capture = function() {
|
||||||
{
|
|
||||||
var txType = this.classify();
|
var txType = this.classify();
|
||||||
var res = [];
|
var res = [];
|
||||||
switch (txType) {
|
switch (txType) {
|
||||||
|
@ -213,19 +201,20 @@ function spec(b) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// return first extracted data item from script
|
// return first extracted data item from script
|
||||||
Script.prototype.captureOne = function ()
|
Script.prototype.captureOne = function() {
|
||||||
{
|
|
||||||
var arr = this.capture();
|
var arr = this.capture();
|
||||||
return arr[0];
|
return arr[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getOutType = function ()
|
Script.prototype.getOutType = function() {
|
||||||
{
|
|
||||||
var txType = this.classify();
|
var txType = this.classify();
|
||||||
switch (txType) {
|
switch (txType) {
|
||||||
case TX_PUBKEY: return 'Pubkey';
|
case TX_PUBKEY:
|
||||||
case TX_PUBKEYHASH: return 'Address';
|
return 'Pubkey';
|
||||||
default: return 'Strange';
|
case TX_PUBKEYHASH:
|
||||||
|
return 'Address';
|
||||||
|
default:
|
||||||
|
return 'Strange';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,8 +222,7 @@ function spec(b) {
|
||||||
return TX_TYPES[this.classify()];
|
return TX_TYPES[this.classify()];
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.simpleOutHash = function ()
|
Script.prototype.simpleOutHash = function() {
|
||||||
{
|
|
||||||
switch (this.getOutType()) {
|
switch (this.getOutType()) {
|
||||||
case 'Address':
|
case 'Address':
|
||||||
return this.chunks[2];
|
return this.chunks[2];
|
||||||
|
@ -247,8 +235,7 @@ function spec(b) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getInType = function ()
|
Script.prototype.getInType = function() {
|
||||||
{
|
|
||||||
if (this.chunks.length == 1) {
|
if (this.chunks.length == 1) {
|
||||||
// Direct IP to IP transactions only have the public key in their scriptSig.
|
// Direct IP to IP transactions only have the public key in their scriptSig.
|
||||||
return 'Pubkey';
|
return 'Pubkey';
|
||||||
|
@ -261,8 +248,7 @@ function spec(b) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.simpleInPubKey = function ()
|
Script.prototype.simpleInPubKey = function() {
|
||||||
{
|
|
||||||
switch (this.getInType()) {
|
switch (this.getInType()) {
|
||||||
case 'Address':
|
case 'Address':
|
||||||
return this.chunks[1];
|
return this.chunks[1];
|
||||||
|
@ -275,42 +261,40 @@ function spec(b) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getBuffer = function ()
|
Script.prototype.getBuffer = function() {
|
||||||
{
|
|
||||||
return this.buffer;
|
return this.buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.fromStringContent = function(s) {
|
Script.fromStringContent = function(s) {
|
||||||
var chunks = [];
|
var chunks = [];
|
||||||
var split = s.split(' ');
|
var split = s.split(' ');
|
||||||
console.log(split);
|
|
||||||
for (var i = 0; i < split.length; i++) {
|
for (var i = 0; i < split.length; i++) {
|
||||||
var word = split[i];
|
var word = split[i];
|
||||||
if (word.length > 2 && word.substring(0, 2) === '0x') {
|
if (word.length > 2 && word.substring(0, 2) === '0x') {
|
||||||
chunks.push(new Buffer(word.substring(2, word.length), 'hex'));
|
chunks.push(new Buffer(word.substring(2, word.length), 'hex'));
|
||||||
} else {
|
} else {
|
||||||
var integer = parseInt(word);
|
var opcode = Opcode.map['OP_' + word];
|
||||||
if (isNaN(integer)) {
|
if (opcode) {
|
||||||
chunks.push(Opcode.map['OP_'+word]);
|
chunks.push(opcode);
|
||||||
} else {
|
} else {
|
||||||
var hexi = integer.toString(16);
|
var integer = parseInt(word);
|
||||||
if (hexi.length %2 === 1) hexi = '0'+hexi;
|
if (!isNaN(integer)) {
|
||||||
console.log(hexi);
|
//console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length);
|
||||||
chunks.push(new Buffer(hexi,'hex'));
|
var data = util.intToBuffer(integer);
|
||||||
|
chunks.push(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Script.fromChunks(chunks);
|
return Script.fromChunks(chunks);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.getStringContent = function (truncate, maxEl)
|
Script.prototype.getStringContent = function(truncate, maxEl) {
|
||||||
{
|
|
||||||
if (truncate === null) {
|
if (truncate === null) {
|
||||||
truncate = true;
|
truncate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("undefined" === typeof maxEl) {
|
if ('undefined' === typeof maxEl) {
|
||||||
maxEl = 15;
|
maxEl = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,8 +320,7 @@ function spec(b) {
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.toString = function (truncate, maxEl)
|
Script.prototype.toString = function(truncate, maxEl) {
|
||||||
{
|
|
||||||
var script = "<Script ";
|
var script = "<Script ";
|
||||||
script += this.getStringContent(truncate, maxEl);
|
script += this.getStringContent(truncate, maxEl);
|
||||||
script += ">";
|
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);
|
var buf = Buffer(this.buffer.length + 1);
|
||||||
this.buffer.copy(buf);
|
this.buffer.copy(buf);
|
||||||
buf.writeUInt8(opcode, this.buffer.length);
|
buf.writeUInt8(opcode, this.buffer.length);
|
||||||
|
@ -356,8 +338,7 @@ function spec(b) {
|
||||||
this.chunks.push(opcode);
|
this.chunks.push(opcode);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.writeN = function (n)
|
Script.prototype.writeN = function(n) {
|
||||||
{
|
|
||||||
if (n < 0 || n > 16)
|
if (n < 0 || n > 16)
|
||||||
throw new Error("writeN: out of range value " + n);
|
throw new Error("writeN: out of range value " + n);
|
||||||
|
|
||||||
|
@ -367,8 +348,7 @@ function spec(b) {
|
||||||
this.writeOp(OP_1 + n - 1);
|
this.writeOp(OP_1 + n - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
function prefixSize(data_length)
|
function prefixSize(data_length) {
|
||||||
{
|
|
||||||
if (data_length < OP_PUSHDATA1) {
|
if (data_length < OP_PUSHDATA1) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (data_length <= 0xff) {
|
} else if (data_length <= 0xff) {
|
||||||
|
@ -385,21 +365,15 @@ function spec(b) {
|
||||||
if (data_length < OP_PUSHDATA1) {
|
if (data_length < OP_PUSHDATA1) {
|
||||||
buf = new Buffer(1);
|
buf = new Buffer(1);
|
||||||
buf.writeUInt8(data_length, 0);
|
buf.writeUInt8(data_length, 0);
|
||||||
}
|
} else if (data_length <= 0xff) {
|
||||||
|
|
||||||
else if (data_length <= 0xff) {
|
|
||||||
buf = new Buffer(1 + 1);
|
buf = new Buffer(1 + 1);
|
||||||
buf.writeUInt8(OP_PUSHDATA1, 0);
|
buf.writeUInt8(OP_PUSHDATA1, 0);
|
||||||
buf.writeUInt8(data_length, 1);
|
buf.writeUInt8(data_length, 1);
|
||||||
}
|
} else if (data_length <= 0xffff) {
|
||||||
|
|
||||||
else if (data_length <= 0xffff) {
|
|
||||||
buf = new Buffer(1 + 2);
|
buf = new Buffer(1 + 2);
|
||||||
buf.writeUInt8(OP_PUSHDATA2, 0);
|
buf.writeUInt8(OP_PUSHDATA2, 0);
|
||||||
buf.writeUInt16LE(data_length, 1);
|
buf.writeUInt16LE(data_length, 1);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
else {
|
|
||||||
buf = new Buffer(1 + 4);
|
buf = new Buffer(1 + 4);
|
||||||
buf.writeUInt8(OP_PUSHDATA4, 0);
|
buf.writeUInt8(OP_PUSHDATA4, 0);
|
||||||
buf.writeUInt32LE(data_length, 1);
|
buf.writeUInt32LE(data_length, 1);
|
||||||
|
@ -408,20 +382,17 @@ function spec(b) {
|
||||||
return buf;
|
return buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.writeBytes = function (data)
|
Script.prototype.writeBytes = function(data) {
|
||||||
{
|
|
||||||
var newSize = this.buffer.length + prefixSize(data.length) + data.length;
|
var newSize = this.buffer.length + prefixSize(data.length) + data.length;
|
||||||
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]);
|
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]);
|
||||||
this.chunks.push(data);
|
this.chunks.push(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.updateBuffer = function ()
|
Script.prototype.updateBuffer = function() {
|
||||||
{
|
|
||||||
this.buffer = Script.chunksToBuffer(this.chunks);
|
this.buffer = Script.chunksToBuffer(this.chunks);
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.prototype.findAndDelete = function (chunk)
|
Script.prototype.findAndDelete = function(chunk) {
|
||||||
{
|
|
||||||
var dirty = false;
|
var dirty = false;
|
||||||
if (Buffer.isBuffer(chunk)) {
|
if (Buffer.isBuffer(chunk)) {
|
||||||
for (var i = 0, l = this.chunks.length; i < l; i++) {
|
for (var i = 0, l = this.chunks.length; i < l; i++) {
|
||||||
|
@ -544,4 +515,3 @@ function spec(b) {
|
||||||
return Script;
|
return Script;
|
||||||
};
|
};
|
||||||
module.defineClass(spec);
|
module.defineClass(spec);
|
||||||
|
|
||||||
|
|
|
@ -88,9 +88,10 @@ describe('Script', function() {
|
||||||
|
|
||||||
test_data.dataScriptValid.forEach(function(datum) {
|
test_data.dataScriptValid.forEach(function(datum) {
|
||||||
if (datum.length < 2) throw new Error('Invalid test data');
|
if (datum.length < 2) throw new Error('Invalid test data');
|
||||||
var human = datum[1];
|
var human = datum[0] + ' ' + datum[1];
|
||||||
it('should parse script from human readable ' + human, function() {
|
it('should parse script from human readable ' + human, function() {
|
||||||
Script.fromStringContent(human).getStringContent(false, null).should.equal(human);
|
var h2 = Script.fromStringContent(human).getStringContent(false, null);
|
||||||
|
Script.fromStringContent(h2).getStringContent(false, null).should.equal(h2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ var Out;
|
||||||
var Script = bitcore.Script.class();
|
var Script = bitcore.Script.class();
|
||||||
var buffertools = require('buffertools');
|
var buffertools = require('buffertools');
|
||||||
var test_data = require('./testdata');
|
var test_data = require('./testdata');
|
||||||
var async = require('async');
|
|
||||||
|
|
||||||
describe('Transaction', function() {
|
describe('Transaction', function() {
|
||||||
it('should initialze the main object', function() {
|
it('should initialze the main object', function() {
|
||||||
|
@ -58,18 +57,10 @@ describe('Transaction', function() {
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var stx = tx.getStandardizedObject();
|
var stx = tx.getStandardizedObject();
|
||||||
async.eachSeries(tx.ins,
|
tx.ins.forEach(function(txin) {
|
||||||
function(txin, next) {
|
|
||||||
var scriptPubKey = map[[stx. in [i].prev_out.hash, stx. in [i].prev_out.n]];
|
var scriptPubKey = map[[stx. in [i].prev_out.hash, stx. in [i].prev_out.n]];
|
||||||
i += 1;
|
i += 1;
|
||||||
next();
|
});
|
||||||
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,4 +46,23 @@ describe('util', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('#intToBuffer', function() {
|
||||||
|
var data = [
|
||||||
|
[0, '00000000'],
|
||||||
|
[-0, '00000000'],
|
||||||
|
[-1, 'ffffffff'],
|
||||||
|
[1, '01000000'],
|
||||||
|
[18, '12000000'],
|
||||||
|
[878082192, '90785634'],
|
||||||
|
[0x01234567890, '1200000090785634'],
|
||||||
|
[-4294967297, 'feffffffffffffff'],
|
||||||
|
];
|
||||||
|
data.forEach(function(datum) {
|
||||||
|
var integer = datum[0];
|
||||||
|
var result = datum[1];
|
||||||
|
it('should work for ' + integer, function() {
|
||||||
|
buffertools.toHex(coinUtil.intToBuffer(integer)).should.equal(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
24
util/util.js
24
util/util.js
|
@ -105,6 +105,30 @@ var bigIntToValue = exports.bigIntToValue = function (valueBigInt) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var intTo64Bits = function(integer) {
|
||||||
|
return {
|
||||||
|
hi: Math.floor(integer / 4294967296),
|
||||||
|
lo: (integer & 0xFFFFFFFF) >>> 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var fitsIn32Bits = function(integer) {
|
||||||
|
// TODO: make this efficient!!!
|
||||||
|
return integer.toString(2).replace('-','').length < 32;
|
||||||
|
}
|
||||||
|
exports.intToBuffer = function(integer) {
|
||||||
|
if (fitsIn32Bits(integer)) {
|
||||||
|
var data = new Buffer(4);
|
||||||
|
data.writeInt32LE(integer, 0);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
var x = intTo64Bits(integer);
|
||||||
|
var data = new Buffer(8);
|
||||||
|
data.writeInt32LE(x.hi, 0); // high part contains sign information (signed)
|
||||||
|
data.writeUInt32LE(x.lo, 4); // low part encoded as unsigned integer
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var formatValue = exports.formatValue = function (valueBuffer) {
|
var formatValue = exports.formatValue = function (valueBuffer) {
|
||||||
var value = valueToBigInt(valueBuffer).toString();
|
var value = valueToBigInt(valueBuffer).toString();
|
||||||
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
|
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0';
|
||||||
|
|
Loading…
Reference in New Issue