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_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 = bigintToBuffer(opint);
|
||||||
|
console.log('op'+opcode+' = '+opint+', '+buffertools.toHex(opbuf));
|
||||||
|
this.stack.push(opbuf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OP_NOP:
|
case OP_NOP:
|
||||||
@ -352,8 +355,6 @@ 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);
|
||||||
//var topSize = util.bytesNeededToStore(castBigint(this.stackTop()).toNumber());
|
|
||||||
//var value = bignum(topSize);
|
|
||||||
this.stack.push(bigintToBuffer(value));
|
this.stack.push(bigintToBuffer(value));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -396,6 +397,10 @@ 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);
|
||||||
|
console.log('equal');
|
||||||
|
console.log(v1);
|
||||||
|
console.log(v2);
|
||||||
|
|
||||||
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
|
||||||
@ -476,6 +481,9 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
|
|||||||
// (x1 x2 -- out)
|
// (x1 x2 -- out)
|
||||||
var v1 = castBigint(this.stackTop(2));
|
var v1 = castBigint(this.stackTop(2));
|
||||||
var v2 = castBigint(this.stackTop(1));
|
var v2 = castBigint(this.stackTop(1));
|
||||||
|
console.log('add');
|
||||||
|
console.log(v1);
|
||||||
|
console.log(v2);
|
||||||
var num;
|
var num;
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case OP_ADD:
|
case OP_ADD:
|
||||||
@ -874,7 +882,6 @@ var castBigint = ScriptInterpreter.castBigint = function castBigint(v) {
|
|||||||
if (!v.length) {
|
if (!v.length) {
|
||||||
return bignum(0);
|
return bignum(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arithmetic operands must be in range [-2^31...2^31]
|
// Arithmetic operands must be in range [-2^31...2^31]
|
||||||
if (v.length > 4) {
|
if (v.length > 4) {
|
||||||
throw new Error("Bigint cast overflow (> 4 bytes)");
|
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);
|
var w = new Buffer(v.length);
|
||||||
v.copy(w);
|
v.copy(w);
|
||||||
w = buffertools.reverse(w);
|
w = buffertools.reverse(w);
|
||||||
if (w[0] & 0x80) {
|
console.log('v ='+buffertools.toHex(w));
|
||||||
w[0] &= 0x7f;
|
var isNeg = w[0] & 0x80;
|
||||||
return bignum.fromBuffer(w).neg();
|
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 {
|
} else {
|
||||||
// Positive number
|
|
||||||
return bignum.fromBuffer(w);
|
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) {
|
var bigintToBuffer = ScriptInterpreter.bigintToBuffer = function bigintToBuffer(v) {
|
||||||
if ("number" === typeof v) {
|
if ("number" === typeof v) {
|
||||||
v = bignum(v);
|
v = bignum(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
var b, c;
|
var b, c;
|
||||||
|
|
||||||
var cmp = v.cmp(0);
|
var cmp = v.cmp(0);
|
||||||
if (cmp > 0) {
|
if (cmp > 0) {
|
||||||
|
console.log('positive');
|
||||||
b = v.toBuffer();
|
b = v.toBuffer();
|
||||||
if (b[0] & 0x80) {
|
c = padSign(b);
|
||||||
c = new Buffer(b.length + 1);
|
c = buffertools.reverse(c);
|
||||||
b.copy(c, 1);
|
|
||||||
c[0] = 0;
|
|
||||||
return buffertools.reverse(c);
|
|
||||||
} else {
|
|
||||||
return buffertools.reverse(b);
|
|
||||||
}
|
|
||||||
} else if (cmp == 0) {
|
} else if (cmp == 0) {
|
||||||
return new Buffer([]);
|
c = new Buffer([]);
|
||||||
} else {
|
} else {
|
||||||
|
console.log('negative '+v);
|
||||||
b = v.neg().toBuffer();
|
b = v.neg().toBuffer();
|
||||||
if (b[0] & 0x80) {
|
console.log(b);
|
||||||
c = new Buffer(b.length + 1);
|
c = padSign(b);
|
||||||
b.copy(c, 1);
|
c = Util.negativeBuffer(c);
|
||||||
c[0] = 0x80;
|
c = buffertools.reverse(c);
|
||||||
return buffertools.reverse(c);
|
|
||||||
} else {
|
|
||||||
b[0] |= 0x80;
|
|
||||||
return buffertools.reverse(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return c;
|
||||||
};
|
};
|
||||||
|
|
||||||
ScriptInterpreter.prototype.getResult = function getResult() {
|
ScriptInterpreter.prototype.getResult = function getResult() {
|
||||||
|
|||||||
@ -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,6 +23,19 @@ describe('ScriptInterpreter', function() {
|
|||||||
var si = new ScriptInterpreter();
|
var si = new ScriptInterpreter();
|
||||||
should.exist(si);
|
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;
|
var i = 0;
|
||||||
testdata.dataScriptValid.forEach(function(datum) {
|
testdata.dataScriptValid.forEach(function(datum) {
|
||||||
if (datum.length < 2) throw new Error('Invalid test data');
|
if (datum.length < 2) throw new Error('Invalid test data');
|
||||||
@ -34,7 +48,7 @@ describe('ScriptInterpreter', function() {
|
|||||||
ScriptInterpreter.verify(Script.fromHumanReadable(scriptSig),
|
ScriptInterpreter.verify(Script.fromHumanReadable(scriptSig),
|
||||||
Script.fromHumanReadable(scriptPubKey),
|
Script.fromHumanReadable(scriptPubKey),
|
||||||
null, 0, 0, // tx, output index, and hashtype
|
null, 0, 0, // tx, output index, and hashtype
|
||||||
function (err, result) {
|
function(err, result) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
result.should.equal(true);
|
result.should.equal(true);
|
||||||
done();
|
done();
|
||||||
@ -67,8 +81,3 @@ describe('ScriptInterpreter', function() {
|
|||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,8 +15,8 @@ var examples = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
describe('Examples', function() {
|
describe('Examples', function() {
|
||||||
before(mute);
|
//before(mute);
|
||||||
after(unmute);
|
//after(unmute);
|
||||||
examples.forEach(function(example) {
|
examples.forEach(function(example) {
|
||||||
it('valid '+example, function() {
|
it('valid '+example, function() {
|
||||||
var ex = require('../examples/'+example);
|
var ex = require('../examples/'+example);
|
||||||
|
|||||||
@ -99,6 +99,8 @@ describe('util', function() {
|
|||||||
[4294967295, 'ffffffff00'],
|
[4294967295, 'ffffffff00'],
|
||||||
[4294967296, '0000000001'],
|
[4294967296, '0000000001'],
|
||||||
[4294967297, '0100000001'],
|
[4294967297, '0100000001'],
|
||||||
|
[2147483647, 'ffffff7f'],
|
||||||
|
[-2147483647, '01000080'],
|
||||||
//[-4294967295, 'feffffffffffffff'],
|
//[-4294967295, 'feffffffffffffff'],
|
||||||
//[-4294967296, 'feffffffffffffff'],
|
//[-4294967296, 'feffffffffffffff'],
|
||||||
//[-4294967297, '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) {
|
var valueToBigInt = exports.valueToBigInt = function (valueBuffer) {
|
||||||
if (Buffer.isBuffer(valueBuffer)) {
|
if (Buffer.isBuffer(valueBuffer)) {
|
||||||
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 8});
|
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 'auto'});
|
||||||
} else {
|
} else {
|
||||||
return valueBuffer;
|
return valueBuffer;
|
||||||
}
|
}
|
||||||
@ -108,16 +108,10 @@ var bigIntToValue = exports.bigIntToValue = function (valueBigInt) {
|
|||||||
if (Buffer.isBuffer(valueBigInt)) {
|
if (Buffer.isBuffer(valueBigInt)) {
|
||||||
return valueBigInt;
|
return valueBigInt;
|
||||||
} else {
|
} 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) {
|
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;
|
||||||
@ -127,6 +121,21 @@ exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
|
|||||||
return Math.ceil(((integer).toString(2).replace('-','').length + 1)/ 8);
|
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) {
|
exports.intToBuffer = function(integer) {
|
||||||
var size = bytesNeededToStore(integer);
|
var size = bytesNeededToStore(integer);
|
||||||
@ -139,27 +148,15 @@ exports.intToBuffer = function(integer) {
|
|||||||
if (si.lenght === 1) {
|
if (si.lenght === 1) {
|
||||||
si = '0' + si;
|
si = '0' + si;
|
||||||
}
|
}
|
||||||
buf.word8((neg?-1:1)*parseInt(si, 16));
|
buf.word8(parseInt(si, 16));
|
||||||
}
|
}
|
||||||
return buf.buffer();
|
var ret = buf.buffer();
|
||||||
|
if (neg) {
|
||||||
var data = null;
|
ret = buffertools.reverse(ret);
|
||||||
if (fitsInNBits(integer, 8)) {
|
ret = negativeBuffer(ret);
|
||||||
data = new Buffer(1);
|
ret = buffertools.reverse(ret);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
return data;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatValue = exports.formatValue = function (valueBuffer) {
|
var formatValue = exports.formatValue = function (valueBuffer) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user