Merge pull request #169 from maraoz/feature/add-bitcoin-core-tests

add bitcoin core ScriptInterpreter valid/invalid script tests
This commit is contained in:
Ryan X. Charles 2014-03-19 10:21:57 -07:00
commit c6dcbc256b
6 changed files with 250 additions and 153 deletions

View File

@ -35,7 +35,7 @@ function Script(buffer) {
} }
this.chunks = []; this.chunks = [];
this.parse(); this.parse();
}; }
this.class = Script; this.class = Script;
Script.TX_UNKNOWN = TX_UNKNOWN; Script.TX_UNKNOWN = TX_UNKNOWN;
@ -51,19 +51,25 @@ Script.prototype.parse = function() {
while (!parser.eof()) { while (!parser.eof()) {
var opcode = parser.word8(); var opcode = parser.word8();
var len; var len, chunk;
if (opcode > 0 && opcode < OP_PUSHDATA1) { if (opcode > 0 && opcode < OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data // Read some bytes of data, opcode value is the length of data
this.chunks.push(parser.buffer(opcode)); this.chunks.push(parser.buffer(opcode));
} else if (opcode == OP_PUSHDATA1) { } else if (opcode === OP_PUSHDATA1) {
len = parser.word8(); len = parser.word8();
this.chunks.push(parser.buffer(len)); chunk = parser.buffer(len);
} else if (opcode == OP_PUSHDATA2) { if (chunk.length < len) throw new Error('Invalid data size: not enough data');
this.chunks.push(chunk);
} else if (opcode === OP_PUSHDATA2) {
len = parser.word16le(); len = parser.word16le();
this.chunks.push(parser.buffer(len)); chunk = parser.buffer(len);
} else if (opcode == OP_PUSHDATA4) { if (chunk.length < len) throw new Error('Invalid data size: not enough data');
this.chunks.push(chunk);
} else if (opcode === OP_PUSHDATA4) {
len = parser.word32le(); len = parser.word32le();
this.chunks.push(parser.buffer(len)); chunk = parser.buffer(len);
if (chunk.length < len) throw new Error('Invalid data size: not enough data');
this.chunks.push(chunk);
} else { } else {
this.chunks.push(opcode); this.chunks.push(opcode);
} }
@ -509,7 +515,7 @@ Script.stringToBuffer = function(s) {
if (!isNaN(integer)) { if (!isNaN(integer)) {
// integer // integer
//console.log('integer'); //console.log('integer');
var data = util.intToBuffer(integer); var data = util.intToBufferSM(integer);
buf.put(Script.chunksToBuffer([data])); buf.put(Script.chunksToBuffer([data]));
} else if (word[0] === '\'' && word[word.length-1] === '\'') { } else if (word[0] === '\'' && word[word.length-1] === '\'') {
// string // string

View File

@ -1,6 +1,7 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
var config = imports.config || require('./config'); var config = imports.config || require('./config');
var log = imports.log || require('./util/log'); var log = imports.log || require('./util/log');
var util = imports.util || require('./util/util');
var Opcode = imports.Opcode || require('./Opcode'); var Opcode = imports.Opcode || require('./Opcode');
var buffertools = imports.buffertools || require('buffertools'); var buffertools = imports.buffertools || require('buffertools');
var bignum = imports.bignum || require('bignum'); var bignum = imports.bignum || require('bignum');
@ -17,6 +18,9 @@ for (var i in Opcode.map) {
eval(i + " = " + Opcode.map[i] + ";"); eval(i + " = " + Opcode.map[i] + ";");
} }
var intToBufferSM = Util.intToBufferSM
var bufferSMToInt = Util.bufferSMToInt;
function ScriptInterpreter() { function ScriptInterpreter() {
this.stack = []; this.stack = [];
this.disableUnsafeOpcodes = true; this.disableUnsafeOpcodes = true;
@ -26,7 +30,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
if ("function" !== typeof callback) { if ("function" !== typeof callback) {
throw new Error("ScriptInterpreter.eval() requires a callback"); throw new Error("ScriptInterpreter.eval() requires a callback");
} }
var pc = 0; var pc = 0;
var execStack = []; var execStack = [];
var altStack = []; var altStack = [];
@ -92,8 +96,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
throw new Error("Encountered a disabled opcode"); throw new Error("Encountered a disabled opcode");
} }
if (exec && Buffer.isBuffer(opcode)) if (exec && Buffer.isBuffer(opcode)) {
this.stack.push(opcode); this.stack.push(opcode);
}
else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF)) else if (exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
switch (opcode) { switch (opcode) {
case OP_0: case OP_0:
@ -117,7 +122,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_14: case OP_14:
case OP_15: case OP_15:
case OP_16: case OP_16:
this.stack.push(bigintToBuffer(opcode - OP_1 + 1)); var opint = opcode - OP_1 + 1;
var opbuf = intToBufferSM(opint);
this.stack.push(opbuf);
break; break;
case OP_NOP: case OP_NOP:
@ -241,7 +248,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_DEPTH: case OP_DEPTH:
// -- stacksize // -- stacksize
var value = bignum(this.stack.length); var value = bignum(this.stack.length);
this.stack.push(bigintToBuffer(value)); this.stack.push(intToBufferSM(value));
break; break;
case OP_DROP: case OP_DROP:
@ -350,7 +357,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_SIZE: case OP_SIZE:
// (in -- in size) // (in -- in size)
var value = bignum(this.stackTop().length); var value = bignum(this.stackTop().length);
this.stack.push(bigintToBuffer(value)); this.stack.push(intToBufferSM(value));
break; break;
case OP_INVERT: case OP_INVERT:
@ -392,6 +399,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
// (x1 x2 - bool) // (x1 x2 - bool)
var v1 = this.stackTop(2); var v1 = this.stackTop(2);
var v2 = this.stackTop(1); var v2 = this.stackTop(1);
var value = buffertools.compare(v1, v2) === 0; var value = buffertools.compare(v1, v2) === 0;
// OP_NOTEQUAL is disabled because it would be too easy to say // OP_NOTEQUAL is disabled because it would be too easy to say
@ -421,7 +429,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_NOT: case OP_NOT:
case OP_0NOTEQUAL: case OP_0NOTEQUAL:
// (in -- out) // (in -- out)
var num = castBigint(this.stackTop()); var num = bufferSMToInt(this.stackTop());
switch (opcode) { switch (opcode) {
case OP_1ADD: case OP_1ADD:
num = num.add(bignum(1)); num = num.add(bignum(1));
@ -448,7 +456,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
num = bignum(num.cmp(0) == 0 ? 0 : 1); num = bignum(num.cmp(0) == 0 ? 0 : 1);
break; break;
} }
this.stack[this.stack.length - 1] = bigintToBuffer(num); this.stack[this.stack.length - 1] = intToBufferSM(num);
break; break;
case OP_ADD: case OP_ADD:
@ -470,8 +478,8 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_MIN: case OP_MIN:
case OP_MAX: case OP_MAX:
// (x1 x2 -- out) // (x1 x2 -- out)
var v1 = castBigint(this.stackTop(2)); var v1 = bufferSMToInt(this.stackTop(2));
var v2 = castBigint(this.stackTop(1)); var v2 = bufferSMToInt(this.stackTop(1));
var num; var num;
switch (opcode) { switch (opcode) {
case OP_ADD: case OP_ADD:
@ -547,7 +555,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
} }
this.stackPop(); this.stackPop();
this.stackPop(); this.stackPop();
this.stack.push(bigintToBuffer(num)); this.stack.push(intToBufferSM(num));
if (opcode === OP_NUMEQUALVERIFY) { if (opcode === OP_NUMEQUALVERIFY) {
if (castBool(this.stackTop())) { if (castBool(this.stackTop())) {
@ -560,14 +568,14 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
case OP_WITHIN: case OP_WITHIN:
// (x min max -- out) // (x min max -- out)
var v1 = castBigint(this.stackTop(3)); var v1 = bufferSMToInt(this.stackTop(3));
var v2 = castBigint(this.stackTop(2)); var v2 = bufferSMToInt(this.stackTop(2));
var v3 = castBigint(this.stackTop(1)); var v3 = bufferSMToInt(this.stackTop(1));
this.stackPop(); this.stackPop();
this.stackPop(); this.stackPop();
this.stackPop(); this.stackPop();
var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0; var value = v1.cmp(v2) >= 0 && v1.cmp(v3) < 0;
this.stack.push(bigintToBuffer(value ? 1 : 0)); this.stack.push(intToBufferSM(value ? 1 : 0));
break; break;
case OP_RIPEMD160: case OP_RIPEMD160:
@ -747,7 +755,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
return; return;
default: default:
console.log('opcode '+opcode);
throw new Error("Unknown opcode encountered"); throw new Error("Unknown opcode encountered");
} }
@ -804,7 +811,7 @@ ScriptInterpreter.prototype.stackTop = function stackTop(offset) {
}; };
ScriptInterpreter.prototype.stackBack = function stackBack() { ScriptInterpreter.prototype.stackBack = function stackBack() {
return this.stack[-1]; return this.stack[this.stack.length -1];
}; };
/** /**
@ -842,7 +849,7 @@ ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() {
if (entry.length > 2) { if (entry.length > 2) {
return buffertools.toHex(entry.slice(0)); return buffertools.toHex(entry.slice(0));
} }
var num = castBigint(entry); var num = bufferSMToInt(entry);
if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) { if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) {
return num.toNumber(); return num.toNumber();
} else { } else {
@ -864,61 +871,7 @@ var castBool = ScriptInterpreter.castBool = function castBool(v) {
return false; return false;
}; };
var castInt = ScriptInterpreter.castInt = function castInt(v) { var castInt = ScriptInterpreter.castInt = function castInt(v) {
return castBigint(v).toNumber(); return bufferSMToInt(v).toNumber();
};
var castBigint = ScriptInterpreter.castBigint = function castBigint(v) {
if (!v.length) {
return bignum(0);
}
// Arithmetic operands must be in range [-2^31...2^31]
if (v.length > 4) {
throw new Error("Bigint cast overflow (> 4 bytes)");
}
var w = new Buffer(v.length);
v.copy(w);
w = buffertools.reverse(w);
if (w[0] & 0x80) {
w[0] &= 0x7f;
return bignum.fromBuffer(w).neg();
} else {
// Positive number
return bignum.fromBuffer(w);
}
};
var bigintToBuffer = ScriptInterpreter.bigintToBuffer = function bigintToBuffer(v) {
if ("number" === typeof v) {
v = bignum(v);
}
var b, c;
var cmp = v.cmp(0);
if (cmp > 0) {
b = v.toBuffer();
if (b[0] & 0x80) {
c = new Buffer(b.length + 1);
b.copy(c, 1);
c[0] = 0;
return buffertools.reverse(c);
} else {
return buffertools.reverse(b);
}
} else if (cmp == 0) {
return new Buffer([]);
} else {
b = v.neg().toBuffer();
if (b[0] & 0x80) {
c = new Buffer(b.length + 1);
b.copy(c, 1);
c[0] = 0x80;
return buffertools.reverse(c);
} else {
b[0] |= 0x80;
return buffertools.reverse(b);
}
}
}; };
ScriptInterpreter.prototype.getResult = function getResult() { ScriptInterpreter.prototype.getResult = function getResult() {
@ -991,7 +944,8 @@ function verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
return; return;
} }
assert.notEqual(siCopy.length, 0); if (siCopy.length === 0)
throw new Error('siCopy should have length != 0');
var subscript = new Script(siCopy.stackPop()); var subscript = new Script(siCopy.stackPop());

View File

@ -60,7 +60,7 @@
"browser-pack": "~2.0.1", "browser-pack": "~2.0.1",
"commander": "~2.1.0", "commander": "~2.1.0",
"browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git",
"browserify-buffertools": "~1.0.2", "browserify-buffertools": "git://github.com/maraoz/browserify-buffertools.git",
"brfs": "~1.0.0", "brfs": "~1.0.0",
"uglifyify": "~1.2.3" "uglifyify": "~1.2.3"
}, },

View File

@ -2,6 +2,7 @@
var chai = chai || require('chai'); var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var buffertools = require('buffertools');
var should = chai.should(); var should = chai.should();
var testdata = testdata || require('./testdata'); var testdata = testdata || require('./testdata');
@ -22,53 +23,69 @@ describe('ScriptInterpreter', function() {
var si = new ScriptInterpreter(); var si = new ScriptInterpreter();
should.exist(si); should.exist(si);
}); });
var i = 0; var testScripts = function(data, valid) {
testdata.dataScriptValid.forEach(function(datum) { var i = 0;
if (datum.length < 2) throw new Error('Invalid test data'); data.forEach(function(datum) {
var scriptSig = datum[0]; // script inputs if (datum.length < 2) throw new Error('Invalid test data');
var scriptPubKey = datum[1]; // output script var scriptSig = datum[0]; // script inputs
var human = scriptSig + ' ' + scriptPubKey; var scriptPubKey = datum[1]; // output script
it.skip('should validate script ' + human, function(done) { var human = scriptSig + ' ' + scriptPubKey;
i++; it('should ' + (!valid ? 'not ' : '') + 'validate script ' + human, function(done) {
console.log(i + ' ' + human); try {
ScriptInterpreter.verify(Script.fromHumanReadable(scriptSig), ScriptInterpreter.verifyFull(
Script.fromHumanReadable(scriptPubKey), Script.fromHumanReadable(scriptSig), // scriptSig
null, 0, 0, // tx, output index, and hashtype Script.fromHumanReadable(scriptPubKey), // scriptPubKey
function (err, result) { null, 0, 0, // tx, output index, hashtype
should.not.exist(err); { verifyP2SH: !valid}, // only verify P2SH for invalid data set
result.should.equal(true); function(err, result) {
if (valid) {
should.not.exist(err);
} else {
var failed = (typeof err !== 'undefined') || (result === false);
failed.should.equal(true);
}
if (typeof result !== 'undefined') {
result.should.equal(valid);
}
done();
}
);
} catch (e) {
if (valid) {
console.log(e);
}
valid.should.equal(false);
done(); done();
} }
);
});
});
var i = 0; });
});
};
testScripts(testdata.dataScriptValid, true);
testScripts(testdata.dataScriptInvalid, false);
testdata.dataSigCanonical.forEach(function(datum) { testdata.dataSigCanonical.forEach(function(datum) {
it('should validate valid canonical signatures', function() { it('should validate valid canonical signatures', function() {
ScriptInterpreter.isCanonicalSignature(new Buffer(datum,'hex')).should.equal(true); ScriptInterpreter.isCanonicalSignature(new Buffer(datum, 'hex')).should.equal(true);
}); });
}); });
testdata.dataSigNonCanonical.forEach(function(datum) { testdata.dataSigNonCanonical.forEach(function(datum) {
it('should NOT validate invalid canonical signatures', function() { it('should NOT validate invalid canonical signatures', function() {
var sig; var sig;
var isHex; var isHex;
//is Hex? //is Hex?
try { try {
sig =new Buffer(datum,'hex'); sig = new Buffer(datum, 'hex');
isHex=1; isHex = 1;
} catch (e) { } } catch (e) {}
if (isHex) if (isHex)
ScriptInterpreter.isCanonicalSignature.bind(sig).should.throw(); ScriptInterpreter.isCanonicalSignature.bind(sig).should.
throw ();
}); });
}); });
}); });

View File

@ -39,12 +39,12 @@ describe('util', function() {
describe('#ripe160', function() { describe('#ripe160', function() {
var pk = 'a5c756101065ac5b8f689139e6d856fa99e54b5000b6428b43729d334cc9277d'; var pk = 'a5c756101065ac5b8f689139e6d856fa99e54b5000b6428b43729d334cc9277d';
it('should work for ' + pk, function() { it('should work for ' + pk, function() {
var pubKeyHash = coinUtil.ripe160(new Buffer(pk,'hex')); var pubKeyHash = coinUtil.ripe160(new Buffer(pk, 'hex'));
var pkh = buffertools.toHex(pubKeyHash); var pkh = buffertools.toHex(pubKeyHash);
pkh.should.equal('d166a41f27fd4b158f70314e5eee8998bf3d97d5'); pkh.should.equal('d166a41f27fd4b158f70314e5eee8998bf3d97d5');
}); });
}); });
describe('#sha256', function() { describe('#sha256', function() {
var pk = '03d95e184cce34c3cfa58e9a277a09a7c5ed1b2a8134ea1e52887bc66fa3f47071' var pk = '03d95e184cce34c3cfa58e9a277a09a7c5ed1b2a8134ea1e52887bc66fa3f47071'
@ -54,7 +54,7 @@ describe('util', function() {
pkh.should.equal('a5c756101065ac5b8f689139e6d856fa99e54b5000b6428b43729d334cc9277d'); pkh.should.equal('a5c756101065ac5b8f689139e6d856fa99e54b5000b6428b43729d334cc9277d');
}); });
}); });
describe('#sha256ripe160', function() { describe('#sha256ripe160', function() {
var pk = '03d95e184cce34c3cfa58e9a277a09a7c5ed1b2a8134ea1e52887bc66fa3f47071' var pk = '03d95e184cce34c3cfa58e9a277a09a7c5ed1b2a8134ea1e52887bc66fa3f47071'
it('should work for ' + pk, function() { it('should work for ' + pk, function() {
@ -71,7 +71,7 @@ describe('util', function() {
]; ];
ripemdData.forEach(function(datum) { ripemdData.forEach(function(datum) {
it('should work for ' + datum[0], function() { it('should work for ' + datum[0], function() {
var r = coinUtil.ripe160( new bitcore.Buffer(datum[0])); var r = coinUtil.ripe160(new bitcore.Buffer(datum[0]));
buffertools.toHex(r).should.equal(datum[1]); buffertools.toHex(r).should.equal(datum[1]);
}); });
it('should work for Buffer ' + datum[0], function() { it('should work for Buffer ' + datum[0], function() {
@ -80,28 +80,41 @@ describe('util', function() {
}); });
}); });
}); });
describe('#intToBuffer', function() { describe('#intToBuffer2C', function() {
var data = [ var data = [
[0, '00'], [0, ''],
[-0, '00'], [-0, ''],
[-1, 'ff'],
[1, '01'], [1, '01'],
[-1, 'ff'],
[18, '12'], [18, '12'],
[-18, 'ee'],
[127, '7f'],
[128, '8000'],
[129, '8100'],
[4096, '0010'],
[-4096, '00f0'],
[32767, 'ff7f'],
[878082192, '90785634'], [878082192, '90785634'],
[0x01234567890, '1200000090785634'], [0x01234567890, '9078563412'],
[-4294967297, 'feffffffffffffff'], [4294967295, 'ffffffff00'],
[4294967296, '0000000001'],
[4294967297, '0100000001'],
[2147483647, 'ffffff7f'],
[-2147483647, '01000080'],
]; ];
data.forEach(function(datum) { data.forEach(function(datum) {
var integer = datum[0]; var integer = datum[0];
var result = datum[1]; var result = datum[1];
it('should work for ' + integer, function() { it('should work for ' + integer, function() {
buffertools.toHex(coinUtil.intToBuffer(integer)).should.equal(result); var buf = coinUtil.intToBuffer2C(integer);
var hex = buffertools.toHex(buf);
hex.should.equal(result);
}); });
}); });
}); });
describe('#varIntBuf', function() { describe('#varIntBuf', function() {
var data = [ var data = [
[0, '00' ], [0, '00'],
[1, '01'], [1, '01'],
[253, 'fdfd00'], [253, 'fdfd00'],
[254, 'fdfe00'], [254, 'fdfe00'],
@ -147,4 +160,22 @@ describe('util', function() {
}); });
}); });
}); });
describe('#intToBufferSM', function() {
var data = [
[0, ''],
[1, '01'],
[-1, '81'],
[2, '02'],
[-2, '82'],
[-32768, '008080'],
];
data.forEach(function(datum) {
var i = datum[0];
var hex = datum[1];
it('should work for ' + i, function() {
var result = coinUtil.intToBufferSM(i);
buffertools.toHex(result).should.equal(hex);
});
});
});
}); });

View File

@ -112,39 +112,128 @@ var bigIntToValue = exports.bigIntToValue = function (valueBigInt) {
} }
}; };
var intTo64Bits = function(integer) {
return {
hi: Math.floor(integer / 4294967296),
lo: (integer & 0xFFFFFFFF) >>> 0
};
};
var fitsInNBits = function(integer, n) { var fitsInNBits = function(integer, n) {
// TODO: make this efficient!!! // TODO: make this efficient!!!
return integer.toString(2).replace('-','').length < n; return integer.toString(2).replace('-','').length < n;
} };
exports.intToBuffer = function(integer) { exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
var data = null; if (integer === 0) return 0;
if (fitsInNBits(integer, 8)) { return Math.ceil(((integer).toString(2).replace('-','').length + 1)/ 8);
data = new Buffer(1); };
data.writeInt8(integer, 0);
return data; exports.negativeBuffer = negativeBuffer = function(b) {
} else if (fitsInNBits(integer, 16)) { // implement two-complement negative
data = new Buffer(2); var c = new Buffer(b.length);
data.writeInt16LE(integer, 0); // negate each byte
return data; for (var i=0; i<b.length; i++){
} else if (fitsInNBits(integer, 32)) { c[i] = ~b[i];
data = new Buffer(4); if (c[i] < 0) c[i] += 256;
data.writeInt32LE(integer, 0); }
return data; // add one
for (var i=b.length - 1; i>=0; i--){
c[i] += 1;
if (c[i] >= 256) c[i] -= 256;
if (c[i] !== 0) break;
}
return c;
};
/*
* Transforms an integer into a buffer using two-complement encoding
* For example, 1 is encoded as 01 and -1 is encoded as ff
* For more info see:
* http://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement
*/
exports.intToBuffer2C = function(integer) {
var size = bytesNeededToStore(integer);
var buf = new Put();
var s = integer.toString(16);
var neg = s[0] === '-';
s = s.replace('-','');
for (var i=0; i<size; i++) {
var si = s.substring(s.length - 2*(i+1), s.length - 2*(i));
if (si.lenght === 1) {
si = '0' + si;
}
var pi = parseInt(si, 16);
buf.word8(pi);
}
var ret = buf.buffer();
if (neg) {
ret = buffertools.reverse(ret);
ret = negativeBuffer(ret);
ret = buffertools.reverse(ret);
}
return ret;
};
var padSign = function(b) {
var c;
if (b[0] & 0x80) {
c = new Buffer(b.length + 1);
b.copy(c, 1);
c[0] = 0;
} else { } else {
var x = intTo64Bits(integer); c = b;
data = new Buffer(8); }
data.writeInt32LE(x.hi, 0); // high part contains sign information (signed) return c;
data.writeUInt32LE(x.lo, 4); // low part encoded as unsigned integer }
return data;
/*
* Transforms an integer into a buffer using sign+magnitude encoding
* For example, 1 is encoded as 01 and -1 is encoded as 81
* For more info see:
* http://en.wikipedia.org/wiki/Signed_number_representations#Signed_magnitude_representation
*/
exports.intToBufferSM = function(v) {
if ("number" === typeof v) {
v = bignum(v);
}
var b, c;
var cmp = v.cmp(0);
if (cmp > 0) {
b = v.toBuffer();
c = padSign(b);
c = buffertools.reverse(c);
} else if (cmp == 0) {
c = new Buffer([]);
} else {
b = v.neg().toBuffer();
c = padSign(b);
c[0] |= 0x80;
c = buffertools.reverse(c);
}
return c;
};
/*
* Reverse of intToBufferSM
*/
exports.bufferSMToInt = function(v) {
if (!v.length) {
return bignum(0);
}
// Arithmetic operands must be in range [-2^31...2^31]
if (v.length > 4) {
throw new Error('Bigint cast overflow (> 4 bytes)');
}
var w = new Buffer(v.length);
v.copy(w);
w = buffertools.reverse(w);
var isNeg = w[0] & 0x80;
if (isNeg) {
w[0] &= 0x7f;
return bignum.fromBuffer(w).neg();
} else {
return bignum.fromBuffer(w);
} }
}; };
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';