Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Experimental] ci test: sync w bitcoin core #759

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ jobs:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
# Install latest bitcoind executable and add to PATH
- run: wget https://bitcoincore.org/bin/bitcoin-core-0.17.1/bitcoin-0.17.1-x86_64-linux-gnu.tar.gz
- run: tar -xvf bitcoin-0.17.1-x86_64-linux-gnu.tar.gz
- run: echo 'export PATH=./bitcoin-0.17.1/bin/:$PATH' >> $BASH_ENV
- run: source $BASH_ENV
# Install bcoin and testing utilities
- run: npm install
- run: npm install eslint istanbul@1.1.0-alpha.1 codecov
# Uncomment the following line to test locally with command:
# circleci local execute --job install
# - run: npm run test-ci
- save_cache:
paths:
- node_modules
Expand All @@ -32,6 +41,8 @@ jobs:
steps:
- attach_workspace:
at: .
- run: echo 'export PATH=./bitcoin-0.17.1/bin/:$PATH' >> $BASH_ENV
- run: source $BASH_ENV
- run:
name: Tests with coverage
command: npm run test-ci
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@
"lint-ci": "eslint $(cat .eslintfiles)",
"lint-file": "eslint",
"test": "bmocha --reporter spec test/*.js",
"bitcoind-test": "bmocha --reporter spec test/bitcoind-sync/*.js",
"test-browser": "NODE_BACKEND=js bmocha --reporter spec test/*.js",
"test-file": "bmocha --reporter spec",
"test-file-browser": "NODE_BACKEND=js bmocha --reporter spec",
"test-ci": "istanbul cover --report lcovonly node_modules/.bin/bmocha -- --reporter spec test/*-test.js"
"test-ci": "istanbul cover --report lcovonly node_modules/.bin/bmocha -- --reporter spec test/*-test.js test/bitcoind-sync/*-test.js"
},
"browser": {
"./lib/hd/nfkd": "./lib/hd/nfkd-compat.js",
Expand Down
3 changes: 3 additions & 0 deletions test/bitcoind-sync/data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Ignore everything in this dir except this file
*
!.gitignore
75 changes: 75 additions & 0 deletions test/bitcoind-sync/reorg-chain-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-env mocha */
/* eslint prefer-arrow-callback: "off" */

'use strict';

const assert = require('../util/assert');
const NodeFactory = require('../util/nodefactory');

const nodeFactory = new NodeFactory();

describe('Sync bcoin and Core through a reorg', function () {
this.timeout(3 * 60 * 60 * 1000);

it('should keep bcoin in sync with core', async () => {
const core = nodeFactory.createCore();
const bcoin = await nodeFactory.createBcoin();

// Core generates 100 blocks
await new Promise(r => setTimeout(r, 2000));
const blocks = await core.rpc(
'generatetoaddress',
[100, 'mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8']
);

// bcoin connects to Core and syncs
await new Promise(r => setTimeout(r, 5000));
await bcoin.rpc(
'addnode',
[`127.0.0.1:${core.ports.port}`, 'add']
);

// Core prompts a reorg by invalidating an old block
// then building a new chain on top of its parent.
// Mine new blocks to different address.
await new Promise(r => setTimeout(r, 5000));
await core.rpc(
'invalidateblock',
[blocks[blocks.length - 5]]
);
await core.rpc(
'generatetoaddress',
[10, 'mrkZVNDhZufJfCSw4nbXAgSUPqroNRPYto']
);

// Output
await new Promise(r => setTimeout(r, 10000));
const coreinfo = await core.rpc(
'getblockchaininfo',
[]
);
const bcoininfo = await bcoin.rpc(
'getblockchaininfo',
[]
);

console.log('Core: ', coreinfo);
console.log('bcoin: ', bcoininfo);

assert.strictEqual(coreinfo.blocks, bcoininfo.blocks);
assert.strictEqual(coreinfo.headers, bcoininfo.headers);
assert.strictEqual(coreinfo.bestblockhash, bcoininfo.bestblockhash);

// Close
await new Promise(r => setTimeout(r, 5000));
await core.rpc(
'stop',
[]
);
await bcoin.rpc(
'stop',
[]
);
await new Promise(r => setTimeout(r, 5000));
});
});
133 changes: 133 additions & 0 deletions test/util/nodefactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use strict';

const FullNode = require('../../lib/node/fullnode');
const format = require('blgr/lib/format');

const path = require('path');
const bcurl = require('bcurl');
const cp = require('child_process');

class NodeFactory {
constructor() {
this.count = 0;
}

createDir(index) {
const dataDir = path.join(
__dirname,
`../bitcoind-sync/data/datadir_${index}`
);

cp.spawnSync('rm', ['-rf', dataDir]);
cp.spawnSync('mkdir', [dataDir]);

return dataDir;
}

getPorts(index) {
return {
port: 10000 + index,
rpcport: 20000 + index
};
}

initNode() {
this.count += 1;
const index = this.count;
const dataDir = this.createDir(index);
const ports = this.getPorts(index);
const client = bcurl.client({
password: 'x',
port: ports.rpcport
});

const rpc = function (cmd, args) {
return client.execute('', cmd, args);
};

return {
index,
dataDir,
ports,
rpc
};
}

async createBcoin() {
const {index, dataDir, ports, rpc} = this.initNode();

const node = new FullNode({
network: 'regtest',
workers: true,
logLevel: 'spam',
listen: true,
prefix: `${dataDir}`,
memory: false,
port: ports.port,
httpPort: ports.rpcport,
maxOutbound: 1
});

const printStdout = this.printStdout;
node.logger.logger.writeConsole = function(level, module, args) {
printStdout(index, '[' + module + '] ' + format(args, false));
};

await node.ensure();
await node.open();
await node.connect();
node.startSync();

return {index, dataDir, ports, rpc, node};
}

createCore() {
const {index, dataDir, ports, rpc} = this.initNode();

this.spawnSyncPrint(
index,
'bitcoind',
[
`-datadir=${dataDir}`,
'-regtest',
'-rpcpassword=x',
`-rpcport=${ports.rpcport}`,
`-port=${ports.port}`,
'-debug=net'
],
{stdio: 'pipe'}
);

return {index, dataDir, ports, rpc};
}

spawnSyncPrint(id, cmd, arg, opt) {
const proc = cp.spawn(cmd, arg, opt);

proc.stdout.on('data', (data) => {
this.printStdout(id, data);
});

proc.stderr.on('data', (data) => {
this.printStdout(id, data);
});

proc.on('close', (code) => {
return(code);
});

proc.on('error', (data) => {
this.printStdout(id, data);
});
}

printStdout(index, data) {
const header = `${index}: `;
let str = data.toString();
str = str.replace(/\n/g, `\n${header}`);
str = header + str;
console.log(`\x1b[${31 + index}m%s\x1b[0m`, str);
}
}

module.exports = NodeFactory;