Skip to content

Commit

Permalink
script: start using scriptnum implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Aug 20, 2017
1 parent 1b0ae3c commit a6b2c23
Show file tree
Hide file tree
Showing 15 changed files with 294 additions and 280 deletions.
14 changes: 7 additions & 7 deletions docs/Scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!).
Expand All @@ -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();
Expand All @@ -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);
```
```
1 change: 1 addition & 0 deletions lib/bcoin-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
1 change: 1 addition & 0 deletions lib/bcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
5 changes: 3 additions & 2 deletions lib/mining/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
126 changes: 22 additions & 104 deletions lib/script/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*/

const assert = require('assert');
const BN = require('../crypto/bn');
const util = require('../utils/util');
const secp256k1 = require('../crypto/secp256k1');

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion lib/script/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
24 changes: 19 additions & 5 deletions lib/script/opcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
};

/**
Expand Down Expand Up @@ -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}
*/

Expand All @@ -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.');
Expand Down
Loading

0 comments on commit a6b2c23

Please sign in to comment.