script: refactor opcode and optimize.
This commit is contained in:
parent
eabcf21f49
commit
49154be76d
@ -23,7 +23,7 @@ const ScriptNum = require('./scriptnum');
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.opcodes = {
|
exports.opcodes = {
|
||||||
OP_FALSE: 0x00,
|
// Push
|
||||||
OP_0: 0x00,
|
OP_0: 0x00,
|
||||||
|
|
||||||
OP_PUSHDATA1: 0x4c,
|
OP_PUSHDATA1: 0x4c,
|
||||||
@ -34,7 +34,6 @@ exports.opcodes = {
|
|||||||
|
|
||||||
OP_RESERVED: 0x50,
|
OP_RESERVED: 0x50,
|
||||||
|
|
||||||
OP_TRUE: 0x51,
|
|
||||||
OP_1: 0x51,
|
OP_1: 0x51,
|
||||||
OP_2: 0x52,
|
OP_2: 0x52,
|
||||||
OP_3: 0x53,
|
OP_3: 0x53,
|
||||||
@ -52,6 +51,7 @@ exports.opcodes = {
|
|||||||
OP_15: 0x5f,
|
OP_15: 0x5f,
|
||||||
OP_16: 0x60,
|
OP_16: 0x60,
|
||||||
|
|
||||||
|
// Control
|
||||||
OP_NOP: 0x61,
|
OP_NOP: 0x61,
|
||||||
OP_VER: 0x62,
|
OP_VER: 0x62,
|
||||||
OP_IF: 0x63,
|
OP_IF: 0x63,
|
||||||
@ -63,6 +63,7 @@ exports.opcodes = {
|
|||||||
OP_VERIFY: 0x69,
|
OP_VERIFY: 0x69,
|
||||||
OP_RETURN: 0x6a,
|
OP_RETURN: 0x6a,
|
||||||
|
|
||||||
|
// Stack
|
||||||
OP_TOALTSTACK: 0x6b,
|
OP_TOALTSTACK: 0x6b,
|
||||||
OP_FROMALTSTACK: 0x6c,
|
OP_FROMALTSTACK: 0x6c,
|
||||||
OP_2DROP: 0x6d,
|
OP_2DROP: 0x6d,
|
||||||
@ -83,22 +84,24 @@ exports.opcodes = {
|
|||||||
OP_SWAP: 0x7c,
|
OP_SWAP: 0x7c,
|
||||||
OP_TUCK: 0x7d,
|
OP_TUCK: 0x7d,
|
||||||
|
|
||||||
|
// Splice
|
||||||
OP_CAT: 0x7e,
|
OP_CAT: 0x7e,
|
||||||
OP_SUBSTR: 0x7f,
|
OP_SUBSTR: 0x7f,
|
||||||
OP_LEFT: 0x80,
|
OP_LEFT: 0x80,
|
||||||
OP_RIGHT: 0x81,
|
OP_RIGHT: 0x81,
|
||||||
OP_SIZE: 0x82,
|
OP_SIZE: 0x82,
|
||||||
|
|
||||||
|
// Bit
|
||||||
OP_INVERT: 0x83,
|
OP_INVERT: 0x83,
|
||||||
OP_AND: 0x84,
|
OP_AND: 0x84,
|
||||||
OP_OR: 0x85,
|
OP_OR: 0x85,
|
||||||
OP_XOR: 0x86,
|
OP_XOR: 0x86,
|
||||||
OP_EQUAL: 0x87,
|
OP_EQUAL: 0x87,
|
||||||
OP_EQUALVERIFY: 0x88,
|
OP_EQUALVERIFY: 0x88,
|
||||||
|
|
||||||
OP_RESERVED1: 0x89,
|
OP_RESERVED1: 0x89,
|
||||||
OP_RESERVED2: 0x8a,
|
OP_RESERVED2: 0x8a,
|
||||||
|
|
||||||
|
// Numeric
|
||||||
OP_1ADD: 0x8b,
|
OP_1ADD: 0x8b,
|
||||||
OP_1SUB: 0x8c,
|
OP_1SUB: 0x8c,
|
||||||
OP_2MUL: 0x8d,
|
OP_2MUL: 0x8d,
|
||||||
@ -127,6 +130,7 @@ exports.opcodes = {
|
|||||||
OP_MAX: 0xa4,
|
OP_MAX: 0xa4,
|
||||||
OP_WITHIN: 0xa5,
|
OP_WITHIN: 0xa5,
|
||||||
|
|
||||||
|
// Crypto
|
||||||
OP_RIPEMD160: 0xa6,
|
OP_RIPEMD160: 0xa6,
|
||||||
OP_SHA1: 0xa7,
|
OP_SHA1: 0xa7,
|
||||||
OP_SHA256: 0xa8,
|
OP_SHA256: 0xa8,
|
||||||
@ -138,11 +142,9 @@ exports.opcodes = {
|
|||||||
OP_CHECKMULTISIG: 0xae,
|
OP_CHECKMULTISIG: 0xae,
|
||||||
OP_CHECKMULTISIGVERIFY: 0xaf,
|
OP_CHECKMULTISIGVERIFY: 0xaf,
|
||||||
|
|
||||||
OP_EVAL: 0xb0,
|
// Expansion
|
||||||
OP_NOP1: 0xb0,
|
OP_NOP1: 0xb0,
|
||||||
OP_NOP2: 0xb1,
|
|
||||||
OP_CHECKLOCKTIMEVERIFY: 0xb1,
|
OP_CHECKLOCKTIMEVERIFY: 0xb1,
|
||||||
OP_NOP3: 0xb2,
|
|
||||||
OP_CHECKSEQUENCEVERIFY: 0xb2,
|
OP_CHECKSEQUENCEVERIFY: 0xb2,
|
||||||
OP_NOP4: 0xb3,
|
OP_NOP4: 0xb3,
|
||||||
OP_NOP5: 0xb4,
|
OP_NOP5: 0xb4,
|
||||||
@ -152,8 +154,7 @@ exports.opcodes = {
|
|||||||
OP_NOP9: 0xb8,
|
OP_NOP9: 0xb8,
|
||||||
OP_NOP10: 0xb9,
|
OP_NOP10: 0xb9,
|
||||||
|
|
||||||
OP_PUBKEYHASH: 0xfd,
|
// Custom
|
||||||
OP_PUBKEY: 0xfe,
|
|
||||||
OP_INVALIDOPCODE: 0xff
|
OP_INVALIDOPCODE: 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -496,7 +497,7 @@ exports.isSignatureEncoding = function isSignatureEncoding(sig) {
|
|||||||
|
|
||||||
exports.toASM = function toASM(item, decode) {
|
exports.toASM = function toASM(item, decode) {
|
||||||
if (item.length <= 4) {
|
if (item.length <= 4) {
|
||||||
const num = ScriptNum.decode(item, false, 4);
|
const num = ScriptNum.decode(item);
|
||||||
return num.toString(10);
|
return num.toString(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,11 +14,15 @@ const common = require('./common');
|
|||||||
const BufferReader = require('../utils/reader');
|
const BufferReader = require('../utils/reader');
|
||||||
const StaticWriter = require('../utils/staticwriter');
|
const StaticWriter = require('../utils/staticwriter');
|
||||||
const opcodes = common.opcodes;
|
const opcodes = common.opcodes;
|
||||||
|
|
||||||
const opCache = [];
|
const opCache = [];
|
||||||
|
|
||||||
|
let PARSE_ERROR = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple struct which contains
|
* A simple struct which contains
|
||||||
* an opcode and pushdata buffer.
|
* an opcode and pushdata buffer.
|
||||||
|
* Note: this should not be called directly.
|
||||||
* @alias module:script.Opcode
|
* @alias module:script.Opcode
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Number} value - Opcode.
|
* @param {Number} value - Opcode.
|
||||||
@ -44,24 +48,25 @@ Opcode.prototype.isMinimal = function isMinimal() {
|
|||||||
if (!this.data)
|
if (!this.data)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (this.data.length === 0)
|
if (this.data.length === 1) {
|
||||||
return this.value === opcodes.OP_0;
|
if (this.data[0] === 0x81)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (this.data.length === 1 && this.data[0] >= 1 && this.data[0] <= 16)
|
if (this.data[0] >= 1 && this.data[0] <= 16)
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.data.length === 1 && this.data[0] === 0x81)
|
if (this.data.length <= 0x4b)
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this.data.length <= 75)
|
|
||||||
return this.value === this.data.length;
|
return this.value === this.data.length;
|
||||||
|
|
||||||
if (this.data.length <= 255)
|
if (this.data.length <= 0xff)
|
||||||
return this.value === opcodes.OP_PUSHDATA1;
|
return this.value === opcodes.OP_PUSHDATA1;
|
||||||
|
|
||||||
if (this.data.length <= 65535)
|
if (this.data.length <= 0xffff)
|
||||||
return this.value === opcodes.OP_PUSHDATA2;
|
return this.value === opcodes.OP_PUSHDATA2;
|
||||||
|
|
||||||
|
assert(this.value === opcodes.OP_PUSHDATA4);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,19 +161,16 @@ Opcode.prototype.toLength = function toLength() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Opcode.prototype.toPush = function toPush() {
|
Opcode.prototype.toPush = function toPush() {
|
||||||
if (this.data)
|
if (this.value === opcodes.OP_0)
|
||||||
return this.data;
|
return common.small[0 + 1];
|
||||||
|
|
||||||
if (this.value === opcodes.OP_1NEGATE)
|
if (this.value === opcodes.OP_1NEGATE)
|
||||||
return common.small[-1 + 1];
|
return common.small[-1 + 1];
|
||||||
|
|
||||||
if (this.value === opcodes.OP_0)
|
|
||||||
return common.small[0 + 1];
|
|
||||||
|
|
||||||
if (this.value >= opcodes.OP_1 && this.value <= opcodes.OP_16)
|
if (this.value >= opcodes.OP_1 && this.value <= opcodes.OP_16)
|
||||||
return common.small[this.value - 0x50 + 1];
|
return common.small[this.value - 0x50 + 1];
|
||||||
|
|
||||||
return null;
|
return this.toData();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,17 +205,20 @@ Opcode.prototype.toSmall = function toSmall() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert opcode to script number.
|
* Convert opcode to script number.
|
||||||
|
* @param {Boolean?} minimal
|
||||||
|
* @param {Number?} limit
|
||||||
* @returns {ScriptNum|null}
|
* @returns {ScriptNum|null}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Opcode.prototype.toNum = function toNum(minimal, limit) {
|
Opcode.prototype.toNum = function toNum(minimal, limit) {
|
||||||
|
if (this.value === opcodes.OP_0)
|
||||||
|
return ScriptNum.fromInt(0);
|
||||||
|
|
||||||
if (this.value === opcodes.OP_1NEGATE)
|
if (this.value === opcodes.OP_1NEGATE)
|
||||||
return ScriptNum.fromInt(-1);
|
return ScriptNum.fromInt(-1);
|
||||||
|
|
||||||
const smi = this.toSmall();
|
if (this.value >= opcodes.OP_1 && this.value <= opcodes.OP_16)
|
||||||
|
return ScriptNum.fromInt(this.value - 0x50);
|
||||||
if (smi !== -1)
|
|
||||||
return ScriptNum.fromInt(smi);
|
|
||||||
|
|
||||||
if (!this.data)
|
if (!this.data)
|
||||||
return null;
|
return null;
|
||||||
@ -223,11 +228,13 @@ Opcode.prototype.toNum = function toNum(minimal, limit) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert opcode to integer.
|
* Convert opcode to integer.
|
||||||
|
* @param {Boolean?} minimal
|
||||||
|
* @param {Number?} limit
|
||||||
* @returns {Number}
|
* @returns {Number}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Opcode.prototype.toInt = function toInt() {
|
Opcode.prototype.toInt = function toInt(minimal, limit) {
|
||||||
const num = this.toNum();
|
const num = this.toNum(minimal, limit);
|
||||||
|
|
||||||
if (!num)
|
if (!num)
|
||||||
return -1;
|
return -1;
|
||||||
@ -255,15 +262,13 @@ Opcode.prototype.toBool = function toBool() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Opcode.prototype.toSymbol = function toSymbol() {
|
Opcode.prototype.toSymbol = function toSymbol() {
|
||||||
let op = this.value;
|
if (this.value === -1)
|
||||||
|
return 'OP_INVALIDOPCODE';
|
||||||
|
|
||||||
if (op === -1)
|
const symbol = common.opcodesByVal[this.value];
|
||||||
op = 0xff;
|
|
||||||
|
|
||||||
let symbol = common.opcodesByVal[op];
|
if (!symbol)
|
||||||
|
return `0x${util.hex8(this.value)}`;
|
||||||
if (symbol == null)
|
|
||||||
symbol = `0x${util.hex8(op)}`;
|
|
||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
};
|
};
|
||||||
@ -277,9 +282,6 @@ Opcode.prototype.getSize = function getSize() {
|
|||||||
if (!this.data)
|
if (!this.data)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (this.value <= 0x4b)
|
|
||||||
return 1 + this.data.length;
|
|
||||||
|
|
||||||
switch (this.value) {
|
switch (this.value) {
|
||||||
case opcodes.OP_PUSHDATA1:
|
case opcodes.OP_PUSHDATA1:
|
||||||
return 2 + this.data.length;
|
return 2 + this.data.length;
|
||||||
@ -288,7 +290,7 @@ Opcode.prototype.getSize = function getSize() {
|
|||||||
case opcodes.OP_PUSHDATA4:
|
case opcodes.OP_PUSHDATA4:
|
||||||
return 5 + this.data.length;
|
return 5 + this.data.length;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown pushdata opcode.');
|
return 1 + this.data.length;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,13 +308,6 @@ Opcode.prototype.toWriter = function toWriter(bw) {
|
|||||||
return bw;
|
return bw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.value <= 0x4b) {
|
|
||||||
assert(this.value === this.data.length);
|
|
||||||
bw.writeU8(this.value);
|
|
||||||
bw.writeBytes(this.data);
|
|
||||||
return bw;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.value) {
|
switch (this.value) {
|
||||||
case opcodes.OP_PUSHDATA1:
|
case opcodes.OP_PUSHDATA1:
|
||||||
bw.writeU8(this.value);
|
bw.writeU8(this.value);
|
||||||
@ -330,7 +325,10 @@ Opcode.prototype.toWriter = function toWriter(bw) {
|
|||||||
bw.writeBytes(this.data);
|
bw.writeBytes(this.data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown pushdata opcode.');
|
assert(this.value === this.data.length);
|
||||||
|
bw.writeU8(this.value);
|
||||||
|
bw.writeBytes(this.data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bw;
|
return bw;
|
||||||
@ -352,11 +350,17 @@ Opcode.prototype.toRaw = function toRaw() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Opcode.prototype.toFormat = function toFormat() {
|
Opcode.prototype.toFormat = function toFormat() {
|
||||||
// Bad push
|
|
||||||
if (this.value === -1)
|
if (this.value === -1)
|
||||||
return 'OP_INVALIDOPCODE';
|
return '0x01';
|
||||||
|
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
|
// Numbers
|
||||||
|
if (this.data.length <= 4) {
|
||||||
|
const num = this.toNum();
|
||||||
|
if (this.equals(Opcode.fromNum(num)))
|
||||||
|
return num.toString(10);
|
||||||
|
}
|
||||||
|
|
||||||
const symbol = common.opcodesByVal[this.value];
|
const symbol = common.opcodesByVal[this.value];
|
||||||
const data = this.data.toString('hex');
|
const data = this.data.toString('hex');
|
||||||
|
|
||||||
@ -413,10 +417,9 @@ Opcode.fromOp = function fromOp(op) {
|
|||||||
|
|
||||||
const cached = opCache[op];
|
const cached = opCache[op];
|
||||||
|
|
||||||
if (cached)
|
assert(cached, 'Bad opcode.');
|
||||||
return cached;
|
|
||||||
|
|
||||||
return new Opcode(op, null);
|
return cached;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -430,11 +433,11 @@ Opcode.fromData = function fromData(data) {
|
|||||||
assert(Buffer.isBuffer(data));
|
assert(Buffer.isBuffer(data));
|
||||||
|
|
||||||
if (data.length === 1) {
|
if (data.length === 1) {
|
||||||
if (data[0] >= 1 && data[0] <= 16)
|
|
||||||
return Opcode.fromOp(data[0] + 0x50);
|
|
||||||
|
|
||||||
if (data[0] === 0x81)
|
if (data[0] === 0x81)
|
||||||
return Opcode.fromOp(opcodes.OP_1NEGATE);
|
return Opcode.fromOp(opcodes.OP_1NEGATE);
|
||||||
|
|
||||||
|
if (data[0] >= 1 && data[0] <= 16)
|
||||||
|
return Opcode.fromOp(data[0] + 0x50);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Opcode.fromPush(data);
|
return Opcode.fromPush(data);
|
||||||
@ -513,12 +516,12 @@ Opcode.fromNum = function fromNum(num) {
|
|||||||
Opcode.fromInt = function fromInt(num) {
|
Opcode.fromInt = function fromInt(num) {
|
||||||
assert(util.isInt(num));
|
assert(util.isInt(num));
|
||||||
|
|
||||||
if (num === -1)
|
|
||||||
return Opcode.fromOp(opcodes.OP_1NEGATE);
|
|
||||||
|
|
||||||
if (num === 0)
|
if (num === 0)
|
||||||
return Opcode.fromOp(opcodes.OP_0);
|
return Opcode.fromOp(opcodes.OP_0);
|
||||||
|
|
||||||
|
if (num === -1)
|
||||||
|
return Opcode.fromOp(opcodes.OP_1NEGATE);
|
||||||
|
|
||||||
if (num >= 1 && num <= 16)
|
if (num >= 1 && num <= 16)
|
||||||
return Opcode.fromOp(num + 0x50);
|
return Opcode.fromOp(num + 0x50);
|
||||||
|
|
||||||
@ -554,18 +557,19 @@ Opcode.fromSymbol = function fromSymbol(name) {
|
|||||||
if (!util.startsWith(name, 'OP_'))
|
if (!util.startsWith(name, 'OP_'))
|
||||||
name = `OP_${name}`;
|
name = `OP_${name}`;
|
||||||
|
|
||||||
let op = common.opcodes[name];
|
const op = common.opcodes[name];
|
||||||
|
|
||||||
if (op == null) {
|
if (op != null)
|
||||||
assert(util.startsWith(name, 'OP_0X'), 'Unknown opcode.');
|
return Opcode.fromOp(op);
|
||||||
assert(name.length === 7, 'Unknown opcode.');
|
|
||||||
|
|
||||||
op = parseInt(name.substring(5), 16);
|
assert(util.startsWith(name, 'OP_0X'), 'Unknown opcode.');
|
||||||
|
assert(name.length === 7, 'Unknown opcode.');
|
||||||
|
|
||||||
assert(util.isU8(op), 'Unknown opcode.');
|
const value = parseInt(name.substring(5), 16);
|
||||||
}
|
|
||||||
|
|
||||||
return Opcode.fromOp(op);
|
assert(util.isU8(value), 'Unknown opcode.');
|
||||||
|
|
||||||
|
return Opcode.fromOp(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -576,84 +580,72 @@ Opcode.fromSymbol = function fromSymbol(name) {
|
|||||||
|
|
||||||
Opcode.fromReader = function fromReader(br) {
|
Opcode.fromReader = function fromReader(br) {
|
||||||
const value = br.readU8();
|
const value = br.readU8();
|
||||||
|
const op = opCache[value];
|
||||||
|
|
||||||
const cached = opCache[value];
|
if (op)
|
||||||
|
|
||||||
if (cached)
|
|
||||||
return cached;
|
|
||||||
|
|
||||||
const op = new Opcode(value, null);
|
|
||||||
|
|
||||||
if (value >= 0x01 && value <= 0x4b) {
|
|
||||||
if (br.left() < value) {
|
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
op.data = br.readBytes(value);
|
|
||||||
return op;
|
return op;
|
||||||
}
|
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case opcodes.OP_PUSHDATA1: {
|
case opcodes.OP_PUSHDATA1: {
|
||||||
if (br.left() < 1) {
|
if (br.left() < 1)
|
||||||
op.value = -1;
|
return PARSE_ERROR;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = br.readU8();
|
const size = br.readU8();
|
||||||
|
|
||||||
if (br.left() < size) {
|
if (br.left() < size) {
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
br.seek(br.left());
|
||||||
break;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
op.data = br.readBytes(size);
|
const data = br.readBytes(size);
|
||||||
|
|
||||||
break;
|
return new Opcode(value, data);
|
||||||
}
|
}
|
||||||
case opcodes.OP_PUSHDATA2: {
|
case opcodes.OP_PUSHDATA2: {
|
||||||
if (br.left() < 2) {
|
if (br.left() < 2) {
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
br.seek(br.left());
|
||||||
break;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size = br.readU16();
|
const size = br.readU16();
|
||||||
|
|
||||||
if (br.left() < size) {
|
if (br.left() < size) {
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
br.seek(br.left());
|
||||||
break;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
op.data = br.readBytes(size);
|
const data = br.readBytes(size);
|
||||||
|
|
||||||
break;
|
return new Opcode(value, data);
|
||||||
}
|
}
|
||||||
case opcodes.OP_PUSHDATA4: {
|
case opcodes.OP_PUSHDATA4: {
|
||||||
if (br.left() < 4) {
|
if (br.left() < 4) {
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
br.seek(br.left());
|
||||||
break;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size = br.readU32();
|
const size = br.readU32();
|
||||||
|
|
||||||
if (br.left() < size) {
|
if (br.left() < size) {
|
||||||
op.value = -1;
|
|
||||||
br.seek(br.left());
|
br.seek(br.left());
|
||||||
break;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
op.data = br.readBytes(size);
|
const data = br.readBytes(size);
|
||||||
|
|
||||||
break;
|
return new Opcode(value, data);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (br.left() < value) {
|
||||||
|
br.seek(br.left());
|
||||||
|
return PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = br.readBytes(value);
|
||||||
|
|
||||||
|
return new Opcode(value, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return op;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -680,17 +672,15 @@ Opcode.isOpcode = function isOpcode(obj) {
|
|||||||
* Fill Cache
|
* Fill Cache
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
PARSE_ERROR = Object.freeze(new Opcode(-1));
|
||||||
|
|
||||||
for (let value = 0x00; value <= 0xff; value++) {
|
for (let value = 0x00; value <= 0xff; value++) {
|
||||||
if (value >= 0x01 && value <= 0x4e) {
|
if (value >= 0x01 && value <= 0x4e) {
|
||||||
opCache.push(null);
|
opCache.push(null);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const op = new Opcode(value);
|
||||||
const op = new Opcode(value, null);
|
opCache.push(Object.freeze(op));
|
||||||
|
|
||||||
Object.freeze(op);
|
|
||||||
|
|
||||||
opCache.push(op);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -657,7 +657,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (locktime.isNeg())
|
if (locktime.isNeg())
|
||||||
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
|
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
|
||||||
|
|
||||||
locktime = locktime.toNumber();
|
locktime = locktime.toDouble();
|
||||||
|
|
||||||
if (!tx.verifyLocktime(index, locktime))
|
if (!tx.verifyLocktime(index, locktime))
|
||||||
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);
|
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);
|
||||||
@ -683,7 +683,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (locktime.isNeg())
|
if (locktime.isNeg())
|
||||||
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
|
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
|
||||||
|
|
||||||
locktime = locktime.toNumber();
|
locktime = locktime.toDouble();
|
||||||
|
|
||||||
if (!tx.verifySequence(index, locktime))
|
if (!tx.verifySequence(index, locktime))
|
||||||
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);
|
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);
|
||||||
@ -896,7 +896,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
const num = stack.getInt(-1, minimal);
|
const num = stack.getInt(-1, minimal, 4);
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
|
||||||
if (num < 0 || num >= stack.length)
|
if (num < 0 || num >= stack.length)
|
||||||
@ -971,7 +971,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < 1)
|
if (stack.length < 1)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
let num = stack.getNum(-1, minimal);
|
let num = stack.getNum(-1, minimal, 4);
|
||||||
let cmp;
|
let cmp;
|
||||||
|
|
||||||
switch (op.value) {
|
switch (op.value) {
|
||||||
@ -1021,8 +1021,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
const n1 = stack.getNum(-2, minimal);
|
const n1 = stack.getNum(-2, minimal, 4);
|
||||||
const n2 = stack.getNum(-1, minimal);
|
const n2 = stack.getNum(-1, minimal, 4);
|
||||||
let num, cmp;
|
let num, cmp;
|
||||||
|
|
||||||
switch (op.value) {
|
switch (op.value) {
|
||||||
@ -1095,9 +1095,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < 3)
|
if (stack.length < 3)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
const n1 = stack.getNum(-3, minimal);
|
const n1 = stack.getNum(-3, minimal, 4);
|
||||||
const n2 = stack.getNum(-2, minimal);
|
const n2 = stack.getNum(-2, minimal, 4);
|
||||||
const n3 = stack.getNum(-1, minimal);
|
const n3 = stack.getNum(-1, minimal, 4);
|
||||||
|
|
||||||
const val = n2.lte(n1) && n1.lt(n3);
|
const val = n2.lte(n1) && n1.lt(n3);
|
||||||
|
|
||||||
@ -1201,7 +1201,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < i)
|
if (stack.length < i)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
let n = stack.getInt(-i, minimal);
|
let n = stack.getInt(-i, minimal, 4);
|
||||||
let okey = n + 2;
|
let okey = n + 2;
|
||||||
let ikey, isig;
|
let ikey, isig;
|
||||||
|
|
||||||
@ -1220,7 +1220,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
|||||||
if (stack.length < i)
|
if (stack.length < i)
|
||||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||||
|
|
||||||
let m = stack.getInt(-i, minimal);
|
let m = stack.getInt(-i, minimal, 4);
|
||||||
|
|
||||||
if (m < 0 || m > n)
|
if (m < 0 || m > n)
|
||||||
throw new ScriptError('SIG_COUNT', op, ip);
|
throw new ScriptError('SIG_COUNT', op, ip);
|
||||||
@ -1425,7 +1425,7 @@ Script.prototype.isCode = function isCode() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.value > opcodes.OP_NOP3)
|
if (op.value > opcodes.OP_CHECKSEQUENCEVERIFY)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1449,7 +1449,7 @@ Script.prototype.fromPubkey = function fromPubkey(key) {
|
|||||||
key = this.raw.slice(1, 1 + key.length);
|
key = this.raw.slice(1, 1 + key.length);
|
||||||
|
|
||||||
this.code.length = 0;
|
this.code.length = 0;
|
||||||
this.code.push(Opcode.fromData(key));
|
this.code.push(Opcode.fromPush(key));
|
||||||
this.code.push(Opcode.fromOp(opcodes.OP_CHECKSIG));
|
this.code.push(Opcode.fromOp(opcodes.OP_CHECKSIG));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -2470,53 +2470,25 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) {
|
|||||||
if (raw.length === 0)
|
if (raw.length === 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// First opcode.
|
if (raw[0] >= opcodes.OP_1 && raw[0] <= opcodes.OP_16)
|
||||||
const value = raw[0];
|
return raw[0] - 0x50;
|
||||||
|
|
||||||
// Small ints are allowed.
|
if (raw[0] > 0x06)
|
||||||
if (value === 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (value >= opcodes.OP_1 && value <= opcodes.OP_16)
|
|
||||||
return value - 0x50;
|
|
||||||
|
|
||||||
// No more than 6 bytes (we can't
|
|
||||||
// handle 7 byte JS numbers and
|
|
||||||
// height 281 trillion is far away).
|
|
||||||
if (value > 0x06)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// No bad pushes allowed.
|
const op = Opcode.fromRaw(raw);
|
||||||
if (raw.length < 1 + value)
|
const num = op.toNum();
|
||||||
|
|
||||||
|
if (!num)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (num.isNeg())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const data = raw.slice(1, 1 + value);
|
if (!op.equals(Opcode.fromNum(num)))
|
||||||
|
|
||||||
// Deserialize the height.
|
|
||||||
let height;
|
|
||||||
try {
|
|
||||||
height = ScriptNum.decode(data, true, 6);
|
|
||||||
} catch (e) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be negative.
|
|
||||||
if (height.isNeg())
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// Reserialize the height.
|
return num.toDouble();
|
||||||
const op = Opcode.fromNum(height);
|
|
||||||
|
|
||||||
// Should have been OP_0-OP_16.
|
|
||||||
if (!op.data)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Ensure the miner serialized the
|
|
||||||
// number in the most minimal fashion.
|
|
||||||
if (!data.equals(op.data))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return height.toNumber();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -128,7 +128,6 @@ ScriptNum.prototype.toRaw = function toRaw() {
|
|||||||
|
|
||||||
ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
||||||
assert(Buffer.isBuffer(data));
|
assert(Buffer.isBuffer(data));
|
||||||
assert(data.length <= 9);
|
|
||||||
|
|
||||||
// Empty arrays are always zero.
|
// Empty arrays are always zero.
|
||||||
if (data.length === 0)
|
if (data.length === 0)
|
||||||
@ -136,10 +135,6 @@ ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
|||||||
|
|
||||||
// Read number (9 bytes max).
|
// Read number (9 bytes max).
|
||||||
switch (data.length) {
|
switch (data.length) {
|
||||||
case 9:
|
|
||||||
// Note: this shift overflows to
|
|
||||||
// zero in modern bitcoin core.
|
|
||||||
this.lo |= data[8];
|
|
||||||
case 8:
|
case 8:
|
||||||
this.hi |= data[7] << 24;
|
this.hi |= data[7] << 24;
|
||||||
case 7:
|
case 7:
|
||||||
@ -156,6 +151,11 @@ ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
|||||||
this.lo |= data[1] << 8;
|
this.lo |= data[1] << 8;
|
||||||
case 1:
|
case 1:
|
||||||
this.lo |= data[0];
|
this.lo |= data[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (let i = 0; i < data.length; i++)
|
||||||
|
this.orb(i, data[i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove high bit and flip sign.
|
// Remove high bit and flip sign.
|
||||||
@ -188,20 +188,9 @@ ScriptNum.prototype.encode = function encode() {
|
|||||||
ScriptNum.prototype.decode = function decode(data, minimal, limit) {
|
ScriptNum.prototype.decode = function decode(data, minimal, limit) {
|
||||||
assert(Buffer.isBuffer(data));
|
assert(Buffer.isBuffer(data));
|
||||||
|
|
||||||
if (minimal == null)
|
if (limit != null && data.length > limit)
|
||||||
minimal = true;
|
|
||||||
|
|
||||||
if (limit == null)
|
|
||||||
limit = 4;
|
|
||||||
|
|
||||||
// We can't handle more than 9 bytes.
|
|
||||||
assert(limit >= 4 && limit <= 9, 'Bad script number size limit.');
|
|
||||||
|
|
||||||
// Max size is 4 bytes by default, 9 bytes max.
|
|
||||||
if (data.length > limit)
|
|
||||||
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
|
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
|
||||||
|
|
||||||
// Ensure minimal serialization.
|
|
||||||
if (minimal && !ScriptNum.isMinimal(data))
|
if (minimal && !ScriptNum.isMinimal(data))
|
||||||
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
||||||
|
|
||||||
@ -234,37 +223,13 @@ ScriptNum.isMinimal = function isMinimal(data) {
|
|||||||
if (data.length === 1)
|
if (data.length === 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!(data[data.length - 2] & 0x80))
|
if ((data[data.length - 2] & 0x80) === 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a script number.
|
|
||||||
* @param {Number|ScriptNum|BN} num
|
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.encode = function encode(num) {
|
|
||||||
assert(num != null);
|
|
||||||
|
|
||||||
if (ScriptNum.isScriptNum(num))
|
|
||||||
return num.encode();
|
|
||||||
|
|
||||||
if (typeof num === 'number')
|
|
||||||
num = ScriptNum.fromNumber(num);
|
|
||||||
else if (I64.isN64(num))
|
|
||||||
num = ScriptNum.fromObject(num);
|
|
||||||
else if (Array.isArray(num.words))
|
|
||||||
num = ScriptNum.fromBN(num);
|
|
||||||
else
|
|
||||||
throw new Error('Object must be encodable.');
|
|
||||||
|
|
||||||
return num.encode();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode and verify script number.
|
* Decode and verify script number.
|
||||||
* @param {Buffer} data
|
* @param {Buffer} data
|
||||||
@ -277,31 +242,6 @@ ScriptNum.decode = function decode(data, minimal, limit) {
|
|||||||
return new ScriptNum().decode(data, minimal, limit);
|
return new ScriptNum().decode(data, minimal, limit);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether object is encodable.
|
|
||||||
* @param {Object} obj
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.isEncodable = function isEncodable(obj) {
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (ScriptNum.isScriptNum(obj))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (typeof obj === 'number')
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (I64.isN64(obj))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (Array.isArray(obj.words))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether object is a script number.
|
* Test whether object is a script number.
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn.js": "4.11.8",
|
"bn.js": "4.11.8",
|
||||||
"elliptic": "6.4.0",
|
"elliptic": "6.4.0",
|
||||||
"n64": "0.0.17"
|
"n64": "0.0.18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"bcoin-native": "0.0.23",
|
"bcoin-native": "0.0.23",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user