Skip to content

Commit

Permalink
Merge PR #796 from 'nodech/import-hs-client'
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech committed May 30, 2023
2 parents 614bfaf + b066126 commit 0389850
Show file tree
Hide file tree
Showing 25 changed files with 3,266 additions and 63 deletions.
1 change: 1 addition & 0 deletions .eslintfiles
Expand Up @@ -11,3 +11,4 @@ bin/hsw-cli
etc/genesis
lib/
test/
scripts/
266 changes: 264 additions & 2 deletions bin/hsd-cli
@@ -1,5 +1,267 @@
#!/usr/bin/env node

'use strict';
'use strict';

require('hs-client/bin/hsd-cli');
const Config = require('bcfg');
const {NodeClient} = require('../lib/client');

// NOTE: This is part of generated `hs-client`.
// Don't introduce any unnecessary dependencies to this.
// This needs to be remain as is for hs-client to be simple.

const ports = {
main: 12037,
testnet: 13037,
regtest: 14037,
simnet: 15037
};

const HELP = `
Commands:
$ block [hash/height]: View block.
$ broadcast [tx-hex]: Broadcast transaction.
$ coin [hash+index/address]: View coins.
$ header [hash/height]: View block header.
$ help: Show help message.
$ info: Get server info.
$ mempool: Get mempool snapshot.
$ reset [height/hash]: Reset chain to desired block.
$ rpc [command] [args]: Execute RPC command.
$ tx [hash/address]: View transactions.
For additional information and a complete list of commands
visit https://hsd-dev.org/api-docs/
`;

class CLI {
constructor() {
this.config = new Config('hsd', {
suffix: 'network',
fallback: 'main',
alias: {
'n': 'network',
'u': 'url',
'uri': 'url',
'k': 'api-key',
's': 'ssl',
'h': 'httphost',
'p': 'httpport'
}
});

this.config.load({
argv: true,
env: true
});

this.config.open('hsd.conf');

this.argv = this.config.argv;
this.network = this.config.str('network', 'main');

this.client = new NodeClient({
url: this.config.str('url'),
apiKey: this.config.str('api-key'),
ssl: this.config.bool('ssl'),
host: this.config.str('http-host'),
port: this.config.uint('http-port')
|| ports[this.network]
|| ports.main,
timeout: this.config.uint('timeout'),
limit: this.config.uint('limit')
});
}

log(json) {
if (typeof json === 'string')
return console.log.apply(console, arguments);
return console.log(JSON.stringify(json, null, 2));
}

async getInfo() {
const info = await this.client.getInfo();
this.log(info);
}

async getTX() {
const hash = this.config.str(0, '');

if (hash.length !== 64) {
const txs = await this.client.getTXByAddress(hash);
this.log(txs);
return;
}

const tx = await this.client.getTX(hash);

if (!tx) {
this.log('TX not found.');
return;
}

this.log(tx);
}

async getBlock() {
let hash = this.config.str(0, '');

if (hash.length !== 64)
hash = parseInt(hash, 10);

const block = await this.client.getBlock(hash);

if (!block) {
this.log('Block not found.');
return;
}

this.log(block);
}

async getBlockHeader() {
let hash = this.config.str(0, '');

if (hash.length !== 64)
hash = parseInt(hash, 10);

const header = await this.client.getBlockHeader(hash);

if (!header) {
this.log('Block header not found.');
return;
}

this.log(header);
}

async getCoin() {
const hash = this.config.str(0, '');
const index = this.config.uint(1);

if (hash.length !== 64) {
const coins = await this.client.getCoinsByAddress(hash);
this.log(coins);
return;
}

const coin = await this.client.getCoin(hash, index);

if (!coin) {
this.log('Coin not found.');
return;
}

this.log(coin);
}

async getMempool() {
const txs = await this.client.getMempool();

this.log(txs);
}

async broadcast() {
const raw = this.config.str([0, 'tx']);
const tx = await this.client.broadcast(raw);

this.log('Broadcasted:');
this.log(tx);
}

async reset() {
let hash = this.config.str(0);

if (hash.length !== 64)
hash = parseInt(hash, 10);

await this.client.reset(hash);

this.log('Chain has been reset.');
}

async rpc() {
const method = this.argv.shift();
if (!method) {
this.log('Missing RPC method');
return;
}

const params = [];

for (const arg of this.argv) {
let param;
try {
param = JSON.parse(arg);
} catch (e) {
param = arg;
}
params.push(param);
}

let result;
try {
result = await this.client.execute(method, params);
} catch (e) {
if (e.type === 'RPCError') {
this.log(e.message);
return;
}
throw e;
}

this.log(result);
}

async open() {
switch (this.argv.shift()) {
case 'block':
await this.getBlock();
break;
case 'broadcast':
await this.broadcast();
break;
case 'coin':
await this.getCoin();
break;
case 'header':
await this.getBlockHeader();
break;
case 'help':
process.stdout.write(HELP + '\n');
break;
case 'info':
await this.getInfo();
break;
case 'mempool':
await this.getMempool();
break;
case 'reset':
await this.reset();
break;
case 'rpc':
await this.rpc();
break;
case 'tx':
await this.getTX();
break;
default:
process.stdout.write('Unrecognized command.\n');
process.stdout.write(HELP + '\n');
break;
}
}

async destroy() {
if (this.client && this.client.opened)
await this.client.close();
}
}

(async () => {
const cli = new CLI();
await cli.open();
await cli.destroy();
})().catch((err) => {
console.error(err.stack);
process.exit(1);
});
23 changes: 23 additions & 0 deletions bin/hsd-rpc
@@ -0,0 +1,23 @@
#!/bin/sh

# NOTE: This is part of generated `hs-client`.

rl=0

if ! type perl > /dev/null 2>& 1; then
if uname | grep -i 'darwin' > /dev/null; then
echo 'hsd-rpc requires perl to start on OSX.' >& 2
exit 1
fi
rl=1
fi

if test $rl -eq 1; then
file=$(readlink -f "$0")
else
file=$(perl -MCwd -e "print Cwd::realpath('$0')")
fi

dir=$(dirname "$file")

exec "${dir}/hsd-cli" rpc "$@"

0 comments on commit 0389850

Please sign in to comment.