bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
255 lines (225 loc) • 6.52 kB
text/typescript
import request = require('request');
import { LoggifyClass } from './decorators/Loggify';
type CallbackType = (err: any, data?: any) => any;
export class RPC {
constructor(
private username: string,
private password: string,
private host: string,
private port: number | string
) {}
public callMethod(method: string, params: any, callback: CallbackType, walletName?: string) {
request(
{
method: 'POST',
url: `http://${this.username}:${this.password}@${this.host}:${this.port}${walletName ? '/wallet/' + walletName : ''}`,
body: {
jsonrpc: '1.0',
id: Date.now(),
method,
params
},
json: true
},
(err, res) => {
if (err) {
return callback(err);
} else if (res) {
if (res.body) {
if (res.body.error) {
return callback(res.body.error);
} else if (res.body.result) {
return callback(null, res.body && res.body.result);
} else {
return callback({ msg: 'No error or body found', body: res.body });
}
}
} else {
return callback('No response or error returned by rpc call');
}
}
);
}
async asyncCall<T>(method: string, params: any[], walletName?: string) {
return new Promise<T>((resolve, reject) => {
this.callMethod(method, params, (err, data) => {
if (err) {
return reject(err);
}
return resolve(data);
}, walletName);
});
}
async getChainTip() {
const tips = await this.asyncCall<{
height: number;
hash: string;
branchlen: number;
status: string;
}>('getchaintips', []);
return tips[0];
}
getBestBlockHash() {
return this.asyncCall('getbestblockhash', []);
}
getBlockHeight() {
return this.asyncCall('getblockcount', []);
}
getBlock(hash: string, verbosity: number) {
return this.asyncCall<RPCBlock<RPCTransaction>>('getblock', [hash, verbosity]);
}
getBlockHash(height: number) {
return this.asyncCall<string>('getblockhash', [height]);
}
async getBlockByHeight(height: number, verbosity: number) {
const hash = await this.getBlockHash(height);
return this.getBlock(hash, verbosity);
}
getTransaction(txid: string) {
return this.asyncCall<RPCTransaction>('getrawtransaction', [txid, true]);
}
sendTransaction(rawTx: string | Array<string>) {
const txs = typeof rawTx === 'string' ? [rawTx] : rawTx;
return this.asyncCall<string>('sendrawtransaction', txs);
}
decodeScript(hex: string) {
return this.asyncCall('decodescript', [hex]);
}
getWalletAddresses(account: string) {
return this.asyncCall('getaddressesbyaccount', [account]);
}
async getEstimateSmartFee(target: number, mode?: string) {
const args: any[] = [target];
if (mode) {
args.push(mode);
}
return this.asyncCall('estimatesmartfee', args);
}
async getEstimateFee() {
return this.asyncCall('estimatefee', []);
}
}
export class AsyncRPC {
private rpc: RPC;
private walletName: string | undefined;
constructor(username: string, password: string, host: string, port: number | string) {
this.rpc = new RPC(username, password, host, port);
this.walletName = process.env.LOADED_MOCHA_OPTS === 'true' ? 'MOCHA_BITCORE_WALLET' : undefined;
}
async call<T = any>(method: string, params: any[], walletName?: string): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.rpc.callMethod(method, params, (err, data) => {
if (err) {
return reject(err);
}
return resolve(data);
}, walletName);
});
}
async setWallet(walletName?: string) {
if (!this.walletName && !walletName) {
throw new Error('No wallet name specified');
}
if (this.walletName == walletName) {
return;
}
walletName = walletName || this.walletName;
try {
await this.call('createwallet', [walletName]);
} catch (err: any) {
if (!err.message.includes('already exists')) {
throw err;
}
}
this.walletName = walletName;
}
async block(hash: string): Promise<RPCBlock<string>> {
return (await this.call('getblock', [hash, 1])) as RPCBlock<string>;
}
async verbose_block(hash: string): Promise<RPCBlock<RPCTransaction>> {
return (await this.call('getblock', [hash, 2])) as RPCBlock<RPCTransaction>;
}
async getnewaddress(account: string): Promise<string> {
await this.setWallet();
return (await this.call('getnewaddress', [account], this.walletName)) as string;
}
async signrawtx(txs: string): Promise<any> {
await this.setWallet();
try {
const ret = await this.call('signrawtransactionwithwallet', [txs], this.walletName);
return ret;
} catch (e: any) {
if (!e.code || e.code != -32601) return Promise.reject(e);
return await this.call('signrawtransaction', [txs]);
}
}
async transaction(txid: string, block?: string): Promise<RPCTransaction> {
const args = [txid, true];
if (block) {
args.push(block);
}
return (await this.call('getrawtransaction', args)) as RPCTransaction;
}
async sendtoaddress(address: string, value: string | number) {
await this.setWallet();
return this.call<string>('sendtoaddress', [address, value], this.walletName);
}
}
export interface RPCBlock<T> {
hash: string;
confirmations: number;
size: number;
strippedsize: number;
weight: number;
height: number;
version: number;
versionHex: string;
merkleroot: string;
tx: T[];
time: number;
mediantime: number;
nonce: number;
bits: string;
difficulty: number;
chainwork: string;
previousblockhash: string;
nextblockhash: string;
}
export interface RPCTransaction {
in_active_chain: boolean;
hex: string;
txid: string;
hash: string;
strippedsize: number;
size: number;
vsize: number;
version: number;
locktime: number;
vin: {
txid: string;
vout: number;
scriptSig: {
asm: string;
hex: string;
};
sequence: number;
txinwitness: string[];
}[];
vout: {
value: number;
n: number;
scriptPubKey: {
asm: string;
hex: string;
reqSigs: number;
type: string;
addresses: string[];
};
}[];
blockhash: string;
confirmations: number;
time: number;
blocktime: number;
}