Basic bitcoind/romanz-electrum support to sync the mempool and blocks.
This commit is contained in:
16
backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts
Normal file
16
backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { MempoolInfo, Transaction, Block, MempoolEntries, MempoolEntry } from '../../interfaces';
|
||||
|
||||
export interface AbstractBitcoinApi {
|
||||
getMempoolInfo(): Promise<MempoolInfo>;
|
||||
getRawMempool(): Promise<Transaction['txid'][]>;
|
||||
getRawTransaction(txId: string): Promise<Transaction>;
|
||||
getBlockHeightTip(): Promise<number>;
|
||||
getTxIdsForBlock(hash: string): Promise<string[]>;
|
||||
getBlockHash(height: number): Promise<string>;
|
||||
getBlock(hash: string): Promise<Block>;
|
||||
getMempoolEntry(txid: string): Promise<MempoolEntry>;
|
||||
|
||||
// Custom
|
||||
getRawMempoolVerbose(): Promise<MempoolEntries>;
|
||||
getRawTransactionBitcond(txId: string): Promise<Transaction>;
|
||||
}
|
||||
19
backend/src/api/bitcoin/bitcoin-api-factory.ts
Normal file
19
backend/src/api/bitcoin/bitcoin-api-factory.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import config from '../../config';
|
||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||
import BitcoindElectrsApi from './bitcoind-electrs-api';
|
||||
import BitcoindApi from './bitcoind-api';
|
||||
import ElectrsApi from './electrs-api';
|
||||
|
||||
function bitcoinApiFactory(): AbstractBitcoinApi {
|
||||
switch (config.MEMPOOL.BACKEND) {
|
||||
case 'electrs':
|
||||
return new ElectrsApi();
|
||||
case 'bitcoind-electrs':
|
||||
return new BitcoindElectrsApi();
|
||||
case 'bitcoind':
|
||||
default:
|
||||
return new BitcoindApi();
|
||||
}
|
||||
}
|
||||
|
||||
export default bitcoinApiFactory();
|
||||
83
backend/src/api/bitcoin/bitcoind-api.ts
Normal file
83
backend/src/api/bitcoin/bitcoind-api.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import config from '../../config';
|
||||
import { Transaction, Block, MempoolInfo, RpcBlock, MempoolEntries, MempoolEntry } from '../../interfaces';
|
||||
import * as bitcoin from '@mempool/bitcoin';
|
||||
|
||||
class BitcoindApi {
|
||||
bitcoindClient: any;
|
||||
|
||||
constructor() {
|
||||
this.bitcoindClient = new bitcoin.Client({
|
||||
host: config.BITCOIND.HOST,
|
||||
port: config.BITCOIND.PORT,
|
||||
user: config.BITCOIND.USERNAME,
|
||||
pass: config.BITCOIND.PASSWORD,
|
||||
timeout: 60000,
|
||||
});
|
||||
}
|
||||
|
||||
getMempoolInfo(): Promise<MempoolInfo> {
|
||||
return this.bitcoindClient.getMempoolInfo();
|
||||
}
|
||||
|
||||
getRawMempool(): Promise<Transaction['txid'][]> {
|
||||
return this.bitcoindClient.getRawMemPool();
|
||||
}
|
||||
|
||||
getRawMempoolVerbose(): Promise<MempoolEntries> {
|
||||
return this.bitcoindClient.getRawMemPool(true);
|
||||
}
|
||||
|
||||
getMempoolEntry(txid: string): Promise<MempoolEntry> {
|
||||
return this.bitcoindClient.getMempoolEntry(txid,);
|
||||
}
|
||||
|
||||
getRawTransaction(txId: string): Promise<Transaction> {
|
||||
return this.bitcoindClient.getRawTransaction(txId, true)
|
||||
.then((transaction: Transaction) => {
|
||||
transaction.vout.forEach((vout) => vout.value = vout.value * 100000000);
|
||||
return transaction;
|
||||
});
|
||||
}
|
||||
|
||||
getBlockHeightTip(): Promise<number> {
|
||||
return this.bitcoindClient.getChainTips()
|
||||
.then((result) => result[0].height);
|
||||
}
|
||||
|
||||
getTxIdsForBlock(hash: string): Promise<string[]> {
|
||||
return this.bitcoindClient.getBlock(hash, 1)
|
||||
.then((rpcBlock: RpcBlock) => {
|
||||
return rpcBlock.tx;
|
||||
});
|
||||
}
|
||||
|
||||
getBlockHash(height: number): Promise<string> {
|
||||
return this.bitcoindClient.getBlockHash(height)
|
||||
}
|
||||
|
||||
getBlock(hash: string): Promise<Block> {
|
||||
return this.bitcoindClient.getBlock(hash)
|
||||
.then((rpcBlock: RpcBlock) => {
|
||||
return {
|
||||
id: rpcBlock.hash,
|
||||
height: rpcBlock.height,
|
||||
version: rpcBlock.version,
|
||||
timestamp: rpcBlock.time,
|
||||
bits: rpcBlock.bits,
|
||||
nonce: rpcBlock.nonce,
|
||||
difficulty: rpcBlock.difficulty,
|
||||
merkle_root: rpcBlock.merkleroot,
|
||||
tx_count: rpcBlock.nTx,
|
||||
size: rpcBlock.size,
|
||||
weight: rpcBlock.weight,
|
||||
previousblockhash: rpcBlock.previousblockhash,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getRawTransactionBitcond(txId: string): Promise<Transaction> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export default BitcoindApi;
|
||||
108
backend/src/api/bitcoin/bitcoind-electrs-api.ts
Normal file
108
backend/src/api/bitcoin/bitcoind-electrs-api.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import config from '../../config';
|
||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||
import { Transaction, Block, MempoolInfo, RpcBlock, MempoolEntries, MempoolEntry } from '../../interfaces';
|
||||
import * as bitcoin from '@mempool/bitcoin';
|
||||
import * as ElectrumClient from '@codewarriorr/electrum-client-js';
|
||||
import logger from '../../logger';
|
||||
|
||||
class BitcoindElectrsApi implements AbstractBitcoinApi {
|
||||
bitcoindClient: any;
|
||||
electrumClient: any;
|
||||
|
||||
constructor() {
|
||||
this.bitcoindClient = new bitcoin.Client({
|
||||
host: config.BITCOIND.HOST,
|
||||
port: config.BITCOIND.PORT,
|
||||
user: config.BITCOIND.USERNAME,
|
||||
pass: config.BITCOIND.PASSWORD,
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
this.electrumClient = new ElectrumClient(
|
||||
config.ELECTRS.HOST,
|
||||
config.ELECTRS.PORT,
|
||||
'ssl'
|
||||
);
|
||||
|
||||
this.electrumClient.connect(
|
||||
'electrum-client-js',
|
||||
'1.4'
|
||||
)
|
||||
}
|
||||
|
||||
getMempoolInfo(): Promise<MempoolInfo> {
|
||||
return this.bitcoindClient.getMempoolInfo();
|
||||
}
|
||||
|
||||
getRawMempool(): Promise<Transaction['txid'][]> {
|
||||
return this.bitcoindClient.getRawMemPool();
|
||||
}
|
||||
|
||||
getRawMempoolVerbose(): Promise<MempoolEntries> {
|
||||
return this.bitcoindClient.getRawMemPool(true);
|
||||
}
|
||||
|
||||
getMempoolEntry(txid: string): Promise<MempoolEntry> {
|
||||
return this.bitcoindClient.getMempoolEntry(txid,);
|
||||
}
|
||||
|
||||
async getRawTransaction(txId: string): Promise<Transaction> {
|
||||
try {
|
||||
const transaction: Transaction = await this.electrumClient.blockchain_transaction_get(txId, true);
|
||||
if (!transaction) {
|
||||
throw new Error('not found');
|
||||
}
|
||||
transaction.vout.forEach((vout) => vout.value = vout.value * 100000000);
|
||||
return transaction;
|
||||
} catch (e) {
|
||||
logger.debug('getRawTransaction error: ' + (e.message || e));
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
getRawTransactionBitcond(txId: string): Promise<Transaction> {
|
||||
return this.bitcoindClient.getRawTransaction(txId, true)
|
||||
.then((transaction: Transaction) => {
|
||||
transaction.vout.forEach((vout) => vout.value = vout.value * 100000000);
|
||||
return transaction;
|
||||
});
|
||||
}
|
||||
|
||||
getBlockHeightTip(): Promise<number> {
|
||||
return this.bitcoindClient.getChainTips()
|
||||
.then((result) => result[0].height);
|
||||
}
|
||||
|
||||
getTxIdsForBlock(hash: string): Promise<string[]> {
|
||||
return this.bitcoindClient.getBlock(hash, 1)
|
||||
.then((rpcBlock: RpcBlock) => {
|
||||
return rpcBlock.tx;
|
||||
});
|
||||
}
|
||||
|
||||
getBlockHash(height: number): Promise<string> {
|
||||
return this.bitcoindClient.getBlockHash(height)
|
||||
}
|
||||
|
||||
getBlock(hash: string): Promise<Block> {
|
||||
return this.bitcoindClient.getBlock(hash)
|
||||
.then((rpcBlock: RpcBlock) => {
|
||||
return {
|
||||
id: rpcBlock.hash,
|
||||
height: rpcBlock.height,
|
||||
version: rpcBlock.version,
|
||||
timestamp: rpcBlock.time,
|
||||
bits: rpcBlock.bits,
|
||||
nonce: rpcBlock.nonce,
|
||||
difficulty: rpcBlock.difficulty,
|
||||
merkle_root: rpcBlock.merkleroot,
|
||||
tx_count: rpcBlock.nTx,
|
||||
size: rpcBlock.size,
|
||||
weight: rpcBlock.weight,
|
||||
previousblockhash: rpcBlock.previousblockhash,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default BitcoindElectrsApi;
|
||||
@@ -1,8 +1,9 @@
|
||||
import config from '../../config';
|
||||
import { Transaction, Block, MempoolInfo } from '../../interfaces';
|
||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||
import { Transaction, Block, MempoolInfo, MempoolEntry, MempoolEntries } from '../../interfaces';
|
||||
import axios from 'axios';
|
||||
|
||||
class ElectrsApi {
|
||||
class ElectrsApi implements AbstractBitcoinApi {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
@@ -42,15 +43,22 @@ class ElectrsApi {
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
getBlocksFromHeight(height: number): Promise<string> {
|
||||
return axios.get<string>(config.ELECTRS.REST_API_URL + '/blocks/' + height)
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
getBlock(hash: string): Promise<Block> {
|
||||
return axios.get<Block>(config.ELECTRS.REST_API_URL + '/block/' + hash)
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
getRawMempoolVerbose(): Promise<MempoolEntries> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getMempoolEntry(): Promise<MempoolEntry> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRawTransactionBitcond(txId: string): Promise<Transaction> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export default new ElectrsApi();
|
||||
export default ElectrsApi;
|
||||
|
||||
Reference in New Issue
Block a user