script: refactor verification and execution.

This commit is contained in:
Christopher Jeffrey 2016-10-02 07:06:55 -07:00
parent c13852f81e
commit c43df089f6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 442 additions and 543 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,6 @@
module.exports = Stack;
var constants = require('../protocol/constants');
var opcodes = constants.opcodes;
var ScriptError = require('../utils/errors').ScriptError;
var Script = require('./script');
var Witness = require('./witness');
@ -88,16 +85,6 @@ Stack.prototype.clone = function clone() {
return new Stack(this.items.slice());
};
/**
* Get total size of the stack, including the alt stack.
* @param {Array} alt - Alt stack.
* @returns {Number}
*/
Stack.prototype.getSize = function getSize(alt) {
return this.items.length + alt.length;
};
/**
* Push item onto stack.
* @see Array#push
@ -125,11 +112,12 @@ Stack.prototype.unshift = function unshift(item) {
* @param {Number} start
* @param {Number} end
* @see Array#slice
* @returns {Buffer[]}
* @returns {Stack}
*/
Stack.prototype.slice = function slice(start, end) {
return this.items.slice(start, end);
this.items = this.items.slice(start, end);
return this;
};
/**
@ -142,11 +130,62 @@ Stack.prototype.slice = function slice(start, end) {
*/
Stack.prototype.splice = function splice(i, remove, insert) {
if (i < 0)
i = this.items.length + i;
if (insert === undefined)
return this.items.splice(i, remove);
return this.items.splice(i, remove, insert);
};
/**
* Erase stack items.
* @param {Number} start
* @param {Number} end
* @returns {Buffer[]}
*/
Stack.prototype.erase = function erase(start, end) {
if (start < 0)
start = this.items.length + start;
if (end < 0)
end = this.items.length + end;
this.items.splice(start, end - start);
};
/**
* Insert an item.
* @param {Number} index
* @param {Buffer} item
* @returns {Buffer}
*/
Stack.prototype.insert = function insert(i, item) {
if (i < 0)
i = this.items.length + i;
this.items.splice(i, 0, item);
};
/**
* Remove an item.
* @param {Number} index
* @returns {Buffer}
*/
Stack.prototype.remove = function remove(i) {
if (i < 0)
i = this.items.length + i;
if (i >= this.items.length)
return;
return this.items.splice(i, 1)[0];
};
/**
* Pop a stack item.
* @see Array#pop
@ -206,21 +245,26 @@ Stack.prototype.clear = function clear() {
*/
Stack.prototype.set = function set(i, value) {
if (i < 0)
i = this.items.length + i;
return this.items[i] = value;
};
/**
* Swap stack values.
* @private
* @param {Number} i1 - Index 1.
* @param {Number} i2 - Index 2.
*/
Stack.prototype._swap = function _swap(i1, i2) {
Stack.prototype.swap = function swap(i1, i2) {
var v1, v2;
i1 = this.items.length + i1;
i2 = this.items.length + i2;
if (i1 < 0)
i1 = this.items.length + i1;
if (i2 < 0)
i2 = this.items.length + i2;
v1 = this.items[i1];
v2 = this.items[i2];
@ -229,300 +273,6 @@ Stack.prototype._swap = function _swap(i1, i2) {
this.items[i2] = v1;
};
/**
* Perform the OP_TOALTSTACK operation.
* @param {Array} alt - Alt stack.
* @throws {ScriptError}
*/
Stack.prototype.toalt = function toalt(alt) {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_TOALTSTACK);
alt.push(this.pop());
};
/**
* Perform the OP_FROMALTSTACK operation.
* @param {Array} alt - Alt stack.
* @throws {ScriptError}
*/
Stack.prototype.fromalt = function fromalt(alt) {
if (alt.length === 0)
throw new ScriptError('INVALID_ALTSTACK_OPERATION', opcodes.OP_FROMALTSTACK);
this.push(alt.pop());
};
/**
* Perform the OP_IFDUP operation.
* @throws {ScriptError}
*/
Stack.prototype.ifdup = function ifdup() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_IFDUP);
if (Script.bool(this.top(-1)))
this.push(this.top(-1));
};
/**
* Perform the OP_DEPTH operation.
* @throws {ScriptError}
*/
Stack.prototype.depth = function depth() {
this.push(Script.array(this.length));
};
/**
* Perform the OP_DROP operation.
* @throws {ScriptError}
*/
Stack.prototype.drop = function drop() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_DROP);
this.pop();
};
/**
* Perform the OP_DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup = function dup() {
if (this.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_DUP);
this.push(this.top(-1));
};
/**
* Perform the OP_NIP operation.
* @throws {ScriptError}
*/
Stack.prototype.nip = function nip() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_NIP);
this.splice(this.length - 2, 1);
};
/**
* Perform the OP_OVER operation.
* @throws {ScriptError}
*/
Stack.prototype.over = function over() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_OVER);
this.push(this.top(-2));
};
/**
* Perform the OP_PICK operation.
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype.pick = function pick(flags) {
return this._pickroll(opcodes.OP_PICK, flags);
};
/**
* Perform the OP_ROLL operation.
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype.roll = function roll(flags) {
return this._pickroll(opcodes.OP_ROLL, flags);
};
/**
* Perform a pick or roll.
* @private
* @param {Number} op
* @param {VerifyFlags} flags
* @throws {ScriptError}
*/
Stack.prototype._pickroll = function pickroll(op, flags) {
var val, n;
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', op);
val = this.pop();
n = Script.num(val, flags).toNumber();
if (n < 0 || n >= this.length)
throw new ScriptError('INVALID_STACK_OPERATION', op);
val = this.top(-n - 1);
if (op === opcodes.OP_ROLL)
this.splice(this.length - n - 1, 1);
this.push(val);
};
/**
* Perform the OP_ROT operation.
* @throws {ScriptError}
*/
Stack.prototype.rot = function rot() {
if (this.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_ROT);
this._swap(-3, -2);
this._swap(-2, -1);
};
/**
* Perform the OP_SWAP operation.
* @throws {ScriptError}
*/
Stack.prototype.swap = function swap() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_SWAP);
this._swap(-2, -1);
};
/**
* Perform the OP_TUCK operation.
* @throws {ScriptError}
*/
Stack.prototype.tuck = function tuck() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_TUCK);
this.splice(this.length - 2, 0, this.top(-1));
};
/**
* Perform the OP_2DROP operation.
* @throws {ScriptError}
*/
Stack.prototype.drop2 = function drop2() {
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2DROP);
this.pop();
this.pop();
};
/**
* Perform the OP_2DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup2 = function dup2() {
var v1, v2;
if (this.length < 2)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2DUP);
v1 = this.top(-2);
v2 = this.top(-1);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_3DUP operation.
* @throws {ScriptError}
*/
Stack.prototype.dup3 = function dup3() {
var v1, v2, v3;
if (this.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_3DUP);
v1 = this.top(-3);
v2 = this.top(-2);
v3 = this.top(-1);
this.push(v1);
this.push(v2);
this.push(v3);
};
/**
* Perform the OP_2OVER operation.
* @throws {ScriptError}
*/
Stack.prototype.over2 = function over2() {
var v1, v2;
if (this.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2OVER);
v1 = this.top(-4);
v2 = this.top(-3);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_2ROT operation.
* @throws {ScriptError}
*/
Stack.prototype.rot2 = function rot2() {
var v1, v2;
if (this.length < 6)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2ROT);
v1 = this.top(-6);
v2 = this.top(-5);
this.splice(this.length - 6, 2);
this.push(v1);
this.push(v2);
};
/**
* Perform the OP_2SWAP operation.
* @throws {ScriptError}
*/
Stack.prototype.swap2 = function swap2() {
if (this.length < 4)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_2SWAP);
this._swap(-4, -2);
this._swap(-3, -1);
};
/**
* Perform the OP_SIZE operation.
* @throws {ScriptError}
*/
Stack.prototype.size = function size() {
if (this.length < 1)
throw new ScriptError('INVALID_STACK_OPERATION', opcodes.OP_SIZE);
this.push(Script.array(this.top(-1).length));
};
/**
* Test an object to see if it is a Stack.
* @param {Object} obj

View File

@ -67,7 +67,7 @@ describe('Script', function() {
inputScript.execute(stack);
var res = prevOutScript.execute(stack);
assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]);
assert.deepEqual(stack.items, [[1], [3], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([
@ -84,7 +84,7 @@ describe('Script', function() {
inputScript.execute(stack);
var res = prevOutScript.execute(stack);
assert(res);
assert.deepEqual(stack.slice(), [[1], [4], [5]]);
assert.deepEqual(stack.items, [[1], [4], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([
@ -99,7 +99,7 @@ describe('Script', function() {
inputScript.execute(stack);
var res = prevOutScript.execute(stack);
assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]);
assert.deepEqual(stack.items, [[1], [3], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([
@ -114,7 +114,7 @@ describe('Script', function() {
inputScript.execute(stack);
var res = prevOutScript.execute(stack);
assert(res);
assert.deepEqual(stack.slice(), [[1], [5]]);
assert.deepEqual(stack.items, [[1], [5]]);
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
var prevOutScript = new Script([
@ -129,7 +129,7 @@ describe('Script', function() {
inputScript.execute(stack);
var res = prevOutScript.execute(stack);
assert(res);
assert.deepEqual(stack.slice(), [[1], [3], [5]]);
assert.deepEqual(stack.items, [[1], [3], [5]]);
});
function success(res, stack) {