itns-sidechain/lib/script/stack.js

577 lines
10 KiB
JavaScript
Raw Permalink Normal View History

2016-08-24 04:08:40 -07:00
/*!
2018-08-01 20:00:09 -07:00
* stack.js - stack object for hsd
2018-02-01 13:40:45 -08:00
* Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
2018-08-01 20:00:09 -07:00
* https://github.com/handshake-org/hsd
2016-08-24 04:08:40 -07:00
*/
'use strict';
2018-07-19 05:40:48 -07:00
const assert = require('bsert');
const bio = require('bufio');
2017-06-29 20:54:07 -07:00
const common = require('./common');
const ScriptNum = require('./scriptnum');
2016-08-24 04:08:40 -07:00
/**
2017-11-16 18:44:38 -08:00
* Stack
2016-08-24 04:08:40 -07:00
* Represents the stack of a Script during execution.
2017-02-03 22:47:26 -08:00
* @alias module:script.Stack
2016-08-24 04:08:40 -07:00
* @property {Buffer[]} items - Stack items.
* @property {Number} length - Size of stack.
*/
class Stack extends bio.Struct {
2017-11-16 18:44:38 -08:00
/**
* Create a stack.
* @constructor
* @param {Buffer[]?} [items] - Stack items.
2017-11-16 18:44:38 -08:00
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
constructor(items) {
super();
2017-11-16 18:44:38 -08:00
this.items = items || [];
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Get length.
* @returns {Number}
*/
2017-01-06 10:18:21 -08:00
2017-11-16 18:44:38 -08:00
get length() {
return this.items.length;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Set length.
* @param {Number} value
*/
2017-11-16 18:44:38 -08:00
set length(value) {
this.items.length = value;
}
2017-11-16 18:44:38 -08:00
/**
* Instantiate a value-only iterator.
* @returns {IterableIterator<Buffer>}
2017-11-16 18:44:38 -08:00
*/
2017-11-16 18:44:38 -08:00
[Symbol.iterator]() {
return this.items[Symbol.iterator]();
}
2017-11-16 18:44:38 -08:00
/**
* Instantiate a value-only iterator.
* @returns {IterableIterator<Buffer>}
2017-11-16 18:44:38 -08:00
*/
2017-11-16 18:44:38 -08:00
values() {
return this.items.values();
}
2017-11-16 18:44:38 -08:00
/**
* Instantiate a key and value iterator.
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
entries() {
return this.items.entries();
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Inspect the stack.
* @returns {String} Human-readable stack.
*/
2016-08-24 04:08:40 -07:00
format() {
2017-11-16 18:44:38 -08:00
return `<Stack: ${this.toString()}>`;
}
2017-11-16 18:44:38 -08:00
/**
* Convert the stack to a string.
* @returns {String} Human-readable stack.
*/
2017-11-16 18:44:38 -08:00
toString() {
const out = [];
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
for (const item of this.items)
out.push(item.toString('hex'));
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
return out.join(' ');
}
2017-11-16 18:44:38 -08:00
/**
* Format the stack as bitcoind asm.
* @param {Boolean?} decode - Attempt to decode hash types.
* @returns {String} Human-readable script.
*/
2017-11-16 18:44:38 -08:00
toASM(decode) {
const out = [];
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
for (const item of this.items)
out.push(common.toASM(item, decode));
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
return out.join(' ');
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Clone the stack.
* @param {this} stack
* @returns {this} Cloned stack.
2017-11-16 18:44:38 -08:00
*/
2016-08-24 04:08:40 -07:00
inject(stack) {
this.items = stack.items.slice();
return this;
2017-11-16 18:44:38 -08:00
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Clear the stack.
* @returns {Stack}
*/
2017-11-16 18:44:38 -08:00
clear() {
this.items.length = 0;
return this;
}
2017-11-16 18:44:38 -08:00
/**
* Get a stack item by index.
* @param {Number} index
* @returns {Buffer|null}
*/
2017-11-16 18:44:38 -08:00
get(index) {
if (index < 0)
index += this.items.length;
2017-11-16 18:44:38 -08:00
if (index < 0 || index >= this.items.length)
return null;
2017-11-16 18:44:38 -08:00
return this.items[index];
}
2017-11-16 18:44:38 -08:00
/**
* Pop a stack item.
* @see Array#pop
* @returns {Buffer|null}
*/
2017-11-16 18:44:38 -08:00
pop() {
const item = this.items.pop();
return item || null;
}
2017-11-16 18:44:38 -08:00
/**
* Shift a stack item.
* @see Array#shift
* @returns {Buffer|null}
*/
2017-11-16 18:44:38 -08:00
shift() {
const item = this.items.shift();
return item || null;
}
2017-11-16 18:44:38 -08:00
/**
* Remove an item.
* @param {Number} index
* @returns {Buffer}
*/
2017-11-16 18:44:38 -08:00
remove(index) {
if (index < 0)
index += this.items.length;
2017-11-16 18:44:38 -08:00
if (index < 0 || index >= this.items.length)
return null;
2017-11-16 18:44:38 -08:00
const items = this.items.splice(index, 1);
2017-11-16 18:44:38 -08:00
if (items.length === 0)
return null;
2017-11-16 18:44:38 -08:00
return items[0];
}
2017-11-16 18:44:38 -08:00
/**
* Set stack item at index.
* @param {Number} index
* @param {Buffer} item
* @returns {this}
2017-11-16 18:44:38 -08:00
*/
2017-11-16 18:44:38 -08:00
set(index, item) {
if (index < 0)
index += this.items.length;
2017-11-16 18:44:38 -08:00
assert(Buffer.isBuffer(item));
assert(index >= 0 && index <= this.items.length);
2017-11-16 18:44:38 -08:00
this.items[index] = item;
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
return this;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Push item onto stack.
* @see Array#push
* @param {Buffer} item
* @returns {this}
2017-11-16 18:44:38 -08:00
*/
push(item) {
assert(Buffer.isBuffer(item));
this.items.push(item);
return this;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Unshift item from stack.
* @see Array#unshift
* @param {Buffer} item
* @returns {this}
2017-11-16 18:44:38 -08:00
*/
unshift(item) {
assert(Buffer.isBuffer(item));
this.items.unshift(item);
return this;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Insert an item.
* @param {Number} index
* @param {Buffer} item
* @returns {this}
2017-11-16 18:44:38 -08:00
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
insert(index, item) {
if (index < 0)
index += this.items.length;
2017-11-16 18:44:38 -08:00
assert(Buffer.isBuffer(item));
assert(index >= 0 && index <= this.items.length);
2017-11-16 18:44:38 -08:00
this.items.splice(index, 0, item);
2017-11-16 18:44:38 -08:00
return this;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Erase stack items.
* @param {Number} start
* @param {Number} end
* @returns {Buffer[]}
*/
2017-11-16 18:44:38 -08:00
erase(start, end) {
if (start < 0)
start = this.items.length + start;
2017-11-16 18:44:38 -08:00
if (end < 0)
end = this.items.length + end;
return this.items.splice(start, end - start);
2017-11-16 18:44:38 -08:00
}
2017-11-16 18:44:38 -08:00
/**
* Swap stack values.
* @param {Number} i1 - Index 1.
* @param {Number} i2 - Index 2.
*/
2017-11-16 18:44:38 -08:00
swap(i1, i2) {
if (i1 < 0)
i1 = this.items.length + i1;
2017-11-16 18:44:38 -08:00
if (i2 < 0)
i2 = this.items.length + i2;
2017-11-16 18:44:38 -08:00
const v1 = this.items[i1];
const v2 = this.items[i2];
2017-11-16 18:44:38 -08:00
this.items[i1] = v2;
this.items[i2] = v1;
}
2017-11-16 18:44:38 -08:00
/*
* Data
*/
2017-11-16 18:44:38 -08:00
getData(index) {
return this.get(index);
}
2017-11-16 18:44:38 -08:00
popData() {
return this.pop();
}
2017-11-16 18:44:38 -08:00
shiftData() {
return this.shift();
}
2017-11-16 18:44:38 -08:00
removeData(index) {
return this.remove(index);
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
setData(index, data) {
return this.set(index, data);
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
pushData(data) {
return this.push(data);
}
2017-11-16 18:44:38 -08:00
unshiftData(data) {
return this.unshift(data);
}
2017-11-16 18:44:38 -08:00
insertData(index, data) {
return this.insert(index, data);
}
2017-11-16 18:44:38 -08:00
/*
* Length
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
getLength(index) {
const item = this.get(index);
return item ? item.length : -1;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/*
* String
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
getString(index, enc) {
const item = this.get(index);
return item ? Stack.toString(item, enc) : null;
}
2017-11-16 18:44:38 -08:00
popString(enc) {
const item = this.pop();
return item ? Stack.toString(item, enc) : null;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
shiftString(enc) {
const item = this.shift();
return item ? Stack.toString(item, enc) : null;
}
2017-11-16 18:44:38 -08:00
removeString(index, enc) {
const item = this.remove(index);
return item ? Stack.toString(item, enc) : null;
}
2017-11-16 18:44:38 -08:00
setString(index, str, enc) {
return this.set(index, Stack.fromString(str, enc));
}
2017-11-16 18:44:38 -08:00
pushString(str, enc) {
return this.push(Stack.fromString(str, enc));
}
2017-11-16 18:44:38 -08:00
unshiftString(str, enc) {
return this.unshift(Stack.fromString(str, enc));
}
2017-11-16 18:44:38 -08:00
insertString(index, str, enc) {
return this.insert(index, Stack.fromString(str, enc));
}
2017-11-16 18:44:38 -08:00
/*
* Num
*/
2017-11-16 18:44:38 -08:00
getNum(index, minimal, limit) {
const item = this.get(index);
return item ? Stack.toNum(item, minimal, limit) : null;
}
2017-11-16 18:44:38 -08:00
popNum(minimal, limit) {
const item = this.pop();
return item ? Stack.toNum(item, minimal, limit) : null;
}
2017-11-16 18:44:38 -08:00
shiftNum(minimal, limit) {
const item = this.shift();
return item ? Stack.toNum(item, minimal, limit) : null;
}
2017-11-16 18:44:38 -08:00
removeNum(index, minimal, limit) {
const item = this.remove(index);
return item ? Stack.toNum(item, minimal, limit) : null;
}
2017-11-16 18:44:38 -08:00
setNum(index, num) {
return this.set(index, Stack.fromNum(num));
}
2017-11-16 18:44:38 -08:00
pushNum(num) {
return this.push(Stack.fromNum(num));
}
2017-11-16 18:44:38 -08:00
unshiftNum(num) {
return this.unshift(Stack.fromNum(num));
}
2017-11-16 18:44:38 -08:00
insertNum(index, num) {
return this.insert(index, Stack.fromNum(num));
}
2017-11-16 18:44:38 -08:00
/*
* Int
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
getInt(index, minimal, limit) {
const item = this.get(index);
return item ? Stack.toInt(item, minimal, limit) : -1;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
popInt(minimal, limit) {
const item = this.pop();
return item ? Stack.toInt(item, minimal, limit) : -1;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
shiftInt(minimal, limit) {
const item = this.shift();
return item ? Stack.toInt(item, minimal, limit) : -1;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
removeInt(index, minimal, limit) {
const item = this.remove(index);
return item ? Stack.toInt(item, minimal, limit) : -1;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
setInt(index, num) {
return this.set(index, Stack.fromInt(num));
}
2017-11-16 18:44:38 -08:00
pushInt(num) {
return this.push(Stack.fromInt(num));
}
2017-11-16 18:44:38 -08:00
unshiftInt(num) {
return this.unshift(Stack.fromInt(num));
}
2017-07-31 00:34:42 -07:00
2017-11-16 18:44:38 -08:00
insertInt(index, num) {
return this.insert(index, Stack.fromInt(num));
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/*
* Bool
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
getBool(index) {
const item = this.get(index);
return item ? Stack.toBool(item) : false;
}
2017-11-16 18:44:38 -08:00
popBool() {
const item = this.pop();
return item ? Stack.toBool(item) : false;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
shiftBool() {
const item = this.shift();
return item ? Stack.toBool(item) : false;
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
removeBool(index) {
const item = this.remove(index);
return item ? Stack.toBool(item) : false;
}
2017-11-16 18:44:38 -08:00
setBool(index, value) {
return this.set(index, Stack.fromBool(value));
}
2017-11-16 18:44:38 -08:00
pushBool(value) {
return this.push(Stack.fromBool(value));
}
2017-11-16 18:44:38 -08:00
unshiftBool(value) {
return this.unshift(Stack.fromBool(value));
}
2017-11-16 18:44:38 -08:00
insertBool(index, value) {
return this.insert(index, Stack.fromBool(value));
}
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
/**
* Test an object to see if it is a Stack.
* @param {Object} obj
* @returns {Boolean}
*/
2016-08-24 04:08:40 -07:00
2017-11-16 18:44:38 -08:00
static isStack(obj) {
return obj instanceof Stack;
}
2017-11-16 18:44:38 -08:00
/*
* Encoding
*/
2017-11-16 18:44:38 -08:00
static toString(item, enc) {
assert(Buffer.isBuffer(item));
return item.toString(enc || 'utf8');
}
2017-11-16 18:44:38 -08:00
static fromString(str, enc) {
assert(typeof str === 'string');
return Buffer.from(str, enc || 'utf8');
}
2017-11-16 18:44:38 -08:00
static toNum(item, minimal, limit) {
return ScriptNum.decode(item, minimal, limit);
}
2017-11-16 18:44:38 -08:00
static fromNum(num) {
assert(ScriptNum.isScriptNum(num));
return num.encode();
}
2017-11-16 18:44:38 -08:00
static toInt(item, minimal, limit) {
const num = Stack.toNum(item, minimal, limit);
return num.getInt();
}
2017-11-16 18:44:38 -08:00
static fromInt(int) {
assert(typeof int === 'number');
2017-11-16 18:44:38 -08:00
if (int >= -1 && int <= 16)
return common.small[int + 1];
2017-11-16 18:44:38 -08:00
const num = ScriptNum.fromNumber(int);
2017-11-16 18:44:38 -08:00
return Stack.fromNum(num);
}
2017-11-16 18:44:38 -08:00
static toBool(item) {
assert(Buffer.isBuffer(item));
2017-11-16 18:44:38 -08:00
for (let i = 0; i < item.length; i++) {
if (item[i] !== 0) {
// Cannot be negative zero
if (i === item.length - 1 && item[i] === 0x80)
return false;
return true;
}
}
2017-11-16 18:44:38 -08:00
return false;
}
2017-11-16 18:44:38 -08:00
static fromBool(value) {
assert(typeof value === 'boolean');
return Stack.fromInt(value ? 1 : 0);
}
}
2016-08-24 04:08:40 -07:00
/*
* Expose
*/
module.exports = Stack;