test: tx sigops tests. more chain tests.

This commit is contained in:
Christopher Jeffrey 2017-01-31 16:27:39 -08:00
parent 389adee8f0
commit cc16b48cf4
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 810 additions and 164 deletions

View File

@ -3,6 +3,7 @@
var assert = require('assert');
var BN = require('bn.js');
var consensus = require('../lib/protocol/consensus');
var encoding = require('../lib/utils/encoding');
var co = require('../lib/utils/co');
var Coin = require('../lib/primitives/coin');
var Script = require('../lib/script/script');
@ -10,29 +11,47 @@ var Chain = require('../lib/blockchain/chain');
var Miner = require('../lib/mining/miner');
var MTX = require('../lib/primitives/mtx');
var MemWallet = require('./util/memwallet');
var Network = require('../lib/protocol/network');
var Output = require('../lib/primitives/output');
var util = require('../lib/utils/util');
var opcodes = Script.opcodes;
describe('Chain', function() {
var chain = new Chain({ db: 'memory', network: 'regtest' });
var miner = new Miner({ chain: chain });
var wallet = new MemWallet({ network: 'regtest' });
var tip1, tip2, cb1, cb2, mineBlock;
var network = Network.get('regtest');
var chain = new Chain({ db: 'memory', network: network });
var miner = new Miner({ chain: chain, version: 4 });
var wallet = new MemWallet({ network: network });
var wwallet = new MemWallet({ network: network, witness: true });
var tip1, tip2, cb1, cb2, addBlock, mineCSV;
this.timeout(5000);
this.timeout(45000);
mineBlock = co(function* mineBlock(tip, tx) {
var attempt = yield miner.createBlock(tip);
addBlock = co(function* addBlock(attempt) {
var block = yield attempt.mineAsync();
try {
yield chain.add(block);
} catch (e) {
assert(e.type === 'VerifyError');
return e.reason;
}
return 'OK';
});
mineCSV = co(function* mineCSV(tx) {
var attempt = yield miner.createBlock();
var rtx;
if (!tx)
return yield attempt.mineAsync();
rtx = new MTX();
rtx.addTX(tx, 0);
rtx.addOutput({
script: [
Script.array(new BN(1)),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10000
});
rtx.addOutput(wallet.getReceive(), 25 * 1e8);
rtx.addOutput(wallet.getChange(), 5 * 1e8);
rtx.addTX(tx, 0);
rtx.setLocktime(chain.height);
@ -52,40 +71,57 @@ describe('Chain', function() {
});
it('should open chain and miner', co(function* () {
consensus.COINBASE_MATURITY = 0;
yield chain.open();
yield miner.open();
}));
it('should open wallet', co(function* () {
it('should add addrs to miner', co(function* () {
miner.addresses.length = 0;
miner.addAddress(wallet.getReceive());
}));
it('should mine a block', co(function* () {
var block = yield miner.mineBlock();
assert(block);
yield chain.add(block);
it('should mine a 200 blocks', co(function* () {
var i, block;
for (i = 0; i < 200; i++) {
block = yield miner.mineBlock();
assert(block);
yield chain.add(block);
}
assert.equal(chain.height, 200);
}));
it('should mine competing chains', co(function* () {
var i, block1, block2;
var i, mtx, at1, at2, blk1, blk2, hash1, hash2;
for (i = 0; i < 10; i++) {
block1 = yield mineBlock(tip1, cb1);
cb1 = block1.txs[0];
at1 = yield miner.createBlock(tip1);
at2 = yield miner.createBlock(tip2);
block2 = yield mineBlock(tip2, cb2);
cb2 = block2.txs[0];
mtx = yield wallet.create({
outputs: [{
address: wallet.getAddress(),
value: 10 * 1e8
}]
});
yield chain.add(block1);
at1.addTX(mtx.toTX(), mtx.view);
at2.addTX(mtx.toTX(), mtx.view);
yield chain.add(block2);
blk1 = yield at1.mineAsync();
blk2 = yield at2.mineAsync();
assert(chain.tip.hash === block1.hash('hex'));
hash1 = blk1.hash('hex');
hash2 = blk2.hash('hex');
tip1 = yield chain.db.getEntry(block1.hash('hex'));
tip2 = yield chain.db.getEntry(block2.hash('hex'));
yield chain.add(blk1);
yield chain.add(blk2);
assert(chain.tip.hash === hash1);
tip1 = yield chain.db.getEntry(hash1);
tip2 = yield chain.db.getEntry(hash2);
assert(tip1);
assert(tip2);
@ -97,22 +133,20 @@ describe('Chain', function() {
}));
it('should have correct chain value', function() {
assert.equal(chain.db.state.value, 55000000000);
assert.equal(chain.db.state.coin, 20);
assert.equal(chain.db.state.tx, 21);
assert.equal(chain.db.state.value, 897500000000);
assert.equal(chain.db.state.coin, 220);
assert.equal(chain.db.state.tx, 221);
});
it('should have correct balance', co(function* () {
assert.equal(wallet.balance, 550 * 1e8);
//assert.equal(wallet.unconfirmed, 550 * 1e8);
//assert.equal(wallet.confirmed, 550 * 1e8);
it('should have correct wallet balance', co(function* () {
assert.equal(wallet.balance, 897500000000);
}));
it('should handle a reorg', co(function* () {
var entry, block, forked;
var forked = false;
var entry, block;
// assert.equal(wallet.height, chain.height);
assert.equal(chain.height, 11);
assert.equal(chain.height, 210);
entry = yield chain.db.getEntry(tip2.hash);
assert(entry);
@ -121,7 +155,6 @@ describe('Chain', function() {
block = yield miner.mineBlock(entry);
assert(block);
forked = false;
chain.once('reorganize', function() {
forked = true;
});
@ -134,15 +167,13 @@ describe('Chain', function() {
}));
it('should have correct chain value', function() {
assert.equal(chain.db.state.value, 60000000000);
assert.equal(chain.db.state.coin, 21);
assert.equal(chain.db.state.tx, 22);
assert.equal(chain.db.state.value, 900000000000);
assert.equal(chain.db.state.coin, 221);
assert.equal(chain.db.state.tx, 222);
});
it('should have correct balance', co(function* () {
assert.equal(wallet.balance, 600 * 1e8);
// assert.equal(wallet.unconfirmed, 1100 * 1e8);
// assert.equal(wallet.confirmed, 600 * 1e8);
it('should have correct wallet balance', co(function* () {
assert.equal(wallet.balance, 900000000000);
}));
it('should check main chain', co(function* () {
@ -151,12 +182,14 @@ describe('Chain', function() {
}));
it('should mine a block after a reorg', co(function* () {
var block = yield mineBlock(null, cb2);
var entry, result;
var block = yield miner.mineBlock();
var hash, entry, result;
yield chain.add(block);
entry = yield chain.db.getEntry(block.hash('hex'));
hash = block.hash('hex');
entry = yield chain.db.getEntry(hash);
assert(entry);
assert(chain.tip.hash === entry.hash);
@ -165,9 +198,29 @@ describe('Chain', function() {
}));
it('should prevent double spend on new chain', co(function* () {
var block = yield mineBlock(null, cb2);
var tip = chain.tip;
var err;
var attempt = yield miner.createBlock();
var mtx, block, tip, err;
mtx = yield wallet.create({
outputs: [{
address: wallet.getAddress(),
value: 10 * 1e8
}]
});
attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync();
tip = yield chain.add(block);
attempt = yield miner.createBlock();
assert(mtx.outputs.length > 1);
mtx.outputs.pop();
attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync();
try {
yield chain.add(block);
@ -181,9 +234,21 @@ describe('Chain', function() {
}));
it('should fail to mine a block with coins on an alternate chain', co(function* () {
var block = yield mineBlock(null, cb1);
var tip = chain.tip;
var err;
var block = yield chain.db.getBlock(tip1.hash);
var cb = block.txs[0];
var mtx = new MTX();
var attempt, block, err;
mtx.addTX(cb, 0);
mtx.addOutput(wallet.getAddress(), 10 * 1e8);
wallet.sign(mtx);
attempt = yield miner.createBlock();
attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync();
try {
yield chain.add(block);
@ -197,43 +262,50 @@ describe('Chain', function() {
}));
it('should have correct chain value', function() {
assert.equal(chain.db.state.value, 65000000000);
assert.equal(chain.db.state.coin, 23);
assert.equal(chain.db.state.tx, 24);
assert.equal(chain.db.state.value, 905000000000);
assert.equal(chain.db.state.coin, 224);
assert.equal(chain.db.state.tx, 225);
});
it('should get coin', co(function* () {
var block, tx, output, coin;
var mtx, attempt, block, tx, output, coin;
block = yield mineBlock();
yield chain.add(block);
mtx = yield wallet.send({
outputs: [
{
address: wallet.getAddress(),
value: 1e8
},
{
address: wallet.getAddress(),
value: 1e8
},
{
address: wallet.getAddress(),
value: 1e8
}
]
});
block = yield mineBlock(null, block.txs[0]);
attempt = yield miner.createBlock();
attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync();
yield chain.add(block);
tx = block.txs[1];
output = Coin.fromTX(tx, 1, chain.height);
output = Coin.fromTX(tx, 2, chain.height);
coin = yield chain.db.getCoin(tx.hash('hex'), 1);
coin = yield chain.db.getCoin(tx.hash('hex'), 2);
assert.deepEqual(coin.toRaw(), output.toRaw());
}));
it('should get balance', co(function* () {
var txs;
assert.equal(wallet.balance, 750 * 1e8);
// assert.equal(wallet.unconfirmed, 1250 * 1e8);
// assert.equal(wallet.confirmed, 750 * 1e8);
assert(wallet.receiveDepth >= 7);
assert(wallet.changeDepth >= 6);
// assert.equal(wallet.height, chain.height);
// txs = wallet.getHistory();
// assert.equal(txs.length, 45);
assert.equal(wallet.txs, 26);
it('should have correct wallet balance', co(function* () {
assert.equal(wallet.balance, 907500000000);
assert.equal(wallet.receiveDepth, 15);
assert.equal(wallet.changeDepth, 14);
assert.equal(wallet.txs, 226);
}));
it('should get tips and remove chains', co(function* () {
@ -258,41 +330,46 @@ describe('Chain', function() {
return Promise.resolve();
});
assert.equal(total, 26);
assert.equal(total, 226);
}));
it('should activate csv', co(function* () {
var deployments = chain.network.deployments;
var deployments = network.deployments;
var i, block, prev, state, cache;
miner.options.version = -1;
assert.equal(chain.height, 214);
prev = yield chain.tip.getPrevious();
state = yield chain.getState(prev, deployments.csv);
assert(state === 0);
assert.equal(state, 1);
for (i = 0; i < 417; i++) {
block = yield miner.mineBlock();
yield chain.add(block);
switch (chain.height) {
case 144:
prev = yield chain.tip.getPrevious();
state = yield chain.getState(prev, deployments.csv);
assert(state === 1);
break;
case 288:
prev = yield chain.tip.getPrevious();
state = yield chain.getState(prev, deployments.csv);
assert(state === 2);
assert.equal(state, 1);
break;
case 432:
prev = yield chain.tip.getPrevious();
state = yield chain.getState(prev, deployments.csv);
assert(state === 3);
assert.equal(state, 2);
break;
case 576:
prev = yield chain.tip.getPrevious();
state = yield chain.getState(prev, deployments.csv);
assert.equal(state, 3);
break;
}
}
assert(chain.height === 432);
assert.equal(chain.height, 631);
assert(chain.state.hasCSV());
assert(chain.state.hasWitness());
cache = yield chain.db.getStateCache();
assert.deepEqual(cache, chain.db.stateCache);
@ -300,56 +377,38 @@ describe('Chain', function() {
assert(yield chain.db.verifyDeployments());
}));
var mineCSV = co(function* mineCSV(tx) {
var attempt = yield miner.createBlock();
var redeemer;
redeemer = new MTX();
redeemer.addOutput({
script: [
Script.array(new BN(1)),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
});
redeemer.addTX(tx, 0);
redeemer.setLocktime(chain.height);
wallet.sign(redeemer);
attempt.addTX(redeemer.toTX(), redeemer.view);
return yield attempt.mineAsync();
});
it('should have activated segwit', co(function* () {
var deployments = network.deployments;
var prev = yield chain.tip.getPrevious();
var state = yield chain.getState(prev, deployments.segwit);
assert.equal(state, 3);
}));
it('should test csv', co(function* () {
var tx = (yield chain.db.getBlock(chain.height)).txs[0];
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var block = yield mineCSV(tx);
var csv, attempt, redeemer;
var csv, attempt, rtx;
yield chain.add(block);
csv = block.txs[1];
redeemer = new MTX();
rtx = new MTX();
redeemer.addOutput({
rtx.addOutput({
script: [
Script.array(new BN(2)),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
value: 10000
});
redeemer.addTX(csv, 0);
redeemer.setSequence(0, 1, false);
rtx.addTX(csv, 0);
rtx.setSequence(0, 1, false);
attempt = yield miner.createBlock();
attempt.addTX(redeemer.toTX(), redeemer.view);
attempt.addTX(rtx.toTX(), rtx.view);
block = yield attempt.mineAsync();
@ -357,12 +416,11 @@ describe('Chain', function() {
}));
it('should fail csv with bad sequence', co(function* () {
var csv = (yield chain.db.getBlock(chain.height)).txs[1];
var block, attempt, redeemer, err;
var csv = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var rtx = new MTX();
var block, attempt, err;
redeemer = new MTX();
redeemer.addOutput({
rtx.addOutput({
script: [
Script.array(new BN(1)),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
@ -370,12 +428,12 @@ describe('Chain', function() {
value: 10 * 1e8
});
redeemer.addTX(csv, 0);
redeemer.setSequence(0, 1, false);
rtx.addTX(csv, 0);
rtx.setSequence(0, 1, false);
attempt = yield miner.createBlock();
attempt.addTX(redeemer.toTX(), redeemer.view);
attempt.addTX(rtx.toTX(), rtx.view);
block = yield attempt.mineAsync();
@ -396,30 +454,30 @@ describe('Chain', function() {
}));
it('should fail csv lock checks', co(function* () {
var tx = (yield chain.db.getBlock(chain.height)).txs[0];
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var block = yield mineCSV(tx);
var csv, attempt, redeemer, err;
var csv, attempt, rtx, err;
yield chain.add(block);
csv = block.txs[1];
redeemer = new MTX();
rtx = new MTX();
redeemer.addOutput({
rtx.addOutput({
script: [
Script.array(new BN(2)),
Script.opcodes.OP_CHECKSEQUENCEVERIFY
],
value: 10 * 1e8
value: 1 * 1e8
});
redeemer.addTX(csv, 0);
redeemer.setSequence(0, 2, false);
rtx.addTX(csv, 0);
rtx.setSequence(0, 2, false);
attempt = yield miner.createBlock();
attempt.addTX(redeemer.toTX(), redeemer.view);
attempt.addTX(rtx.toTX(), rtx.view);
block = yield attempt.mineAsync();
@ -434,11 +492,376 @@ describe('Chain', function() {
}));
it('should have correct wallet balance', co(function* () {
assert.equal(wallet.balance, 1289250000000);
assert.equal(wallet.balance, 1412499980000);
}));
it('should fail to connect bad bits', co(function* () {
var attempt = yield miner.createBlock();
attempt.block.bits = 553713663;
assert.equal(yield addBlock(attempt), 'bad-diffbits');
}));
it('should fail to connect bad MTP', co(function* () {
var mtp = yield chain.tip.getMedianTimeAsync();
var attempt = yield miner.createBlock();
attempt.block.ts = mtp - 1;
assert.equal(yield addBlock(attempt), 'time-too-old');
}));
it('should fail to connect bad time', co(function* () {
var mtp = yield chain.tip.getMedianTimeAsync();
var attempt = yield miner.createBlock();
var now = network.now() + 3 * 60 * 60;
attempt.block.ts = now;
assert.equal(yield addBlock(attempt), 'time-too-new');
}));
it('should fail to connect bad locktime', co(function* () {
var attempt = yield miner.createBlock();
var tx = yield wallet.send({ locktime: 100000 });
attempt.block.txs.push(tx.toTX());
attempt.refresh();
assert.equal(yield addBlock(attempt), 'bad-txns-nonfinal');
}));
it('should fail to connect bad cb height', co(function* () {
var bip34height = network.block.bip34height;
var attempt = yield miner.createBlock();
var tx = attempt.block.txs[0];
var input = tx.inputs[0];
input.script.set(0, new BN(10));
input.script.compile();
attempt.refresh();
try {
network.block.bip34height = 0;
assert.equal(yield addBlock(attempt), 'bad-cb-height');
} finally {
network.block.bip34height = bip34height;
}
}));
it('should fail to connect bad witness nonce size', co(function* () {
var attempt = yield miner.createBlock();
var tx = attempt.block.txs[0];
var input = tx.inputs[0];
input.witness.set(0, new Buffer(33));
input.witness.compile();
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-size');
}));
it('should fail to connect bad witness nonce', co(function* () {
var attempt = yield miner.createBlock();
var tx = attempt.block.txs[0];
var input = tx.inputs[0];
input.witness.set(0, encoding.ONE_HASH);
input.witness.compile();
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-match');
}));
it('should fail to connect bad witness commitment', co(function* () {
var attempt = yield miner.createBlock();
var tx = attempt.block.txs[0];
var output = tx.outputs[1];
var commit;
assert(output.script.isCommitment());
commit = util.copy(output.script.get(1));
commit.fill(0, 10);
output.script.set(1, commit);
output.script.compile();
attempt.updateMerkle();
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-match');
}));
it('should fail to connect unexpected witness', co(function* () {
var attempt = yield miner.createBlock();
var tx = attempt.block.txs[0];
var output = tx.outputs[1];
assert(output.script.isCommitment());
tx.outputs.pop();
attempt.updateMerkle();
assert.equal(yield addBlock(attempt), 'unexpected-witness');
}));
it('should add wit addrs to miner', co(function* () {
miner.addresses.length = 0;
miner.addAddress(wwallet.getReceive());
assert.equal(wwallet.getReceive().getType(), 'witnesspubkeyhash');
}));
it('should mine 2000 witness blocks', co(function* () {
var i, block;
for (i = 0; i < 2001; i++) {
block = yield miner.mineBlock();
assert(block);
yield chain.add(block);
}
assert.equal(chain.height, 2636);
}));
it('should mine a witness tx', co(function* () {
var block = yield chain.db.getBlock(chain.height - 2000);
var cb = block.txs[0];
var mtx = new MTX();
var attempt, block;
mtx.addTX(cb, 0);
mtx.addOutput(wwallet.getAddress(), 1000);
wwallet.sign(mtx);
attempt = yield miner.createBlock();
attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync();
yield chain.add(block);
}));
it('should mine fail to mine too much weight', co(function* () {
var start = chain.height - 2000;
var end = chain.height - 200;
var attempt = yield miner.createBlock();
var mtx = new MTX();
var i, j, block, cb, block;
for (i = start; i <= end; i++) {
block = yield chain.db.getBlock(i);
cb = block.txs[0];
mtx = new MTX();
mtx.addTX(cb, 0);
for (j = 0; j < 16; j++)
mtx.addOutput(wwallet.getAddress(), 1);
wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX());
}
attempt.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-weight');
}));
it('should mine fail to mine too much size', co(function* () {
var start = chain.height - 2000;
var end = chain.height - 200;
var attempt = yield miner.createBlock();
var mtx = new MTX();
var i, j, block, cb, block;
for (i = start; i <= end; i++) {
block = yield chain.db.getBlock(i);
cb = block.txs[0];
mtx = new MTX();
mtx.addTX(cb, 0);
for (j = 0; j < 20; j++)
mtx.addOutput(wwallet.getAddress(), 1);
wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX());
}
attempt.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-length');
}));
it('should mine a big block', co(function* () {
var start = chain.height - 2000;
var end = chain.height - 200;
var attempt = yield miner.createBlock();
var mtx = new MTX();
var i, j, block, cb, block;
for (i = start; i <= end; i++) {
block = yield chain.db.getBlock(i);
cb = block.txs[0];
mtx = new MTX();
mtx.addTX(cb, 0);
for (j = 0; j < 15; j++)
mtx.addOutput(wwallet.getAddress(), 1);
wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX());
}
attempt.refresh();
assert.equal(yield addBlock(attempt), 'OK');
}));
it('should fail to mine bad versions', co(function* () {
var i, attempt;
for (i = 0; i <= 3; i++) {
attempt = yield miner.createBlock();
attempt.block.version = i;
assert.equal(yield addBlock(attempt), 'bad-version');
}
}));
it('should fail to mine bad amount', co(function* () {
var attempt = yield miner.createBlock();
var i;
attempt.block.txs[0].outputs[0].value += 1;
attempt.updateMerkle();
assert.equal(yield addBlock(attempt), 'bad-cb-amount');
}));
it('should fail to mine premature cb spend', co(function* () {
var attempt = yield miner.createBlock();
var block = yield chain.db.getBlock(chain.height - 98);
var cb = block.txs[0];
var mtx = new MTX();
var i;
mtx.addTX(cb, 0);
mtx.addOutput(wwallet.getAddress(), 1);
wwallet.sign(mtx);
attempt.addTX(mtx.toTX(), mtx.view);
assert.equal(yield addBlock(attempt),
'bad-txns-premature-spend-of-coinbase');
}));
it('should fail to mine vout belowout', co(function* () {
var attempt = yield miner.createBlock();
var block = yield chain.db.getBlock(chain.height - 99);
var cb = block.txs[0];
var mtx = new MTX();
var i;
mtx.addTX(cb, 0);
mtx.addOutput(wwallet.getAddress(), 1e8);
wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX());
attempt.refresh();
assert.equal(yield addBlock(attempt),
'bad-txns-in-belowout');
}));
it('should fail to mine in out toolarge', co(function* () {
var attempt = yield miner.createBlock();
var block = yield chain.db.getBlock(chain.height - 99);
var cb = block.txs[0];
var mtx = new MTX();
var i;
mtx.addTX(cb, 0);
mtx.addOutput(wwallet.getAddress(), Math.floor(consensus.MAX_MONEY / 2));
mtx.addOutput(wwallet.getAddress(), Math.floor(consensus.MAX_MONEY / 2));
mtx.addOutput(wwallet.getAddress(), Math.floor(consensus.MAX_MONEY / 2));
wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX());
attempt.refresh();
assert.equal(yield addBlock(attempt),
'bad-txns-txouttotal-toolarge');
}));
it('should mine 111 multisig blocks', co(function* () {
var i, j, script, attempt, cb, output, val, block;
script = new Script();
script.push(new BN(20));
for (i = 0; i < 20; i++)
script.push(encoding.ZERO_KEY);
script.push(new BN(20));
script.push(opcodes.OP_CHECKMULTISIG);
script.compile();
script = Script.fromScripthash(script.hash160());
for (i = 0; i < 111; i++) {
attempt = yield miner.createBlock();
cb = attempt.block.txs[0];
val = cb.outputs[0].value;
cb.outputs[0].value = 0;
for (j = 0; j < Math.min(100, val); j++) {
output = new Output();
output.script = script.clone();
output.value = 1;
cb.outputs.push(output);
}
attempt.updateMerkle();
block = yield attempt.mineAsync();
yield chain.add(block);
}
assert.equal(chain.height, 2749);
}));
it('should mine fail to mine too many sigops', co(function* () {
var start = chain.height - 110;
var end = chain.height - 100;
var attempt = yield miner.createBlock();
var i, j, mtx, script, block, cb, block;
script = new Script();
script.push(new BN(20));
for (i = 0; i < 20; i++)
script.push(encoding.ZERO_KEY);
script.push(new BN(20));
script.push(opcodes.OP_CHECKMULTISIG);
script.compile();
for (i = start; i <= end; i++) {
block = yield chain.db.getBlock(i);
cb = block.txs[0];
if (cb.outputs.length === 2)
continue;
mtx = new MTX();
for (j = 2; j < cb.outputs.length; j++) {
mtx.addTX(cb, j);
mtx.inputs[j - 2].script = new Script([script.toRaw()]);
}
mtx.addOutput(wwallet.getAddress(), 1);
attempt.block.txs.push(mtx.toTX());
}
attempt.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-sigops');
}));
it('should cleanup', co(function* () {
consensus.COINBASE_MATURITY = 100;
yield miner.close();
yield chain.close();
}));

View File

@ -12,7 +12,11 @@ var Block = require('../lib/primitives/block');
var Coin = require('../lib/primitives/coin');
var Output = require('../lib/primitives/output');
var Script = require('../lib/script/script');
var Witness = require('../lib/script/witness');
var Input = require('../lib/primitives/input');
var CoinView = require('../lib/coins/coinview');
var KeyRing = require('../lib/primitives/keyring');
var opcodes = Script.opcodes;
var valid = require('./data/tx_valid.json');
var invalid = require('./data/tx_invalid.json');
@ -102,6 +106,43 @@ function parseTest(data) {
};
}
function buildTX(spk, ss, wit, view) {
var input, output, fund, spend, tx;
input = new Input();
output = new Output();
output.value = 1;
output.script = spk;
fund = new TX();
fund.version = 1;
fund.inputs.push(input);
fund.outputs.push(output);
fund.refresh();
input = new Input();
input.prevout.hash = fund.hash('hex');
input.prevout.index = 0;
input.script = ss;
input.witness = wit;
output = new Output();
output.value = 1;
spend = new TX();
spend.version = 1;
spend.inputs.push(input);
spend.outputs.push(output);
spend.refresh();
view.addTX(fund, 0);
return {
fund: fund,
spend: spend
};
}
describe('TX', function() {
var raw = '010000000125393c67cd4f581456dd0805fa8e9db3abdf90dbe1d4b53e28' +
'6490f35d22b6f2010000006b483045022100f4fa5ced20d2dbd2f905809d' +
@ -687,4 +728,165 @@ describe('TX', function() {
assert.equal(block.getReward(view, 0), -1);
});
});
it('should count sigops for multisig', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, spk, ss, wit;
spk = Script.fromMultisig(1, 2, [pub, pub]);
ss = new Script([
opcodes.OP_0,
opcodes.OP_0
]);
wit = new Witness();
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 0);
assert.equal(txs.fund.getSigopsCost(view, flags),
consensus.MAX_MULTISIG_PUBKEYS * consensus.WITNESS_SCALE_FACTOR);
});
it('should count sigops for p2sh multisig', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, redeem, spk, ss, wit;
redeem = Script.fromMultisig(1, 2, [pub, pub]);
spk = Script.fromScripthash(redeem.hash160());
ss = new Script([
opcodes.OP_0,
opcodes.OP_0,
redeem.toRaw()
]);
wit = new Witness();
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags),
2 * consensus.WITNESS_SCALE_FACTOR);
});
it('should count sigops for p2wpkh', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, p2pk, spk, ss, wit;
spk = Script.fromProgram(0, key.getKeyHash());
ss = new Script();
wit = new Witness([
new Buffer([0]),
new Buffer([0])
]);
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 1);
assert.equal(
txs.spend.getSigopsCost(view, flags & ~Script.flags.VERIFY_WITNESS),
0);
spk = Script.fromProgram(1, key.getKeyHash());
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 0);
spk = Script.fromProgram(0, key.getKeyHash());
txs = buildTX(spk, ss, wit, view);
txs.spend.inputs[0].prevout.hash = encoding.NULL_HASH;
txs.spend.inputs[0].prevout.index = 0xffffffff;
txs.spend.refresh();
assert.equal(txs.spend.getSigopsCost(view, flags), 0);
});
it('should count sigops for nested p2wpkh', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, p2pk, spk, ss, wit;
p2pk = Script.fromProgram(0, key.getKeyHash());
spk = Script.fromScripthash(p2pk.hash160());
ss = new Script([
p2pk.toRaw()
]);
wit = new Witness([
new Buffer([0]),
new Buffer([0])
]);
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 1);
});
it('should count sigops for p2wsh', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, ws, spk, ss, wit;
ws = Script.fromMultisig(1, 2, [pub, pub]);
spk = Script.fromProgram(0, ws.sha256());
ss = new Script();
wit = new Witness([
new Buffer([0]),
new Buffer([0]),
ws.toRaw()
]);
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 2);
assert.equal(
txs.spend.getSigopsCost(view, flags & ~Script.flags.VERIFY_WITNESS),
0);
});
it('should count sigops for nested p2wsh', function() {
var flags = Script.flags.VERIFY_WITNESS | Script.flags.VERIFY_P2SH;
var view = new CoinView();
var key = KeyRing.generate();
var pub = key.publicKey;
var txs, ws, p2sh, spk, ss, wit;
ws = Script.fromMultisig(1, 2, [pub, pub]);
p2sh = Script.fromProgram(0, ws.sha256());
spk = Script.fromScripthash(p2sh.hash160());
ss = new Script([
p2sh.toRaw()
]);
wit = new Witness([
new Buffer([0]),
new Buffer([0]),
ws.toRaw()
]);
txs = buildTX(spk, ss, wit, view);
assert.equal(txs.spend.getSigopsCost(view, flags), 2);
});
});

View File

@ -15,6 +15,7 @@ var Bloom = require('../../lib/utils/bloom');
var KeyRing = require('../../lib/primitives/keyring');
var Outpoint = require('../../lib/primitives/outpoint');
var Coin = require('../../lib/primitives/coin');
var co = require('../../lib/utils/co');
function MemWallet(options) {
if (!(this instanceof MemWallet))
@ -23,13 +24,15 @@ function MemWallet(options) {
this.network = Network.primary;
this.master = null;
this.key = null;
this.witness = false;
this.account = 0;
this.receiveDepth = 1;
this.changeDepth = 1;
this.receive = null;
this.change = null;
this.map = {};
this.coins = {};
this.undo = {};
this.spent = {};
this.paths = {};
this.balance = 0;
this.txs = 0;
@ -57,6 +60,11 @@ MemWallet.prototype.fromOptions = function fromOptions(options) {
this.key = options.key;
}
if (options.witness != null) {
assert(typeof options.witness === 'boolean');
this.witness = options.witness;
}
if (options.account != null) {
assert(typeof options.account === 'number');
this.account = options.account;
@ -128,10 +136,13 @@ MemWallet.prototype.derivePath = function derivePath(path) {
MemWallet.prototype.deriveKey = function deriveKey(branch, index) {
var key = this.master.deriveAccount44(this.account);
key = key.derive(branch).derive(index);
return new KeyRing({
key = new KeyRing({
network: this.network,
privateKey: key.privateKey
privateKey: key.privateKey,
witness: this.witness
});
key.witness = this.witness;
return key;
};
MemWallet.prototype.getKey = function getKey(hash) {
@ -150,7 +161,7 @@ MemWallet.prototype.getCoin = function getCoin(key) {
};
MemWallet.prototype.getUndo = function getUndo(key) {
return this.undo[key];
return this.spent[key];
};
MemWallet.prototype.addCoin = function addCoin(coin) {
@ -159,7 +170,7 @@ MemWallet.prototype.addCoin = function addCoin(coin) {
this.filter.add(op.toRaw());
delete this.undo[key];
delete this.spent[key];
this.coins[key] = coin;
this.balance += coin.value;
@ -171,7 +182,7 @@ MemWallet.prototype.removeCoin = function removeCoin(key) {
if (!coin)
return;
this.undo[key] = coin;
this.spent[key] = coin;
this.balance -= coin.value;
delete this.coins[key];
@ -228,12 +239,16 @@ MemWallet.prototype.removeBlock = function removeBlock(entry, txs) {
};
MemWallet.prototype.addTX = function addTX(tx, height) {
var hash = tx.hash('hex');
var result = false;
var i, op, path, addr, coin, input, output;
if (height == null)
height = -1;
if (this.map[hash])
return true;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
op = input.prevout.toKey();
@ -266,8 +281,10 @@ MemWallet.prototype.addTX = function addTX(tx, height) {
this.syncKey(path);
}
if (result)
if (result) {
this.txs++;
this.map[hash] = true;
}
return result;
};
@ -277,6 +294,9 @@ MemWallet.prototype.removeTX = function removeTX(tx, height) {
var result = false;
var i, op, coin, input, output;
if (!this.map[hash])
return false;
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
op = Outpoint(hash, i).toKey();
@ -306,6 +326,8 @@ MemWallet.prototype.removeTX = function removeTX(tx, height) {
if (result)
this.txs--;
delete this.map[hash];
return result;
};
@ -368,33 +390,32 @@ MemWallet.prototype.sign = function sign(mtx) {
mtx.sign(keys);
};
MemWallet.prototype.send = function send(options) {
var self = this;
MemWallet.prototype.create = co(function* create(options) {
var mtx = new MTX(options);
var tx;
this.fund(mtx, options).then(function() {
assert(mtx.getFee() <= MTX.Selector.MAX_FEE, 'TX exceeds MAX_FEE.');
yield this.fund(mtx, options);
mtx.sortMembers();
assert(mtx.getFee() <= MTX.Selector.MAX_FEE, 'TX exceeds MAX_FEE.');
if (options.locktime != null)
mtx.setLocktime(options.locktime);
mtx.sortMembers();
self.sign(mtx);
if (options.locktime != null)
mtx.setLocktime(options.locktime);
if (!mtx.isSigned())
throw new Error('Cannot sign tx.');
this.sign(mtx);
tx = mtx.toTX();
if (!mtx.isSigned())
throw new Error('Cannot sign tx.');
self.addTX(tx);
}).catch(function(err) {
throw err;
});
return mtx;
});
return tx;
};
MemWallet.prototype.send = co(function* send(options) {
var mtx = yield this.create(options);
this.addTX(mtx.toTX());
return mtx;
});
function Path(hash, branch, index) {
this.hash = hash;