Adding optional Blockstream esplora backend support.

This commit is contained in:
Simon Lindh
2019-10-22 17:09:07 +08:00
parent 143949bab4
commit 136b6eb76e
12 changed files with 1361 additions and 33 deletions

View File

@@ -0,0 +1,10 @@
import { IMempoolInfo, ITransaction, IBlock } from '../../interfaces';
export interface AbstractBitcoinApi {
getMempoolInfo(): Promise<IMempoolInfo>;
getRawMempool(): Promise<ITransaction['txid'][]>;
getRawTransaction(txId: string): Promise<ITransaction>;
getBlockCount(): Promise<number>;
getBlock(hash: string): Promise<IBlock>;
getBlockHash(height: number): Promise<string>;
}

View File

@@ -0,0 +1,16 @@
const config = require('../../../mempool-config.json');
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
import BitcoindApi from './bitcoind-api';
import EsploraApi from './esplora-api';
function factory(): AbstractBitcoinApi {
switch (config.BACKEND_API) {
case 'esplora':
return new EsploraApi();
case 'bitcoind':
default:
return new BitcoindApi();
}
}
export default factory();

View File

@@ -0,0 +1,85 @@
const config = require('../../../mempool-config.json');
import * as bitcoin from 'bitcoin';
import { ITransaction, IMempoolInfo, IBlock } from '../../interfaces';
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
class BitcoindApi implements AbstractBitcoinApi {
client: any;
constructor() {
this.client = new bitcoin.Client({
host: config.BITCOIN_NODE_HOST,
port: config.BITCOIN_NODE_PORT,
user: config.BITCOIN_NODE_USER,
pass: config.BITCOIN_NODE_PASS,
});
}
getMempoolInfo(): Promise<IMempoolInfo> {
return new Promise((resolve, reject) => {
this.client.getMempoolInfo((err: Error, mempoolInfo: any) => {
if (err) {
return reject(err);
}
resolve(mempoolInfo);
});
});
}
getRawMempool(): Promise<ITransaction['txid'][]> {
return new Promise((resolve, reject) => {
this.client.getRawMemPool((err: Error, transactions: ITransaction['txid'][]) => {
if (err) {
return reject(err);
}
resolve(transactions);
});
});
}
getRawTransaction(txId: string): Promise<ITransaction> {
return new Promise((resolve, reject) => {
this.client.getRawTransaction(txId, true, (err: Error, txData: ITransaction) => {
if (err) {
return reject(err);
}
resolve(txData);
});
});
}
getBlockCount(): Promise<number> {
return new Promise((resolve, reject) => {
this.client.getBlockCount((err: Error, response: number) => {
if (err) {
return reject(err);
}
resolve(response);
});
});
}
getBlock(hash: string, verbosity: 1 | 2 = 1): Promise<IBlock> {
return new Promise((resolve, reject) => {
this.client.getBlock(hash, verbosity, (err: Error, block: IBlock) => {
if (err) {
return reject(err);
}
resolve(block);
});
});
}
getBlockHash(height: number): Promise<string> {
return new Promise((resolve, reject) => {
this.client.getBlockHash(height, (err: Error, response: string) => {
if (err) {
return reject(err);
}
resolve(response);
});
});
}
}
export default BitcoindApi;

View File

@@ -0,0 +1,99 @@
const config = require('../../../mempool-config.json');
import { ITransaction, IMempoolInfo, IBlock } from '../../interfaces';
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
import axios, { AxiosResponse } from 'axios';
class EsploraApi implements AbstractBitcoinApi {
client: any;
constructor() {
this.client = axios.create({
baseURL: config.ESPLORA_API_URL,
timeout: 15000,
});
}
getMempoolInfo(): Promise<IMempoolInfo> {
return new Promise(async (resolve, reject) => {
try {
const response: AxiosResponse = await this.client.get('/mempool');
resolve({
size: response.data.count,
bytes: response.data.vsize,
});
} catch (error) {
reject(error);
}
});
}
getRawMempool(): Promise<ITransaction['txid'][]> {
return new Promise(async (resolve, reject) => {
try {
const response: AxiosResponse = await this.client.get('/mempool/txids');
resolve(response.data);
} catch (error) {
reject(error);
}
});
}
getRawTransaction(txId: string): Promise<ITransaction> {
return new Promise(async (resolve, reject) => {
try {
const response: AxiosResponse = await this.client.get('/tx/' + txId);
response.data.vsize = response.data.size;
response.data.size = response.data.weight;
response.data.fee = response.data.fee / 100000000;
resolve(response.data);
} catch (error) {
reject(error);
}
});
}
getBlockCount(): Promise<number> {
return new Promise(async (resolve, reject) => {
try {
const response: AxiosResponse = await this.client.get('/blocks/tip/height');
resolve(response.data);
} catch (error) {
reject(error);
}
});
}
getBlock(hash: string): Promise<IBlock> {
return new Promise(async (resolve, reject) => {
try {
const blockInfo: AxiosResponse = await this.client.get('/block/' + hash);
const blockTxs: AxiosResponse = await this.client.get('/block/' + hash + '/txids');
const block = blockInfo.data;
block.hash = hash;
block.nTx = block.tx_count;
block.time = block.timestamp;
block.tx = blockTxs.data;
resolve(block);
} catch (error) {
reject(error);
}
});
}
getBlockHash(height: number): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
const response: AxiosResponse = await this.client.get('/block-height/' + height);
resolve(response.data);
} catch (error) {
reject(error);
}
});
}
}
export default EsploraApi;