fix negative number arithmetic!
This commit is contained in:
parent
19e15f91ca
commit
5505491e8d
|
@ -119,7 +119,10 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
case OP_14:
|
||||
case OP_15:
|
||||
case OP_16:
|
||||
this.stack.push(bigintToBuffer(opcode - OP_1 + 1));
|
||||
var opint = opcode - OP_1 + 1;
|
||||
var opbuf = bigintToBuffer(opint);
|
||||
console.log('op'+opcode+' = '+opint+', '+buffertools.toHex(opbuf));
|
||||
this.stack.push(opbuf);
|
||||
break;
|
||||
|
||||
case OP_NOP:
|
||||
|
@ -352,8 +355,6 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
case OP_SIZE:
|
||||
// (in -- in size)
|
||||
var value = bignum(this.stackTop().length);
|
||||
//var topSize = util.bytesNeededToStore(castBigint(this.stackTop()).toNumber());
|
||||
//var value = bignum(topSize);
|
||||
this.stack.push(bigintToBuffer(value));
|
||||
break;
|
||||
|
||||
|
@ -396,6 +397,10 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
// (x1 x2 - bool)
|
||||
var v1 = this.stackTop(2);
|
||||
var v2 = this.stackTop(1);
|
||||
console.log('equal');
|
||||
console.log(v1);
|
||||
console.log(v2);
|
||||
|
||||
var value = buffertools.compare(v1, v2) === 0;
|
||||
|
||||
// OP_NOTEQUAL is disabled because it would be too easy to say
|
||||
|
@ -476,6 +481,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||
// (x1 x2 -- out)
|
||||
var v1 = castBigint(this.stackTop(2));
|
||||
var v2 = castBigint(this.stackTop(1));
|
||||
console.log('add');
|
||||
console.log(v1);
|
||||
console.log(v2);
|
||||
var num;
|
||||
switch (opcode) {
|
||||
case OP_ADD:
|
||||
|
@ -874,7 +882,6 @@ 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)");
|
||||
|
@ -883,46 +890,55 @@ var castBigint = ScriptInterpreter.castBigint = function castBigint(v) {
|
|||
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();
|
||||
console.log('v ='+buffertools.toHex(w));
|
||||
var isNeg = w[0] & 0x80;
|
||||
if (isNeg) {
|
||||
for (var i = 0; i<w.length; i++) {
|
||||
console.log('before = '+w[i]);
|
||||
w[i] = ~w[i];
|
||||
console.log('after = '+w[i]);
|
||||
}
|
||||
console.log('w ='+buffertools.toHex(w));
|
||||
return bignum.fromBuffer(w).add(bignum(1)).neg();
|
||||
} else {
|
||||
// Positive number
|
||||
return bignum.fromBuffer(w);
|
||||
}
|
||||
};
|
||||
|
||||
var padSign = function(b) {
|
||||
var c;
|
||||
if (b[0] & 0x80) {
|
||||
c = new Buffer(b.length + 1);
|
||||
b.copy(c, 1);
|
||||
c[0] = 0;
|
||||
} else {
|
||||
c = b;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
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) {
|
||||
console.log('positive');
|
||||
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);
|
||||
}
|
||||
c = padSign(b);
|
||||
c = buffertools.reverse(c);
|
||||
} else if (cmp == 0) {
|
||||
return new Buffer([]);
|
||||
c = new Buffer([]);
|
||||
} else {
|
||||
console.log('negative '+v);
|
||||
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);
|
||||
}
|
||||
console.log(b);
|
||||
c = padSign(b);
|
||||
c = Util.negativeBuffer(c);
|
||||
c = buffertools.reverse(c);
|
||||
}
|
||||
return c;
|
||||
};
|
||||
|
||||
ScriptInterpreter.prototype.getResult = function getResult() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
var chai = chai || require('chai');
|
||||
var bitcore = bitcore || require('../bitcore');
|
||||
var buffertools = require('buffertools');
|
||||
|
||||
var should = chai.should();
|
||||
var testdata = testdata || require('./testdata');
|
||||
|
@ -22,6 +23,19 @@ describe('ScriptInterpreter', function() {
|
|||
var si = new ScriptInterpreter();
|
||||
should.exist(si);
|
||||
});
|
||||
var data = [
|
||||
[0, ''],
|
||||
[1, '01'],
|
||||
[-1, 'ff'],
|
||||
];
|
||||
data.forEach(function(datum) {
|
||||
var i = datum[0];
|
||||
var hex = datum[1];
|
||||
it('bigintToBuffer should work for ' + i, function() {
|
||||
var result = ScriptInterpreter.bigintToBuffer(i);
|
||||
buffertools.toHex(result).should.equal(hex);
|
||||
});
|
||||
});
|
||||
var i = 0;
|
||||
testdata.dataScriptValid.forEach(function(datum) {
|
||||
if (datum.length < 2) throw new Error('Invalid test data');
|
||||
|
@ -34,7 +48,7 @@ describe('ScriptInterpreter', function() {
|
|||
ScriptInterpreter.verify(Script.fromHumanReadable(scriptSig),
|
||||
Script.fromHumanReadable(scriptPubKey),
|
||||
null, 0, 0, // tx, output index, and hashtype
|
||||
function (err, result) {
|
||||
function(err, result) {
|
||||
should.not.exist(err);
|
||||
result.should.equal(true);
|
||||
done();
|
||||
|
@ -67,8 +81,3 @@ describe('ScriptInterpreter', function() {
|
|||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ var examples = [
|
|||
];
|
||||
|
||||
describe('Examples', function() {
|
||||
before(mute);
|
||||
after(unmute);
|
||||
//before(mute);
|
||||
//after(unmute);
|
||||
examples.forEach(function(example) {
|
||||
it('valid '+example, function() {
|
||||
var ex = require('../examples/'+example);
|
||||
|
|
|
@ -99,6 +99,8 @@ describe('util', function() {
|
|||
[4294967295, 'ffffffff00'],
|
||||
[4294967296, '0000000001'],
|
||||
[4294967297, '0100000001'],
|
||||
[2147483647, 'ffffff7f'],
|
||||
[-2147483647, '01000080'],
|
||||
//[-4294967295, 'feffffffffffffff'],
|
||||
//[-4294967296, 'feffffffffffffff'],
|
||||
//[-4294967297, 'feffffffffffffff'],
|
||||
|
|
51
util/util.js
51
util/util.js
|
@ -98,7 +98,7 @@ var formatBuffer = exports.formatBuffer = function (buffer, maxLen) {
|
|||
|
||||
var valueToBigInt = exports.valueToBigInt = function (valueBuffer) {
|
||||
if (Buffer.isBuffer(valueBuffer)) {
|
||||
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 8});
|
||||
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 'auto'});
|
||||
} else {
|
||||
return valueBuffer;
|
||||
}
|
||||
|
@ -108,16 +108,10 @@ var bigIntToValue = exports.bigIntToValue = function (valueBigInt) {
|
|||
if (Buffer.isBuffer(valueBigInt)) {
|
||||
return valueBigInt;
|
||||
} else {
|
||||
return valueBigInt.toBuffer({endian: 'little', size: 8});
|
||||
return valueBigInt.toBuffer({endian: 'little', size: 'auto'});
|
||||
}
|
||||
};
|
||||
|
||||
var intTo64Bits = function(integer) {
|
||||
return {
|
||||
hi: Math.floor(integer / 4294967296),
|
||||
lo: (integer & 0xFFFFFFFF) >>> 0
|
||||
};
|
||||
};
|
||||
var fitsInNBits = function(integer, n) {
|
||||
// TODO: make this efficient!!!
|
||||
return integer.toString(2).replace('-','').length < n;
|
||||
|
@ -127,6 +121,21 @@ exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
|
|||
return Math.ceil(((integer).toString(2).replace('-','').length + 1)/ 8);
|
||||
};
|
||||
|
||||
exports.negativeBuffer = negativeBuffer = function(b) {
|
||||
// implement two-complement negative
|
||||
var c = new Buffer(b.length);
|
||||
// negate each byte
|
||||
for (var i=0; i<b.length; i++){
|
||||
c[i] = ~b[i];
|
||||
}
|
||||
// add one
|
||||
for (var i=b.length - 1; i>=0; i--){
|
||||
c[i] += 1;
|
||||
if (c[i] !== 0) break;
|
||||
}
|
||||
console.log('negative of '+buffertools.toHex(b)+' is '+buffertools.toHex(c));
|
||||
return c;
|
||||
}
|
||||
|
||||
exports.intToBuffer = function(integer) {
|
||||
var size = bytesNeededToStore(integer);
|
||||
|
@ -139,27 +148,15 @@ exports.intToBuffer = function(integer) {
|
|||
if (si.lenght === 1) {
|
||||
si = '0' + si;
|
||||
}
|
||||
buf.word8((neg?-1:1)*parseInt(si, 16));
|
||||
buf.word8(parseInt(si, 16));
|
||||
}
|
||||
return buf.buffer();
|
||||
|
||||
var data = null;
|
||||
if (fitsInNBits(integer, 8)) {
|
||||
data = new Buffer(1);
|
||||
data.writeInt8(integer, 0);
|
||||
} else if (fitsInNBits(integer, 16)) {
|
||||
data = new Buffer(2);
|
||||
data.writeInt16LE(integer, 0);
|
||||
} else if (fitsInNBits(integer, 32)) {
|
||||
data = new Buffer(4);
|
||||
data.writeInt32LE(integer, 0);
|
||||
} else {
|
||||
var x = intTo64Bits(integer);
|
||||
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
|
||||
var ret = buf.buffer();
|
||||
if (neg) {
|
||||
ret = buffertools.reverse(ret);
|
||||
ret = negativeBuffer(ret);
|
||||
ret = buffertools.reverse(ret);
|
||||
}
|
||||
return data;
|
||||
return ret;
|
||||
};
|
||||
|
||||
var formatValue = exports.formatValue = function (valueBuffer) {
|
||||
|
|
Loading…
Reference in New Issue