script: refactor script and stack mutation.
This allows more reasonable signing behavior and eliminates all polymorphism.
This commit is contained in:
parent
9d74c837e8
commit
d6ce66bce9
12
bench/tx.js
12
bench/tx.js
@ -27,10 +27,8 @@ const tx10 = common.readTX('tx10');
|
||||
const [tx, view] = tx5.getTX();
|
||||
const end = bench('sigops');
|
||||
|
||||
let sigops = 0;
|
||||
|
||||
for (let i = 0; i < 100000; i++)
|
||||
sigops += tx.getSigopsCost(view);
|
||||
tx.getSigopsCost(view);
|
||||
|
||||
end(100000);
|
||||
}
|
||||
@ -161,10 +159,10 @@ for (let i = 0; i < 100; i++) {
|
||||
hash: encoding.NULL_HASH,
|
||||
index: 0
|
||||
},
|
||||
script: [
|
||||
Buffer.allocUnsafe(9),
|
||||
random.randomBytes(33)
|
||||
]
|
||||
script: new Script()
|
||||
.pushData(Buffer.allocUnsafe(9))
|
||||
.pushData(random.randomBytes(33))
|
||||
.compile()
|
||||
});
|
||||
mtx.addOutput({
|
||||
address: Address.fromHash(random.randomBytes(20)),
|
||||
|
||||
@ -1,37 +1,38 @@
|
||||
Scripts are array-like objects with some helper functions.
|
||||
|
||||
``` js
|
||||
var bcoin = require('bcoin');
|
||||
var assert = require('assert');
|
||||
var ScriptNum = bcoin.scriptnum;
|
||||
var opcodes = bcoin.script.opcodes;
|
||||
const bcoin = require('bcoin');
|
||||
const assert = require('assert');
|
||||
const Script = bcoin.script;
|
||||
const Witness = bcoin.witness;
|
||||
const Stack = bcoin.stack;
|
||||
|
||||
var output = new bcoin.script();
|
||||
output.push(opcodes.OP_DROP);
|
||||
output.push(opcodes.OP_ADD);
|
||||
output.push(new ScriptNum(7));
|
||||
output.push(opcodes.OP_NUMEQUAL);
|
||||
const output = new Script();
|
||||
output.pushSym('OP_DROP');
|
||||
output.pushSym('OP_ADD');
|
||||
output.pushInt(7);
|
||||
output.pushSym('OP_NUMEQUAL');
|
||||
// Compile the script to its binary representation
|
||||
// (you must do this if you change something!).
|
||||
output.compile();
|
||||
assert(output.getSmall(2) === 7); // compiled as OP_7
|
||||
output.compile();
|
||||
|
||||
var input = new bcoin.script();
|
||||
input.set(0, 'hello world'); // add some metadata
|
||||
input.push(new ScriptNum(2));
|
||||
input.push(new ScriptNum(5));
|
||||
const input = new Script();
|
||||
input.setString(0, 'hello world'); // add some metadata
|
||||
input.pushInt(2);
|
||||
input.pushInt(5);
|
||||
input.push(input.shift());
|
||||
assert(input.getString(2) === 'hello world');
|
||||
input.compile();
|
||||
|
||||
// A stack is another array-like object which contains
|
||||
// only Buffers (whereas scripts contain Opcode objects).
|
||||
var stack = new bcoin.stack();
|
||||
const stack = new Stack();
|
||||
input.execute(stack);
|
||||
output.execute(stack);
|
||||
// Verify the script was successful in its execution:
|
||||
assert(stack.length === 1);
|
||||
assert(bcoin.script.bool(stack.pop()) === true);
|
||||
assert(stack.getBool(-1) === true);
|
||||
```
|
||||
|
||||
Using a witness would be similar, but witnesses do not get executed, they
|
||||
@ -39,11 +40,11 @@ simply _become_ the stack. The witness object itself is very similar to the
|
||||
Stack object (an array-like object containing Buffers).
|
||||
|
||||
``` js
|
||||
var witness = new bcoin.witness();
|
||||
witness.push(new ScriptNum(2));
|
||||
witness.push(new ScriptNum(5));
|
||||
witness.push('hello world');
|
||||
const witness = new Witness();
|
||||
witness.pushInt(2);
|
||||
witness.pushInt(5);
|
||||
witness.pushString('hello world');
|
||||
|
||||
var stack = witness.toStack();
|
||||
const stack = witness.toStack();
|
||||
output.execute(stack);
|
||||
```
|
||||
|
||||
@ -38,29 +38,29 @@ function compressScript(script, bw) {
|
||||
|
||||
// P2PKH -> 0 | key-hash
|
||||
// Saves 5 bytes.
|
||||
if (script.isPubkeyhash(true)) {
|
||||
const hash = script.code[2].data;
|
||||
const pkh = script.getPubkeyhash(true);
|
||||
if (pkh) {
|
||||
bw.writeU8(0);
|
||||
bw.writeBytes(hash);
|
||||
bw.writeBytes(pkh);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2SH -> 1 | script-hash
|
||||
// Saves 3 bytes.
|
||||
if (script.isScripthash()) {
|
||||
const hash = script.code[1].data;
|
||||
const sh = script.getScripthash();
|
||||
if (sh) {
|
||||
bw.writeU8(1);
|
||||
bw.writeBytes(hash);
|
||||
bw.writeBytes(sh);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2PK -> 2-5 | compressed-key
|
||||
// Only works if the key is valid.
|
||||
// Saves up to 35 bytes.
|
||||
if (script.isPubkey(true)) {
|
||||
const data = script.code[0].data;
|
||||
if (publicKeyVerify(data)) {
|
||||
const key = compressKey(data);
|
||||
const pk = script.getPubkey(true);
|
||||
if (pk) {
|
||||
if (publicKeyVerify(pk)) {
|
||||
const key = compressKey(pk);
|
||||
bw.writeBytes(key);
|
||||
return bw;
|
||||
}
|
||||
@ -135,9 +135,9 @@ function sizeScript(script) {
|
||||
if (script.isScripthash())
|
||||
return 21;
|
||||
|
||||
if (script.isPubkey(true)) {
|
||||
const key = script.code[0].data;
|
||||
if (publicKeyVerify(key))
|
||||
const pk = script.getPubkey(true);
|
||||
if (pk) {
|
||||
if (publicKeyVerify(pk))
|
||||
return 33;
|
||||
}
|
||||
|
||||
|
||||
@ -1408,10 +1408,11 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb
|
||||
// instead of a coinbasevalue.
|
||||
if (coinbase) {
|
||||
const tx = attempt.toCoinbase();
|
||||
const input = tx.inputs[0];
|
||||
|
||||
// Pop off the nonces.
|
||||
tx.inputs[0].script.code.pop();
|
||||
tx.inputs[0].script.compile();
|
||||
input.script.pop();
|
||||
input.script.compile();
|
||||
|
||||
if (attempt.witness) {
|
||||
// We don't include the commitment
|
||||
@ -1420,12 +1421,11 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb
|
||||
assert(output.script.isCommitment());
|
||||
|
||||
// Also not including the witness nonce.
|
||||
tx.inputs[0].witness.length = 0;
|
||||
tx.inputs[0].witness.compile();
|
||||
|
||||
tx.refresh();
|
||||
input.witness.clear();
|
||||
}
|
||||
|
||||
tx.refresh();
|
||||
|
||||
json.coinbasetxn = {
|
||||
data: tx.toRaw().toString('hex'),
|
||||
txid: tx.txid(),
|
||||
@ -2334,13 +2334,14 @@ RPC.prototype._addBlock = async function _addBlock(block) {
|
||||
// Fix eloipool bug (witness nonce is not present).
|
||||
if (state.hasWitness() && block.getCommitmentHash()) {
|
||||
const tx = block.txs[0];
|
||||
const input = tx.inputs[0];
|
||||
if (!tx.hasWitness()) {
|
||||
this.logger.warning('Submitted block had no witness nonce.');
|
||||
this.logger.debug(tx);
|
||||
|
||||
// Recreate witness nonce (all zeroes).
|
||||
tx.inputs[0].witness.set(0, encoding.ZERO_HASH);
|
||||
tx.inputs[0].witness.compile();
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
|
||||
tx.refresh();
|
||||
block.refresh();
|
||||
|
||||
@ -22,7 +22,6 @@ 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);
|
||||
|
||||
@ -241,24 +240,23 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
const input = new Input();
|
||||
|
||||
// Height (required in v2+ blocks)
|
||||
const height = ScriptNum.fromNumber(this.height);
|
||||
input.script.push(height);
|
||||
input.script.pushInt(this.height);
|
||||
|
||||
// Coinbase flags.
|
||||
input.script.push(encoding.ZERO_HASH160);
|
||||
input.script.pushData(encoding.ZERO_HASH160);
|
||||
|
||||
// Smaller nonce for good measure.
|
||||
input.script.push(util.nonce(4));
|
||||
input.script.pushData(util.nonce(4));
|
||||
|
||||
// Extra nonce: incremented when
|
||||
// the nonce overflows.
|
||||
input.script.push(encoding.ZERO_U64);
|
||||
input.script.pushData(encoding.ZERO_U64);
|
||||
|
||||
input.script.compile();
|
||||
|
||||
// Set up the witness nonce.
|
||||
if (this.witness) {
|
||||
input.witness.set(0, encoding.ZERO_HASH);
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
}
|
||||
|
||||
@ -281,7 +279,9 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
}
|
||||
|
||||
// Padding for the CB height (constant size).
|
||||
const padding = 5 - input.script.code[0].getSize();
|
||||
const op = input.script.get(0);
|
||||
assert(op);
|
||||
const padding = 5 - op.getSize();
|
||||
assert(padding >= 0);
|
||||
|
||||
// Reserved size.
|
||||
@ -306,11 +306,10 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
}
|
||||
|
||||
// Setup coinbase flags (variable size).
|
||||
input.script.set(1, this.coinbaseFlags);
|
||||
input.script.setData(1, this.coinbaseFlags);
|
||||
input.script.compile();
|
||||
|
||||
// Setup output script (variable size).
|
||||
output.script.clear();
|
||||
output.script.fromAddress(this.address);
|
||||
|
||||
cb.refresh();
|
||||
|
||||
@ -426,45 +426,39 @@ Address.fromBech32 = function fromBech32(data, network) {
|
||||
*/
|
||||
|
||||
Address.prototype.fromScript = function fromScript(script) {
|
||||
if (script.isPubkey()) {
|
||||
this.hash = digest.hash160(script.get(0));
|
||||
const pk = script.getPubkey();
|
||||
|
||||
if (pk) {
|
||||
this.hash = digest.hash160(pk);
|
||||
this.type = Address.types.PUBKEYHASH;
|
||||
this.version = -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (script.isPubkeyhash()) {
|
||||
this.hash = script.get(2);
|
||||
const pkh = script.getPubkeyhash();
|
||||
|
||||
if (pkh) {
|
||||
this.hash = pkh;
|
||||
this.type = Address.types.PUBKEYHASH;
|
||||
this.version = -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (script.isScripthash()) {
|
||||
this.hash = script.get(1);
|
||||
const sh = script.getScripthash();
|
||||
|
||||
if (sh) {
|
||||
this.hash = sh;
|
||||
this.type = Address.types.SCRIPTHASH;
|
||||
this.version = -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (script.isWitnessPubkeyhash()) {
|
||||
this.hash = script.get(1);
|
||||
this.type = Address.types.WITNESS;
|
||||
this.version = 0;
|
||||
return this;
|
||||
}
|
||||
const program = script.getProgram();
|
||||
|
||||
if (script.isWitnessScripthash()) {
|
||||
this.hash = script.get(1);
|
||||
if (program && !program.isMalformed()) {
|
||||
this.hash = program.data;
|
||||
this.type = Address.types.WITNESS;
|
||||
this.version = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (script.isWitnessMasthash()) {
|
||||
this.hash = script.get(1);
|
||||
this.type = Address.types.WITNESS;
|
||||
this.version = 1;
|
||||
this.version = program.version;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -486,17 +480,21 @@ Address.prototype.fromScript = function fromScript(script) {
|
||||
*/
|
||||
|
||||
Address.prototype.fromWitness = function fromWitness(witness) {
|
||||
const [, pk] = witness.getPubkeyhashInput();
|
||||
|
||||
// We're pretty much screwed here
|
||||
// since we can't get the version.
|
||||
if (witness.isPubkeyhashInput()) {
|
||||
this.hash = digest.hash160(witness.get(1));
|
||||
if (pk) {
|
||||
this.hash = digest.hash160(pk);
|
||||
this.type = Address.types.WITNESS;
|
||||
this.version = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (witness.isScripthashInput()) {
|
||||
this.hash = digest.sha256(witness.get(witness.length - 1));
|
||||
const redeem = witness.getScripthashInput();
|
||||
|
||||
if (redeem) {
|
||||
this.hash = digest.sha256(redeem);
|
||||
this.type = Address.types.WITNESS;
|
||||
this.version = 0;
|
||||
return this;
|
||||
@ -512,15 +510,19 @@ Address.prototype.fromWitness = function fromWitness(witness) {
|
||||
*/
|
||||
|
||||
Address.prototype.fromInputScript = function fromInputScript(script) {
|
||||
if (script.isPubkeyhashInput()) {
|
||||
this.hash = digest.hash160(script.get(1));
|
||||
const [, pk] = script.getPubkeyhashInput();
|
||||
|
||||
if (pk) {
|
||||
this.hash = digest.hash160(pk);
|
||||
this.type = Address.types.PUBKEYHASH;
|
||||
this.version = -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (script.isScripthashInput()) {
|
||||
this.hash = digest.hash160(script.get(script.length - 1));
|
||||
const redeem = script.getScripthashInput();
|
||||
|
||||
if (redeem) {
|
||||
this.hash = digest.hash160(redeem);
|
||||
this.type = Address.types.SCRIPTHASH;
|
||||
this.version = -1;
|
||||
return this;
|
||||
|
||||
@ -384,7 +384,7 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) {
|
||||
for (let i = coinbase.outputs.length - 1; i >= 0; i--) {
|
||||
const output = coinbase.outputs[i];
|
||||
if (output.script.isCommitment()) {
|
||||
hash = output.script.getCommitmentHash();
|
||||
hash = output.script.getCommitment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +89,28 @@ Input.prototype.clone = function clone() {
|
||||
return input;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test equality against another input.
|
||||
* @param {Input} input
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Input.prototype.equals = function equals(input) {
|
||||
assert(Input.isInput(input));
|
||||
return this.prevout.equals(input.prevout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare against another input (BIP69).
|
||||
* @param {Input} input
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Input.prototype.compare = function compare(input) {
|
||||
assert(Input.isInput(input));
|
||||
return this.prevout.compare(input.prevout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the previous output script type as a string.
|
||||
* Will "guess" based on the input script and/or
|
||||
@ -488,11 +510,7 @@ Input.fromTX = function fromTX(tx, index) {
|
||||
*/
|
||||
|
||||
Input.isInput = function isInput(obj) {
|
||||
return obj
|
||||
&& typeof obj.prevout === 'object'
|
||||
&& typeof obj.script === 'object'
|
||||
&& typeof obj.witness === 'object'
|
||||
&& typeof obj.getAddress === 'function';
|
||||
return obj instanceof Input;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -21,7 +21,7 @@ const encoding = require('../utils/encoding');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const policy = require('../protocol/policy');
|
||||
const Amount = require('../btc/amount');
|
||||
const opcodes = Script.opcodes;
|
||||
const Stack = require('../script/stack');
|
||||
|
||||
/**
|
||||
* A mutable transaction object.
|
||||
@ -436,20 +436,22 @@ MTX.prototype.scriptInput = function scriptInput(index, coin, ring) {
|
||||
|
||||
// Don't bother with any below calculation
|
||||
// if the output is already templated.
|
||||
if (input.script.length !== 0
|
||||
|| input.witness.length !== 0) {
|
||||
if (input.script.raw.length !== 0
|
||||
|| input.witness.items.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the previous output's script
|
||||
let prev = coin.script;
|
||||
const prev = coin.script;
|
||||
|
||||
// This is easily the hardest part about
|
||||
// building a transaction with segwit:
|
||||
// figuring out where the redeem script
|
||||
// and witness redeem scripts go.
|
||||
if (prev.isScripthash()) {
|
||||
const redeem = ring.getRedeem(prev.get(1));
|
||||
const sh = prev.getScripthash();
|
||||
|
||||
if (sh) {
|
||||
const redeem = ring.getRedeem(sh);
|
||||
|
||||
if (!redeem)
|
||||
return false;
|
||||
@ -457,33 +459,37 @@ MTX.prototype.scriptInput = function scriptInput(index, coin, ring) {
|
||||
// Witness program nested in regular P2SH.
|
||||
if (redeem.isProgram()) {
|
||||
// P2WSH nested within pay-to-scripthash.
|
||||
if (redeem.isWitnessScripthash()) {
|
||||
prev = ring.getRedeem(redeem.get(1));
|
||||
const wsh = redeem.getWitnessScripthash();
|
||||
if (wsh) {
|
||||
const wredeem = ring.getRedeem(wsh);
|
||||
|
||||
if (!prev)
|
||||
if (!wredeem)
|
||||
return false;
|
||||
|
||||
if (!this.scriptVector(prev, input.witness, ring))
|
||||
const witness = this.scriptVector(wredeem, ring);
|
||||
|
||||
if (!witness)
|
||||
return false;
|
||||
|
||||
input.witness.push(prev.toRaw());
|
||||
input.witness.compile();
|
||||
witness.push(wredeem.toRaw());
|
||||
|
||||
input.script.push(redeem.toRaw());
|
||||
input.script.compile();
|
||||
input.witness.fromStack(witness);
|
||||
input.script.fromItems([redeem.toRaw()]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// P2WPKH nested within pay-to-scripthash.
|
||||
if (redeem.isWitnessPubkeyhash()) {
|
||||
prev = Script.fromPubkeyhash(ring.getKeyHash());
|
||||
const wpkh = redeem.getWitnessPubkeyhash();
|
||||
if (wpkh) {
|
||||
const pkh = Script.fromPubkeyhash(wpkh);
|
||||
const witness = this.scriptVector(pkh, ring);
|
||||
|
||||
if (!this.scriptVector(prev, input.witness, ring))
|
||||
if (!witness)
|
||||
return false;
|
||||
|
||||
input.script.push(redeem.toRaw());
|
||||
input.script.compile();
|
||||
input.witness.fromStack(witness);
|
||||
input.script.fromItems([redeem.toRaw()]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -493,11 +499,14 @@ MTX.prototype.scriptInput = function scriptInput(index, coin, ring) {
|
||||
}
|
||||
|
||||
// Regular P2SH.
|
||||
if (!this.scriptVector(redeem, input.script, ring))
|
||||
const vector = this.scriptVector(redeem, ring);
|
||||
|
||||
if (!vector)
|
||||
return false;
|
||||
|
||||
input.script.push(redeem.toRaw());
|
||||
input.script.compile();
|
||||
vector.push(redeem.toRaw());
|
||||
|
||||
input.script.fromStack(vector);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -505,29 +514,35 @@ MTX.prototype.scriptInput = function scriptInput(index, coin, ring) {
|
||||
// Witness program.
|
||||
if (prev.isProgram()) {
|
||||
// Bare P2WSH.
|
||||
if (prev.isWitnessScripthash()) {
|
||||
const redeem = ring.getRedeem(prev.get(1));
|
||||
const wsh = prev.getWitnessScripthash();
|
||||
if (wsh) {
|
||||
const wredeem = ring.getRedeem(wsh);
|
||||
|
||||
if (!redeem)
|
||||
if (!wredeem)
|
||||
return false;
|
||||
|
||||
if (!this.scriptVector(redeem, input.witness, ring))
|
||||
const vector = this.scriptVector(wredeem, ring);
|
||||
|
||||
if (!vector)
|
||||
return false;
|
||||
|
||||
input.witness.push(redeem.toRaw());
|
||||
input.witness.compile();
|
||||
vector.push(wredeem.toRaw());
|
||||
|
||||
input.witness.fromStack(vector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bare P2WPKH.
|
||||
if (prev.isWitnessPubkeyhash()) {
|
||||
prev = Script.fromPubkeyhash(prev.get(1));
|
||||
const wpkh = prev.getWitnessPubkeyhash();
|
||||
if (wpkh) {
|
||||
const pkh = Script.fromPubkeyhash(wpkh);
|
||||
const vector = this.scriptVector(pkh, ring);
|
||||
|
||||
if (!this.scriptVector(prev, input.witness, ring))
|
||||
if (!vector)
|
||||
return false;
|
||||
|
||||
input.witness.compile();
|
||||
input.witness.fromStack(vector);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -537,61 +552,73 @@ MTX.prototype.scriptInput = function scriptInput(index, coin, ring) {
|
||||
}
|
||||
|
||||
// Wow, a normal output! Praise be to Jengus and Gord.
|
||||
return this.scriptVector(prev, input.script, ring);
|
||||
const vector = this.scriptVector(prev, ring);
|
||||
|
||||
if (!vector)
|
||||
return false;
|
||||
|
||||
input.script.fromStack(vector);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build script for a single vector
|
||||
* based on a previous script.
|
||||
* @param {Script} prev
|
||||
* @param {Witness|Script} vector
|
||||
* @param {Buffer} ring
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
|
||||
MTX.prototype.scriptVector = function scriptVector(prev, ring) {
|
||||
// P2PK
|
||||
if (prev.isPubkey()) {
|
||||
if (!prev.get(0).equals(ring.publicKey))
|
||||
return false;
|
||||
const pk = prev.getPubkey();
|
||||
if (pk) {
|
||||
if (!pk.equals(ring.publicKey))
|
||||
return null;
|
||||
|
||||
vector.set(0, opcodes.OP_0);
|
||||
const stack = new Stack();
|
||||
|
||||
return true;
|
||||
stack.pushInt(0);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// P2PKH
|
||||
if (prev.isPubkeyhash()) {
|
||||
if (!prev.get(2).equals(ring.getKeyHash()))
|
||||
return false;
|
||||
const pkh = prev.getPubkeyhash();
|
||||
if (pkh) {
|
||||
if (!pkh.equals(ring.getKeyHash()))
|
||||
return null;
|
||||
|
||||
vector.set(0, opcodes.OP_0);
|
||||
vector.set(1, ring.publicKey);
|
||||
const stack = new Stack();
|
||||
|
||||
return true;
|
||||
stack.pushInt(0);
|
||||
stack.pushData(ring.publicKey);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Multisig
|
||||
if (prev.isMultisig()) {
|
||||
const [, n] = prev.getMultisig();
|
||||
if (n !== -1) {
|
||||
if (prev.indexOf(ring.publicKey) === -1)
|
||||
return false;
|
||||
return null;
|
||||
|
||||
// Technically we should create m signature slots,
|
||||
// but we create n signature slots so we can order
|
||||
// the signatures properly.
|
||||
vector.set(0, opcodes.OP_0);
|
||||
const stack = new Stack();
|
||||
|
||||
// Grab `n` value (number of keys).
|
||||
const n = prev.getSmall(prev.length - 2);
|
||||
stack.pushInt(0);
|
||||
|
||||
// Fill script with `n` signature slots.
|
||||
for (let i = 0; i < n; i++)
|
||||
vector.set(i + 1, opcodes.OP_0);
|
||||
stack.pushInt(0);
|
||||
|
||||
return true;
|
||||
return stack;
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -605,11 +632,11 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MTX.prototype.signInputAsync = function signInputAsync(index, coin, ring, type, pool) {
|
||||
MTX.prototype.signInputAsync = async function signInputAsync(index, coin, ring, type, pool) {
|
||||
if (!pool)
|
||||
return this.signInput(index, coin, ring, type);
|
||||
|
||||
return pool.signInput(this, index, coin, ring, type, pool);
|
||||
return await pool.signInput(this, index, coin, ring, type, pool);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -656,32 +683,51 @@ MTX.prototype.signInput = function signInput(index, coin, ring, type) {
|
||||
vector = input.witness;
|
||||
redeem = true;
|
||||
version = 1;
|
||||
} else if (prev.isWitnessPubkeyhash()) {
|
||||
prev = Script.fromPubkeyhash(prev.get(1));
|
||||
vector = input.witness;
|
||||
redeem = false;
|
||||
version = 1;
|
||||
} else {
|
||||
const wpkh = prev.getWitnessPubkeyhash();
|
||||
if (wpkh) {
|
||||
prev = Script.fromPubkeyhash(wpkh);
|
||||
vector = input.witness;
|
||||
redeem = false;
|
||||
version = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create our signature.
|
||||
const sig = this.signature(index, prev, value, key, type, version);
|
||||
|
||||
if (redeem) {
|
||||
const redeem = vector.pop();
|
||||
const result = this.signVector(prev, vector, sig, ring);
|
||||
vector.push(redeem);
|
||||
vector.compile();
|
||||
return result;
|
||||
const stack = vector.toStack();
|
||||
const redeem = stack.pop();
|
||||
|
||||
const result = this.signVector(prev, stack, sig, ring);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
result.push(redeem);
|
||||
|
||||
vector.fromStack(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.signVector(prev, vector, sig, ring);
|
||||
const stack = vector.toStack();
|
||||
const result = this.signVector(prev, stack, sig, ring);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
vector.fromStack(result);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a signature to a vector
|
||||
* based on a previous script.
|
||||
* @param {Script} prev
|
||||
* @param {Witness|Script} vector
|
||||
* @param {Stack} vector
|
||||
* @param {Buffer} sig
|
||||
* @param {KeyRing} ring
|
||||
* @return {Boolean}
|
||||
@ -689,79 +735,83 @@ MTX.prototype.signInput = function signInput(index, coin, ring, type) {
|
||||
|
||||
MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
// P2PK
|
||||
if (prev.isPubkey()) {
|
||||
const pk = prev.getPubkey();
|
||||
if (pk) {
|
||||
// Make sure the pubkey is ours.
|
||||
if (!ring.publicKey.equals(prev.get(0)))
|
||||
return false;
|
||||
if (!ring.publicKey.equals(pk))
|
||||
return null;
|
||||
|
||||
// Already signed.
|
||||
if (Script.isSignature(vector.get(0)))
|
||||
return true;
|
||||
|
||||
if (vector.getSmall(0) !== 0)
|
||||
if (vector.length === 0)
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
vector.set(0, sig);
|
||||
vector.compile();
|
||||
// Already signed.
|
||||
if (vector.get(0).length > 0)
|
||||
return vector;
|
||||
|
||||
return true;
|
||||
vector.set(0, sig);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
// P2PKH
|
||||
if (prev.isPubkeyhash()) {
|
||||
const pkh = prev.getPubkeyhash();
|
||||
if (pkh) {
|
||||
// Make sure the pubkey hash is ours.
|
||||
if (!ring.getKeyHash().equals(prev.get(2)))
|
||||
return false;
|
||||
if (!ring.getKeyHash().equals(pkh))
|
||||
return null;
|
||||
|
||||
// Already signed.
|
||||
if (Script.isSignature(vector.get(0)))
|
||||
return true;
|
||||
|
||||
if (!Script.isKey(vector.get(1)))
|
||||
if (vector.length !== 2)
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
vector.set(0, sig);
|
||||
vector.compile();
|
||||
if (vector.get(1).length === 0)
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
return true;
|
||||
// Already signed.
|
||||
if (vector.get(0).length > 0)
|
||||
return vector;
|
||||
|
||||
vector.set(0, sig);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
// Multisig
|
||||
if (prev.isMultisig()) {
|
||||
if (vector.getSmall(0) !== 0)
|
||||
const [m, n] = prev.getMultisig();
|
||||
if (m !== -1) {
|
||||
if (vector.length < 2)
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
// Grab `n` value (number of keys).
|
||||
const n = prev.getSmall(prev.length - 2);
|
||||
if (vector.get(0).length !== 0)
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
// Too many signature slots. Abort.
|
||||
if (vector.length - 1 > n)
|
||||
return false;
|
||||
throw new Error('Input has not been templated.');
|
||||
|
||||
// Count the number of current signatures.
|
||||
let total = 0;
|
||||
for (let i = 1; i < vector.length; i++) {
|
||||
if (Script.isSignature(vector.get(i)))
|
||||
const item = vector.get(i);
|
||||
if (item.length > 0)
|
||||
total++;
|
||||
}
|
||||
|
||||
// Grab `m` value (number of sigs required).
|
||||
const m = prev.getSmall(0);
|
||||
|
||||
// Signatures are already finalized.
|
||||
if (total === m && vector.length - 1 === m)
|
||||
return true;
|
||||
return vector;
|
||||
|
||||
// Add some signature slots for us to use if
|
||||
// there was for some reason not enough.
|
||||
while (vector.length - 1 < n)
|
||||
vector.push(opcodes.OP_0);
|
||||
vector.pushInt(0);
|
||||
|
||||
// Grab the redeem script's keys to figure
|
||||
// out where our key should go.
|
||||
const keys = [];
|
||||
for (let i = 1; i < prev.length - 2; i++)
|
||||
keys.push(prev.get(i));
|
||||
for (const op of prev.code) {
|
||||
if (op.data)
|
||||
keys.push(op.data);
|
||||
}
|
||||
|
||||
// Find the key index so we can place
|
||||
// the signature in the same index.
|
||||
@ -771,7 +821,7 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
// script. We tried to sign a transaction
|
||||
// that is not redeemable by us.
|
||||
if (keyIndex === -1)
|
||||
return false;
|
||||
return null;
|
||||
|
||||
// Offset key index by one to turn it into
|
||||
// "sig index". Accounts for OP_0 byte at
|
||||
@ -782,7 +832,7 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
// and increment the total number of
|
||||
// signatures.
|
||||
if (keyIndex < vector.length && total < m) {
|
||||
if (vector.getSmall(keyIndex) === 0) {
|
||||
if (vector.get(keyIndex).length === 0) {
|
||||
vector.set(keyIndex, sig);
|
||||
total++;
|
||||
}
|
||||
@ -792,7 +842,8 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
if (total >= m) {
|
||||
// Remove empty slots left over.
|
||||
for (let i = vector.length - 1; i >= 1; i--) {
|
||||
if (vector.getSmall(i) === 0)
|
||||
const item = vector.get(i);
|
||||
if (item.length === 0)
|
||||
vector.remove(i);
|
||||
}
|
||||
|
||||
@ -808,15 +859,10 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
assert(vector.length - 1 === m);
|
||||
}
|
||||
|
||||
vector.compile();
|
||||
|
||||
if (total !== m)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return vector;
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -873,46 +919,57 @@ MTX.prototype.isInputSigned = function isInputSigned(index, coin) {
|
||||
return false;
|
||||
vector = input.witness;
|
||||
redeem = true;
|
||||
} else if (prev.isWitnessPubkeyhash()) {
|
||||
prev = Script.fromPubkeyhash(prev.get(1));
|
||||
vector = input.witness;
|
||||
redeem = false;
|
||||
} else {
|
||||
const wpkh = prev.getWitnessPubkeyhash();
|
||||
if (wpkh) {
|
||||
prev = Script.fromPubkeyhash(wpkh);
|
||||
vector = input.witness;
|
||||
redeem = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (redeem) {
|
||||
const redeem = vector.pop();
|
||||
const result = this.isVectorSigned(prev, vector);
|
||||
vector.push(redeem);
|
||||
return result;
|
||||
}
|
||||
const stack = vector.toStack();
|
||||
|
||||
return this.isVectorSigned(prev, vector);
|
||||
if (redeem)
|
||||
stack.pop();
|
||||
|
||||
return this.isVectorSigned(prev, stack);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a vector is fully-signed.
|
||||
* @param {Script} prev
|
||||
* @param {Script|Witness} vector
|
||||
* @param {Stack} vector
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
MTX.prototype.isVectorSigned = function isVectorSigned(prev, vector) {
|
||||
if (prev.isPubkey()) {
|
||||
if (!Script.isSignature(vector.get(0)))
|
||||
if (vector.length !== 1)
|
||||
return false;
|
||||
|
||||
if (vector.get(0).length === 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prev.isPubkeyhash()) {
|
||||
if (!Script.isSignature(vector.get(0)))
|
||||
if (vector.length !== 2)
|
||||
return false;
|
||||
|
||||
if (vector.get(0).length === 0)
|
||||
return false;
|
||||
|
||||
if (vector.get(1).length === 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prev.isMultisig()) {
|
||||
// Grab `m` value (number of required sigs).
|
||||
const m = prev.getSmall(0);
|
||||
const [m] = prev.getMultisig();
|
||||
|
||||
if (m !== -1) {
|
||||
// Ensure we have the correct number
|
||||
// of required signatures.
|
||||
if (vector.length - 1 !== m)
|
||||
@ -920,7 +977,8 @@ MTX.prototype.isVectorSigned = function isVectorSigned(prev, vector) {
|
||||
|
||||
// Ensure all members are signatures.
|
||||
for (let i = 1; i < vector.length; i++) {
|
||||
if (!Script.isSignature(vector.get(i)))
|
||||
const item = vector.get(i);
|
||||
if (item.length === 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1020,11 +1078,11 @@ MTX.prototype.sign = function sign(ring, type) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MTX.prototype.signAsync = function signAsync(ring, type, pool) {
|
||||
MTX.prototype.signAsync = async function signAsync(ring, type, pool) {
|
||||
if (!pool)
|
||||
return this.sign(ring, type);
|
||||
|
||||
return pool.sign(this, ring, type);
|
||||
return await pool.sign(this, ring, type);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1035,6 +1093,7 @@ MTX.prototype.signAsync = function signAsync(ring, type, pool) {
|
||||
|
||||
MTX.prototype.estimateSize = async function estimateSize(estimate) {
|
||||
const scale = consensus.WITNESS_SCALE_FACTOR;
|
||||
|
||||
let total = 0;
|
||||
|
||||
// Calculate the size, minus the input scripts.
|
||||
@ -1083,13 +1142,14 @@ MTX.prototype.estimateSize = async function estimateSize(estimate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev.isMultisig()) {
|
||||
const [m] = prev.getMultisig();
|
||||
if (m !== -1) {
|
||||
let size = 0;
|
||||
// Bare Multisig
|
||||
// OP_0
|
||||
size += 1;
|
||||
// OP_PUSHDATA0 [signature] ...
|
||||
size += (1 + 73) * prev.getSmall(0);
|
||||
size += (1 + 73) * m;
|
||||
// varint len
|
||||
size += encoding.sizeVarint(size);
|
||||
total += size;
|
||||
@ -1427,10 +1487,7 @@ MTX.fromTX = function fromTX(tx) {
|
||||
*/
|
||||
|
||||
MTX.isMTX = function isMTX(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.inputs)
|
||||
&& typeof obj.locktime === 'number'
|
||||
&& typeof obj.scriptInput === 'function';
|
||||
return obj instanceof MTX;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1849,23 +1906,11 @@ function sortValue(a, b) {
|
||||
}
|
||||
|
||||
function sortInputs(a, b) {
|
||||
const ahash = a.prevout.txid();
|
||||
const bhash = b.prevout.txid();
|
||||
const cmp = util.strcmp(ahash, bhash);
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
return a.prevout.index - b.prevout.index;
|
||||
return a.compare(b);
|
||||
}
|
||||
|
||||
function sortOutputs(a, b) {
|
||||
const cmp = a.value - b.value;
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
return a.script.raw.compare(b.script.raw);
|
||||
return a.compare(b);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const util = require('../utils/util');
|
||||
const assert = require('assert');
|
||||
const util = require('../utils/util');
|
||||
const StaticWriter = require('../utils/writer');
|
||||
const BufferReader = require('../utils/reader');
|
||||
const encoding = require('../utils/encoding');
|
||||
@ -62,6 +62,47 @@ Outpoint.fromOptions = function fromOptions(options) {
|
||||
return new Outpoint().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone the outpoint.
|
||||
* @returns {Outpoint}
|
||||
*/
|
||||
|
||||
Outpoint.prototype.clone = function clone() {
|
||||
const outpoint = new Outpoint();
|
||||
outpoint.hash = this.value;
|
||||
outpoint.index = this.index;
|
||||
return outpoint;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test equality against another outpoint.
|
||||
* @param {Outpoint} prevout
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Outpoint.prototype.equals = function equals(prevout) {
|
||||
assert(Outpoint.isOutpoint(prevout));
|
||||
return this.hash === prevout.hash
|
||||
&& this.index === prevout.index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare against another outpoint (BIP69).
|
||||
* @param {Outpoint} prevout
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Outpoint.prototype.compare = function compare(prevout) {
|
||||
assert(Outpoint.isOutpoint(prevout));
|
||||
|
||||
const cmp = util.strcmp(this.txid(), prevout.txid());
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
return this.index - prevout.index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the outpoint is null (hash of zeroes
|
||||
* with max-u32 index). Used to detect coinbases.
|
||||
@ -293,10 +334,7 @@ Outpoint.prototype.inspect = function inspect() {
|
||||
*/
|
||||
|
||||
Outpoint.isOutpoint = function isOutpoint(obj) {
|
||||
return obj
|
||||
&& typeof obj.hash === 'string'
|
||||
&& typeof obj.index === 'number'
|
||||
&& typeof obj.toKey === 'function';
|
||||
return obj instanceof Outpoint;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -118,6 +118,35 @@ Output.prototype.clone = function clone() {
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test equality against another output.
|
||||
* @param {Output} output
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Output.prototype.equals = function equals(output) {
|
||||
assert(Output.isOutput(output));
|
||||
return this.value === output.value
|
||||
&& this.script.equals(output.script);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare against another output (BIP69).
|
||||
* @param {Output} output
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Output.prototype.compare = function compare(output) {
|
||||
assert(Output.isOutput(output));
|
||||
|
||||
const cmp = this.value - output.value;
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
return this.script.compare(output.script);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the script type as a string.
|
||||
* @returns {ScriptType} type
|
||||
@ -339,10 +368,7 @@ Output.fromRaw = function fromRaw(data, enc) {
|
||||
*/
|
||||
|
||||
Output.isOutput = function isOutput(obj) {
|
||||
return obj
|
||||
&& typeof obj.value === 'number'
|
||||
&& typeof obj.script === 'object'
|
||||
&& typeof obj.getAddress === 'function';
|
||||
return obj instanceof Output;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -24,7 +24,7 @@ const InvItem = require('./invitem');
|
||||
const Bloom = require('../utils/bloom');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const policy = require('../protocol/policy');
|
||||
const {ScriptError} = require('../script/common');
|
||||
const ScriptError = require('../script/scripterror');
|
||||
const hashType = Script.hashType;
|
||||
|
||||
/**
|
||||
@ -60,6 +60,7 @@ function TX(options) {
|
||||
this._raw = null;
|
||||
this._size = -1;
|
||||
this._witness = -1;
|
||||
this._sigops = -1;
|
||||
|
||||
this._hashPrevouts = null;
|
||||
this._hashSequence = null;
|
||||
@ -156,6 +157,7 @@ TX.prototype.refresh = function refresh() {
|
||||
this._raw = null;
|
||||
this._size = -1;
|
||||
this._witness = -1;
|
||||
this._sigops = -1;
|
||||
|
||||
this._hashPrevouts = null;
|
||||
this._hashSequence = null;
|
||||
@ -1295,6 +1297,9 @@ TX.prototype.verifySequence = function verifySequence(index, locktime) {
|
||||
*/
|
||||
|
||||
TX.prototype.getLegacySigops = function getLegacySigops() {
|
||||
if (this._sigops !== -1)
|
||||
return this._sigops;
|
||||
|
||||
let total = 0;
|
||||
|
||||
for (const input of this.inputs)
|
||||
@ -1303,6 +1308,9 @@ TX.prototype.getLegacySigops = function getLegacySigops() {
|
||||
for (const output of this.outputs)
|
||||
total += output.script.getSigops(false);
|
||||
|
||||
if (!this.mutable)
|
||||
this._sigops = total;
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
@ -1313,11 +1321,11 @@ TX.prototype.getLegacySigops = function getLegacySigops() {
|
||||
*/
|
||||
|
||||
TX.prototype.getScripthashSigops = function getScripthashSigops(view) {
|
||||
let total = 0;
|
||||
|
||||
if (this.isCoinbase())
|
||||
return 0;
|
||||
|
||||
let total = 0;
|
||||
|
||||
for (const input of this.inputs) {
|
||||
const coin = view.getOutputFor(input);
|
||||
|
||||
@ -1334,27 +1342,16 @@ TX.prototype.getScripthashSigops = function getScripthashSigops(view) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate sigops cost, taking into account witness programs.
|
||||
* Calculate accurate sigop count, taking into account redeem scripts.
|
||||
* @param {CoinView} view
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Number} sigop weight
|
||||
* @returns {Number} sigop count
|
||||
*/
|
||||
|
||||
TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
const scale = consensus.WITNESS_SCALE_FACTOR;
|
||||
let cost = this.getLegacySigops() * scale;
|
||||
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
TX.prototype.getWitnessSigops = function getWitnessSigops(view) {
|
||||
if (this.isCoinbase())
|
||||
return cost;
|
||||
return 0;
|
||||
|
||||
if (flags & Script.flags.VERIFY_P2SH)
|
||||
cost += this.getScripthashSigops(view) * scale;
|
||||
|
||||
if (!(flags & Script.flags.VERIFY_WITNESS))
|
||||
return cost;
|
||||
let total = 0;
|
||||
|
||||
for (const input of this.inputs) {
|
||||
const coin = view.getOutputFor(input);
|
||||
@ -1362,9 +1359,33 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
cost += coin.script.getWitnessSigops(input.script, input.witness);
|
||||
total += coin.script.getWitnessSigops(input.script, input.witness);
|
||||
}
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate sigops cost, taking into account witness programs.
|
||||
* @param {CoinView} view
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Number} sigop weight
|
||||
*/
|
||||
|
||||
TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
const scale = consensus.WITNESS_SCALE_FACTOR;
|
||||
|
||||
let cost = this.getLegacySigops() * scale;
|
||||
|
||||
if (flags & Script.flags.VERIFY_P2SH)
|
||||
cost += this.getScripthashSigops(view) * scale;
|
||||
|
||||
if (flags & Script.flags.VERIFY_WITNESS)
|
||||
cost += this.getWitnessSigops(view);
|
||||
|
||||
return cost;
|
||||
};
|
||||
|
||||
@ -1627,7 +1648,7 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) {
|
||||
}
|
||||
|
||||
if (prev.isPubkeyhash()) {
|
||||
if (input.witness.length - 1 !== 2)
|
||||
if (input.witness.items.length - 1 !== 2)
|
||||
return false;
|
||||
|
||||
if (witness.items[0].length > 73)
|
||||
@ -2513,10 +2534,7 @@ TX.isWitness = function isWitness(br) {
|
||||
*/
|
||||
|
||||
TX.isTX = function isTX(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.inputs)
|
||||
&& typeof obj.locktime === 'number'
|
||||
&& typeof obj.witnessHash === 'function';
|
||||
return obj instanceof TX;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
const assert = require('assert');
|
||||
const util = require('../utils/util');
|
||||
const secp256k1 = require('../crypto/secp256k1');
|
||||
const ScriptNum = require('./scriptnum');
|
||||
|
||||
/**
|
||||
* Script opcodes.
|
||||
@ -163,6 +164,32 @@ exports.opcodes = {
|
||||
|
||||
exports.opcodesByVal = util.reverse(exports.opcodes);
|
||||
|
||||
/**
|
||||
* Small ints (1 indexed, 1==0).
|
||||
* @const {Buffer[]}
|
||||
*/
|
||||
|
||||
exports.small = [
|
||||
Buffer.from([0x81]),
|
||||
Buffer.from([]),
|
||||
Buffer.from([0x01]),
|
||||
Buffer.from([0x02]),
|
||||
Buffer.from([0x03]),
|
||||
Buffer.from([0x04]),
|
||||
Buffer.from([0x05]),
|
||||
Buffer.from([0x06]),
|
||||
Buffer.from([0x07]),
|
||||
Buffer.from([0x08]),
|
||||
Buffer.from([0x09]),
|
||||
Buffer.from([0x0a]),
|
||||
Buffer.from([0x0b]),
|
||||
Buffer.from([0x0c]),
|
||||
Buffer.from([0x0d]),
|
||||
Buffer.from([0x0e]),
|
||||
Buffer.from([0x0f]),
|
||||
Buffer.from([0x10])
|
||||
];
|
||||
|
||||
/**
|
||||
* Script and locktime flags. See {@link VerifyFlags}.
|
||||
* @enum {Number}
|
||||
@ -293,27 +320,6 @@ exports.types = {
|
||||
|
||||
exports.typesByVal = util.reverse(exports.types);
|
||||
|
||||
/**
|
||||
* False stack return value.
|
||||
* @const {Buffer}
|
||||
*/
|
||||
|
||||
exports.STACK_FALSE = Buffer.from([]);
|
||||
|
||||
/**
|
||||
* True stack return value.
|
||||
* @const {Buffer}
|
||||
*/
|
||||
|
||||
exports.STACK_TRUE = Buffer.from([0x01]);
|
||||
|
||||
/**
|
||||
* -1 stack return value.
|
||||
* @const {Buffer}
|
||||
*/
|
||||
|
||||
exports.STACK_NEGATE = Buffer.from([0x81]);
|
||||
|
||||
/**
|
||||
* Test a signature to see whether it contains a valid sighash type.
|
||||
* @param {Buffer} sig
|
||||
@ -347,56 +353,6 @@ exports.isLowDER = function isLowDER(sig) {
|
||||
return secp256k1.isLowS(sig.slice(0, -1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a small integer from an opcode (OP_0-OP_16).
|
||||
* @param {Number} index
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
exports.getSmall = function getSmall(op) {
|
||||
assert(typeof op === 'number');
|
||||
|
||||
if (op === exports.opcodes.OP_0)
|
||||
return 0;
|
||||
|
||||
if (op >= exports.opcodes.OP_1 && op <= exports.opcodes.OP_16)
|
||||
return op - 0x50;
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a ripemd160 hash.
|
||||
* @param {Buffer?} hash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isHash = function isHash(hash) {
|
||||
return Buffer.isBuffer(hash) && hash.length === 20;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a public key. Note that
|
||||
* this does not verify the format of the key, only the length.
|
||||
* @param {Buffer?} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isKey = function isKey(key) {
|
||||
return Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a signature. Note that
|
||||
* this does not verify the format of the signature, only the length.
|
||||
* @param {Buffer?} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isSignature = function isSignature(sig) {
|
||||
return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid key.
|
||||
* @param {Buffer} key
|
||||
@ -532,224 +488,31 @@ exports.isSignatureEncoding = function isSignatureEncoding(sig) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Cast a big number or Buffer to a bool.
|
||||
* @see CastToBool
|
||||
* @param {Buffer} value
|
||||
* @returns {Boolean}
|
||||
* Format stack item into bitcoind asm format.
|
||||
* @param {Buffer} item
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
exports.toASM = function toASM(item, decode) {
|
||||
if (item.length <= 4) {
|
||||
const num = ScriptNum.decode(item, false, 4);
|
||||
return num.toString(10);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
if (decode && exports.isSignatureEncoding(item)) {
|
||||
const type = item[item.length - 1];
|
||||
|
||||
/**
|
||||
* Format script code into a human readable-string.
|
||||
* @param {Array} code
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
let symbol = exports.hashTypeByVal[type & 0x1f] || '';
|
||||
|
||||
exports.formatStack = function formatStack(items) {
|
||||
const out = [];
|
||||
|
||||
for (const item of items)
|
||||
out.push(item.toString('hex'));
|
||||
|
||||
return out.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Format script code into a human readable-string.
|
||||
* @param {Array} code
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
|
||||
exports.formatCode = function formatCode(code) {
|
||||
const out = [];
|
||||
|
||||
for (const op of code) {
|
||||
// Bad push
|
||||
if (op.value === -1) {
|
||||
out.push('OP_INVALIDOPCODE');
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.data) {
|
||||
const symbol = exports.opcodesByVal[op.value];
|
||||
|
||||
// Direct push
|
||||
if (!symbol) {
|
||||
let size = op.value.toString(16);
|
||||
if (size.length < 2)
|
||||
size = '0' + size;
|
||||
out.push(`0x${size} 0x${op.data.toString('hex')}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pushdatas
|
||||
let size = op.data.length.toString(16);
|
||||
|
||||
while (size.length % 2 !== 0)
|
||||
size = '0' + size;
|
||||
|
||||
out.push(`${symbol} 0x${size} 0x${op.data.toString('hex')}`);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Opcodes
|
||||
let symbol = exports.opcodesByVal[op.value];
|
||||
if (symbol) {
|
||||
out.push(symbol);
|
||||
continue;
|
||||
if (type & exports.hashType.ANYONECANPAY)
|
||||
symbol += '|ANYONECANPAY';
|
||||
symbol = `[${symbol}]`;
|
||||
}
|
||||
|
||||
// Unknown opcodes
|
||||
symbol = op.value.toString(16);
|
||||
|
||||
if (symbol.length < 2)
|
||||
symbol = '0' + symbol;
|
||||
|
||||
out.push(`0x${symbol}`);
|
||||
return item.slice(0, -1).toString('hex') + symbol;
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
return item.toString('hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* Format script code into bitcoind asm format.
|
||||
* @param {Array} code
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
|
||||
exports.formatItem = function formatItem(data, decode) {
|
||||
if (data.length <= 4) {
|
||||
data = exports.num(data, exports.flags.VERIFY_NONE);
|
||||
return data.toString(10);
|
||||
}
|
||||
|
||||
if (decode) {
|
||||
let symbol = '';
|
||||
if (exports.isSignatureEncoding(data)) {
|
||||
const type = data[data.length - 1];
|
||||
|
||||
symbol = exports.hashTypeByVal[type & 0x1f] || '';
|
||||
|
||||
if (symbol) {
|
||||
if (type & exports.hashType.ANYONECANPAY)
|
||||
symbol += '|ANYONECANPAY';
|
||||
symbol = `[${symbol}]`;
|
||||
}
|
||||
|
||||
data = data.slice(0, -1);
|
||||
}
|
||||
return data.toString('hex') + symbol;
|
||||
}
|
||||
|
||||
return data.toString('hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* Format script code into bitcoind asm format.
|
||||
* @param {Array} code
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
|
||||
exports.formatASM = function formatASM(code, decode) {
|
||||
if (code.length > 0) {
|
||||
if (code[0].value === exports.opcodes.OP_RETURN)
|
||||
decode = false;
|
||||
}
|
||||
|
||||
const out = [];
|
||||
|
||||
for (const op of code) {
|
||||
if (op.value === -1) {
|
||||
out.push('[error]');
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.data) {
|
||||
const data = exports.formatItem(op.data, decode);
|
||||
out.push(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
const symbol = exports.opcodesByVal[op.value] || 'OP_UNKNOWN';
|
||||
|
||||
out.push(symbol);
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Format script code into bitcoind asm format.
|
||||
* @param {Array} code
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable string.
|
||||
*/
|
||||
|
||||
exports.formatStackASM = function formatStackASM(items, decode) {
|
||||
const out = [];
|
||||
|
||||
for (const item of items) {
|
||||
const data = exports.formatItem(item, decode);
|
||||
out.push(data);
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* An error thrown from the scripting system,
|
||||
* potentially pertaining to Script execution.
|
||||
* @alias module:script.ScriptError
|
||||
* @constructor
|
||||
* @extends Error
|
||||
* @param {String} code - Error code.
|
||||
* @param {Opcode} op - Opcode.
|
||||
* @param {Number?} ip - Instruction pointer.
|
||||
* @property {String} message - Error message.
|
||||
* @property {String} code - Original code passed in.
|
||||
* @property {Number} op - Opcode.
|
||||
* @property {Number} ip - Instruction pointer.
|
||||
*/
|
||||
|
||||
exports.ScriptError = function ScriptError(code, op, ip) {
|
||||
if (!(this instanceof ScriptError))
|
||||
return new ScriptError(code, op, ip);
|
||||
|
||||
Error.call(this);
|
||||
|
||||
this.type = 'ScriptError';
|
||||
this.code = code;
|
||||
this.message = code;
|
||||
this.op = -1;
|
||||
this.ip = -1;
|
||||
|
||||
if (typeof op === 'string') {
|
||||
this.message = op;
|
||||
} else if (op) {
|
||||
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
||||
this.op = op.value;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, ScriptError);
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(exports.ScriptError.prototype, Error.prototype);
|
||||
|
||||
@ -14,6 +14,7 @@ exports.common = require('./common');
|
||||
exports.Opcode = require('./opcode');
|
||||
exports.Program = require('./program');
|
||||
exports.Script = require('./script');
|
||||
exports.ScriptError = require('./scripterror');
|
||||
exports.ScriptNum = require('./scriptnum');
|
||||
exports.sigcache = require('./sigcache');
|
||||
exports.Stack = require('./stack');
|
||||
|
||||
@ -101,6 +101,194 @@ Opcode.prototype.isBranch = function isBranch() {
|
||||
return this.value >= opcodes.OP_IF && this.value <= opcodes.OP_ENDIF;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test opcode equality.
|
||||
* @param {Opcode} op
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Opcode.prototype.equals = function equals(op) {
|
||||
assert(Opcode.isOpcode(op));
|
||||
|
||||
if (this.value !== op.value)
|
||||
return false;
|
||||
|
||||
if (!this.data) {
|
||||
assert(!op.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(op.data);
|
||||
|
||||
return this.data.equals(op.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert Opcode to opcode value.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toOp = function toOp() {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Covert opcode to data push.
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toData = function toData() {
|
||||
return this.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Covert opcode to data length.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toLength = function toLength() {
|
||||
return this.data ? this.data.length : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Covert and _cast_ opcode to data push.
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toPush = function toPush() {
|
||||
if (this.data)
|
||||
return this.data;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string for opcode.
|
||||
* @param {String?} enc
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toString = function toString(enc) {
|
||||
const data = this.toPush();
|
||||
|
||||
if (!data)
|
||||
return null;
|
||||
|
||||
return data.toString(enc || 'utf8');
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to small integer.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toSmall = function toSmall() {
|
||||
if (this.value === opcodes.OP_0)
|
||||
return 0;
|
||||
|
||||
if (this.value >= opcodes.OP_1 && this.value <= opcodes.OP_16)
|
||||
return this.value - 0x50;
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to script number.
|
||||
* @returns {ScriptNum|null}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toNum = function toNum(minimal, limit) {
|
||||
const smi = this.toSmall();
|
||||
|
||||
if (smi !== -1)
|
||||
return ScriptNum.fromInt(smi);
|
||||
|
||||
if (!this.data)
|
||||
return null;
|
||||
|
||||
return ScriptNum.decode(this.data, minimal, limit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to integer.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toInt = function toInt() {
|
||||
const num = this.toNum();
|
||||
|
||||
if (!num)
|
||||
return -1;
|
||||
|
||||
return num.getInt();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to boolean.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toBool = function toBool() {
|
||||
const smi = this.toSmall();
|
||||
|
||||
if (smi === -1)
|
||||
return false;
|
||||
|
||||
return smi === 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to its symbolic representation.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toSymbol = function toSymbol() {
|
||||
let op = this.value;
|
||||
|
||||
if (op === -1)
|
||||
op = 0xff;
|
||||
|
||||
let symbol = common.opcodesByVal[op];
|
||||
|
||||
if (symbol == null)
|
||||
symbol = `0x${util.hex8(op)}`;
|
||||
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate opcode size.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
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;
|
||||
case opcodes.OP_PUSHDATA2:
|
||||
return 3 + this.data.length;
|
||||
case opcodes.OP_PUSHDATA4:
|
||||
return 5 + this.data.length;
|
||||
default:
|
||||
throw new Error('Unknown pushdata opcode.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode the opcode to a buffer writer.
|
||||
* @param {BufferWriter} bw
|
||||
@ -156,27 +344,225 @@ Opcode.prototype.toRaw = function toRaw() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate opcode size.
|
||||
* @returns {Number}
|
||||
* Convert the opcode to a bitcoind test string.
|
||||
* @returns {String} Human-readable script code.
|
||||
*/
|
||||
|
||||
Opcode.prototype.getSize = function getSize() {
|
||||
if (!this.data)
|
||||
return 1;
|
||||
Opcode.prototype.toFormat = function toFormat() {
|
||||
// Bad push
|
||||
if (this.value === -1)
|
||||
return 'OP_INVALIDOPCODE';
|
||||
|
||||
if (this.value <= 0x4b)
|
||||
return 1 + this.data.length;
|
||||
if (this.data) {
|
||||
const symbol = common.opcodesByVal[this.value];
|
||||
const data = this.data.toString('hex');
|
||||
|
||||
switch (this.value) {
|
||||
case opcodes.OP_PUSHDATA1:
|
||||
return 2 + this.data.length;
|
||||
case opcodes.OP_PUSHDATA2:
|
||||
return 3 + this.data.length;
|
||||
case opcodes.OP_PUSHDATA4:
|
||||
return 5 + this.data.length;
|
||||
default:
|
||||
throw new Error('Unknown pushdata opcode.');
|
||||
// Direct push
|
||||
if (!symbol) {
|
||||
const size = util.hex8(this.value);
|
||||
return `0x${size} 0x${data}`;
|
||||
}
|
||||
|
||||
// Pushdatas
|
||||
let size = this.data.length.toString(16);
|
||||
|
||||
while (size.length % 2 !== 0)
|
||||
size = '0' + size;
|
||||
|
||||
return `${symbol} 0x${size} 0x${data}`;
|
||||
}
|
||||
|
||||
// Opcodes
|
||||
const symbol = common.opcodesByVal[this.value];
|
||||
if (symbol)
|
||||
return symbol;
|
||||
|
||||
// Unknown opcodes
|
||||
const value = util.hex8(this.value);
|
||||
|
||||
return `0x${value}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the opcode as bitcoind asm.
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable script.
|
||||
*/
|
||||
|
||||
Opcode.prototype.toASM = function toASM(decode) {
|
||||
if (this.value === -1)
|
||||
return '[error]';
|
||||
|
||||
if (this.data)
|
||||
return common.toASM(this.data, decode);
|
||||
|
||||
return common.opcodesByVal[this.value] || 'OP_UNKNOWN';
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a number opcode.
|
||||
* @param {Number} op
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromOp = function fromOp(op) {
|
||||
assert(typeof op === 'number');
|
||||
|
||||
const cached = opCache[op];
|
||||
|
||||
if (cached)
|
||||
return cached;
|
||||
|
||||
return new Opcode(op, null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from
|
||||
* a buffer (will encode minimaldata).
|
||||
* @param {Buffer} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromData = function fromData(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
|
||||
if (data.length === 0)
|
||||
return Opcode.fromOp(opcodes.OP_0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return Opcode.fromPush(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from a
|
||||
* buffer (this differs from fromData in
|
||||
* that it will _always_ be a pushdata op).
|
||||
* @param {Buffer} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromPush = function fromPush(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
|
||||
if (data.length <= 0x4b)
|
||||
return new Opcode(data.length, data);
|
||||
|
||||
if (data.length <= 0xff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA1, data);
|
||||
|
||||
if (data.length <= 0xffff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA2, data);
|
||||
|
||||
if (data.length <= 0xffffffff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA4, data);
|
||||
|
||||
throw new Error('Pushdata size too large.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from a string.
|
||||
* @param {String} str
|
||||
* @param {String} [enc=utf8]
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromString = function fromString(str, enc) {
|
||||
assert(typeof str === 'string');
|
||||
const data = Buffer.from(str, enc || 'utf8');
|
||||
return Opcode.fromData(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a small number.
|
||||
* @param {Number} num
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromSmall = function fromSmall(num) {
|
||||
assert(util.isU8(num) && num >= 0 && num <= 16);
|
||||
return Opcode.fromOp(num === 0 ? 0 : num + 0x50);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a ScriptNum.
|
||||
* @param {ScriptNumber} num
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromNum = function fromNum(num) {
|
||||
assert(ScriptNum.isScriptNum(num));
|
||||
return Opcode.fromData(num.encode());
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a Number.
|
||||
* @param {Number} num
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
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 && num <= 16)
|
||||
return Opcode.fromOp(num + 0x50);
|
||||
|
||||
return Opcode.fromNum(ScriptNum.fromNumber(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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from symbolic name.
|
||||
* @example
|
||||
* Opcode.fromSymbol('checksequenceverify')
|
||||
* @param {String} name
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromSymbol = function fromSymbol(name) {
|
||||
assert(typeof name === 'string');
|
||||
assert(name.length > 0);
|
||||
|
||||
if (!util.isUpperCase(name))
|
||||
name = name.toUpperCase();
|
||||
|
||||
if (!util.startsWith(name, 'OP_'))
|
||||
name = `OP_${name}`;
|
||||
|
||||
let op = common.opcodes[name];
|
||||
|
||||
if (op == null) {
|
||||
assert(util.startsWith(name, 'OP_0X'), 'Unknown opcode.');
|
||||
assert(name.length === 7, 'Unknown opcode.');
|
||||
|
||||
op = parseInt(name.substring(5), 16);
|
||||
|
||||
assert(util.isU8(op), 'Unknown opcode.');
|
||||
}
|
||||
|
||||
return Opcode.fromOp(op);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -201,61 +587,67 @@ Opcode.fromReader = function fromReader(br) {
|
||||
br.seek(br.left());
|
||||
return op;
|
||||
}
|
||||
op.value = value;
|
||||
op.data = br.readBytes(value);
|
||||
return op;
|
||||
}
|
||||
|
||||
let size;
|
||||
|
||||
switch (value) {
|
||||
case opcodes.OP_PUSHDATA1:
|
||||
case opcodes.OP_PUSHDATA1: {
|
||||
if (br.left() < 1) {
|
||||
op.value = -1;
|
||||
break;
|
||||
}
|
||||
size = br.readU8();
|
||||
|
||||
const size = br.readU8();
|
||||
|
||||
if (br.left() < size) {
|
||||
op.value = -1;
|
||||
br.seek(br.left());
|
||||
break;
|
||||
}
|
||||
op.value = value;
|
||||
|
||||
op.data = br.readBytes(size);
|
||||
|
||||
break;
|
||||
case opcodes.OP_PUSHDATA2:
|
||||
}
|
||||
case opcodes.OP_PUSHDATA2: {
|
||||
if (br.left() < 2) {
|
||||
op.value = -1;
|
||||
br.seek(br.left());
|
||||
break;
|
||||
}
|
||||
size = br.readU16();
|
||||
|
||||
const size = br.readU16();
|
||||
|
||||
if (br.left() < size) {
|
||||
op.value = -1;
|
||||
br.seek(br.left());
|
||||
break;
|
||||
}
|
||||
op.value = value;
|
||||
|
||||
op.data = br.readBytes(size);
|
||||
|
||||
break;
|
||||
case opcodes.OP_PUSHDATA4:
|
||||
}
|
||||
case opcodes.OP_PUSHDATA4: {
|
||||
if (br.left() < 4) {
|
||||
op.value = -1;
|
||||
br.seek(br.left());
|
||||
break;
|
||||
}
|
||||
size = br.readU32();
|
||||
|
||||
const size = br.readU32();
|
||||
|
||||
if (br.left() < size) {
|
||||
op.value = -1;
|
||||
br.seek(br.left());
|
||||
break;
|
||||
}
|
||||
op.value = value;
|
||||
|
||||
op.data = br.readBytes(size);
|
||||
|
||||
break;
|
||||
default:
|
||||
op.value = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
@ -271,183 +663,6 @@ Opcode.fromRaw = function fromRaw(data) {
|
||||
return Opcode.fromReader(new BufferReader(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a number opcode.
|
||||
* @param {Number} op
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromOp = function fromOp(op) {
|
||||
const cached = opCache[op];
|
||||
|
||||
if (cached)
|
||||
return cached;
|
||||
|
||||
return new Opcode(op, null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from
|
||||
* a buffer (will encode minimaldata).
|
||||
* @param {Buffer} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromData = function fromData(data) {
|
||||
if (data.length === 0)
|
||||
return Opcode.fromOp(opcodes.OP_0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return Opcode.fromPush(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from a
|
||||
* buffer (this differs from fromData in
|
||||
* that it will _always_ be a pushdata op).
|
||||
* @param {Buffer} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromPush = function fromPush(data) {
|
||||
if (data.length <= 0x4b)
|
||||
return new Opcode(data.length, data);
|
||||
|
||||
if (data.length <= 0xff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA1, data);
|
||||
|
||||
if (data.length <= 0xffff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA2, data);
|
||||
|
||||
if (data.length <= 0xffffffff)
|
||||
return new Opcode(opcodes.OP_PUSHDATA4, data);
|
||||
|
||||
throw new Error('Pushdata size too large.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a Number.
|
||||
* @param {Number|ScriptNum|BN} num
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromNumber = function fromNumber(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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an opcode from a small number.
|
||||
* @param {Number} num
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromSmall = function fromSmall(num) {
|
||||
assert(util.isU8(num) && num >= 0 && num <= 16);
|
||||
return Opcode.fromOp(num === 0 ? 0 : num + 0x50);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from a string.
|
||||
* @param {String} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromString = function fromString(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = Buffer.from(data, enc);
|
||||
|
||||
return Opcode.fromData(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from anything.
|
||||
* @param {String|Buffer|Number|ScriptNum|Opcode} data
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.from = function from(data) {
|
||||
if (data instanceof Opcode)
|
||||
return data;
|
||||
|
||||
if (typeof data === 'number')
|
||||
return Opcode.fromOp(data);
|
||||
|
||||
if (Buffer.isBuffer(data))
|
||||
return Opcode.fromData(data);
|
||||
|
||||
if (typeof data === 'string')
|
||||
return Opcode.fromString(data, 'utf8');
|
||||
|
||||
if (typeof data === 'boolean')
|
||||
return Opcode.fromBool(data);
|
||||
|
||||
if (ScriptNum.isEncodable(data))
|
||||
return Opcode.fromNumber(data);
|
||||
|
||||
throw new Error('Bad data for opcode.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a pushdata opcode from symbolic name.
|
||||
* @example
|
||||
* Opcode.fromSymbol('checksequenceverify')
|
||||
* @param {String} name
|
||||
* @returns {Opcode}
|
||||
*/
|
||||
|
||||
Opcode.fromSymbol = function fromSymbol(name) {
|
||||
assert(typeof name === 'string');
|
||||
assert(name.length > 0);
|
||||
|
||||
if (!util.isUpperCase(name))
|
||||
name = name.toUpperCase();
|
||||
|
||||
if (!util.startsWith(name, 'OP_'))
|
||||
name = `OP_${name}`;
|
||||
|
||||
const op = common.opcodes[name];
|
||||
assert(op != null, 'Unknown opcode.');
|
||||
|
||||
return Opcode.fromOp(op);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert opcode to its symbolic representation.
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Opcode.prototype.toSymbol = function toSymbol() {
|
||||
let op = this.value;
|
||||
|
||||
if (op === -1)
|
||||
op = 0xff;
|
||||
|
||||
let symbol = common.opcodesByVal[op];
|
||||
|
||||
if (symbol == null)
|
||||
symbol = util.hex8(op);
|
||||
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an object an Opcode.
|
||||
* @param {Object} obj
|
||||
@ -455,9 +670,7 @@ Opcode.prototype.toSymbol = function toSymbol() {
|
||||
*/
|
||||
|
||||
Opcode.isOpcode = function isOpcode(obj) {
|
||||
return obj
|
||||
&& typeof obj.value === 'number'
|
||||
&& (Buffer.isBuffer(obj.data) || obj.data === null);
|
||||
return obj instanceof Opcode;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
1651
lib/script/script.js
1651
lib/script/script.js
File diff suppressed because it is too large
Load Diff
50
lib/script/scripterror.js
Normal file
50
lib/script/scripterror.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*!
|
||||
* scripterror.js - script error for bcoin
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* An error thrown from the scripting system,
|
||||
* potentially pertaining to Script execution.
|
||||
* @alias module:script.ScriptError
|
||||
* @constructor
|
||||
* @extends Error
|
||||
* @param {String} code - Error code.
|
||||
* @param {Opcode} op - Opcode.
|
||||
* @param {Number?} ip - Instruction pointer.
|
||||
* @property {String} message - Error message.
|
||||
* @property {String} code - Original code passed in.
|
||||
* @property {Number} op - Opcode.
|
||||
* @property {Number} ip - Instruction pointer.
|
||||
*/
|
||||
|
||||
function ScriptError(code, op, ip) {
|
||||
if (!(this instanceof ScriptError))
|
||||
return new ScriptError(code, op, ip);
|
||||
|
||||
Error.call(this);
|
||||
|
||||
this.type = 'ScriptError';
|
||||
this.code = code;
|
||||
this.message = code;
|
||||
this.op = -1;
|
||||
this.ip = -1;
|
||||
|
||||
if (typeof op === 'string') {
|
||||
this.message = op;
|
||||
} else if (op) {
|
||||
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
||||
this.op = op.value;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, ScriptError);
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(ScriptError.prototype, Error.prototype);
|
||||
|
||||
module.exports = ScriptError;
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
const assert = require('assert');
|
||||
const {I64} = require('../utils/int64');
|
||||
const {ScriptError} = require('./common');
|
||||
const ScriptError = require('./scripterror');
|
||||
|
||||
/*
|
||||
* Constants
|
||||
|
||||
@ -10,10 +10,6 @@
|
||||
const assert = require('assert');
|
||||
const common = require('./common');
|
||||
const ScriptNum = require('./scriptnum');
|
||||
const ScriptError = common.ScriptError;
|
||||
const STACK_FALSE = common.STACK_FALSE;
|
||||
const STACK_TRUE = common.STACK_TRUE;
|
||||
const STACK_NEGATE = common.STACK_NEGATE;
|
||||
|
||||
/**
|
||||
* Represents the stack of a Script during execution.
|
||||
@ -45,6 +41,33 @@ Object.defineProperty(Stack.prototype, 'length', {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Instantiate a key and value iterator.
|
||||
* @returns {StackIterator}
|
||||
*/
|
||||
|
||||
Stack.prototype[Symbol.iterator] = function iterator() {
|
||||
return this.items[Symbol.iterator]();
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a value-only iterator.
|
||||
* @returns {StackIterator}
|
||||
*/
|
||||
|
||||
Stack.prototype.values = function values() {
|
||||
return this.items.values();
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a key and value iterator.
|
||||
* @returns {StackIterator}
|
||||
*/
|
||||
|
||||
Stack.prototype.entries = function entries() {
|
||||
return this.items.entries();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the stack.
|
||||
* @returns {String} Human-readable stack.
|
||||
@ -60,7 +83,12 @@ Stack.prototype.inspect = function inspect() {
|
||||
*/
|
||||
|
||||
Stack.prototype.toString = function toString() {
|
||||
return common.formatStack(this.items);
|
||||
const out = [];
|
||||
|
||||
for (const item of this.items)
|
||||
out.push(item.toString('hex'));
|
||||
|
||||
return out.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -70,7 +98,12 @@ Stack.prototype.toString = function toString() {
|
||||
*/
|
||||
|
||||
Stack.prototype.toASM = function toASM(decode) {
|
||||
return common.formatStackASM(this.items, decode);
|
||||
const out = [];
|
||||
|
||||
for (const item of this.items)
|
||||
out.push(common.toASM(item, decode));
|
||||
|
||||
return out.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -82,6 +115,94 @@ Stack.prototype.clone = function clone() {
|
||||
return new Stack(this.items.slice());
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the stack.
|
||||
* @returns {Stack}
|
||||
*/
|
||||
|
||||
Stack.prototype.clear = function clear() {
|
||||
this.items.length = 0;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item by index.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.get = function get(index) {
|
||||
if (index < 0)
|
||||
index += this.items.length;
|
||||
|
||||
if (index < 0 || index >= this.items.length)
|
||||
return null;
|
||||
|
||||
return this.items[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Pop a stack item.
|
||||
* @see Array#pop
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.pop = function pop() {
|
||||
const item = this.items.pop();
|
||||
return item || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shift a stack item.
|
||||
* @see Array#shift
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.shift = function shift() {
|
||||
const item = this.items.shift();
|
||||
return item || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an item.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Stack.prototype.remove = function remove(index) {
|
||||
if (index < 0)
|
||||
index += this.items.length;
|
||||
|
||||
if (index < 0 || index >= this.items.length)
|
||||
return null;
|
||||
|
||||
const items = this.items.splice(index, 1);
|
||||
|
||||
if (items.length === 0)
|
||||
return null;
|
||||
|
||||
return items[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set stack item at index.
|
||||
* @param {Number} index
|
||||
* @param {Buffer} value
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Stack.prototype.set = function set(index, item) {
|
||||
if (index < 0)
|
||||
index += this.items.length;
|
||||
|
||||
assert(Buffer.isBuffer(item));
|
||||
assert(index >= 0 && index <= this.items.length);
|
||||
|
||||
this.items[index] = item;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push item onto stack.
|
||||
* @see Array#push
|
||||
@ -91,60 +212,8 @@ Stack.prototype.clone = function clone() {
|
||||
|
||||
Stack.prototype.push = function push(item) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
return this.items.push(item);
|
||||
};
|
||||
|
||||
/**
|
||||
* Push boolean onto stack.
|
||||
* @see Array#push
|
||||
* @param {Boolean} value
|
||||
* @returns {Number} Stack size.
|
||||
*/
|
||||
|
||||
Stack.prototype.pushBool = function pushBool(value) {
|
||||
assert(typeof value === 'boolean');
|
||||
return this.items.push(value ? STACK_TRUE : STACK_FALSE);
|
||||
};
|
||||
|
||||
/**
|
||||
* Push script number onto stack.
|
||||
* @see Array#push
|
||||
* @param {ScriptNum} num
|
||||
* @returns {Number} Stack size.
|
||||
*/
|
||||
|
||||
Stack.prototype.pushNum = function pushNum(num) {
|
||||
assert(ScriptNum.isScriptNum(num));
|
||||
return this.items.push(num.encode());
|
||||
};
|
||||
|
||||
/**
|
||||
* Push integer onto stack.
|
||||
* @see Array#push
|
||||
* @param {Number} value
|
||||
* @returns {Number} Stack size.
|
||||
*/
|
||||
|
||||
Stack.prototype.pushInt = function pushInt(value) {
|
||||
assert(typeof value === 'number');
|
||||
|
||||
if (value >= -1 && value <= 16) {
|
||||
switch (value) {
|
||||
case -1:
|
||||
return this.items.push(STACK_NEGATE);
|
||||
case 0:
|
||||
return this.items.push(STACK_FALSE);
|
||||
case 1:
|
||||
return this.items.push(STACK_TRUE);
|
||||
}
|
||||
const item = Buffer.allocUnsafe(1);
|
||||
item[0] = value;
|
||||
return this.items.push(item);
|
||||
}
|
||||
|
||||
const num = ScriptNum.fromNumber(value);
|
||||
|
||||
return this.items.push(num.encode());
|
||||
this.items.push(item);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -156,41 +225,27 @@ Stack.prototype.pushInt = function pushInt(value) {
|
||||
|
||||
Stack.prototype.unshift = function unshift(item) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
return this.items.unshift(item);
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice out part of the stack items.
|
||||
* @param {Number} start
|
||||
* @param {Number} end
|
||||
* @see Array#slice
|
||||
* @returns {Stack}
|
||||
*/
|
||||
|
||||
Stack.prototype.slice = function slice(start, end) {
|
||||
this.items = this.items.slice(start, end);
|
||||
this.items.unshift(item);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Splice stack items.
|
||||
* @see Array#splice
|
||||
* Insert an item.
|
||||
* @param {Number} index
|
||||
* @param {Number} remove
|
||||
* @param {Buffer?} insert
|
||||
* @returns {Buffer[]}
|
||||
* @param {Buffer} item
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Stack.prototype.splice = function splice(i, remove, insert) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
Stack.prototype.insert = function insert(index, item) {
|
||||
if (index < 0)
|
||||
index += this.items.length;
|
||||
|
||||
if (insert === undefined)
|
||||
return this.items.splice(i, remove);
|
||||
assert(Buffer.isBuffer(item));
|
||||
assert(index >= 0 && index <= this.items.length);
|
||||
|
||||
assert(Buffer.isBuffer(insert));
|
||||
this.items.splice(index, 0, item);
|
||||
|
||||
return this.items.splice(i, remove, insert);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -210,167 +265,6 @@ Stack.prototype.erase = function erase(start, 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;
|
||||
|
||||
assert(Buffer.isBuffer(item));
|
||||
|
||||
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 undefined;
|
||||
|
||||
return this.items.splice(i, 1)[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Pop a stack item.
|
||||
* @see Array#pop
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.pop = function pop() {
|
||||
return this.items.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shift a stack item.
|
||||
* @see Array#shift
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.shift = function shift() {
|
||||
return this.items.shift();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item by index.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.get = function get(i) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
|
||||
return this.items[i];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item by index
|
||||
* and decode as a boolean.
|
||||
* @param {Number} index
|
||||
* @returns {Boolean}
|
||||
* @throws on invalid stack operation
|
||||
*/
|
||||
|
||||
Stack.prototype.bool = function bool(i) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
|
||||
if (i < 0 || i >= this.items.length)
|
||||
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
|
||||
|
||||
return common.toBool(this.items[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item by index
|
||||
* and decode as a scriptnum.
|
||||
* @param {Number} index
|
||||
* @param {Boolean?} minimal
|
||||
* @param {Number?} limit
|
||||
* @returns {ScriptNum}
|
||||
* @throws on invalid stack operation
|
||||
*/
|
||||
|
||||
Stack.prototype.num = function num(i, minimal, limit) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
|
||||
if (i < 0 || i >= this.items.length)
|
||||
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
|
||||
|
||||
return ScriptNum.decode(this.items[i], minimal, limit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item by index
|
||||
* and decode as an integer.
|
||||
* @param {Number} index
|
||||
* @param {Boolean?} minimal
|
||||
* @returns {Number}
|
||||
* @throws on invalid stack operation
|
||||
*/
|
||||
|
||||
Stack.prototype.int = function int(i, minimal) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
|
||||
if (i < 0 || i >= this.items.length)
|
||||
throw new ScriptError('INVALID_STACK_OPERATION', -1, -1);
|
||||
|
||||
return ScriptNum.decode(this.items[i], minimal).getInt();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a stack item relative to
|
||||
* the top of the stack.
|
||||
* @example
|
||||
* stack.top(-1);
|
||||
* @param {Number} index
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Stack.prototype.top = function top(i) {
|
||||
return this.items[this.items.length + i];
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the stack.
|
||||
*/
|
||||
|
||||
Stack.prototype.clear = function clear() {
|
||||
this.items.length = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set stack item at index.
|
||||
* @param {Number} index
|
||||
* @param {Buffer} value
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Stack.prototype.set = function set(i, value) {
|
||||
if (i < 0)
|
||||
i = this.items.length + i;
|
||||
|
||||
assert(Buffer.isBuffer(value));
|
||||
|
||||
this.items[i] = value;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Swap stack values.
|
||||
* @param {Number} i1 - Index 1.
|
||||
@ -391,6 +285,211 @@ Stack.prototype.swap = function swap(i1, i2) {
|
||||
this.items[i2] = v1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Data
|
||||
*/
|
||||
|
||||
Stack.prototype.getData = function getData(index) {
|
||||
return this.get(index);
|
||||
};
|
||||
|
||||
Stack.prototype.popData = function popData() {
|
||||
return this.pop();
|
||||
};
|
||||
|
||||
Stack.prototype.shiftData = function shiftData() {
|
||||
return this.shift();
|
||||
};
|
||||
|
||||
Stack.prototype.removeData = function removeData(index) {
|
||||
return this.remove(index);
|
||||
};
|
||||
|
||||
Stack.prototype.setData = function setData(index, data) {
|
||||
return this.set(index, data);
|
||||
};
|
||||
|
||||
Stack.prototype.pushData = function pushData(data) {
|
||||
return this.push(data);
|
||||
};
|
||||
|
||||
Stack.prototype.unshiftData = function unshiftData(data) {
|
||||
return this.unshift(data);
|
||||
};
|
||||
|
||||
Stack.prototype.insertData = function insertData(index, data) {
|
||||
return this.insert(index, data);
|
||||
};
|
||||
|
||||
/*
|
||||
* Length
|
||||
*/
|
||||
|
||||
Stack.prototype.getLength = function getLength(index) {
|
||||
const item = this.get(index);
|
||||
return item ? item.length : -1;
|
||||
};
|
||||
|
||||
/*
|
||||
* String
|
||||
*/
|
||||
|
||||
Stack.prototype.getString = function getString(index, enc) {
|
||||
const item = this.get(index);
|
||||
return item ? Stack.toString(item, enc) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.popString = function popString(enc) {
|
||||
const item = this.pop();
|
||||
return item ? Stack.toString(item, enc) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.shiftString = function shiftString(enc) {
|
||||
const item = this.shift();
|
||||
return item ? Stack.toString(item, enc) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.removeString = function removeString(index, enc) {
|
||||
const item = this.remove(index);
|
||||
return item ? Stack.toString(item, enc) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.setString = function setString(index, str, enc) {
|
||||
return this.set(index, Stack.fromString(str, enc));
|
||||
};
|
||||
|
||||
Stack.prototype.pushString = function pushString(str, enc) {
|
||||
return this.push(Stack.fromString(str, enc));
|
||||
};
|
||||
|
||||
Stack.prototype.unshiftString = function unshiftString(str, enc) {
|
||||
return this.unshift(Stack.fromString(str, enc));
|
||||
};
|
||||
|
||||
Stack.prototype.insertString = function insertString(index, str, enc) {
|
||||
return this.insert(index, Stack.fromString(str, enc));
|
||||
};
|
||||
|
||||
/*
|
||||
* Num
|
||||
*/
|
||||
|
||||
Stack.prototype.getNum = function getNum(index, minimal, limit) {
|
||||
const item = this.get(index);
|
||||
return item ? Stack.toNum(item, minimal, limit) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.popNum = function popNum(minimal, limit) {
|
||||
const item = this.pop();
|
||||
return item ? Stack.toNum(item, minimal, limit) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.shiftNum = function shiftNum(minimal, limit) {
|
||||
const item = this.shift();
|
||||
return item ? Stack.toNum(item, minimal, limit) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.removeNum = function removeNum(index, minimal, limit) {
|
||||
const item = this.remove(index);
|
||||
return item ? Stack.toNum(item, minimal, limit) : null;
|
||||
};
|
||||
|
||||
Stack.prototype.setNum = function setNum(index, num) {
|
||||
return this.set(index, Stack.fromNum(num));
|
||||
};
|
||||
|
||||
Stack.prototype.pushNum = function pushNum(num) {
|
||||
return this.push(Stack.fromNum(num));
|
||||
};
|
||||
|
||||
Stack.prototype.unshiftNum = function unshiftNum(num) {
|
||||
return this.unshift(Stack.fromNum(num));
|
||||
};
|
||||
|
||||
Stack.prototype.insertNum = function insertNum(index, num) {
|
||||
return this.insert(index, Stack.fromNum(num));
|
||||
};
|
||||
|
||||
/*
|
||||
* Int
|
||||
*/
|
||||
|
||||
Stack.prototype.getInt = function getInt(index, minimal, limit) {
|
||||
const item = this.get(index);
|
||||
return item ? Stack.toInt(item, minimal, limit) : -1;
|
||||
};
|
||||
|
||||
Stack.prototype.popInt = function popInt(minimal, limit) {
|
||||
const item = this.pop();
|
||||
return item ? Stack.toInt(item, minimal, limit) : -1;
|
||||
};
|
||||
|
||||
Stack.prototype.shiftInt = function shiftInt(minimal, limit) {
|
||||
const item = this.shift();
|
||||
return item ? Stack.toInt(item, minimal, limit) : -1;
|
||||
};
|
||||
|
||||
Stack.prototype.removeInt = function removeInt(index, minimal, limit) {
|
||||
const item = this.remove(index);
|
||||
return item ? Stack.toInt(item, minimal, limit) : -1;
|
||||
};
|
||||
|
||||
Stack.prototype.setInt = function setInt(index, num) {
|
||||
return this.set(index, Stack.fromInt(num));
|
||||
};
|
||||
|
||||
Stack.prototype.pushInt = function pushInt(num) {
|
||||
return this.push(Stack.fromInt(num));
|
||||
};
|
||||
|
||||
Stack.prototype.unshiftInt = function unshiftInt(num) {
|
||||
return this.unshift(Stack.fromInt(num));
|
||||
};
|
||||
|
||||
Stack.prototype.insertInt = function insertInt(index, num) {
|
||||
return this.insert(index, Stack.fromInt(num));
|
||||
};
|
||||
|
||||
/*
|
||||
* Bool
|
||||
*/
|
||||
|
||||
Stack.prototype.getBool = function getBool(index) {
|
||||
const item = this.get(index);
|
||||
return item ? Stack.toBool(item) : false;
|
||||
};
|
||||
|
||||
Stack.prototype.popBool = function popBool() {
|
||||
const item = this.pop();
|
||||
return item ? Stack.toBool(item) : false;
|
||||
};
|
||||
|
||||
Stack.prototype.shiftBool = function shiftBool() {
|
||||
const item = this.shift();
|
||||
return item ? Stack.toBool(item) : false;
|
||||
};
|
||||
|
||||
Stack.prototype.removeBool = function removeBool(index) {
|
||||
const item = this.remove(index);
|
||||
return item ? Stack.toBool(item) : false;
|
||||
};
|
||||
|
||||
Stack.prototype.setBool = function setBool(index, value) {
|
||||
return this.set(index, Stack.fromBool(value));
|
||||
};
|
||||
|
||||
Stack.prototype.pushBool = function pushBool(value) {
|
||||
return this.push(Stack.fromBool(value));
|
||||
};
|
||||
|
||||
Stack.prototype.unshiftBool = function unshiftBool(value) {
|
||||
return this.unshift(Stack.fromBool(value));
|
||||
};
|
||||
|
||||
Stack.prototype.insertBool = function insertBool(index, value) {
|
||||
return this.insert(index, Stack.fromBool(value));
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an object to see if it is a Stack.
|
||||
* @param {Object} obj
|
||||
@ -398,7 +497,73 @@ Stack.prototype.swap = function swap(i1, i2) {
|
||||
*/
|
||||
|
||||
Stack.isStack = function isStack(obj) {
|
||||
return obj && Array.isArray(obj.items) && typeof obj.swap === 'function';
|
||||
return obj instanceof Stack;
|
||||
};
|
||||
|
||||
/*
|
||||
* Encoding
|
||||
*/
|
||||
|
||||
Stack.toString = function toString(item, enc) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
return item.toString(enc || 'utf8');
|
||||
};
|
||||
|
||||
Stack.fromString = function fromString(str, enc) {
|
||||
assert(typeof str === 'string');
|
||||
return Buffer.from(str, enc || 'utf8');
|
||||
};
|
||||
|
||||
Stack.toNum = function toNum(item, minimal, limit) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
return ScriptNum.decode(item, minimal, limit);
|
||||
};
|
||||
|
||||
Stack.fromNum = function fromNum(num) {
|
||||
assert(ScriptNum.isScriptNum(num));
|
||||
return num.encode();
|
||||
};
|
||||
|
||||
Stack.toInt = function toInt(item, minimal, limit) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
|
||||
const num = Stack.toNum(item, minimal, limit);
|
||||
|
||||
if (!num)
|
||||
return -1;
|
||||
|
||||
return num.getInt();
|
||||
};
|
||||
|
||||
Stack.fromInt = function fromInt(int) {
|
||||
assert(typeof int === 'number');
|
||||
|
||||
if (int >= -1 && int <= 16)
|
||||
return common.small[int + 1];
|
||||
|
||||
const num = ScriptNum.fromNumber(int);
|
||||
|
||||
return Stack.fromNum(num);
|
||||
};
|
||||
|
||||
Stack.toBool = function toBool(item) {
|
||||
assert(Buffer.isBuffer(item));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Stack.fromBool = function fromBool(value) {
|
||||
assert(typeof value === 'boolean');
|
||||
return Stack.fromInt(value ? 1 : 0);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -8,21 +8,15 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const ScriptNum = require('./scriptnum');
|
||||
const util = require('../utils/util');
|
||||
const Script = require('./script');
|
||||
const common = require('./common');
|
||||
const encoding = require('../utils/encoding');
|
||||
const Opcode = require('./opcode');
|
||||
const BufferReader = require('../utils/reader');
|
||||
const StaticWriter = require('../utils/staticwriter');
|
||||
const Address = require('../primitives/address');
|
||||
const Stack = require('./stack');
|
||||
const opcodes = common.opcodes;
|
||||
const scriptTypes = common.types;
|
||||
const STACK_TRUE = common.STACK_TRUE;
|
||||
const STACK_FALSE = common.STACK_FALSE;
|
||||
const STACK_NEGATE = common.STACK_NEGATE;
|
||||
|
||||
/**
|
||||
* Refers to the witness field of segregated witness transactions.
|
||||
@ -39,25 +33,13 @@ function Witness(options) {
|
||||
if (!(this instanceof Witness))
|
||||
return new Witness(options);
|
||||
|
||||
this.items = [];
|
||||
Stack.call(this, []);
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose length setter and getter.
|
||||
*/
|
||||
|
||||
Object.defineProperty(Witness.prototype, 'length', {
|
||||
get() {
|
||||
return this.items.length;
|
||||
},
|
||||
set(length) {
|
||||
this.items.length = length;
|
||||
return this.items.length;
|
||||
}
|
||||
});
|
||||
Object.setPrototypeOf(Witness.prototype, Stack.prototype);
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
@ -118,6 +100,66 @@ Witness.fromArray = function fromArray(items) {
|
||||
return new Witness().fromArray(items);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert witness to an array of buffers.
|
||||
* @returns {Buffer[]}
|
||||
*/
|
||||
|
||||
Witness.prototype.toItems = function toItems() {
|
||||
return this.items.slice();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from an array of buffers.
|
||||
* @private
|
||||
* @param {Buffer[]} items
|
||||
*/
|
||||
|
||||
Witness.prototype.fromItems = function fromItems(items) {
|
||||
assert(Array.isArray(items));
|
||||
this.items = items;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insantiate witness from an array of buffers.
|
||||
* @param {Buffer[]} items
|
||||
* @returns {Witness}
|
||||
*/
|
||||
|
||||
Witness.fromItems = function fromItems(items) {
|
||||
return new Witness().fromItems(items);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert witness to a stack.
|
||||
* @returns {Stack}
|
||||
*/
|
||||
|
||||
Witness.prototype.toStack = function toStack() {
|
||||
return new Stack(this.toArray());
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from a stack.
|
||||
* @private
|
||||
* @param {Stack} stack
|
||||
*/
|
||||
|
||||
Witness.prototype.fromStack = function fromStack(stack) {
|
||||
return this.fromArray(stack.items);
|
||||
};
|
||||
|
||||
/**
|
||||
* Insantiate witness from a stack.
|
||||
* @param {Stack} stack
|
||||
* @returns {Witness}
|
||||
*/
|
||||
|
||||
Witness.fromStack = function fromStack(stack) {
|
||||
return new Witness().fromStack(stack);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect a Witness object.
|
||||
* @returns {String} Human-readable script.
|
||||
@ -127,25 +169,6 @@ Witness.prototype.inspect = function inspect() {
|
||||
return `<Witness: ${this.toString()}>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a Witness object to a String.
|
||||
* @returns {String} Human-readable script.
|
||||
*/
|
||||
|
||||
Witness.prototype.toString = function toString() {
|
||||
return common.formatStack(this.items);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the witness object as bitcoind asm.
|
||||
* @param {Boolean?} decode - Attempt to decode hash types.
|
||||
* @returns {String} Human-readable script.
|
||||
*/
|
||||
|
||||
Witness.prototype.toASM = function toASM(decode) {
|
||||
return common.formatStackASM(this.items, decode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone the witness object.
|
||||
* @returns {Witness} A clone of the current witness object.
|
||||
@ -169,14 +192,12 @@ Witness.prototype.inject = function inject(witness) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the Witness to a Stack object.
|
||||
* This is usually done before executing
|
||||
* a witness program.
|
||||
* @returns {Stack}
|
||||
* Compile witness (NOP).
|
||||
* @returns {Witness}
|
||||
*/
|
||||
|
||||
Witness.prototype.toStack = function toStack() {
|
||||
return new Stack(this.items.slice());
|
||||
Witness.prototype.compile = function compile() {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -215,6 +236,16 @@ Witness.prototype.isPubkeyInput = function isPubkeyInput() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get P2PK signature if present.
|
||||
* Always returns null.
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Witness.prototype.getPubkeyInput = function getPubkeyInput() {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Guess" whether the witness is a pubkeyhash input.
|
||||
* This method is not 100% reliable.
|
||||
@ -227,6 +258,17 @@ Witness.prototype.isPubkeyhashInput = function isPubkeyhashInput() {
|
||||
&& common.isKeyEncoding(this.items[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get P2PKH signature and key if present.
|
||||
* @returns {Array} [sig, key]
|
||||
*/
|
||||
|
||||
Witness.prototype.getPubkeyhashInput = function getPubkeyhashInput() {
|
||||
if (!this.isPubkeyhashInput())
|
||||
return [null, null];
|
||||
return [this.items[0], this.items[1]];
|
||||
};
|
||||
|
||||
/**
|
||||
* "Test" whether the witness is a multisig input.
|
||||
* Always returns false.
|
||||
@ -237,6 +279,16 @@ Witness.prototype.isMultisigInput = function isMultisigInput() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get multisig signatures key if present.
|
||||
* Always returns null.
|
||||
* @returns {Buffer[]|null}
|
||||
*/
|
||||
|
||||
Witness.prototype.getMultisigInput = function getMultisigInput() {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Guess" whether the witness is a scripthash input.
|
||||
* This method is not 100% reliable.
|
||||
@ -247,6 +299,17 @@ Witness.prototype.isScripthashInput = function isScripthashInput() {
|
||||
return this.items.length > 0 && !this.isPubkeyhashInput();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get P2SH redeem script if present.
|
||||
* @returns {Buffer|null}
|
||||
*/
|
||||
|
||||
Witness.prototype.getScripthashInput = function getScripthashInput() {
|
||||
if (!this.isScripthashInput())
|
||||
return null;
|
||||
return this.items[this.items.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* "Guess" whether the witness is an unknown/non-standard type.
|
||||
* This method is not 100% reliable.
|
||||
@ -292,14 +355,6 @@ Witness.prototype.getRedeem = function getRedeem() {
|
||||
return Script.fromRaw(redeem);
|
||||
};
|
||||
|
||||
/**
|
||||
* Does nothing currently.
|
||||
*/
|
||||
|
||||
Witness.prototype.compile = function compile() {
|
||||
// NOP
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a data element in a witness.
|
||||
* @param {Buffer} data - Data element to match against.
|
||||
@ -390,179 +445,6 @@ Witness.fromJSON = function fromJSON(json) {
|
||||
return new Witness().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unshift an item onto the witness vector.
|
||||
* @param {Number|String|Buffer|ScriptNum} data
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Witness.prototype.unshift = function unshift(data) {
|
||||
return this.items.unshift(Witness.encodeItem(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Push an item onto the witness vector.
|
||||
* @param {Number|String|Buffer|ScriptNum} data
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Witness.prototype.push = function push(data) {
|
||||
return this.items.push(Witness.encodeItem(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Shift an item off the witness vector.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Witness.prototype.shift = function shift() {
|
||||
return this.items.shift();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shift an item off the witness vector.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Witness.prototype.pop = function pop(data) {
|
||||
return this.items.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an item from the witness vector.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Witness.prototype.remove = function remove(i) {
|
||||
return this.items.splice(i, 1)[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert an item into the witness vector.
|
||||
* @param {Number} index
|
||||
* @param {Number|String|Buffer|ScriptNum} data
|
||||
*/
|
||||
|
||||
Witness.prototype.insert = function insert(i, data) {
|
||||
assert(i <= this.items.length, 'Index out of bounds.');
|
||||
this.items.splice(i, 0, Witness.encodeItem(data))[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an item from the witness vector.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Witness.prototype.get = function get(i) {
|
||||
return this.items[i];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a small int (0-16) from the witness vector.
|
||||
* @param {Number} index
|
||||
* @returns {Number} `-1` on non-existent.
|
||||
*/
|
||||
|
||||
Witness.prototype.getSmall = function getSmall(i) {
|
||||
const item = this.items[i];
|
||||
if (!item || item.length > 1)
|
||||
return -1;
|
||||
if (item.length === 0)
|
||||
return 0;
|
||||
if (!(item[0] >= 1 && item[1] <= 16))
|
||||
return -1;
|
||||
return item[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a number from the witness vector.
|
||||
* @param {Number} index
|
||||
* @returns {ScriptNum}
|
||||
*/
|
||||
|
||||
Witness.prototype.getNumber = function getNumber(i) {
|
||||
const item = this.items[i];
|
||||
|
||||
if (!item || item.length > 5)
|
||||
return null;
|
||||
|
||||
return ScriptNum.decode(item, false, 5);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a string from the witness vector.
|
||||
* @param {Number} index
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
Witness.prototype.getString = function getString(i) {
|
||||
const item = this.items[i];
|
||||
|
||||
if (!item)
|
||||
return null;
|
||||
|
||||
return item.toString('utf8');
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the witness items.
|
||||
*/
|
||||
|
||||
Witness.prototype.clear = function clear() {
|
||||
this.items.length = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an item in the witness vector.
|
||||
* @param {Number} index
|
||||
* @param {Number|String|Buffer|ScriptNum} data
|
||||
*/
|
||||
|
||||
Witness.prototype.set = function set(i, data) {
|
||||
assert(i <= this.items.length, 'Index out of bounds.');
|
||||
this.items[i] = Witness.encodeItem(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a witness item.
|
||||
* @param {Number|String|Buffer|ScriptNum} data
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Witness.encodeItem = function encodeItem(data) {
|
||||
if (data instanceof Opcode)
|
||||
data = data.data || data.value;
|
||||
|
||||
if (Buffer.isBuffer(data))
|
||||
return data;
|
||||
|
||||
if (typeof data === 'number') {
|
||||
if (data === opcodes.OP_1NEGATE)
|
||||
return STACK_NEGATE;
|
||||
|
||||
if (data === opcodes.OP_0)
|
||||
return STACK_FALSE;
|
||||
|
||||
if (data >= opcodes.OP_1 && data <= opcodes.OP_16)
|
||||
return Buffer.from([data - 0x50]);
|
||||
|
||||
throw new Error('Non-push opcode in witness.');
|
||||
}
|
||||
|
||||
if (typeof data === 'string')
|
||||
return Buffer.from(data, 'utf8');
|
||||
|
||||
if (typeof data === 'boolean')
|
||||
return data ? STACK_TRUE : STACK_FALSE;
|
||||
|
||||
if (ScriptNum.isEncodable(data))
|
||||
return ScriptNum.encode(data);
|
||||
|
||||
throw new Error('Not a witness item.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from buffer reader.
|
||||
* @private
|
||||
@ -655,9 +537,7 @@ Witness.fromString = function fromString(items) {
|
||||
*/
|
||||
|
||||
Witness.isWitness = function isWitness(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.items)
|
||||
&& typeof obj.toStack === 'function';
|
||||
return obj instanceof Witness;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
165
lib/utils/enforce.js
Normal file
165
lib/utils/enforce.js
Normal file
@ -0,0 +1,165 @@
|
||||
/*!
|
||||
* enforce.js - type enforcement for bcoin
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const util = require('./util');
|
||||
|
||||
function enforce(value, name, type, func) {
|
||||
if (!value) {
|
||||
if (!func)
|
||||
func = enforce;
|
||||
|
||||
if (name && !type)
|
||||
throwError(name, func);
|
||||
|
||||
if (!name)
|
||||
name = 'value';
|
||||
|
||||
throwError(`'${name}' must be a(n) ${type}.`, func);
|
||||
}
|
||||
}
|
||||
|
||||
function throwError(msg, func) {
|
||||
const error = new TypeError(msg);
|
||||
if (Error.captureStackTrace && func)
|
||||
Error.captureStackTrace(error, func);
|
||||
throw error;
|
||||
}
|
||||
|
||||
enforce.none = function none(value, name) {
|
||||
enforce(value == null, name, 'object', none);
|
||||
};
|
||||
|
||||
enforce.nul = function nul(value, name) {
|
||||
enforce(value === null, name, 'object', nul);
|
||||
};
|
||||
|
||||
enforce.undef = function undef(value, name) {
|
||||
enforce(value === undefined, name, 'object', undef);
|
||||
};
|
||||
|
||||
enforce.str = function str(value, name) {
|
||||
enforce(typeof value === 'string', name, 'string', str);
|
||||
};
|
||||
|
||||
enforce.bool = function bool(value, name) {
|
||||
enforce(typeof value === 'boolean', name, 'boolean', bool);
|
||||
};
|
||||
|
||||
enforce.num = function num(value, name) {
|
||||
enforce(util.isNumber(value), name, 'number', num);
|
||||
};
|
||||
|
||||
enforce.obj = function obj(v, name) {
|
||||
enforce(v && typeof v === 'object' && !Array.isArray(v), name, 'object', obj);
|
||||
};
|
||||
|
||||
enforce.array = function array(value, name) {
|
||||
enforce(Array.isArray(value), name, 'object', array);
|
||||
};
|
||||
|
||||
enforce.func = function func(value, name) {
|
||||
enforce(typeof value === 'function', name, 'function', func);
|
||||
};
|
||||
|
||||
enforce.error = function error(value, name) {
|
||||
enforce(value instanceof Error, name, 'object', error);
|
||||
};
|
||||
|
||||
enforce.regexp = function regexp(value, name) {
|
||||
enforce(value && typeof value.exec === 'function' , name, 'object', regexp);
|
||||
};
|
||||
|
||||
enforce.buf = function buf(value, name) {
|
||||
enforce(Buffer.isBuffer(value), name, 'buffer', buf);
|
||||
};
|
||||
|
||||
enforce.len = function len(value, length, name) {
|
||||
if ((typeof value !== 'string' && !value) || value.length !== length) {
|
||||
if (!name)
|
||||
name = 'value';
|
||||
throwError(`'${name}' must have a length of ${length}.`, len);
|
||||
}
|
||||
};
|
||||
|
||||
enforce.instance = function instance(obj, parent, name) {
|
||||
if (!(obj instanceof parent)) {
|
||||
if (!name)
|
||||
name = 'value';
|
||||
throwError(`'${name}' must be an instance of ${parent.name}.`, instance);
|
||||
}
|
||||
};
|
||||
|
||||
enforce.uint = function uint(value, name) {
|
||||
enforce(util.isUInt(value), name, 'uint', uint);
|
||||
};
|
||||
|
||||
enforce.int = function int(value, name) {
|
||||
enforce(util.isInt(value), name, 'int', int);
|
||||
};
|
||||
|
||||
enforce.u8 = function u8(value, name) {
|
||||
enforce(util.isU8(value), name, 'uint8', u8);
|
||||
};
|
||||
|
||||
enforce.u16 = function u16(value, name) {
|
||||
enforce(util.isU16(value), name, 'uint16', u16);
|
||||
};
|
||||
|
||||
enforce.u32 = function u32(value, name) {
|
||||
enforce(util.isU32(value), name, 'uint32', u32);
|
||||
};
|
||||
|
||||
enforce.u64 = function u64(value, name) {
|
||||
enforce(util.isU64(value), name, 'uint64', u64);
|
||||
};
|
||||
|
||||
enforce.i8 = function i8(value, name) {
|
||||
enforce(util.isI8(value), name, 'int8', i8);
|
||||
};
|
||||
|
||||
enforce.i16 = function i16(value, name) {
|
||||
enforce(util.isI16(value), name, 'int16', i16);
|
||||
};
|
||||
|
||||
enforce.i32 = function i32(value, name) {
|
||||
enforce(util.isI32(value), name, 'int32', i32);
|
||||
};
|
||||
|
||||
enforce.i64 = function i64(value, name) {
|
||||
enforce(util.isI64(value), name, 'int64', i64);
|
||||
};
|
||||
|
||||
enforce.ufloat = function ufloat(value, name) {
|
||||
enforce(util.isUfloat(value), name, 'positive float', ufloat);
|
||||
};
|
||||
|
||||
enforce.float = function float(value, name) {
|
||||
enforce(util.isFloat(value), name, 'float', float);
|
||||
};
|
||||
|
||||
enforce.ascii = function ascii(value, name) {
|
||||
enforce(util.isAscii(value), name, 'ascii string', ascii);
|
||||
};
|
||||
|
||||
enforce.hex = function hex(value, name) {
|
||||
enforce(util.isHex(value), name, 'hex string', hex);
|
||||
};
|
||||
|
||||
enforce.hex160 = function hex160(value, name) {
|
||||
enforce(util.isHex160(value), name, '160 bit hex string', hex160);
|
||||
};
|
||||
|
||||
enforce.hex256 = function hex256(value, name) {
|
||||
enforce(util.isHex256(value), name, '256 bit hex string', hex256);
|
||||
};
|
||||
|
||||
enforce.base58 = function base58(value, name) {
|
||||
enforce(util.isBase58(value), name, 'base58 string', base58);
|
||||
};
|
||||
|
||||
module.exports = enforce;
|
||||
@ -19,6 +19,7 @@ exports.bech32 = require('./bech32');
|
||||
exports.Bloom = require('./bloom');
|
||||
exports.co = require('./co');
|
||||
exports.encoding = require('./encoding');
|
||||
exports.enforce = require('./enforce');
|
||||
exports.fs = require('./fs');
|
||||
exports.GCSFilter = require('./gcs');
|
||||
exports.Heap = require('./heap');
|
||||
|
||||
@ -281,6 +281,7 @@ util.inspectify = function inspectify(obj, color) {
|
||||
return obj;
|
||||
|
||||
inspectOptions.colors = color !== false;
|
||||
|
||||
return nodeUtil.inspect(obj, inspectOptions);
|
||||
};
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ const Mnemonic = HD.Mnemonic;
|
||||
* @param {Number?} options.m - `m` value for multisig.
|
||||
* @param {Number?} options.n - `n` value for multisig.
|
||||
* @param {String?} options.id - Wallet ID (used for storage)
|
||||
* @param {String?} options.mnemonic - mnemonic phrase to use to instantiate an
|
||||
* @param {String?} options.mnemonic - mnemonic phrase to use to instantiate an
|
||||
* hd private key for wallet
|
||||
* (default=account key "address").
|
||||
*/
|
||||
@ -1643,6 +1643,7 @@ Wallet.prototype.increaseFee = async function increaseFee(hash, rate, passphrase
|
||||
throw new Error('Not all coins available.');
|
||||
|
||||
const oldFee = tx.getFee(view);
|
||||
|
||||
let fee = tx.getMinFee(null, rate);
|
||||
|
||||
if (fee > MTX.Selector.MAX_FEE)
|
||||
@ -1655,10 +1656,8 @@ Wallet.prototype.increaseFee = async function increaseFee(hash, rate, passphrase
|
||||
mtx.view = view;
|
||||
|
||||
for (const input of mtx.inputs) {
|
||||
input.script.length = 0;
|
||||
input.script.compile();
|
||||
input.witness.length = 0;
|
||||
input.witness.compile();
|
||||
input.script.clear();
|
||||
input.witness.clear();
|
||||
}
|
||||
|
||||
let change;
|
||||
|
||||
@ -20,7 +20,7 @@ const MTX = require('../primitives/mtx');
|
||||
const TX = require('../primitives/tx');
|
||||
const KeyRing = require('../primitives/keyring');
|
||||
const CoinView = require('../coins/coinview');
|
||||
const {ScriptError} = require('../script/common');
|
||||
const ScriptError = require('../script/scripterror');
|
||||
|
||||
/*
|
||||
* Constants
|
||||
|
||||
@ -9,10 +9,10 @@ const Output = require('../lib/primitives/output');
|
||||
const Outpoint = require('../lib/primitives/outpoint');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const random = require('../lib/crypto/random');
|
||||
const flags = Script.flags;
|
||||
|
||||
const MANDATORY = Script.flags.MANDATORY_VERIFY_FLAGS
|
||||
| Script.flags.VERIFY_WITNESS;
|
||||
const STANDARD = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
const MANDATORY = flags.MANDATORY_VERIFY_FLAGS | flags.VERIFY_WITNESS;
|
||||
const STANDARD = flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
function randomOutpoint() {
|
||||
const hash = random.randomBytes(32).toString('hex');
|
||||
@ -36,14 +36,13 @@ function randomTX() {
|
||||
const tx = new TX();
|
||||
const inputs = util.random(1, 5);
|
||||
const outputs = util.random(0, 5);
|
||||
let i;
|
||||
|
||||
tx.version = util.random(0, 0xffffffff);
|
||||
|
||||
for (i = 0; i < inputs; i++)
|
||||
for (let i = 0; i < inputs; i++)
|
||||
tx.inputs.push(randomInput());
|
||||
|
||||
for (i = 0; i < outputs; i++)
|
||||
for (let i = 0; i < outputs; i++)
|
||||
tx.inputs.push(randomOutput());
|
||||
|
||||
if (util.random(0, 5) === 0)
|
||||
@ -57,10 +56,9 @@ function randomTX() {
|
||||
function randomWitness(redeem) {
|
||||
const size = util.random(1, 100);
|
||||
const witness = new Witness();
|
||||
let i, len;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
len = util.random(0, 100);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const len = util.random(0, 100);
|
||||
witness.push(random.randomBytes(len));
|
||||
}
|
||||
|
||||
@ -75,19 +73,16 @@ function randomWitness(redeem) {
|
||||
function randomInputScript(redeem) {
|
||||
const size = util.random(1, 100);
|
||||
const script = new Script();
|
||||
let i, len;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
len = util.random(0, 100);
|
||||
script.push(random.randomBytes(len));
|
||||
for (let i = 0; i < size; i++) {
|
||||
const len = util.random(0, 100);
|
||||
script.pushData(random.randomBytes(len));
|
||||
}
|
||||
|
||||
if (redeem)
|
||||
script.push(redeem);
|
||||
script.pushData(redeem);
|
||||
|
||||
script.compile();
|
||||
|
||||
return script;
|
||||
return script.compile();
|
||||
}
|
||||
|
||||
function randomOutputScript() {
|
||||
@ -96,14 +91,10 @@ function randomOutputScript() {
|
||||
}
|
||||
|
||||
function isPushOnly(script) {
|
||||
let i, op;
|
||||
|
||||
if (script.isPushOnly())
|
||||
return true;
|
||||
|
||||
for (i = 0; i < script.code.length; i++) {
|
||||
op = script.code[i];
|
||||
|
||||
for (const op of script.code) {
|
||||
if (op.value === Script.opcodes.NOP)
|
||||
continue;
|
||||
|
||||
@ -132,10 +123,9 @@ function randomMultisig() {
|
||||
const n = util.random(1, 16);
|
||||
const m = util.random(1, n);
|
||||
const keys = [];
|
||||
let i, len;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
len = util.random(0, 2) === 0 ? 33 : 65;
|
||||
for (let i = 0; i < n; i++) {
|
||||
const len = util.random(0, 2) === 0 ? 33 : 65;
|
||||
keys.push(random.randomBytes(len));
|
||||
}
|
||||
|
||||
@ -247,7 +237,7 @@ function randomWitnessNestedContext() {
|
||||
const redeem = randomRedeem();
|
||||
const program = Script.fromProgram(0, redeem.sha256());
|
||||
return {
|
||||
input: new Script([program.toRaw()]),
|
||||
input: Script.fromItems([program.toRaw()]),
|
||||
witness: randomWitness(redeem.toRaw()),
|
||||
output: Script.fromScripthash(program.hash160()),
|
||||
redeem: redeem
|
||||
@ -275,7 +265,6 @@ function randomContext() {
|
||||
function fuzzSimple(flags) {
|
||||
let tx = randomTX();
|
||||
let total = -1;
|
||||
let stack, input, output;
|
||||
|
||||
for (;;) {
|
||||
if (++total % 1000 === 0)
|
||||
@ -284,8 +273,8 @@ function fuzzSimple(flags) {
|
||||
if (total % 500 === 0)
|
||||
tx = randomTX();
|
||||
|
||||
stack = new Stack();
|
||||
input = randomInputScript();
|
||||
const stack = new Stack();
|
||||
const input = randomInputScript();
|
||||
|
||||
try {
|
||||
input.execute(stack, flags, tx, 0, 0, 0);
|
||||
@ -295,7 +284,7 @@ function fuzzSimple(flags) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
output = randomOutputScript();
|
||||
const output = randomOutputScript();
|
||||
|
||||
try {
|
||||
output.execute(stack, flags, tx, 0, 0, 0);
|
||||
@ -308,7 +297,7 @@ function fuzzSimple(flags) {
|
||||
if (stack.length === 0)
|
||||
continue;
|
||||
|
||||
if (!stack.bool(-1))
|
||||
if (!stack.getBool(-1))
|
||||
continue;
|
||||
|
||||
if (isPushOnly(output))
|
||||
@ -332,7 +321,6 @@ function fuzzSimple(flags) {
|
||||
function fuzzVerify(flags) {
|
||||
let tx = randomTX();
|
||||
let total = -1;
|
||||
let input, output, witness;
|
||||
|
||||
for (;;) {
|
||||
if (++total % 1000 === 0)
|
||||
@ -341,9 +329,9 @@ function fuzzVerify(flags) {
|
||||
if (total % 500 === 0)
|
||||
tx = randomTX();
|
||||
|
||||
input = randomInputScript();
|
||||
witness = randomWitness();
|
||||
output = randomOutputScript();
|
||||
const input = randomInputScript();
|
||||
const witness = randomWitness();
|
||||
const output = randomOutputScript();
|
||||
|
||||
try {
|
||||
Script.verify(
|
||||
@ -382,7 +370,6 @@ function fuzzVerify(flags) {
|
||||
function fuzzLess(flags) {
|
||||
let tx = randomTX();
|
||||
let total = -1;
|
||||
let ctx;
|
||||
|
||||
for (;;) {
|
||||
if (++total % 1000 === 0)
|
||||
@ -391,7 +378,7 @@ function fuzzLess(flags) {
|
||||
if (total % 500 === 0)
|
||||
tx = randomTX();
|
||||
|
||||
ctx = randomContext();
|
||||
const ctx = randomContext();
|
||||
|
||||
try {
|
||||
Script.verify(
|
||||
|
||||
@ -6,13 +6,10 @@ const encoding = require('../lib/utils/encoding');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const Block = require('../lib/primitives/block');
|
||||
const Script = require('../lib/script/script');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const ScriptNum = require('../lib/script/scriptnum');
|
||||
const opcodes = Script.opcodes;
|
||||
|
||||
function createGenesisBlock(options) {
|
||||
let flags = options.flags;
|
||||
let script = options.script;
|
||||
let key = options.key;
|
||||
let reward = options.reward;
|
||||
|
||||
if (!flags) {
|
||||
@ -21,13 +18,11 @@ function createGenesisBlock(options) {
|
||||
'ascii');
|
||||
}
|
||||
|
||||
if (!script) {
|
||||
script = Script.fromArray([
|
||||
Buffer.from('04678afdb0fe5548271967f1a67130b7105cd6a828e039'
|
||||
+ '09a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c3'
|
||||
+ '84df7ba0b8d578a4c702b6bf11d5f', 'hex'),
|
||||
opcodes.OP_CHECKSIG
|
||||
]);
|
||||
if (!key) {
|
||||
key = Buffer.from(''
|
||||
+ '04678afdb0fe5548271967f1a67130b7105cd6a828e039'
|
||||
+ '09a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c3'
|
||||
+ '84df7ba0b8d578a4c702b6bf11d5f', 'hex');
|
||||
}
|
||||
|
||||
if (!reward)
|
||||
@ -40,16 +35,16 @@ function createGenesisBlock(options) {
|
||||
hash: encoding.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: [
|
||||
Opcode.fromNumber(new ScriptNum(486604799)),
|
||||
Opcode.fromPush(Buffer.from([4])),
|
||||
Opcode.fromData(flags)
|
||||
],
|
||||
script: Script()
|
||||
.pushInt(486604799)
|
||||
.pushPush(Buffer.from([4]))
|
||||
.pushData(flags)
|
||||
.compile(),
|
||||
sequence: 0xffffffff
|
||||
}],
|
||||
outputs: [{
|
||||
value: reward,
|
||||
script: script
|
||||
script: Script.fromPubkey(key)
|
||||
}],
|
||||
locktime: 0
|
||||
});
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('./util/assert');
|
||||
const ScriptNum = require('../lib/script/scriptnum');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const encoding = require('../lib/utils/encoding');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
@ -17,6 +16,7 @@ const MemWallet = require('./util/memwallet');
|
||||
const Network = require('../lib/protocol/network');
|
||||
const Output = require('../lib/primitives/output');
|
||||
const common = require('../lib/blockchain/common');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const opcodes = Script.opcodes;
|
||||
|
||||
const network = Network.get('regtest');
|
||||
@ -79,8 +79,8 @@ async function mineCSV(fund) {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(1),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10000
|
||||
});
|
||||
@ -419,8 +419,8 @@ describe('Chain', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(2),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10000
|
||||
});
|
||||
@ -444,8 +444,8 @@ describe('Chain', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(1),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 1 * 1e8
|
||||
});
|
||||
@ -479,8 +479,8 @@ describe('Chain', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(2),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 1 * 1e8
|
||||
});
|
||||
@ -547,7 +547,6 @@ describe('Chain', function() {
|
||||
const tx = block.txs[0];
|
||||
const input = tx.inputs[0];
|
||||
input.witness.set(0, Buffer.allocUnsafe(33));
|
||||
input.witness.compile();
|
||||
block.refresh(true);
|
||||
assert.strictEqual(await addBlock(block), 'bad-witness-nonce-size');
|
||||
});
|
||||
@ -557,7 +556,6 @@ describe('Chain', function() {
|
||||
const tx = block.txs[0];
|
||||
const input = tx.inputs[0];
|
||||
input.witness.set(0, encoding.ONE_HASH);
|
||||
input.witness.compile();
|
||||
block.refresh(true);
|
||||
assert.strictEqual(await addBlock(block), 'bad-witness-merkle-match');
|
||||
});
|
||||
@ -570,9 +568,9 @@ describe('Chain', function() {
|
||||
|
||||
assert(output.script.isCommitment());
|
||||
|
||||
const commit = Buffer.from(output.script.get(1));
|
||||
const commit = Buffer.from(output.script.getData(1));
|
||||
commit.fill(0, 10);
|
||||
output.script.set(1, commit);
|
||||
output.script.setData(1, commit);
|
||||
output.script.compile();
|
||||
|
||||
block.refresh(true);
|
||||
@ -787,13 +785,14 @@ describe('Chain', function() {
|
||||
const flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
|
||||
|
||||
const redeem = new Script();
|
||||
redeem.push(new ScriptNum(20));
|
||||
redeem.pushInt(20);
|
||||
|
||||
for (let i = 0; i < 20; i++)
|
||||
redeem.push(encoding.ZERO_KEY);
|
||||
redeem.pushData(encoding.ZERO_KEY);
|
||||
|
||||
redeem.pushInt(20);
|
||||
redeem.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
|
||||
redeem.push(new ScriptNum(20));
|
||||
redeem.push(opcodes.OP_CHECKMULTISIG);
|
||||
redeem.compile();
|
||||
|
||||
const script = Script.fromScripthash(redeem.hash160());
|
||||
@ -828,13 +827,15 @@ describe('Chain', function() {
|
||||
const job = await cpu.createJob();
|
||||
|
||||
const script = new Script();
|
||||
script.push(new ScriptNum(20));
|
||||
|
||||
script.pushInt(20);
|
||||
|
||||
for (let i = 0; i < 20; i++)
|
||||
script.push(encoding.ZERO_KEY);
|
||||
script.pushData(encoding.ZERO_KEY);
|
||||
|
||||
script.pushInt(20);
|
||||
script.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
|
||||
script.push(new ScriptNum(20));
|
||||
script.push(opcodes.OP_CHECKMULTISIG);
|
||||
script.compile();
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
@ -848,7 +849,7 @@ describe('Chain', function() {
|
||||
|
||||
for (let j = 2; j < cb.outputs.length; j++) {
|
||||
mtx.addTX(cb, j);
|
||||
mtx.inputs[j - 2].script = new Script([script.toRaw()]);
|
||||
mtx.inputs[j - 2].script.fromItems([script.toRaw()]);
|
||||
}
|
||||
|
||||
mtx.addOutput(witWallet.getAddress(), 1);
|
||||
|
||||
@ -81,7 +81,7 @@ describe('Mempool', function() {
|
||||
|
||||
const sig = t1.signature(0, script, 70000, key.privateKey, ALL, 0);
|
||||
|
||||
t1.inputs[0].script = new Script([sig]);
|
||||
t1.inputs[0].script = Script.fromItems([sig]);
|
||||
|
||||
// balance: 51000
|
||||
wallet.sign(t1);
|
||||
@ -126,8 +126,9 @@ describe('Mempool', function() {
|
||||
wallet.template(fake);
|
||||
|
||||
// Fake signature
|
||||
fake.inputs[0].script.set(0, encoding.ZERO_SIG);
|
||||
fake.inputs[0].script.compile();
|
||||
const input = fake.inputs[0];
|
||||
input.script.setData(0, encoding.ZERO_SIG);
|
||||
input.script.compile();
|
||||
// balance: 11000
|
||||
|
||||
{
|
||||
@ -188,7 +189,7 @@ describe('Mempool', function() {
|
||||
chain.tip.height = 200;
|
||||
|
||||
const sig = tx.signature(0, prev, 70000, key.privateKey, ALL, 0);
|
||||
tx.inputs[0].script = new Script([sig]);
|
||||
tx.inputs[0].script = Script.fromItems([sig]);
|
||||
|
||||
await mempool.addTX(tx.toTX());
|
||||
chain.tip.height = 0;
|
||||
@ -209,7 +210,7 @@ describe('Mempool', function() {
|
||||
chain.tip.height = 200 - 1;
|
||||
|
||||
const sig = tx.signature(0, prev, 70000, key.privateKey, ALL, 0);
|
||||
tx.inputs[0].script = new Script([sig]);
|
||||
tx.inputs[0].script = Script.fromItems([sig]);
|
||||
|
||||
let err;
|
||||
try {
|
||||
@ -268,7 +269,7 @@ describe('Mempool', function() {
|
||||
tx.addCoin(dummyInput(prev, prevHash));
|
||||
|
||||
const sig = tx.signature(0, prev, 70000, key.privateKey, ALL, 0);
|
||||
tx.inputs[0].script = new Script([sig]);
|
||||
tx.inputs[0].script = Script.fromItems([sig]);
|
||||
tx.inputs[0].witness.push(Buffer.alloc(0));
|
||||
|
||||
let err;
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('./util/assert');
|
||||
const ScriptNum = require('../lib/script/scriptnum');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const co = require('../lib/utils/co');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
const Script = require('../lib/script/script');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const FullNode = require('../lib/node/fullnode');
|
||||
const MTX = require('../lib/primitives/mtx');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
@ -63,8 +63,8 @@ async function mineCSV(fund) {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(1),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10 * 1e8
|
||||
});
|
||||
@ -348,8 +348,8 @@ describe('Node', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(2),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10 * 1e8
|
||||
});
|
||||
@ -373,8 +373,8 @@ describe('Node', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(1),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10 * 1e8
|
||||
});
|
||||
@ -418,8 +418,8 @@ describe('Node', function() {
|
||||
|
||||
spend.addOutput({
|
||||
script: [
|
||||
ScriptNum.encode(2),
|
||||
Script.opcodes.OP_CHECKSEQUENCEVERIFY
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('checksequenceverify')
|
||||
],
|
||||
value: 10 * 1e8
|
||||
});
|
||||
|
||||
@ -7,10 +7,10 @@ const assert = require('./util/assert');
|
||||
const Script = require('../lib/script/script');
|
||||
const Witness = require('../lib/script/witness');
|
||||
const Stack = require('../lib/script/stack');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const util = require('../lib/utils/util');
|
||||
const encoding = require('../lib/utils/encoding');
|
||||
const opcodes = Script.opcodes;
|
||||
|
||||
const scripts = require('./data/script-tests.json');
|
||||
|
||||
@ -18,7 +18,7 @@ function isSuccess(stack) {
|
||||
if (stack.length === 0)
|
||||
return false;
|
||||
|
||||
if (!stack.bool(-1))
|
||||
if (!stack.getBool(-1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -67,34 +67,6 @@ function parseScriptTest(data) {
|
||||
}
|
||||
|
||||
describe('Script', function() {
|
||||
it('should encode/decode script', () => {
|
||||
const src = Buffer.from(''
|
||||
+ '20'
|
||||
+ '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
||||
+ '20'
|
||||
+ '101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f'
|
||||
+ 'ac',
|
||||
'hex');
|
||||
|
||||
const decoded = Script.fromRaw(src);
|
||||
assert.strictEqual(decoded.code.length, 3);
|
||||
assert.strictEqual(decoded.code[0].data.toString('hex'),
|
||||
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
|
||||
assert.strictEqual(decoded.code[1].data.toString('hex'),
|
||||
'101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f');
|
||||
assert.strictEqual(decoded.code[2].value, opcodes.OP_CHECKSIG);
|
||||
|
||||
const dst = decoded.toRaw();
|
||||
assert.bufferEqual(dst, src);
|
||||
});
|
||||
|
||||
it('should encode/decode numbers', () => {
|
||||
const script = [0, 0x51, 0x52, 0x60];
|
||||
const encoded = Script.fromArray(script).raw;
|
||||
const decoded = Script(encoded).toArray();
|
||||
assert.deepStrictEqual(decoded, script);
|
||||
});
|
||||
|
||||
it('should recognize a P2SH output', () => {
|
||||
const hex = 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87';
|
||||
const decoded = Script.fromRaw(hex, 'hex');
|
||||
@ -110,17 +82,20 @@ describe('Script', function() {
|
||||
|
||||
it('should handle if statements correctly', () => {
|
||||
{
|
||||
const input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
const input = new Script([
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromInt(2)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
opcodes.OP_3,
|
||||
opcodes.OP_ELSE,
|
||||
opcodes.OP_4,
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('equal'),
|
||||
Opcode.fromSymbol('if'),
|
||||
Opcode.fromInt(3),
|
||||
Opcode.fromSymbol('else'),
|
||||
Opcode.fromInt(4),
|
||||
Opcode.fromSymbol('endif'),
|
||||
Opcode.fromInt(5)
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -132,17 +107,20 @@ describe('Script', function() {
|
||||
}
|
||||
|
||||
{
|
||||
const input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
const input = new Script([
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromInt(2)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
opcodes.OP_3,
|
||||
opcodes.OP_ELSE,
|
||||
opcodes.OP_4,
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
Opcode.fromInt(9),
|
||||
Opcode.fromSymbol('equal'),
|
||||
Opcode.fromSymbol('if'),
|
||||
Opcode.fromInt(3),
|
||||
Opcode.fromSymbol('else'),
|
||||
Opcode.fromInt(4),
|
||||
Opcode.fromSymbol('endif'),
|
||||
Opcode.fromInt(5)
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -154,15 +132,18 @@ describe('Script', function() {
|
||||
}
|
||||
|
||||
{
|
||||
const input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
const input = new Script([
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromInt(2)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
opcodes.OP_3,
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('equal'),
|
||||
Opcode.fromSymbol('if'),
|
||||
Opcode.fromInt(3),
|
||||
Opcode.fromSymbol('endif'),
|
||||
Opcode.fromInt(5)
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -174,15 +155,18 @@ describe('Script', function() {
|
||||
}
|
||||
|
||||
{
|
||||
const input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
const input = new Script([
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromInt(2)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
opcodes.OP_3,
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
Opcode.fromInt(9),
|
||||
Opcode.fromSymbol('equal'),
|
||||
Opcode.fromSymbol('if'),
|
||||
Opcode.fromInt(3),
|
||||
Opcode.fromSymbol('endif'),
|
||||
Opcode.fromInt(5)
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -194,15 +178,18 @@ describe('Script', function() {
|
||||
}
|
||||
|
||||
{
|
||||
const input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
const input = new Script([
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromInt(2)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_NOTIF,
|
||||
opcodes.OP_3,
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
Opcode.fromInt(9),
|
||||
Opcode.fromSymbol('equal'),
|
||||
Opcode.fromSymbol('notif'),
|
||||
Opcode.fromInt(3),
|
||||
Opcode.fromSymbol('endif'),
|
||||
Opcode.fromInt(5)
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -216,15 +203,15 @@ describe('Script', function() {
|
||||
|
||||
it('should handle CScriptNums correctly', () => {
|
||||
const input = new Script([
|
||||
Buffer.from('ffffff7f', 'hex'),
|
||||
opcodes.OP_NEGATE,
|
||||
opcodes.OP_DUP,
|
||||
opcodes.OP_ADD
|
||||
Opcode.fromString('ffffff7f', 'hex'),
|
||||
Opcode.fromSymbol('negate'),
|
||||
Opcode.fromSymbol('dup'),
|
||||
Opcode.fromSymbol('add')
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
Buffer.from('feffffff80', 'hex'),
|
||||
opcodes.OP_EQUAL
|
||||
Opcode.fromString('feffffff80', 'hex'),
|
||||
Opcode.fromSymbol('equal')
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -237,15 +224,15 @@ describe('Script', function() {
|
||||
|
||||
it('should handle CScriptNums correctly', () => {
|
||||
const input = new Script([
|
||||
opcodes.OP_11,
|
||||
opcodes.OP_10,
|
||||
opcodes.OP_1,
|
||||
opcodes.OP_ADD
|
||||
Opcode.fromInt(11),
|
||||
Opcode.fromInt(10),
|
||||
Opcode.fromInt(1),
|
||||
Opcode.fromSymbol('add')
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_NUMNOTEQUAL,
|
||||
opcodes.OP_NOT
|
||||
Opcode.fromSymbol('numnotequal'),
|
||||
Opcode.fromSymbol('not')
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -258,19 +245,19 @@ describe('Script', function() {
|
||||
|
||||
it('should handle OP_ROLL correctly', () => {
|
||||
const input = new Script([
|
||||
Buffer.from([0x16]),
|
||||
Buffer.from([0x15]),
|
||||
Buffer.from([0x14])
|
||||
Opcode.fromInt(0x16),
|
||||
Opcode.fromInt(0x15),
|
||||
Opcode.fromInt(0x14)
|
||||
]);
|
||||
|
||||
const output = new Script([
|
||||
opcodes.OP_0,
|
||||
opcodes.OP_ROLL,
|
||||
Buffer.from([0x14]),
|
||||
opcodes.OP_EQUALVERIFY,
|
||||
opcodes.OP_DEPTH,
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL
|
||||
Opcode.fromInt(0),
|
||||
Opcode.fromSymbol('roll'),
|
||||
Opcode.fromInt(0x14),
|
||||
Opcode.fromSymbol('equalverify'),
|
||||
Opcode.fromSymbol('depth'),
|
||||
Opcode.fromInt(2),
|
||||
Opcode.fromSymbol('equal')
|
||||
]);
|
||||
|
||||
const stack = new Stack();
|
||||
@ -302,7 +289,10 @@ describe('Script', function() {
|
||||
hash: encoding.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: [opcodes.OP_0, opcodes.OP_0],
|
||||
script: [
|
||||
Opcode.fromOp(0),
|
||||
Opcode.fromOp(0)
|
||||
],
|
||||
witness: [],
|
||||
sequence: 0xffffffff
|
||||
}],
|
||||
|
||||
@ -13,11 +13,11 @@ const Output = require('../lib/primitives/output');
|
||||
const Outpoint = require('../lib/primitives/outpoint');
|
||||
const Script = require('../lib/script/script');
|
||||
const Witness = require('../lib/script/witness');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const Input = require('../lib/primitives/input');
|
||||
const CoinView = require('../lib/coins/coinview');
|
||||
const KeyRing = require('../lib/primitives/keyring');
|
||||
const common = require('./util/common');
|
||||
const opcodes = Script.opcodes;
|
||||
|
||||
const validTests = require('./data/tx-valid.json');
|
||||
const invalidTests = require('./data/tx-invalid.json');
|
||||
@ -624,8 +624,8 @@ describe('TX', function() {
|
||||
const output = Script.fromMultisig(1, 2, [pub, pub]);
|
||||
|
||||
const input = new Script([
|
||||
opcodes.OP_0,
|
||||
opcodes.OP_0
|
||||
Opcode.fromOp(0),
|
||||
Opcode.fromOp(0)
|
||||
]);
|
||||
|
||||
const witness = new Witness();
|
||||
@ -646,9 +646,9 @@ describe('TX', function() {
|
||||
const output = Script.fromScripthash(redeem.hash160());
|
||||
|
||||
const input = new Script([
|
||||
opcodes.OP_0,
|
||||
opcodes.OP_0,
|
||||
redeem.toRaw()
|
||||
Opcode.fromOp(0),
|
||||
Opcode.fromOp(0),
|
||||
Opcode.fromData(redeem.toRaw())
|
||||
]);
|
||||
|
||||
const witness = new Witness();
|
||||
@ -707,7 +707,7 @@ describe('TX', function() {
|
||||
const output = Script.fromScripthash(redeem.hash160());
|
||||
|
||||
const input = new Script([
|
||||
redeem.toRaw()
|
||||
Opcode.fromData(redeem.toRaw())
|
||||
]);
|
||||
|
||||
const witness = new Witness([
|
||||
@ -754,7 +754,7 @@ describe('TX', function() {
|
||||
const output = Script.fromScripthash(redeem.hash160());
|
||||
|
||||
const input = new Script([
|
||||
redeem.toRaw()
|
||||
Opcode.fromData(redeem.toRaw())
|
||||
]);
|
||||
|
||||
const witness = new Witness([
|
||||
|
||||
@ -27,7 +27,7 @@ const KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
|
||||
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
|
||||
|
||||
const workers = new WorkerPool({
|
||||
enabled: true
|
||||
enabled: false
|
||||
});
|
||||
|
||||
const wdb = new WalletDB({
|
||||
@ -222,8 +222,9 @@ async function testP2SH(witness, nesting) {
|
||||
assert(bob.account.change.getAddress().equals(change2));
|
||||
assert(carol.account.change.getAddress().equals(change2));
|
||||
|
||||
tx.inputs[0][vector].set(2, encoding.ZERO_SIG);
|
||||
tx.inputs[0][vector].compile();
|
||||
const input = tx.inputs[0];
|
||||
input[vector].setData(2, encoding.ZERO_SIG);
|
||||
input[vector].compile();
|
||||
|
||||
assert(!tx.verify(view, flags));
|
||||
assert.strictEqual(tx.getFee(view), 10000);
|
||||
@ -373,8 +374,9 @@ describe('Wallet', function() {
|
||||
// Script inputs but do not sign
|
||||
await alice.template(fake);
|
||||
// Fake signature
|
||||
fake.inputs[0].script.set(0, encoding.ZERO_SIG);
|
||||
fake.inputs[0].script.compile();
|
||||
const input = fake.inputs[0];
|
||||
input.script.setData(0, encoding.ZERO_SIG);
|
||||
input.script.compile();
|
||||
// balance: 11000
|
||||
|
||||
// Fake TX should temporarily change output.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user