Browse Source

feat(bitcoind): connect bitcoin peers after they all start up

master
jamaljsr 5 years ago
parent
commit
0ef41fe468
  1. 3
      TODO.md
  2. 43
      src/lib/bitcoin/bitcoindService.ts
  3. 4
      src/lib/docker/composeFile.ts
  4. 2
      src/lib/docker/nodeTemplates.ts
  5. 6
      src/store/models/bitcoind.ts
  6. 7
      src/store/models/network.ts
  7. 9
      src/types/index.ts
  8. 1
      src/utils/tests/renderWithProviders.tsx

3
TODO.md

@ -1,10 +1,9 @@
# TODO List
- connect bitcoin peers after they all start up
- fix broken peer link when a middle btc node is removed
- investigate c-lightning pending channel after opening
- drag to link LN node to new backend
- prompt for the new backend
- refactor BitcoindService to use node as args instead of port
- display compatibility warnings with LND + bitcoind
Small Stuff

43
src/lib/bitcoin/bitcoindService.ts

@ -11,22 +11,28 @@ import {
} from 'utils/constants';
class BitcoindService implements BitcoindLibrary {
creatClient(port = 18433) {
creatClient(node: BitcoinNode) {
return new BitcoinCore({
port: `${port}`,
port: `${node.ports.rpc}`,
username: bitcoinCredentials.user,
password: bitcoinCredentials.pass,
logger: logger as any,
});
}
async getBlockchainInfo(port?: number) {
await this.creatClient(port).listUnspent();
return await this.creatClient(port).getBlockchainInfo();
async getBlockchainInfo(node: BitcoinNode) {
return await this.creatClient(node).getBlockchainInfo();
}
async getWalletInfo(port?: number) {
return await this.creatClient(port).getWalletInfo();
async getWalletInfo(node: BitcoinNode) {
return await this.creatClient(node).getWalletInfo();
}
async connectPeers(node: BitcoinNode) {
const client = this.creatClient(node);
for (const peer of node.peers) {
await client.addNode(peer, 'add');
}
}
/**
@ -36,22 +42,21 @@ class BitcoindService implements BitcoindLibrary {
* @param amount the amount denominated in bitcoin
*/
async sendFunds(node: BitcoinNode, toAddress: string, amount: number) {
const port = node.ports.rpc;
const client = this.creatClient(port);
const client = this.creatClient(node);
const { blocks } = await this.getBlockchainInfo(port);
const { blocks } = await this.getBlockchainInfo(node);
const { balance } = await client.getWalletInfo();
// if the bitcoin node doesn't have enough coins then mine more
if (balance <= amount) {
await this.mineUntilMaturity(port);
await this.mine(this.getBlocksToMine(blocks, amount - balance), port);
await this.mineUntilMaturity(node);
await this.mine(this.getBlocksToMine(blocks, amount - balance), node);
}
const txid = await client.sendToAddress(toAddress, amount);
return txid;
}
async mine(numBlocks: number, port?: number) {
const client = this.creatClient(port);
async mine(numBlocks: number, node: BitcoinNode) {
const client = this.creatClient(node);
const addr = await client.getNewAddress();
return await client.generateToAddress(numBlocks, addr);
}
@ -61,13 +66,13 @@ class BitcoindService implements BitcoindLibrary {
* response is received or it times out
*/
async waitUntilOnline(
port?: number,
node: BitcoinNode,
interval = 3 * 1000, // check every 3 seconds
timeout = 30 * 1000, // timeout after 30 seconds
): Promise<void> {
return waitFor(
async () => {
await this.getBlockchainInfo(port);
await this.getBlockchainInfo(node);
},
interval,
timeout,
@ -78,15 +83,15 @@ class BitcoindService implements BitcoindLibrary {
* will mine up to block #100 if necessary in order for
* fresh coins to be spendable
*/
private async mineUntilMaturity(port?: number) {
private async mineUntilMaturity(node: BitcoinNode) {
// get all of this node's utxos
const utxos = await this.creatClient(port).listTransactions();
const utxos = await this.creatClient(node).listTransactions();
// determine the highest # of confirmations of all utxos. this is
// the utxo we'd like to spend from
const confs = Math.max(0, ...utxos.map(u => u.confirmations));
const neededConfs = Math.max(0, COINBASE_MATURITY_DELAY - confs);
if (neededConfs > 0) {
await this.mine(neededConfs, port);
await this.mine(neededConfs, node);
// this may mines up to 100 blocks at once, so add a couple second
// delay to allow the other nodes to process all of the new blocks
await delay(2 * 1000);

4
src/lib/docker/composeFile.ts

@ -33,9 +33,9 @@ class ComposeFile {
}
addBitcoind(node: BitcoinNode) {
const { name, version, ports, peers } = node;
const { name, version, ports } = node;
const container = getContainerName(node);
this.content.services[name] = bitcoind(name, container, version, ports.rpc, peers);
this.content.services[name] = bitcoind(name, container, version, ports.rpc);
}
addLnd(node: LndNode, backend: CommonNode) {

2
src/lib/docker/nodeTemplates.ts

@ -11,7 +11,6 @@ export const bitcoind = (
container: string,
version: string,
rpcPort: number,
peers: string[],
): ComposeService => ({
image: `polarlightning/bitcoind:${version}`,
container_name: container,
@ -26,7 +25,6 @@ export const bitcoind = (
-server=1
-regtest=1
-rpcauth=${bitcoinCredentials.user}:${bitcoinCredentials.rpcauth}
${peers.map(p => `-addnode=${p}`).join(' ')}
-debug=0
-zmqpubrawblock=tcp://0.0.0.0:28334
-zmqpubrawtx=tcp://0.0.0.0:28335

6
src/store/models/bitcoind.ts

@ -49,15 +49,15 @@ const bitcoindModel: BitcoindModel = {
state.nodes[node.name].walletInfo = walletInfo;
}),
getInfo: thunk(async (actions, node, { injections }) => {
const chainInfo = await injections.bitcoindService.getBlockchainInfo(node.ports.rpc);
const chainInfo = await injections.bitcoindService.getBlockchainInfo(node);
actions.setChainInfo({ node, chainInfo });
const walletInfo = await injections.bitcoindService.getWalletInfo(node.ports.rpc);
const walletInfo = await injections.bitcoindService.getWalletInfo(node);
actions.setWalletinfo({ node, walletInfo });
}),
mine: thunk(async (actions, { blocks, node }, { injections, getStoreState }) => {
if (blocks < 0) throw new Error(l('mineError'));
await injections.bitcoindService.mine(blocks, node.ports.rpc);
await injections.bitcoindService.mine(blocks, node);
// add a small delay to allow the block to propagate to all nodes
await delay(500);
// update info for all bitcoin nodes

7
src/store/models/network.ts

@ -325,10 +325,11 @@ const networkModel: NetworkModel = {
for (const node of network.nodes.bitcoin) {
// use .then() to continue execution while the promises are waiting to complete
injections.bitcoindService
.waitUntilOnline(node.ports.rpc)
.then(() => {
.waitUntilOnline(node)
.then(async () => {
await injections.bitcoindService.connectPeers(node);
actions.setStatus({ id, status: Status.Started, only: node.name });
return getStoreActions().bitcoind.getInfo(node);
await getStoreActions().bitcoind.getInfo(node);
})
.catch(error =>
actions.setStatus({ id, status: Status.Error, only: node.name, error }),

9
src/types/index.ts

@ -39,10 +39,11 @@ export interface DockerLibrary {
}
export interface BitcoindLibrary {
waitUntilOnline: (port?: number) => Promise<void>;
getBlockchainInfo: (port?: number) => Promise<ChainInfo>;
getWalletInfo: (port?: number) => Promise<WalletInfo>;
mine: (numBlocks: number, port?: number) => Promise<string[]>;
waitUntilOnline: (node: BitcoinNode) => Promise<void>;
getBlockchainInfo: (node: BitcoinNode) => Promise<ChainInfo>;
getWalletInfo: (node: BitcoinNode) => Promise<WalletInfo>;
connectPeers: (node: BitcoinNode) => Promise<void>;
mine: (numBlocks: number, node: BitcoinNode) => Promise<string[]>;
sendFunds: (node: BitcoinNode, addr: string, amount: number) => Promise<string>;
}

1
src/utils/tests/renderWithProviders.tsx

@ -37,6 +37,7 @@ export const injections: StoreInjections = {
waitUntilOnline: jest.fn(),
getBlockchainInfo: jest.fn(),
getWalletInfo: jest.fn(),
connectPeers: jest.fn(),
sendFunds: jest.fn(),
mine: jest.fn(),
},

Loading…
Cancel
Save