script: iterate over scripts incrementally (note: use --patience).
This commit is contained in:
parent
57b98b2368
commit
861eea07fd
@ -159,7 +159,7 @@ for (let i = 0; i < 100; i++) {
|
||||
hash: encoding.NULL_HASH,
|
||||
index: 0
|
||||
},
|
||||
script: new Script()
|
||||
script: Script.write()
|
||||
.pushData(Buffer.allocUnsafe(9))
|
||||
.pushData(random.randomBytes(33))
|
||||
.compile()
|
||||
|
||||
@ -7,7 +7,7 @@ const Script = bcoin.script;
|
||||
const Witness = bcoin.witness;
|
||||
const Stack = bcoin.stack;
|
||||
|
||||
const output = new Script();
|
||||
let output = Script.write();
|
||||
output.pushSym('OP_DROP');
|
||||
output.pushSym('OP_ADD');
|
||||
output.pushInt(7);
|
||||
@ -15,15 +15,15 @@ output.pushSym('OP_NUMEQUAL');
|
||||
// Compile the script to its binary representation
|
||||
// (you must do this if you change something!).
|
||||
assert(output.getSmall(2) === 7); // compiled as OP_7
|
||||
output.compile();
|
||||
output = output.compile();
|
||||
|
||||
const input = new Script();
|
||||
let input = Script.write();
|
||||
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();
|
||||
input = input.compile();
|
||||
|
||||
// A stack is another array-like object which contains
|
||||
// only Buffers (whereas scripts contain Opcode objects).
|
||||
|
||||
@ -1409,10 +1409,11 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb
|
||||
if (coinbase) {
|
||||
const tx = attempt.toCoinbase();
|
||||
const input = tx.inputs[0];
|
||||
const script = input.script.write();
|
||||
|
||||
// Pop off the nonces.
|
||||
input.script.pop();
|
||||
input.script.compile();
|
||||
script.pop();
|
||||
script.compile();
|
||||
|
||||
if (attempt.witness) {
|
||||
// We don't include the commitment
|
||||
@ -1863,7 +1864,7 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help)
|
||||
|
||||
const redeem = Script.fromRaw(redeemRaw);
|
||||
|
||||
for (const op of redeem.code) {
|
||||
for (const op of redeem.values()) {
|
||||
if (!op.data)
|
||||
continue;
|
||||
|
||||
@ -2341,7 +2342,6 @@ RPC.prototype._addBlock = async function _addBlock(block) {
|
||||
|
||||
// Recreate witness nonce (all zeroes).
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
|
||||
tx.refresh();
|
||||
block.refresh();
|
||||
|
||||
@ -226,6 +226,7 @@ MempoolEntry.prototype.getDescRate = function getDescRate() {
|
||||
|
||||
MempoolEntry.prototype.memUsage = function memUsage() {
|
||||
const tx = this.tx;
|
||||
|
||||
let total = 0;
|
||||
|
||||
total += 176; // mempool entry
|
||||
@ -247,13 +248,6 @@ MempoolEntry.prototype.memUsage = function memUsage() {
|
||||
|
||||
total += 40; // script
|
||||
total += 80; // script raw buffer
|
||||
total += 32; // script code array
|
||||
total += input.script.code.length * 40; // opcodes
|
||||
|
||||
for (const op of input.script.code) {
|
||||
if (op.data)
|
||||
total += 80; // op buffers
|
||||
}
|
||||
|
||||
total += 96; // witness
|
||||
total += 32; // witness items
|
||||
@ -262,18 +256,9 @@ MempoolEntry.prototype.memUsage = function memUsage() {
|
||||
|
||||
total += 32; // output array
|
||||
|
||||
for (const output of tx.outputs) {
|
||||
total += 104; // output
|
||||
total += 40; // script
|
||||
total += 80; // script raw buffer
|
||||
total += 32; // script code array
|
||||
total += output.script.code.length * 40; // opcodes
|
||||
|
||||
for (const op of output.script.code) {
|
||||
if (op.data)
|
||||
total += 80; // op buffers
|
||||
}
|
||||
}
|
||||
total += tx.outputs.length * 104; // output
|
||||
total += tx.outputs.length * 40; // script
|
||||
total += tx.outputs.length * 80; // script raw buffer
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
@ -238,27 +238,26 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
|
||||
// Coinbase input.
|
||||
const input = new Input();
|
||||
const script = input.script.write();
|
||||
|
||||
// Height (required in v2+ blocks)
|
||||
input.script.pushInt(this.height);
|
||||
script.pushInt(this.height);
|
||||
|
||||
// Coinbase flags.
|
||||
input.script.pushData(encoding.ZERO_HASH160);
|
||||
script.pushData(encoding.ZERO_HASH160);
|
||||
|
||||
// Smaller nonce for good measure.
|
||||
input.script.pushData(util.nonce(4));
|
||||
script.pushData(util.nonce(4));
|
||||
|
||||
// Extra nonce: incremented when
|
||||
// the nonce overflows.
|
||||
input.script.pushData(encoding.ZERO_U64);
|
||||
script.pushData(encoding.ZERO_U64);
|
||||
|
||||
input.script.compile();
|
||||
script.compile();
|
||||
|
||||
// Set up the witness nonce.
|
||||
if (this.witness) {
|
||||
if (this.witness)
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
}
|
||||
|
||||
cb.inputs.push(input);
|
||||
|
||||
@ -279,7 +278,7 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
}
|
||||
|
||||
// Padding for the CB height (constant size).
|
||||
const op = input.script.get(0);
|
||||
const op = input.script.read().get();
|
||||
assert(op);
|
||||
const padding = 5 - op.getSize();
|
||||
assert(padding >= 0);
|
||||
@ -306,8 +305,9 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
|
||||
}
|
||||
|
||||
// Setup coinbase flags (variable size).
|
||||
input.script.setData(1, this.coinbaseFlags);
|
||||
input.script.compile();
|
||||
input.script.write()
|
||||
.setData(1, this.coinbaseFlags)
|
||||
.compile();
|
||||
|
||||
// Setup output script (variable size).
|
||||
output.script.fromAddress(this.address);
|
||||
@ -436,7 +436,6 @@ BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) {
|
||||
if (this.witness) {
|
||||
const input = tx.inputs[0];
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
tx.refresh();
|
||||
}
|
||||
|
||||
|
||||
@ -808,7 +808,7 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
|
||||
// Grab the redeem script's keys to figure
|
||||
// out where our key should go.
|
||||
const keys = [];
|
||||
for (const op of prev.code) {
|
||||
for (const op of prev.values()) {
|
||||
if (op.data)
|
||||
keys.push(op.data);
|
||||
}
|
||||
|
||||
@ -354,6 +354,24 @@ 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 valid key.
|
||||
* @param {Buffer} key
|
||||
|
||||
@ -572,6 +572,158 @@ Opcode.fromSymbol = function fromSymbol(name) {
|
||||
return Opcode.fromOp(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate opcode from buffer reader.
|
||||
* Only reads opcode values.
|
||||
* @param {BufferReader} br
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.readOp = function readOp(br) {
|
||||
const value = br.readU8();
|
||||
|
||||
if (opCache[value])
|
||||
return value;
|
||||
|
||||
switch (value) {
|
||||
case opcodes.OP_PUSHDATA1: {
|
||||
if (br.left() < 1)
|
||||
return -1;
|
||||
|
||||
const size = br.readU8();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return value;
|
||||
}
|
||||
case opcodes.OP_PUSHDATA2: {
|
||||
if (br.left() < 2) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size = br.readU16();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return value;
|
||||
}
|
||||
case opcodes.OP_PUSHDATA4: {
|
||||
if (br.left() < 4) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size = br.readU32();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return value;
|
||||
}
|
||||
default: {
|
||||
if (br.left() < value) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate opcode from buffer reader.
|
||||
* Reads pushdata size only.
|
||||
* @param {BufferReader} br
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Opcode.readLength = function readLength(br) {
|
||||
const value = br.readU8();
|
||||
|
||||
if (opCache[value])
|
||||
return -1;
|
||||
|
||||
switch (value) {
|
||||
case opcodes.OP_PUSHDATA1: {
|
||||
if (br.left() < 1)
|
||||
return -1;
|
||||
|
||||
const size = br.readU8();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
case opcodes.OP_PUSHDATA2: {
|
||||
if (br.left() < 2) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size = br.readU16();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
case opcodes.OP_PUSHDATA4: {
|
||||
if (br.left() < 4) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size = br.readU32();
|
||||
|
||||
if (br.left() < size) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
default: {
|
||||
if (br.left() < value) {
|
||||
br.seek(br.left());
|
||||
return -1;
|
||||
}
|
||||
|
||||
br.seek(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate opcode from buffer reader.
|
||||
* @param {BufferReader} br
|
||||
|
||||
1582
lib/script/script.js
1582
lib/script/script.js
File diff suppressed because it is too large
Load Diff
@ -191,15 +191,6 @@ Witness.prototype.inject = function inject(witness) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compile witness (NOP).
|
||||
* @returns {Witness}
|
||||
*/
|
||||
|
||||
Witness.prototype.compile = function compile() {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Guess" the type of the witness.
|
||||
* This method is not 100% reliable.
|
||||
|
||||
@ -516,7 +516,7 @@ function siphash24(data, key) {
|
||||
}
|
||||
|
||||
function getPushes(items, script) {
|
||||
for (const op of script.code) {
|
||||
for (const op of script.values()) {
|
||||
if (!op.data || op.data.length === 0)
|
||||
continue;
|
||||
|
||||
|
||||
@ -119,14 +119,12 @@ function randomWitness(redeem) {
|
||||
if (redeem)
|
||||
witness.push(redeem);
|
||||
|
||||
witness.compile();
|
||||
|
||||
return witness;
|
||||
}
|
||||
|
||||
function randomInputScript(redeem) {
|
||||
const size = util.random(1, 100);
|
||||
const script = new Script();
|
||||
const script = Script.write();
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
const len = util.random(0, 100);
|
||||
@ -148,7 +146,7 @@ function isPushOnly(script) {
|
||||
if (script.isPushOnly())
|
||||
return true;
|
||||
|
||||
for (const op of script.code) {
|
||||
for (const op of script.values()) {
|
||||
if (op.value === Script.opcodes.NOP)
|
||||
continue;
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ function createGenesisBlock(options) {
|
||||
hash: encoding.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: Script()
|
||||
script: Script.write()
|
||||
.pushInt(486604799)
|
||||
.pushPush(Buffer.from([4]))
|
||||
.pushData(flags)
|
||||
|
||||
@ -568,10 +568,11 @@ describe('Chain', function() {
|
||||
|
||||
assert(output.script.isCommitment());
|
||||
|
||||
const commit = Buffer.from(output.script.getData(1));
|
||||
const script = output.script.write();
|
||||
const commit = Buffer.from(script.getData(1));
|
||||
commit.fill(0, 10);
|
||||
output.script.setData(1, commit);
|
||||
output.script.compile();
|
||||
script.setData(1, commit);
|
||||
script.compile();
|
||||
|
||||
block.refresh(true);
|
||||
block.merkleRoot = block.createMerkleRoot('hex');
|
||||
@ -784,16 +785,16 @@ describe('Chain', function() {
|
||||
it('should mine 111 multisig blocks', async () => {
|
||||
const flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
|
||||
|
||||
const redeem = new Script();
|
||||
redeem.pushInt(20);
|
||||
const sw = Script.write();
|
||||
sw.pushInt(20);
|
||||
|
||||
for (let i = 0; i < 20; i++)
|
||||
redeem.pushData(encoding.ZERO_KEY);
|
||||
sw.pushData(encoding.ZERO_KEY);
|
||||
|
||||
redeem.pushInt(20);
|
||||
redeem.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
sw.pushInt(20);
|
||||
sw.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
|
||||
redeem.compile();
|
||||
const redeem = sw.compile();
|
||||
|
||||
const script = Script.fromScripthash(redeem.hash160());
|
||||
|
||||
@ -826,17 +827,17 @@ describe('Chain', function() {
|
||||
const end = chain.height - 100;
|
||||
const job = await cpu.createJob();
|
||||
|
||||
const script = new Script();
|
||||
const sw = Script.write();
|
||||
|
||||
script.pushInt(20);
|
||||
sw.pushInt(20);
|
||||
|
||||
for (let i = 0; i < 20; i++)
|
||||
script.pushData(encoding.ZERO_KEY);
|
||||
sw.pushData(encoding.ZERO_KEY);
|
||||
|
||||
script.pushInt(20);
|
||||
script.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
sw.pushInt(20);
|
||||
sw.pushOp(opcodes.OP_CHECKMULTISIG);
|
||||
|
||||
script.compile();
|
||||
const script = sw.compile();
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
const block = await chain.db.getBlock(i);
|
||||
|
||||
@ -127,8 +127,9 @@ describe('Mempool', function() {
|
||||
|
||||
// Fake signature
|
||||
const input = fake.inputs[0];
|
||||
input.script.setData(0, encoding.ZERO_SIG);
|
||||
input.script.compile();
|
||||
const sw = input.script.write();
|
||||
sw.setData(0, encoding.ZERO_SIG);
|
||||
sw.compile();
|
||||
// balance: 11000
|
||||
|
||||
{
|
||||
|
||||
@ -223,8 +223,9 @@ async function testP2SH(witness, nesting) {
|
||||
assert(carol.account.change.getAddress().equals(change2));
|
||||
|
||||
const input = tx.inputs[0];
|
||||
input[vector].setData(2, encoding.ZERO_SIG);
|
||||
input[vector].compile();
|
||||
const stack = input[vector].toStack();
|
||||
stack.set(2, encoding.ZERO_SIG);
|
||||
input[vector].fromStack(stack);
|
||||
|
||||
assert(!tx.verify(view, flags));
|
||||
assert.strictEqual(tx.getFee(view), 10000);
|
||||
@ -375,8 +376,9 @@ describe('Wallet', function() {
|
||||
await alice.template(fake);
|
||||
// Fake signature
|
||||
const input = fake.inputs[0];
|
||||
input.script.setData(0, encoding.ZERO_SIG);
|
||||
input.script.compile();
|
||||
const script = input.script.write();
|
||||
script.setData(0, encoding.ZERO_SIG);
|
||||
script.compile();
|
||||
// balance: 11000
|
||||
|
||||
// Fake TX should temporarily change output.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user