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