Compare commits

...

1 Commits

Author SHA1 Message Date
Christopher Jeffrey
861eea07fd
script: iterate over scripts incrementally (note: use --patience). 2017-08-30 10:15:07 -07:00
16 changed files with 1132 additions and 751 deletions

View File

@ -159,7 +159,7 @@ for (let i = 0; i < 100; i++) {
hash: encoding.NULL_HASH, hash: encoding.NULL_HASH,
index: 0 index: 0
}, },
script: new Script() script: Script.write()
.pushData(Buffer.allocUnsafe(9)) .pushData(Buffer.allocUnsafe(9))
.pushData(random.randomBytes(33)) .pushData(random.randomBytes(33))
.compile() .compile()

View File

@ -7,7 +7,7 @@ const Script = bcoin.script;
const Witness = bcoin.witness; const Witness = bcoin.witness;
const Stack = bcoin.stack; const Stack = bcoin.stack;
const output = new Script(); let output = Script.write();
output.pushSym('OP_DROP'); output.pushSym('OP_DROP');
output.pushSym('OP_ADD'); output.pushSym('OP_ADD');
output.pushInt(7); output.pushInt(7);
@ -15,15 +15,15 @@ output.pushSym('OP_NUMEQUAL');
// Compile the script to its binary representation // Compile the script to its binary representation
// (you must do this if you change something!). // (you must do this if you change something!).
assert(output.getSmall(2) === 7); // compiled as OP_7 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.setString(0, 'hello world'); // add some metadata
input.pushInt(2); input.pushInt(2);
input.pushInt(5); input.pushInt(5);
input.push(input.shift()); input.push(input.shift());
assert(input.getString(2) === 'hello world'); assert(input.getString(2) === 'hello world');
input.compile(); input = input.compile();
// A stack is another array-like object which contains // A stack is another array-like object which contains
// only Buffers (whereas scripts contain Opcode objects). // only Buffers (whereas scripts contain Opcode objects).

View File

@ -1409,10 +1409,11 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb
if (coinbase) { if (coinbase) {
const tx = attempt.toCoinbase(); const tx = attempt.toCoinbase();
const input = tx.inputs[0]; const input = tx.inputs[0];
const script = input.script.write();
// Pop off the nonces. // Pop off the nonces.
input.script.pop(); script.pop();
input.script.compile(); script.compile();
if (attempt.witness) { if (attempt.witness) {
// We don't include the commitment // We don't include the commitment
@ -1863,7 +1864,7 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help)
const redeem = Script.fromRaw(redeemRaw); const redeem = Script.fromRaw(redeemRaw);
for (const op of redeem.code) { for (const op of redeem.values()) {
if (!op.data) if (!op.data)
continue; continue;
@ -2341,7 +2342,6 @@ RPC.prototype._addBlock = async function _addBlock(block) {
// Recreate witness nonce (all zeroes). // Recreate witness nonce (all zeroes).
input.witness.push(encoding.ZERO_HASH); input.witness.push(encoding.ZERO_HASH);
input.witness.compile();
tx.refresh(); tx.refresh();
block.refresh(); block.refresh();

View File

@ -226,6 +226,7 @@ MempoolEntry.prototype.getDescRate = function getDescRate() {
MempoolEntry.prototype.memUsage = function memUsage() { MempoolEntry.prototype.memUsage = function memUsage() {
const tx = this.tx; const tx = this.tx;
let total = 0; let total = 0;
total += 176; // mempool entry total += 176; // mempool entry
@ -247,13 +248,6 @@ MempoolEntry.prototype.memUsage = function memUsage() {
total += 40; // script total += 40; // script
total += 80; // script raw buffer 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 += 96; // witness
total += 32; // witness items total += 32; // witness items
@ -262,18 +256,9 @@ MempoolEntry.prototype.memUsage = function memUsage() {
total += 32; // output array total += 32; // output array
for (const output of tx.outputs) { total += tx.outputs.length * 104; // output
total += 104; // output total += tx.outputs.length * 40; // script
total += 40; // script total += tx.outputs.length * 80; // script raw buffer
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
}
}
return total; return total;
}; };

View File

@ -238,27 +238,26 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
// Coinbase input. // Coinbase input.
const input = new Input(); const input = new Input();
const script = input.script.write();
// Height (required in v2+ blocks) // Height (required in v2+ blocks)
input.script.pushInt(this.height); script.pushInt(this.height);
// Coinbase flags. // Coinbase flags.
input.script.pushData(encoding.ZERO_HASH160); script.pushData(encoding.ZERO_HASH160);
// Smaller nonce for good measure. // Smaller nonce for good measure.
input.script.pushData(util.nonce(4)); script.pushData(util.nonce(4));
// Extra nonce: incremented when // Extra nonce: incremented when
// the nonce overflows. // the nonce overflows.
input.script.pushData(encoding.ZERO_U64); script.pushData(encoding.ZERO_U64);
input.script.compile(); script.compile();
// Set up the witness nonce. // Set up the witness nonce.
if (this.witness) { if (this.witness)
input.witness.push(encoding.ZERO_HASH); input.witness.push(encoding.ZERO_HASH);
input.witness.compile();
}
cb.inputs.push(input); cb.inputs.push(input);
@ -279,7 +278,7 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
} }
// Padding for the CB height (constant size). // Padding for the CB height (constant size).
const op = input.script.get(0); const op = input.script.read().get();
assert(op); assert(op);
const padding = 5 - op.getSize(); const padding = 5 - op.getSize();
assert(padding >= 0); assert(padding >= 0);
@ -306,8 +305,9 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) {
} }
// Setup coinbase flags (variable size). // Setup coinbase flags (variable size).
input.script.setData(1, this.coinbaseFlags); input.script.write()
input.script.compile(); .setData(1, this.coinbaseFlags)
.compile();
// Setup output script (variable size). // Setup output script (variable size).
output.script.fromAddress(this.address); output.script.fromAddress(this.address);
@ -436,7 +436,6 @@ BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) {
if (this.witness) { if (this.witness) {
const input = tx.inputs[0]; const input = tx.inputs[0];
input.witness.push(encoding.ZERO_HASH); input.witness.push(encoding.ZERO_HASH);
input.witness.compile();
tx.refresh(); tx.refresh();
} }

View File

@ -808,7 +808,7 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) {
// Grab the redeem script's keys to figure // Grab the redeem script's keys to figure
// out where our key should go. // out where our key should go.
const keys = []; const keys = [];
for (const op of prev.code) { for (const op of prev.values()) {
if (op.data) if (op.data)
keys.push(op.data); keys.push(op.data);
} }

View File

@ -354,6 +354,24 @@ exports.isLowDER = function isLowDER(sig) {
return secp256k1.isLowS(sig.slice(0, -1)); 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. * Test whether the data element is a valid key.
* @param {Buffer} key * @param {Buffer} key

View File

@ -572,6 +572,158 @@ Opcode.fromSymbol = function fromSymbol(name) {
return Opcode.fromOp(value); 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. * Instantiate opcode from buffer reader.
* @param {BufferReader} br * @param {BufferReader} br

File diff suppressed because it is too large Load Diff

View File

@ -191,15 +191,6 @@ Witness.prototype.inject = function inject(witness) {
return this; return this;
}; };
/**
* Compile witness (NOP).
* @returns {Witness}
*/
Witness.prototype.compile = function compile() {
return this;
};
/** /**
* "Guess" the type of the witness. * "Guess" the type of the witness.
* This method is not 100% reliable. * This method is not 100% reliable.

View File

@ -516,7 +516,7 @@ function siphash24(data, key) {
} }
function getPushes(items, script) { function getPushes(items, script) {
for (const op of script.code) { for (const op of script.values()) {
if (!op.data || op.data.length === 0) if (!op.data || op.data.length === 0)
continue; continue;

View File

@ -119,14 +119,12 @@ function randomWitness(redeem) {
if (redeem) if (redeem)
witness.push(redeem); witness.push(redeem);
witness.compile();
return witness; return witness;
} }
function randomInputScript(redeem) { function randomInputScript(redeem) {
const size = util.random(1, 100); const size = util.random(1, 100);
const script = new Script(); const script = Script.write();
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const len = util.random(0, 100); const len = util.random(0, 100);
@ -148,7 +146,7 @@ function isPushOnly(script) {
if (script.isPushOnly()) if (script.isPushOnly())
return true; return true;
for (const op of script.code) { for (const op of script.values()) {
if (op.value === Script.opcodes.NOP) if (op.value === Script.opcodes.NOP)
continue; continue;

View File

@ -35,7 +35,7 @@ function createGenesisBlock(options) {
hash: encoding.NULL_HASH, hash: encoding.NULL_HASH,
index: 0xffffffff index: 0xffffffff
}, },
script: Script() script: Script.write()
.pushInt(486604799) .pushInt(486604799)
.pushPush(Buffer.from([4])) .pushPush(Buffer.from([4]))
.pushData(flags) .pushData(flags)

View File

@ -568,10 +568,11 @@ describe('Chain', function() {
assert(output.script.isCommitment()); 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); commit.fill(0, 10);
output.script.setData(1, commit); script.setData(1, commit);
output.script.compile(); script.compile();
block.refresh(true); block.refresh(true);
block.merkleRoot = block.createMerkleRoot('hex'); block.merkleRoot = block.createMerkleRoot('hex');
@ -784,16 +785,16 @@ describe('Chain', function() {
it('should mine 111 multisig blocks', async () => { it('should mine 111 multisig blocks', async () => {
const flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW; const flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
const redeem = new Script(); const sw = Script.write();
redeem.pushInt(20); sw.pushInt(20);
for (let i = 0; i < 20; i++) for (let i = 0; i < 20; i++)
redeem.pushData(encoding.ZERO_KEY); sw.pushData(encoding.ZERO_KEY);
redeem.pushInt(20); sw.pushInt(20);
redeem.pushOp(opcodes.OP_CHECKMULTISIG); sw.pushOp(opcodes.OP_CHECKMULTISIG);
redeem.compile(); const redeem = sw.compile();
const script = Script.fromScripthash(redeem.hash160()); const script = Script.fromScripthash(redeem.hash160());
@ -826,17 +827,17 @@ describe('Chain', function() {
const end = chain.height - 100; const end = chain.height - 100;
const job = await cpu.createJob(); 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++) for (let i = 0; i < 20; i++)
script.pushData(encoding.ZERO_KEY); sw.pushData(encoding.ZERO_KEY);
script.pushInt(20); sw.pushInt(20);
script.pushOp(opcodes.OP_CHECKMULTISIG); sw.pushOp(opcodes.OP_CHECKMULTISIG);
script.compile(); const script = sw.compile();
for (let i = start; i <= end; i++) { for (let i = start; i <= end; i++) {
const block = await chain.db.getBlock(i); const block = await chain.db.getBlock(i);

View File

@ -127,8 +127,9 @@ describe('Mempool', function() {
// Fake signature // Fake signature
const input = fake.inputs[0]; const input = fake.inputs[0];
input.script.setData(0, encoding.ZERO_SIG); const sw = input.script.write();
input.script.compile(); sw.setData(0, encoding.ZERO_SIG);
sw.compile();
// balance: 11000 // balance: 11000
{ {

View File

@ -223,8 +223,9 @@ async function testP2SH(witness, nesting) {
assert(carol.account.change.getAddress().equals(change2)); assert(carol.account.change.getAddress().equals(change2));
const input = tx.inputs[0]; const input = tx.inputs[0];
input[vector].setData(2, encoding.ZERO_SIG); const stack = input[vector].toStack();
input[vector].compile(); stack.set(2, encoding.ZERO_SIG);
input[vector].fromStack(stack);
assert(!tx.verify(view, flags)); assert(!tx.verify(view, flags));
assert.strictEqual(tx.getFee(view), 10000); assert.strictEqual(tx.getFee(view), 10000);
@ -375,8 +376,9 @@ describe('Wallet', function() {
await alice.template(fake); await alice.template(fake);
// Fake signature // Fake signature
const input = fake.inputs[0]; const input = fake.inputs[0];
input.script.setData(0, encoding.ZERO_SIG); const script = input.script.write();
input.script.compile(); script.setData(0, encoding.ZERO_SIG);
script.compile();
// balance: 11000 // balance: 11000
// Fake TX should temporarily change output. // Fake TX should temporarily change output.