script: start using scriptnum implementation.

This commit is contained in:
Christopher Jeffrey 2017-08-17 11:00:58 -07:00
parent 1b0ae3ca79
commit a6b2c23a41
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
15 changed files with 294 additions and 280 deletions

View File

@ -3,13 +3,13 @@ Scripts are array-like objects with some helper functions.
``` js
var bcoin = require('bcoin');
var assert = require('assert');
var BN = bcoin.bn;
var ScriptNum = bcoin.scriptnum;
var opcodes = bcoin.script.opcodes;
var output = new bcoin.script();
output.push(opcodes.OP_DROP);
output.push(opcodes.OP_ADD);
output.push(new BN(7));
output.push(new ScriptNum(7));
output.push(opcodes.OP_NUMEQUAL);
// Compile the script to its binary representation
// (you must do this if you change something!).
@ -18,8 +18,8 @@ assert(output.getSmall(2) === 7); // compiled as OP_7
var input = new bcoin.script();
input.set(0, 'hello world'); // add some metadata
input.push(new BN(2));
input.push(new BN(5));
input.push(new ScriptNum(2));
input.push(new ScriptNum(5));
input.push(input.shift());
assert(input.getString(2) === 'hello world');
input.compile();
@ -40,10 +40,10 @@ Stack object (an array-like object containing Buffers).
``` js
var witness = new bcoin.witness();
witness.push(new BN(2));
witness.push(new BN(5));
witness.push(new ScriptNum(2));
witness.push(new ScriptNum(5));
witness.push('hello world');
var stack = witness.toStack();
output.execute(stack);
```
```

View File

@ -240,6 +240,7 @@ bcoin.txscript = require('./script');
bcoin.opcode = require('./script/opcode');
bcoin.program = require('./script/program');
bcoin.script = require('./script/script');
bcoin.scriptnum = require('./script/scriptnum');
bcoin.sigcache = require('./script/sigcache');
bcoin.stack = require('./script/stack');
bcoin.witness = require('./script/witness');

View File

@ -277,6 +277,7 @@ bcoin.define('txscript', './script');
bcoin.define('opcode', './script/opcode');
bcoin.define('program', './script/program');
bcoin.define('script', './script/script');
bcoin.define('scriptnum', './script/scriptnum');
bcoin.define('sigcache', './script/sigcache');
bcoin.define('stack', './script/stack');
bcoin.define('witness', './script/witness');

View File

@ -11,7 +11,6 @@ const assert = require('assert');
const util = require('../utils/util');
const digest = require('../crypto/digest');
const merkle = require('../crypto/merkle');
const BN = require('../crypto/bn');
const StaticWriter = require('../utils/staticwriter');
const Address = require('../primitives/address');
const TX = require('../primitives/tx');
@ -23,6 +22,7 @@ const policy = require('../protocol/policy');
const encoding = require('../utils/encoding');
const CoinView = require('../coins/coinview');
const Script = require('../script/script');
const ScriptNum = require('../script/scriptnum');
const common = require('./common');
const DUMMY = Buffer.alloc(0);
@ -241,7 +241,8 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
const input = new Input();
// Height (required in v2+ blocks)
input.script.push(new BN(this.height));
const height = ScriptNum.fromNumber(this.height);
input.script.push(height);
// Coinbase flags.
input.script.push(encoding.ZERO_HASH160);

View File

@ -12,7 +12,6 @@
*/
const assert = require('assert');
const BN = require('../crypto/bn');
const util = require('../utils/util');
const secp256k1 = require('../crypto/secp256k1');
@ -532,6 +531,28 @@ exports.isSignatureEncoding = function isSignatureEncoding(sig) {
return true;
};
/**
* Cast a big number or Buffer to a bool.
* @see CastToBool
* @param {Buffer} value
* @returns {Boolean}
*/
exports.toBool = function toBool(value) {
assert(Buffer.isBuffer(value));
for (let i = 0; i < value.length; i++) {
if (value[i] !== 0) {
// Cannot be negative zero
if (i === value.length - 1 && value[i] === 0x80)
return false;
return true;
}
}
return false;
};
/**
* Format script code into a human readable-string.
* @param {Array} code
@ -692,109 +713,6 @@ exports.formatStackASM = function formatStackASM(items, decode) {
return out.join(' ');
};
/**
* Create a CScriptNum.
* @param {Buffer} value
* @param {Boolean?} minimal
* @param {Number?} size - Max size in bytes.
* @returns {BN}
* @throws {ScriptError}
*/
exports.num = function num(value, minimal, size) {
assert(Buffer.isBuffer(value));
if (size == null)
size = 4;
if (value.length > size)
throw new exports.ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
if (minimal && value.length > 0) {
// If the low bits on the last byte are unset,
// fail if the value's second to last byte does
// not have the high bit set. A number can't
// justify having the last byte's low bits unset
// unless they ran out of space for the sign bit
// in the second to last bit. We also fail on [0]
// to avoid negative zero (also avoids positive
// zero).
if (!(value[value.length - 1] & 0x7f)) {
if (value.length === 1 || !(value[value.length - 2] & 0x80)) {
throw new exports.ScriptError(
'UNKNOWN_ERROR',
'Non-minimally encoded Script number.');
}
}
}
if (value.length === 0)
return new BN(0);
const result = new BN(value, 'le');
// If the input vector's most significant byte is
// 0x80, remove it from the result's msb and return
// a negative.
// Equivalent to:
// -(result & ~(0x80 << (8 * (value.length - 1))))
if (value[value.length - 1] & 0x80)
result.setn((value.length * 8) - 1, 0).ineg();
return result;
};
/**
* Create a script array. Will convert Numbers and big
* numbers to a little-endian buffer while taking into
* account negative zero, minimaldata, etc.
* @example
* assert.deepEqual(Script.array(0), Buffer.alloc(0));
* assert.deepEqual(Script.array(0xffee), Buffer.from('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0xffee)), Buffer.from('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0x1e).neg()), Buffer.from('9e', 'hex'));
* @param {Number|BN} value
* @returns {Buffer}
*/
exports.array = function array(value) {
if (typeof value === 'number') {
assert(util.isInt(value));
value = new BN(value);
}
assert(BN.isBN(value));
if (value.cmpn(0) === 0)
return exports.STACK_FALSE;
// If the most significant byte is >= 0x80
// and the value is positive, push a new
// zero-byte to make the significant
// byte < 0x80 again.
// If the most significant byte is >= 0x80
// and the value is negative, push a new
// 0x80 byte that will be popped off when
// converting to an integral.
// If the most significant byte is < 0x80
// and the value is negative, add 0x80 to
// it, since it will be subtracted and
// interpreted as a negative when
// converting to an integral.
const neg = value.cmpn(0) < 0;
const result = value.toArray('le');
if (result[result.length - 1] & 0x80)
result.push(neg ? 0x80 : 0);
else if (neg)
result[result.length - 1] |= 0x80;
return Buffer.from(result);
};
/**
* An error thrown from the scripting system,
* potentially pertaining to Script execution.

View File

@ -14,7 +14,7 @@ exports.common = require('./common');
exports.Opcode = require('./opcode');
exports.Program = require('./program');
exports.Script = require('./script');
// exports.ScriptNum = require('./scriptnum');
exports.ScriptNum = require('./scriptnum');
exports.sigcache = require('./sigcache');
exports.Stack = require('./stack');
exports.Witness = require('./witness');

View File

@ -8,7 +8,7 @@
'use strict';
const assert = require('assert');
const BN = require('../crypto/bn');
const ScriptNum = require('./scriptnum');
const util = require('../utils/util');
const common = require('./common');
const BufferReader = require('../utils/reader');
@ -334,12 +334,23 @@ Opcode.fromPush = function fromPush(data) {
/**
* Instantiate an opcode from a Number.
* @param {Number|BN} num
* @param {Number|ScriptNum|BN} num
* @returns {Opcode}
*/
Opcode.fromNumber = function fromNumber(num) {
return Opcode.fromData(common.array(num));
return Opcode.fromData(ScriptNum.encode(num));
};
/**
* Instantiate an opcode from a Number.
* @param {Boolean} value
* @returns {Opcode}
*/
Opcode.fromBool = function fromBool(value) {
assert(typeof value === 'boolean');
return Opcode.fromSmall(value ? 1 : 0);
};
/**
@ -368,7 +379,7 @@ Opcode.fromString = function fromString(data, enc) {
/**
* Instantiate a pushdata opcode from anything.
* @param {String|Buffer|Number|BN|Opcode} data
* @param {String|Buffer|Number|ScriptNum|Opcode} data
* @returns {Opcode}
*/
@ -385,7 +396,10 @@ Opcode.from = function from(data) {
if (typeof data === 'string')
return Opcode.fromString(data, 'utf8');
if (BN.isBN(data))
if (typeof data === 'boolean')
return Opcode.fromBool(data);
if (ScriptNum.isEncodable(data))
return Opcode.fromNumber(data);
throw new Error('Bad data for opcode.');

View File

@ -8,7 +8,6 @@
'use strict';
const assert = require('assert');
const BN = require('../crypto/bn');
const consensus = require('../protocol/consensus');
const policy = require('../protocol/policy');
const util = require('../utils/util');
@ -20,6 +19,7 @@ const StaticWriter = require('../utils/staticwriter');
const Program = require('./program');
const Opcode = require('./opcode');
const Stack = require('./stack');
const ScriptNum = require('./scriptnum');
const common = require('./common');
const encoding = require('../utils/encoding');
const secp256k1 = require('../crypto/secp256k1');
@ -27,9 +27,7 @@ const Address = require('../primitives/address');
const opcodes = common.opcodes;
const scriptTypes = common.types;
const ScriptError = common.ScriptError;
const STACK_TRUE = common.STACK_TRUE;
const STACK_FALSE = common.STACK_FALSE;
const STACK_NEGATE = common.STACK_NEGATE;
/**
* Represents a input or output script.
@ -521,17 +519,14 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
switch (op.value) {
case opcodes.OP_0: {
stack.push(STACK_FALSE);
stack.pushInt(0);
break;
}
case opcodes.OP_1NEGATE: {
stack.push(STACK_NEGATE);
break;
}
case opcodes.OP_1: {
stack.push(STACK_TRUE);
stack.pushInt(-1);
break;
}
case opcodes.OP_1:
case opcodes.OP_2:
case opcodes.OP_3:
case opcodes.OP_4:
@ -547,7 +542,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
case opcodes.OP_14:
case opcodes.OP_15:
case opcodes.OP_16: {
stack.push(Buffer.from([op.value - 0x50]));
stack.pushInt(op.value - 0x50);
break;
}
case opcodes.OP_NOP: {
@ -567,9 +562,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let locktime = Script.num(stack.top(-1), minimal, 5);
let locktime = stack.num(-1, minimal, 5);
if (locktime.cmpn(0) < 0)
if (locktime.isNeg())
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
locktime = locktime.toNumber();
@ -593,9 +588,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let locktime = Script.num(stack.top(-1), minimal, 5);
let locktime = stack.num(-1, minimal, 5);
if (locktime.cmpn(0) < 0)
if (locktime.isNeg())
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
locktime = locktime.toNumber();
@ -625,17 +620,17 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 1)
throw new ScriptError('UNBALANCED_CONDITIONAL', op, ip);
val = stack.top(-1);
if (version === 1 && (flags & Script.flags.VERIFY_MINIMALIF)) {
if (val.length > 1)
const item = stack.top(-1);
if (item.length > 1)
throw new ScriptError('MINIMALIF');
if (val.length === 1 && val[0] !== 1)
if (item.length === 1 && item[0] !== 1)
throw new ScriptError('MINIMALIF');
}
val = Script.bool(val);
val = stack.bool(-1);
if (op.value === opcodes.OP_NOTIF)
val = !val;
@ -676,7 +671,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
if (!Script.bool(stack.top(-1)))
if (!stack.bool(-1))
throw new ScriptError('VERIFY', op, ip);
stack.pop();
@ -767,14 +762,15 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
const val = stack.top(-1);
if (Script.bool(val))
if (stack.bool(-1)) {
const val = stack.top(-1);
stack.push(val);
}
break;
}
case opcodes.OP_DEPTH: {
stack.push(Script.array(stack.length));
stack.pushInt(stack.length);
break;
}
case opcodes.OP_DROP: {
@ -810,7 +806,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
const num = Script.num(stack.top(-1), minimal).toNumber();
const num = stack.int(-1, minimal);
stack.pop();
if (num < 0 || num >= stack.length)
@ -850,7 +846,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
stack.push(Script.array(stack.top(-1).length));
stack.pushInt(stack.top(-1).length);
break;
}
case opcodes.OP_EQUAL:
@ -866,7 +862,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
stack.pop();
stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
stack.pushBool(res);
if (op.value === opcodes.OP_EQUALVERIFY) {
if (!res)
@ -885,7 +881,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let num = Script.num(stack.top(-1), minimal);
let num = stack.num(-1, minimal);
let cmp;
switch (op.value) {
case opcodes.OP_1ADD:
@ -901,12 +898,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
num.iabs();
break;
case opcodes.OP_NOT:
num = num.cmpn(0) === 0;
num = new BN(num ? 1 : 0);
cmp = num.isZero();
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_0NOTEQUAL:
num = num.cmpn(0) !== 0;
num = new BN(num ? 1 : 0);
cmp = !num.isZero();
num = ScriptNum.fromBool(cmp);
break;
default:
assert(false, 'Fatal script error.');
@ -914,7 +911,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
}
stack.pop();
stack.push(Script.array(num));
stack.pushNum(num);
break;
}
@ -934,9 +931,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
const n1 = Script.num(stack.top(-2), minimal);
const n2 = Script.num(stack.top(-1), minimal);
let num;
const n1 = stack.num(-2, minimal);
const n2 = stack.num(-1, minimal);
let num, cmp;
switch (op.value) {
case opcodes.OP_ADD:
@ -946,46 +943,46 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
num = n1.isub(n2);
break;
case opcodes.OP_BOOLAND:
num = n1.cmpn(0) !== 0 && n2.cmpn(0) !== 0;
num = new BN(num ? 1 : 0);
cmp = n1.toBool() && n2.toBool();
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_BOOLOR:
num = n1.cmpn(0) !== 0 || n2.cmpn(0) !== 0;
num = new BN(num ? 1 : 0);
cmp = n1.toBool() || n2.toBool();
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_NUMEQUAL:
num = n1.cmp(n2) === 0;
num = new BN(num ? 1 : 0);
cmp = n1.eq(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_NUMEQUALVERIFY:
num = n1.cmp(n2) === 0;
num = new BN(num ? 1 : 0);
cmp = n1.eq(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_NUMNOTEQUAL:
num = n1.cmp(n2) !== 0;
num = new BN(num ? 1 : 0);
cmp = !n1.eq(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_LESSTHAN:
num = n1.cmp(n2) < 0;
num = new BN(num ? 1 : 0);
cmp = n1.lt(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_GREATERTHAN:
num = n1.cmp(n2) > 0;
num = new BN(num ? 1 : 0);
cmp = n1.gt(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_LESSTHANOREQUAL:
num = n1.cmp(n2) <= 0;
num = new BN(num ? 1 : 0);
cmp = n1.lte(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_GREATERTHANOREQUAL:
num = n1.cmp(n2) >= 0;
num = new BN(num ? 1 : 0);
cmp = n1.gte(n2);
num = ScriptNum.fromBool(cmp);
break;
case opcodes.OP_MIN:
num = n1.cmp(n2) < 0 ? n1 : n2;
num = ScriptNum.min(n1, n2);
break;
case opcodes.OP_MAX:
num = n1.cmp(n2) > 0 ? n1 : n2;
num = ScriptNum.max(n1, n2);
break;
default:
assert(false, 'Fatal script error.');
@ -994,10 +991,10 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
stack.pop();
stack.pop();
stack.push(Script.array(num));
stack.pushNum(num);
if (op.value === opcodes.OP_NUMEQUALVERIFY) {
if (!Script.bool(stack.top(-1)))
if (!stack.bool(-1))
throw new ScriptError('NUMEQUALVERIFY', op, ip);
stack.pop();
}
@ -1008,17 +1005,17 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
const n1 = Script.num(stack.top(-3), minimal);
const n2 = Script.num(stack.top(-2), minimal);
const n3 = Script.num(stack.top(-1), minimal);
const n1 = stack.num(-3, minimal);
const n2 = stack.num(-2, minimal);
const n3 = stack.num(-1, minimal);
const val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
const val = n2.lte(n1) && n1.lt(n3);
stack.pop();
stack.pop();
stack.pop();
stack.push(val ? STACK_TRUE : STACK_FALSE);
stack.pushBool(val);
break;
}
case opcodes.OP_RIPEMD160: {
@ -1095,7 +1092,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
stack.pop();
stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
stack.pushBool(res);
if (op.value === opcodes.OP_CHECKSIGVERIFY) {
if (!res)
@ -1114,7 +1111,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < i)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let n = Script.num(stack.top(-i), minimal).toNumber();
let n = stack.int(-i, minimal);
let ikey2 = n + 2;
if (!(n >= 0 && n <= consensus.MAX_MULTISIG_PUBKEYS))
@ -1132,7 +1129,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length < i)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let m = Script.num(stack.top(-i), minimal).toNumber();
let m = stack.int(-i, minimal);
if (!(m >= 0 && m <= n))
throw new ScriptError('SIG_COUNT', op, ip);
@ -1203,7 +1200,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
stack.pop();
stack.push(res ? STACK_TRUE : STACK_FALSE);
stack.pushBool(res);
if (op.value === opcodes.OP_CHECKMULTISIGVERIFY) {
if (!res)
@ -1226,58 +1223,6 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
throw new ScriptError('UNBALANCED_CONDITIONAL');
};
/**
* Cast a big number or Buffer to a bool.
* @see CastToBool
* @param {BN|Buffer} value
* @returns {Boolean}
*/
Script.bool = function bool(value) {
assert(Buffer.isBuffer(value));
for (let i = 0; i < value.length; i++) {
if (value[i] !== 0) {
// Cannot be negative zero
if (i === value.length - 1 && value[i] === 0x80)
return false;
return true;
}
}
return false;
};
/**
* Create a CScriptNum.
* @param {Buffer} value
* @param {Boolean?} minimal
* @param {Number?} size - Max size in bytes.
* @returns {BN}
* @throws {ScriptError}
*/
Script.num = function num(value, minimal, size) {
return common.num(value, minimal, size);
};
/**
* Create a script array. Will convert Numbers and big
* numbers to a little-endian buffer while taking into
* account negative zero, minimaldata, etc.
* @example
* assert.deepEqual(Script.array(0), Buffer.alloc(0));
* assert.deepEqual(Script.array(0xffee), Buffer.from('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0xffee)), Buffer.from('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0x1e).neg()), Buffer.from('9e', 'hex'));
* @param {Number|BN} value
* @returns {Buffer}
*/
Script.array = function array(value) {
return common.array(value);
};
/**
* Remove all matched data elements from
* a script's code (used to remove signatures
@ -2288,7 +2233,7 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) {
// Deserialize the height.
let height;
try {
height = Script.num(data, true, 6);
height = ScriptNum.decode(data, true, 6);
} catch (e) {
return -1;
}
@ -2331,7 +2276,7 @@ Script.prototype.test = function test(filter) {
/**
* Unshift an item onto the `code` array.
* @param {Number|String|BN|Buffer} data
* @param {Number|String|ScriptNum|Buffer} data
* @returns {Number} Length.
*/
@ -2341,7 +2286,7 @@ Script.prototype.unshift = function unshift(data) {
/**
* Push an item onto the `code` array.
* @param {Number|String|BN|Buffer} data
* @param {Number|String|ScriptNum|Buffer} data
* @returns {Number} Length.
*/
@ -2395,7 +2340,7 @@ Script.prototype.remove = function remove(i) {
/**
* Insert an item into the `code` array.
* @param {Number} index
* @param {Number|String|BN|Buffer} data
* @param {Number|String|ScriptNum|Buffer} data
*/
Script.prototype.insert = function insert(i, data) {
@ -2436,7 +2381,7 @@ Script.prototype.getSmall = function getSmall(i) {
/**
* Get a number from the `code` array (5-byte limit).
* @params {Number} index
* @returns {BN}
* @returns {ScriptNum}
*/
Script.prototype.getNumber = function getNumber(i) {
@ -2444,12 +2389,12 @@ Script.prototype.getNumber = function getNumber(i) {
const op = this.code[i];
if (small !== -1)
return new BN(small);
return ScriptNum.fromInt(small);
if (!op || !op.data || op.data.length > 5)
return null;
return Script.num(op.data, false, 5);
return ScriptNum.decode(op.data, false, 5);
};
/**
@ -2478,7 +2423,7 @@ Script.prototype.clear = function clear() {
/**
* Set an item in the `code` array.
* @param {Number} index
* @param {Buffer|Number|String|BN} data
* @param {Buffer|Number|String|ScriptNum} data
*/
Script.prototype.set = function set(i, data) {
@ -2688,7 +2633,7 @@ Script.prototype.fromString = function fromString(code) {
}
if (/^-?\d+$/.test(item)) {
const num = new BN(item, 10);
const num = ScriptNum.fromString(item, 10);
const op = Opcode.fromNumber(num);
bw.writeBytes(op.toRaw());
continue;
@ -2760,7 +2705,7 @@ Script.verify = function verify(input, witness, output, tx, index, value, flags)
output.execute(stack, flags, tx, index, value, 0);
// Verify the stack values.
if (stack.length === 0 || !Script.bool(stack.top(-1)))
if (stack.length === 0 || !stack.bool(-1))
throw new ScriptError('EVAL_FALSE');
let hadWitness = false;
@ -2800,7 +2745,7 @@ Script.verify = function verify(input, witness, output, tx, index, value, flags)
redeem.execute(stack, flags, tx, index, value, 0);
// Verify the the stack values.
if (stack.length === 0 || !Script.bool(stack.top(-1)))
if (stack.length === 0 || !stack.bool(-1))
throw new ScriptError('EVAL_FALSE');
if ((flags & Script.flags.VERIFY_WITNESS) && redeem.isProgram()) {
@ -2901,7 +2846,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, index,
redeem.execute(stack, flags, tx, index, value, 1);
// Verify the stack values.
if (stack.length !== 1 || !Script.bool(stack.top(-1)))
if (stack.length !== 1 || !stack.bool(-1))
throw new ScriptError('EVAL_FALSE');
};

View File

@ -7,7 +7,13 @@
'use strict';
const assert = require('assert');
const common = require('./common');
const ScriptNum = require('./scriptnum');
const ScriptError = common.ScriptError;
const STACK_FALSE = common.STACK_FALSE;
const STACK_TRUE = common.STACK_TRUE;
const STACK_NEGATE = common.STACK_NEGATE;
/**
* Represents the stack of a Script during execution.
@ -84,9 +90,63 @@ Stack.prototype.clone = function clone() {
*/
Stack.prototype.push = function push(item) {
assert(Buffer.isBuffer(item));
return this.items.push(item);
};
/**
* Push boolean onto stack.
* @see Array#push
* @param {Boolean} value
* @returns {Number} Stack size.
*/
Stack.prototype.pushBool = function pushBool(value) {
assert(typeof value === 'boolean');
return this.items.push(value ? STACK_TRUE : STACK_FALSE);
};
/**
* Push script number onto stack.
* @see Array#push
* @param {ScriptNum} num
* @returns {Number} Stack size.
*/
Stack.prototype.pushNum = function pushNum(num) {
assert(ScriptNum.isScriptNum(num));
return this.items.push(num.encode());
};
/**
* Push integer onto stack.
* @see Array#push
* @param {Number} value
* @returns {Number} Stack size.
*/
Stack.prototype.pushInt = function pushInt(value) {
assert(typeof value === 'number');
if (value >= -1 && value <= 16) {
switch (value) {
case -1:
return this.items.push(STACK_NEGATE);
case 0:
return this.items.push(STACK_FALSE);
case 1:
return this.items.push(STACK_TRUE);
}
const item = Buffer.allocUnsafe(1);
item[0] = value;
return this.items.push(item);
}
const num = ScriptNum.fromNumber(value);
return this.items.push(num.encode());
};
/**
* Unshift item from stack.
* @see Array#unshift
@ -95,6 +155,7 @@ Stack.prototype.push = function push(item) {
*/
Stack.prototype.unshift = function unshift(item) {
assert(Buffer.isBuffer(item));
return this.items.unshift(item);
};
@ -127,6 +188,8 @@ Stack.prototype.splice = function splice(i, remove, insert) {
if (insert === undefined)
return this.items.splice(i, remove);
assert(Buffer.isBuffer(insert));
return this.items.splice(i, remove, insert);
};
@ -158,6 +221,8 @@ Stack.prototype.insert = function insert(i, item) {
if (i < 0)
i = this.items.length + i;
assert(Buffer.isBuffer(item));
this.items.splice(i, 0, item);
};
@ -204,9 +269,69 @@ Stack.prototype.shift = function shift() {
*/
Stack.prototype.get = function get(i) {
if (i < 0)
i = this.items.length + i;
return this.items[i];
};
/**
* Get a stack item by index
* and decode as a boolean.
* @param {Number} index
* @returns {Boolean}
* @throws on invalid stack operation
*/
Stack.prototype.bool = function bool(i) {
if (i < 0)
i = this.items.length + i;
if (i < 0 || i >= this.items.length)
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
return common.toBool(this.items[i]);
};
/**
* Get a stack item by index
* and decode as a scriptnum.
* @param {Number} index
* @param {Boolean?} minimal
* @param {Number?} limit
* @returns {ScriptNum}
* @throws on invalid stack operation
*/
Stack.prototype.num = function num(i, minimal, limit) {
if (i < 0)
i = this.items.length + i;
if (i < 0 || i >= this.items.length)
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
return ScriptNum.decode(this.items[i], minimal, limit);
};
/**
* Get a stack item by index
* and decode as an integer.
* @param {Number} index
* @param {Boolean?} minimal
* @returns {Number}
* @throws on invalid stack operation
*/
Stack.prototype.int = function int(i, minimal) {
if (i < 0)
i = this.items.length + i;
if (i < 0 || i >= this.items.length)
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
return ScriptNum.decode(this.items[i], minimal).getInt();
};
/**
* Get a stack item relative to
* the top of the stack.
@ -239,6 +364,8 @@ Stack.prototype.set = function set(i, value) {
if (i < 0)
i = this.items.length + i;
assert(Buffer.isBuffer(value));
this.items[i] = value;
return value;

View File

@ -8,7 +8,7 @@
'use strict';
const assert = require('assert');
const BN = require('../crypto/bn');
const ScriptNum = require('./scriptnum');
const util = require('../utils/util');
const Script = require('./script');
const common = require('./common');
@ -20,6 +20,7 @@ const Address = require('../primitives/address');
const Stack = require('./stack');
const opcodes = common.opcodes;
const scriptTypes = common.types;
const STACK_TRUE = common.STACK_TRUE;
const STACK_FALSE = common.STACK_FALSE;
const STACK_NEGATE = common.STACK_NEGATE;
@ -391,7 +392,7 @@ Witness.fromJSON = function fromJSON(json) {
/**
* Unshift an item onto the witness vector.
* @param {Number|String|Buffer|BN} data
* @param {Number|String|Buffer|ScriptNum} data
* @returns {Number}
*/
@ -401,7 +402,7 @@ Witness.prototype.unshift = function unshift(data) {
/**
* Push an item onto the witness vector.
* @param {Number|String|Buffer|BN} data
* @param {Number|String|Buffer|ScriptNum} data
* @returns {Number}
*/
@ -440,7 +441,7 @@ Witness.prototype.remove = function remove(i) {
/**
* Insert an item into the witness vector.
* @param {Number} index
* @param {Number|String|Buffer|BN} data
* @param {Number|String|Buffer|ScriptNum} data
*/
Witness.prototype.insert = function insert(i, data) {
@ -478,7 +479,7 @@ Witness.prototype.getSmall = function getSmall(i) {
/**
* Get a number from the witness vector.
* @param {Number} index
* @returns {BN}
* @returns {ScriptNum}
*/
Witness.prototype.getNumber = function getNumber(i) {
@ -487,7 +488,7 @@ Witness.prototype.getNumber = function getNumber(i) {
if (!item || item.length > 5)
return null;
return common.num(item, false, 5);
return ScriptNum.decode(item, false, 5);
};
/**
@ -516,7 +517,7 @@ Witness.prototype.clear = function clear() {
/**
* Set an item in the witness vector.
* @param {Number} index
* @param {Number|String|Buffer|BN} data
* @param {Number|String|Buffer|ScriptNum} data
*/
Witness.prototype.set = function set(i, data) {
@ -526,7 +527,7 @@ Witness.prototype.set = function set(i, data) {
/**
* Encode a witness item.
* @param {Number|String|Buffer|BN} data
* @param {Number|String|Buffer|ScriptNum} data
* @returns {Buffer}
*/
@ -534,6 +535,9 @@ Witness.encodeItem = function encodeItem(data) {
if (data instanceof Opcode)
data = data.data || data.value;
if (Buffer.isBuffer(data))
return data;
if (typeof data === 'number') {
if (data === opcodes.OP_1NEGATE)
return STACK_NEGATE;
@ -547,13 +551,16 @@ Witness.encodeItem = function encodeItem(data) {
throw new Error('Non-push opcode in witness.');
}
if (BN.isBN(data))
return common.array(data);
if (typeof data === 'string')
return Buffer.from(data, 'utf8');
return data;
if (typeof data === 'boolean')
return data ? STACK_TRUE : STACK_FALSE;
if (ScriptNum.isEncodable(data))
return ScriptNum.encode(data);
throw new Error('Not a witness item.');
};
/**

View File

@ -308,7 +308,7 @@ function fuzzSimple(flags) {
if (stack.length === 0)
continue;
if (!Script.bool(stack.top(-1)))
if (!stack.bool(-1))
continue;
if (isPushOnly(output))

View File

@ -1,6 +1,5 @@
'use strict';
const BN = require('../lib/crypto/bn');
const util = require('../lib/utils/util');
const consensus = require('../lib/protocol/consensus');
const encoding = require('../lib/utils/encoding');
@ -8,6 +7,7 @@ const TX = require('../lib/primitives/tx');
const Block = require('../lib/primitives/block');
const Script = require('../lib/script/script');
const Opcode = require('../lib/script/opcode');
const ScriptNum = require('../lib/script/scriptnum');
const opcodes = Script.opcodes;
function createGenesisBlock(options) {
@ -41,7 +41,7 @@ function createGenesisBlock(options) {
index: 0xffffffff
},
script: [
Opcode.fromNumber(new BN(486604799)),
Opcode.fromNumber(new ScriptNum(486604799)),
Opcode.fromPush(Buffer.from([4])),
Opcode.fromData(flags)
],

View File

@ -4,7 +4,7 @@
'use strict';
const assert = require('./util/assert');
const BN = require('../lib/crypto/bn');
const ScriptNum = require('../lib/script/scriptnum');
const consensus = require('../lib/protocol/consensus');
const encoding = require('../lib/utils/encoding');
const Coin = require('../lib/primitives/coin');
@ -79,7 +79,7 @@ async function mineCSV(fund) {
spend.addOutput({
script: [
Script.array(new BN(1)),
ScriptNum.encode(1),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10000
@ -419,7 +419,7 @@ describe('Chain', function() {
spend.addOutput({
script: [
Script.array(new BN(2)),
ScriptNum.encode(2),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10000
@ -444,7 +444,7 @@ describe('Chain', function() {
spend.addOutput({
script: [
Script.array(new BN(1)),
ScriptNum.encode(1),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 1 * 1e8
@ -479,7 +479,7 @@ describe('Chain', function() {
spend.addOutput({
script: [
Script.array(new BN(2)),
ScriptNum.encode(2),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 1 * 1e8
@ -787,12 +787,12 @@ describe('Chain', function() {
const flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
const redeem = new Script();
redeem.push(new BN(20));
redeem.push(new ScriptNum(20));
for (let i = 0; i < 20; i++)
redeem.push(encoding.ZERO_KEY);
redeem.push(new BN(20));
redeem.push(new ScriptNum(20));
redeem.push(opcodes.OP_CHECKMULTISIG);
redeem.compile();
@ -828,12 +828,12 @@ describe('Chain', function() {
const job = await cpu.createJob();
const script = new Script();
script.push(new BN(20));
script.push(new ScriptNum(20));
for (let i = 0; i < 20; i++)
script.push(encoding.ZERO_KEY);
script.push(new BN(20));
script.push(new ScriptNum(20));
script.push(opcodes.OP_CHECKMULTISIG);
script.compile();

View File

@ -4,7 +4,7 @@
'use strict';
const assert = require('./util/assert');
const BN = require('../lib/crypto/bn');
const ScriptNum = require('../lib/script/scriptnum');
const consensus = require('../lib/protocol/consensus');
const co = require('../lib/utils/co');
const Coin = require('../lib/primitives/coin');
@ -63,7 +63,7 @@ async function mineCSV(fund) {
spend.addOutput({
script: [
Script.array(new BN(1)),
ScriptNum.encode(1),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
@ -348,7 +348,7 @@ describe('Node', function() {
spend.addOutput({
script: [
Script.array(new BN(2)),
ScriptNum.encode(2),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
@ -373,7 +373,7 @@ describe('Node', function() {
spend.addOutput({
script: [
Script.array(new BN(1)),
ScriptNum.encode(1),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
@ -418,7 +418,7 @@ describe('Node', function() {
spend.addOutput({
script: [
Script.array(new BN(2)),
ScriptNum.encode(2),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8

View File

@ -18,7 +18,7 @@ function isSuccess(stack) {
if (stack.length === 0)
return false;
if (!Script.bool(stack.top(-1)))
if (!stack.bool(-1))
return false;
return true;