itns-sidechain/bin/hsw-cli
Nodari Chkuaselidze 062d1f6d6b
wallet: deprecate getLast and getRange. Update hsw-cli history and
pending.

hsw-cli:
  - hsw-cli: `history` now accepts new args on top of `--account`: `--reverse`,
    `--limit`, `--after`, `--after`.
  - hsw-cli: `pending` now accepts new args, same as above.

wallet-http:
  - Deprecate and remove: `GET /wallet/:id/tx/range`
  - Deprecate and remove: `GET /wallet/:id/tx/last`
2024-08-29 14:15:47 +04:00

752 lines
18 KiB
JavaScript
Executable file

#!/usr/bin/env node
'use strict';
const Config = require('bcfg');
const {WalletClient} = require('../lib/client');
const EXP = 6;
// 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: 12039,
testnet: 13039,
regtest: 14039,
simnet: 15039
};
const HELP = `
Commands:
$ abandon [hash]: Abandon a transaction.
$ account create [account-name]: Create account.
$ account modify [account-name]: Set account options.
$ account get [account-name]: Get account details.
$ account list: List account names.
$ address [account-name]: Derive new address.
$ balance: Get wallet balance.
$ block [height]: View wallet block.
$ blocks: List wallet blocks.
$ change [account-name]: Derive new change address.
$ coins: View wallet coins.
$ dump [address]: Get wallet key WIF by address.
$ get: View wallet.
$ help: Show help message.
$ history: View TX history.
$ import [wif|hex]: Import private or public key.
$ key [address]: Get wallet key by address.
$ listen: Listen for events.
$ lock: Lock wallet.
$ mkauctiontxs [name] [bid] [lockup] [broadcast]: Create bid and reveal TXs.
$ mktx [address] [value]: Create transaction.
$ mkwallet [id]: Create wallet.
$ pending: View pending TXs.
$ resendwallet [id]: Resend pending transactions for a single wallet.
$ retoken: Create new api key.
$ send [address] [value]: Send transaction.
$ shared add [account-name] [xpubkey]: Add key to account.
$ shared remove [account-name] [xpubkey]: Remove key from account.
$ shared list [account-name]: List keys in account.
$ sign [tx-hex]: Sign transaction.
$ tx [hash]: View transaction details.
$ unlock [passphrase] [timeout?]: Unlock wallet.
$ view [tx-hex]: Parse and view transaction.
$ watch [address]: Import an address.
$ zap [age]: Zap pending wallet TXs.
If node is run with wallet-auth flag, then wallet commands
require authorization token.
Admin commands require admin permissions for provided authorization token:
$ backup [path]: Backup the wallet db.
$ master: View wallet master key.
$ rescan [height]: Rescan for transactions.
$ resend: Resend pending transactions for all wallets.
$ rpc [command] [args]: Execute RPC command.
$ wallets: List all wallets.
Other options:
--id [wallet id]: Wallet id.
--passphrase [passphrase]: For signing/account-creation.
--account [account-name]: Account name.
--token [token]: Wallet-specific or admin authorization token.
--api-key [key]: General API authorization key.
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': 'apikey',
's': 'ssl',
'h': 'httphost',
'p': 'httpport'
}
});
this.config.load({
argv: true,
env: true
});
this.config.open('hsw.conf');
this.argv = this.config.argv;
this.network = this.config.str('network', 'main');
const id = this.config.str('id', 'primary');
const token = this.config.str('token', '');
this.client = new WalletClient({
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'),
token
});
this.wallet = this.client.wallet(id, token);
}
log(json) {
if (typeof json === 'string')
return console.log.apply(console, arguments);
return console.log(JSON.stringify(json, null, 2));
}
async getWallets() {
const wallets = await this.client.getWallets();
this.log(wallets);
}
async createWallet() {
const id = this.config.str([0, 'id']);
const options = {
type: this.config.str('type'),
master: this.config.str('master'),
mnemonic: this.config.str('mnemonic'),
m: this.config.uint('m'),
n: this.config.uint('n'),
witness: this.config.bool('witness'),
passphrase: this.config.str('passphrase'),
bip39Passphrase: this.config.str('bip39Passphrase'),
watchOnly: this.config.has('key') ? true : this.config.bool('watch'),
accountKey: this.config.str('key'),
lookahead: this.config.uint('lookahead'),
language: this.config.str('language')
};
const wallet = await this.client.createWallet(id, options);
this.log(wallet);
}
async getMaster() {
const master = await this.wallet.getMaster();
this.log(master);
}
async getKey() {
const address = this.config.str(0);
const key = await this.wallet.getKey(address);
this.log(key);
}
async getWIF() {
const address = this.config.str(0);
const passphrase = this.config.str('passphrase');
const key = await this.wallet.getWIF(address, passphrase);
if (!key) {
this.log('Key not found.');
return;
}
this.log(key.privateKey);
}
async addSharedKey() {
const key = this.config.str(0);
const account = this.config.str('account');
await this.wallet.addSharedKey(account, key);
this.log('Added key.');
}
async removeSharedKey() {
const key = this.config.str(0);
const account = this.config.str('account');
await this.wallet.removeSharedKey(account, key);
this.log('Removed key.');
}
async getSharedKeys() {
const acct = this.config.str([0, 'account']);
const account = await this.wallet.getAccount(acct);
if (!account) {
this.log('Account not found.');
return;
}
this.log(account.keys);
}
async getAccount() {
const acct = this.config.str([0, 'account']);
const account = await this.wallet.getAccount(acct);
this.log(account);
}
async createAccount() {
const name = this.config.str([0, 'name']);
const options = {
type: this.config.str('type'),
m: this.config.uint('m'),
n: this.config.uint('n'),
witness: this.config.bool('witness'),
accountKey: this.config.str('key'),
lookahead: this.config.uint('lookahead')
};
const account = await this.wallet.createAccount(name, options);
this.log(account);
}
async modifyAccount() {
const name = this.config.str([0, 'name']);
const options = {
lookahead: this.config.uint('lookahead')
};
const account = await this.wallet.modifyAccount(name, options);
this.log(account);
}
async createAddress() {
const account = this.config.str([0, 'account']);
const addr = await this.wallet.createAddress(account);
this.log(addr);
}
async createChange() {
const account = this.config.str([0, 'account']);
const addr = await this.wallet.createChange(account);
this.log(addr);
}
async getAccounts() {
const accounts = await this.wallet.getAccounts();
this.log(accounts);
}
async getWallet() {
const info = await this.wallet.getInfo();
this.log(info);
}
async getWalletHistory() {
const options = {
account: this.config.str('account'),
limit: this.config.uint('limit'),
reverse: this.config.bool('reverse'),
after: this.config.str('after'),
time: this.config.uint('time')
};
const txs = await this.wallet.getHistory(options);
this.log(txs);
}
async getWalletPending() {
const options = {
account: this.config.str('account'),
limit: this.config.uint('limit'),
reverse: this.config.bool('reverse'),
after: this.config.str('after'),
time: this.config.uint('time')
};
const txs = await this.wallet.getPending(options);
this.log(txs);
}
async getWalletCoins() {
const account = this.config.str('account');
const coins = await this.wallet.getCoins(account);
this.log(coins);
}
async listenWallet() {
await this.client.open();
await this.wallet.open();
this.wallet.on('tx', (details) => {
this.log('TX:');
this.log(details);
});
this.wallet.on('confirmed', (details) => {
this.log('TX confirmed:');
this.log(details);
});
this.wallet.on('unconfirmed', (details) => {
this.log('TX unconfirmed:');
this.log(details);
});
this.wallet.on('conflict', (details) => {
this.log('TX conflict:');
this.log(details);
});
this.wallet.on('address', (receive) => {
this.log('New addresses allocated:');
this.log(receive);
});
this.wallet.on('balance', (balance) => {
this.log('Balance:');
this.log(balance);
});
return new Promise((resolve, reject) => {
this.client.once('disconnect', resolve);
});
}
async getBalance() {
const account = this.config.str('account');
const balance = await this.wallet.getBalance(account);
this.log(balance);
}
async getMempool() {
const txs = await this.wallet.getMempool();
this.log(txs);
}
async sendTX() {
const outputs = [];
if (this.config.has('script')) {
outputs.push({
script: this.config.str('script'),
value: this.config.ufixed([0, 'value'], EXP)
});
} else {
outputs.push({
address: this.config.str([0, 'address']),
value: this.config.ufixed([1, 'value'], EXP)
});
}
const options = {
account: this.config.str('account'),
passphrase: this.config.str('passphrase'),
outputs: outputs,
smart: this.config.bool('smart'),
rate: this.config.ufixed('rate', EXP),
subtractFee: this.config.bool('subtract-fee')
};
const tx = await this.wallet.send(options);
this.log(tx);
}
async createAuctionTXs() {
const options = {
name: this.config.str([0, 'name']),
bid: this.config.ufixed([1, 'bid'], EXP),
lockup: this.config.ufixed([2, 'lockup'], EXP),
broadcastBid: this.config.bool([3, 'broadcastBid']),
passphrase: this.config.str('passphrase')
};
const txs = await this.wallet.createAuctionTXs(options);
this.log(txs);
}
async createTX() {
let output;
if (this.config.has('script')) {
output = {
script: this.config.str('script'),
value: this.config.ufixed([0, 'value'], EXP)
};
} else {
output = {
address: this.config.str([0, 'address']),
value: this.config.ufixed([1, 'value'], EXP)
};
}
const options = {
account: this.config.str('account'),
passphrase: this.config.str('passphrase'),
outputs: [output],
smart: this.config.bool('smart'),
rate: this.config.ufixed('rate', EXP),
subtractFee: this.config.bool('subtract-fee')
};
const tx = await this.wallet.createTX(options);
this.log(tx);
}
async signTX() {
const passphrase = this.config.str('passphrase');
const tx = this.config.str([0, 'tx']);
const signedTx = await this.wallet.sign({tx, passphrase});
this.log(signedTx);
}
async zapWallet() {
const age = this.config.uint([0, 'age'], 72 * 60 * 60);
const account = this.config.str('account');
await this.wallet.zap(account, age);
this.log('Zapped!');
}
async abandonTX() {
const hash = this.config.str(0);
await this.wallet.abandon(hash);
this.log('Abandoned tx: ' + hash);
}
async viewTX() {
const raw = this.config.str([0, 'tx']);
const tx = await this.wallet.fill(raw);
this.log(tx);
}
async getDetails() {
const hash = this.config.str(0);
const details = await this.wallet.getTX(hash);
this.log(details);
}
async getWalletBlocks() {
const blocks = await this.wallet.getBlocks();
this.log(blocks);
}
async getWalletBlock() {
const height = this.config.uint(0);
const block = await this.wallet.getBlock(height);
this.log(block);
}
async retoken() {
const passphrase = this.config.str('passphrase');
const result = await this.wallet.retoken(passphrase);
this.log(result);
}
async rescan() {
const height = this.config.uint(0);
await this.client.rescan(height);
this.log('Rescanning...');
}
async resend() {
await this.client.resend();
this.log('Resending...');
}
async resendWallet() {
await this.wallet.resend();
this.log('Resending...');
}
async backup() {
const path = this.config.str(0);
await this.client.backup(path);
this.log('Backup complete.');
}
async importKey() {
const key = this.config.str(0);
const account = this.config.str('account');
const passphrase = this.config.str('passphrase');
if (!key)
throw new Error('No key for import.');
if (key.length === 66 || key.length === 130) {
await this.wallet.importPublic(account, key);
this.log('Imported public key.');
return;
}
await this.wallet.importPrivate(account, key, passphrase);
this.log('Imported private key.');
}
async importAddress() {
const address = this.config.str(0);
const account = this.config.str('account');
await this.wallet.importAddress(account, address);
this.log('Imported address.');
}
async lock() {
await this.wallet.lock();
this.log('Locked.');
}
async unlock() {
const passphrase = this.config.str(0);
const timeout = this.config.uint(1);
await this.wallet.unlock(passphrase, timeout);
this.log('Unlocked.');
}
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 handleWallet() {
switch (this.argv.shift()) {
case 'abandon':
await this.abandonTX();
break;
case 'account':
if (this.argv[0] === 'list') {
this.argv.shift();
await this.getAccounts();
break;
}
if (this.argv[0] === 'create') {
this.argv.shift();
await this.createAccount();
break;
}
if (this.argv[0] === 'modify') {
this.argv.shift();
await this.modifyAccount();
break;
}
if (this.argv[0] === 'get')
this.argv.shift();
await this.getAccount();
break;
case 'address':
await this.createAddress();
break;
case 'backup':
await this.backup();
break;
case 'balance':
await this.getBalance();
break;
case 'block':
await this.getWalletBlock();
break;
case 'blocks':
await this.getWalletBlocks();
break;
case 'change':
await this.createChange();
break;
case 'coins':
await this.getWalletCoins();
break;
case 'dump':
await this.getWIF();
break;
case 'get':
await this.getWallet();
break;
case 'help':
process.stdout.write(HELP + '\n');
break;
case 'history':
await this.getWalletHistory();
break;
case 'import':
await this.importKey();
break;
case 'key':
await this.getKey();
break;
case 'listen':
await this.listenWallet();
break;
case 'lock':
await this.lock();
break;
case 'master':
await this.getMaster();
break;
case 'mkauctiontxs':
await this.createAuctionTXs();
break;
case 'mktx':
await this.createTX();
break;
case 'mkwallet':
await this.createWallet();
break;
case 'pending':
await this.getWalletPending();
break;
case 'rescan':
await this.rescan();
break;
case 'resend':
await this.resend();
break;
case 'resendwallet':
await this.resendWallet();
break;
case 'retoken':
await this.retoken();
break;
case 'rpc':
await this.rpc();
break;
case 'send':
await this.sendTX();
break;
case 'shared':
if (this.argv[0] === 'add') {
this.argv.shift();
await this.addSharedKey();
break;
}
if (this.argv[0] === 'remove') {
this.argv.shift();
await this.removeSharedKey();
break;
}
if (this.argv[0] === 'list')
this.argv.shift();
await this.getSharedKeys();
break;
case 'sign':
await this.signTX();
break;
case 'tx':
await this.getDetails();
break;
case 'unlock':
await this.unlock();
break;
case 'view':
await this.viewTX();
break;
case 'wallets':
await this.getWallets();
break;
case 'watch':
await this.importAddress();
break;
case 'zap':
await this.zapWallet();
break;
default:
process.stdout.write('Unrecognized command.\n');
process.stdout.write(HELP + '\n');
break;
}
}
async destroy() {
if (this.client.opened)
await this.client.close();
}
}
(async () => {
const cli = new CLI();
await cli.handleWallet();
await cli.destroy();
})().catch((err) => {
console.error(err.stack);
process.exit(1);
});