-
Notifications
You must be signed in to change notification settings - Fork 7
/
sw-cli-repl.js
321 lines (270 loc) · 17.8 KB
/
sw-cli-repl.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
// Distributed under AGPLv3 license: see /LICENSE for terms. Copyright 2019-2023 Dominic Morris.
const repl = require('repl')
const colors = require('colors')
//const parseSentence = require('minimist-string')
const stringParseArgs = require('./ext/minimist-string-opt')
const appStore = require('./store').store
const utilsWallet = require('./utils')
const svrWorkers = require('./svr-workers')
const svrRouter = require('./svr-wallet/sw-router')
const svrWalletCreate = require('./svr-wallet/sw-create')
const configWallet = require('./config/wallet')
const log = require('./sw-cli-log')
const info = require('./sw-cli-info')
const rpc = require('./sw-rpc')
const helpBanner = '\n' + ' HELP '.bgCyan.white.bold + ' '
const walletNewHelp = `${helpBanner}` +
`(wallet-new) - creates and persists in-memory a new wallet with new random seed values\n`.cyan.bold
const walletInitHelp = `${helpBanner}` +
`(wallet-init) - recreates a wallet from supplied seed values\n`.cyan.bold +
`\t--mpk <master private key> <required> entropy for keygen and redux store (L1) encryption\n`
const walletConnectHelp = `${helpBanner}` +
`(wallet-connect) - connects to 3PBPs and populates tx and balance data for the loaded wallet\n`.cyan.bold
const walletDumpHelp = `${helpBanner}` +
`(wallet-dump) - decrypts and dumps sub-asset private key, addresses, tx and utxo values from the loaded wallet\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] [optional] restrict output to supplied asset symbol if supplied, e.g. "ETH" or "BTC"\n` +
`\t--addr [string] [optional] restrict output to supplied address\n` +
`\t--txs [bool] [optional] dump all address transactions (default: false)\n` +
`\t--txid [string] [optional] seach & dump specific TXID\n` +
`\t--keys [bool] [optional] dump private keys (default: false)\n`
const walletAddAddrHelp = `${helpBanner}` +
`(wallet-add-address) - adds a receive address to the loaded wallet for the specified asset\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset for which to add an address, e.g. "ETH" or "BTC"\n` +
`\t--save [boolean] [optional] save the wallet after adding the address (default: false)\n`
const walletImportPrivKeysHelp = `${helpBanner}` +
`(wallet-import-priv-keys) - adds one or more private keys to a new import account in the loaded wallet\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset for which to add an address, e.g. "ETH" or "BTC"\n` +
`\t--privKeys [string] <required> comma-separated list of WIF privkeys (UTXO assets) or 64 hex char (ETH assets)"\n`
const walletRemovePrivKeysHelp = `${helpBanner}` +
`(wallet-remove-priv-keys) - removes an import account and its associated private keys from the loaded wallet\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset for which to add an address, e.g. "ETH" or "BTC"\n` +
`\t--accountName [string] <required> the import account name to remove e.g. "Import #1 Bitcoin"\n`
const walletSaveHelp = `${helpBanner}` +
`(wallet-save) - saves the loaded wallet in encrypted form to file\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--name (--n) [string] <required> a name for the saved wallet; the wallet can subsequently be loaded by this name\n` +
`\t--force [bool] [optional] overwrite (without warning) any existing file with the same name (default: false)\n`
const walletLoadHelp = `${helpBanner}` +
`(wallet-load) - loads a previously saved wallet from file\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--name (--n) [string] <required> the name of the wallet to load\n`
const walletServerLoadHelp = `${helpBanner}` +
`(wallet-server-load) - loads a previously saved wallet from the Scoop Data Storage Contract\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--email (--e) [string] <required> the pseudo-email of the wallet in the Scoop Data Storage Contract, e.g. "[email protected]"\n`
const walletServerSaveHelp = `${helpBanner}` +
`(wallet-server-save) - saves a previously loaded server wallet back to the Scoop Data Storage Contract\n`.cyan.bold +
`\t--mpk <master private key> <required> \n`
const walletBalanceHelp = `${helpBanner}` +
`(wallet-balance) - shows aub-asset balances in the loaded wallet\n`.cyan.bold +
`\t--symbol (--s) [string] <required> restrict output to supplied asset symbol if supplied, e.g. "ETH" or "BTC"\n`
const assetGetFeesHelp = `${helpBanner}` +
`(asset-get-fees) - fetches recommended network fee rates from oracles\n`.cyan.bold +
`\t--symbol (--s) [string] <required> the asset to get fee rates for, e.g. "ETH" or "BTC"\n`
const assetConvertHelp = `${helpBanner}` +
`(asset-convert) - exchange service: converts from one asset to another\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset to convert, e.g. "ZEC"\n` +
`\t--value (--v) [number] <required> the amount to convert, e.g. 0.01\n` +
`\t--from (--f) [string] <optional> the address to send from; mandatory for account-type assets, e.g. ETH and ERC20s\n` +
`\t--to (--t) [string] <required> the asset to convert to, e.g. "ETH" or "BTC"\n` +
`\t--status [bool] [optional] display conversion status(es) (default: false)\n`
const txGetFeeHelp = `${helpBanner}` +
`(tx-get-fee) - gets the network fee for the specified single-recipient transaction\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset to use for the fee estimate, e.g. "ETH" or "BTC"\n` +
`\t--value (--v) [number] <required> the send value to use for the fee estimate, e.g. 0.01\n`
const txPushHelp = `${helpBanner}` +
`(tx-push) - broadcasts the specified single-recipient transaction\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset to use for the transaction, e.g. "ZEC"\n` +
`\t--value (--v) [number] <required> the amount to send, e.g. 0.01\n` +
`\t--from (--f) [string] <optional> the address to send from (account-type assets)\n` +
`\t--to (--t) [string] <required> the address to send to, e.g. "t1RGM2uztDM3iqGjBsK7UvuLFAYiSJWczLh"\n` +
`\t--dsigCltvPubKey [string] <optional> creates a (non-std) dual-sig "PROTECT_OP" output (P2SH(DSIG/CLTV)) - can also be spent after a timelock this pubkey address (BTC_TEST)\n` +
`\t--dsigLockHours [integer] <optional> when creating a PROTECT_OP output, the timelock in hours for dsigCltvPubKey (default: 1)\n` +
`\t--spendFullUtxos [string] <optional> spend (in full) one or more specifc UTXO(s) - format "txid:vout,..."\n`
const claimableListHelp = `${helpBanner}` +
`.cll (claimable-list) - lists any spendable PROTECT_OP UTXOs\n`.cyan.bold +
`\t--symbol (--s) [string] <required> the asset for which to list claimables (BTC_TEST)\n`
const claimableClaimHelp = `${helpBanner}` +
`.clc (claimable-claim) - claims (sends to self, standard UTXO) claimable PROTECT_OP UTXOs\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset to use for the transaction (BTC_TEST)\n` +
`\t--spendFullUtxos [string] [optional] claims (in full) one or more PROTECT_OP UTXO(s) - format "txid:vout,..." (or claims all available UTXOs if not supplied)\n`
const claimableResetHelp = `${helpBanner}` +
`.clr (claimable-reset) - rolls over PROTECT_OP UTXOs resetting their timelocks\n`.cyan.bold +
`\t--mpk <master private key> <required> \n` +
`\t--symbol (--s) [string] <required> the asset to use for the transaction (BTC_TEST)\n` +
`\t--resetFullUtxos [string] [optional] resets (in full) one or more PROTECT_OP UTXO(s) - format "txid:vout,..." (or resets all available UTXOs if not supplied)\n`
const clsHelp = `${helpBanner}` +
`.cls (clear-screen) - clears the console screen \n`.cyan.bold
const exitHelp = `${helpBanner}` +
`.exit - terminates the wallet\n`.cyan.bold
// dbg/utils
const rpcTestHelp = `${helpBanner}` +
`.rt (rpc-test) - DBG: calls sw-cli RPC server \n`.cyan.bold +
`\t--rpcPort [number] [required] RPC port \n` +
`\t--rpcHost [string] [required] RPC host \n` +
`\t--rpcUsername [string] [required] RPC username \n` +
`\t--rpcPassword [string] [required] RPC password \n` +
`\t--cmd [string] [required] CLI command, e.g. ".tx-push" \n` +
`\t--params [string] [required] CLI parameters in JSON format, e.g. " { \\\"mpk\\\": \\\"...\\\", \\\"symbol\\\": \\\"...\\\", \\\"value\\\": \\\"...\\\", ... } \n`
const logTailHelp = `${helpBanner}` +
`.lt (log-tail) - DBG: tails the last n lines of the debug log \n`.cyan.bold +
`\t--lines (--l) [int] [optional] number of lines to tail (default: 100)\n` +
`\t--debug [bool] [optional] tails the verbose (debug) log instead of the info log (default: false) \n`
const infoHelp = `${helpBanner}` +
`.i (info) - DBG: displays summary wallet info \n`.cyan.bold
//const clearCacheHelp = `${helpBanner}` +
// `.cc (clear-tx-db-cache) - clears the TX cache file\n`.cyan.bold
module.exports = {
init: (walletContext, enableFileHistory) => {
// todo
// if (utilsWallet.isParamTrue(enableFileHistory)) {
// log.warn('command history is being saved to file at ./node_history. This will include sensitive data.\n')
// }
// init repl
const colors = { RED: "31", GREEN: "32", YELLOW: "33", BLUE: "34", MAGENTA: "35", CYAN: "36" }
const colorize = (color, s) => `\x1b[${color}m${s}\x1b[0m`
const nodeVersion = colorize(colors.GREEN, `${process.title} ${process.version}`)
const prompt = repl.start({
terminal: true,
historySize: 100,
//historyFile: /*utilsWallet.isParamTrue(enableFileHistory) ?*/ './.node_history', // : undefined, // todo
removeHistoryDuplicates: true,
useGlobal: true,
useColors: true,
prompt: `${nodeVersion} SW-CLI > `,
breakEvalOnSigint: true,
// eval: (text, context, filename, callback) => {
// prompt.setPrompt(`new [${new Date().getTime()}] >`)
// }
})
// init file history
// if (utilsWallet.isParamTrue(enableFileHistory)) {
// prompt.clearBufferedCommand()
// require('repl.history')(prompt, './.node_history')
// prompt.displayPrompt()
// }
prompt.context.w = walletContext
// custom commands
delete prompt.commands.save
delete prompt.commands.break
delete prompt.commands.clear
delete prompt.commands.load
delete prompt.commands.editor
delete prompt.commands.history
delete prompt.commands.exit
prompt.commands.help.help += '\n'
const defineWalletCmd = (prompt, names, help, fn, walletFnName) => {
names.forEach(name => {
prompt.defineCommand(name, {
help,
action: function (args) {
prompt.clearBufferedCommand()
//var argv = require('minimist')(args.split(' '))
const argv = stringParseArgs(args, {
string: ['t', 'f'],
alias: {
s: 'symbol', // e.g. "-s" == --symbol (== --s)
n: 'name',
l: 'lines',
e: 'email',
v: 'value',
t: 'to',
f: 'from',
}
})
if (argv.help) postCmd(prompt, null, help)
else {
//console.group()
fn(utilsWallet.getAppWorker(), walletContext.store, argv, walletFnName)
.then(res => {
if (res) postCmd(prompt, res, help)
else process.exit(1)
})
//.finally(() => console.groupEnd())
}
}
})
})
}
defineWalletCmd(prompt, ['/wn', 'wallet-new'], walletNewHelp, svrWalletCreate.walletNew)
defineWalletCmd(prompt, ['/wi', 'wallet-init'], walletInitHelp, svrWalletCreate.walletInit)
defineWalletCmd(prompt, ['/wl', 'wallet-load'], walletLoadHelp, svrRouter.fn, 'LOAD')
defineWalletCmd(prompt, ['/ws', 'wallet-save'], walletSaveHelp, svrRouter.fn, 'SAVE')
defineWalletCmd(prompt, ['/wsl', 'wallet-server-load'], walletServerLoadHelp, svrRouter.fn, 'SERVER-LOAD')
defineWalletCmd(prompt, ['/wss', 'wallet-server-save'], walletServerSaveHelp, svrRouter.fn, 'SERVER-SAVE')
defineWalletCmd(prompt, ['/wc', 'wallet-connect'], walletConnectHelp, svrRouter.fn, 'CONNECT')
defineWalletCmd(prompt, ['/wd', 'wallet-dump'], walletDumpHelp, svrRouter.fn, 'DUMP')
defineWalletCmd(prompt, ['/wb', 'wallet-balance'], walletBalanceHelp, svrRouter.fn, 'BALANCE')
defineWalletCmd(prompt, ['/waa', 'wallet-add-address'], walletAddAddrHelp, svrRouter.fn, 'ADD-ADDR')
defineWalletCmd(prompt, ['/wipk', 'wallet-import-priv-keys'], walletImportPrivKeysHelp, svrRouter.fn, 'ADD-PRIV-KEYS')
defineWalletCmd(prompt, ['/wrpk', 'wallet-remove-priv-keys'], walletRemovePrivKeysHelp, svrRouter.fn, 'REMOVE-PRIV-KEYS')
defineWalletCmd(prompt, ['/agf', 'asset-get-fees'], assetGetFeesHelp, svrRouter.fn, 'ASSET-GET-FEES')
defineWalletCmd(prompt, ['/ac', 'asset-convert'], assetConvertHelp, svrRouter.fn, 'ASSET-CONVERT')
defineWalletCmd(prompt, ['/txgf', 'tx-get-fee'], txGetFeeHelp, svrRouter.fn, 'TX-GET-FEE')
defineWalletCmd(prompt, ['/txp', 'tx-push'], txPushHelp, svrRouter.fn, 'TX-PUSH')
defineWalletCmd(prompt, ['/cll', 'list-claimable'], claimableListHelp, svrRouter.fn, 'CLAIMABLE-LIST')
defineWalletCmd(prompt, ['/clc', 'claim-claimable'], claimableClaimHelp, svrRouter.fn, 'CLAIMABLE-CLAIM')
defineWalletCmd(prompt, ['/clr', 'claim-reset'], claimableResetHelp, svrRouter.fn, 'CLAIMABLE-RESET')
defineWalletCmd(prompt, ['/rt', 'rpc-test'], rpcTestHelp, rpc.rpcTest)
defineWalletCmd(prompt, ['/lt', 'log-tail'], logTailHelp, log.logTail)
defineWalletCmd(prompt, ['/i', 'info'], infoHelp, info.show)
// clear, tx db cache
// defineWalletCmd(prompt, 'cc', clearCacheHelp, async () => {
// const txDbClear = new Promise((resolve) => {
// const listener = (event) => {
// if (event.msg === 'SERVER_NUKE_TX_DB_DONE') {
// utilsWallet.getAppWorker().removeEventListener('message', listener)
// // ## re-init of txdb after clear causes cli commands to be written to it
// // setTimeout(() => {
// // svrWorkers.txdb_init()
// // resolve()
// // }, 1000)
// }
// }
// utilsWallet.getAppWorker().addEventListener('message', listener)
// utilsWallet.getAppWorker().postMessage({ msg: 'SERVER_NUKE_TX_DB', data: {} })
// })
// await txDbClear
// return { ok: true }
// })
// cls, clear console screen
defineWalletCmd(prompt, ['cls'], clsHelp, async () => {
require('clear')()
return { ok: true }
})
// exit, clear console screen
defineWalletCmd(prompt, ['exit'], exitHelp, async () => {
process.exit(0)
return null
})
return prompt
},
postCmd: (prompt, res, help) => {
postCmd(prompt, res, help)
}
}
function postCmd(prompt, res, help) {
setTimeout(() => {
if (!res || res.err) {
if (res) log.error(res.err)
if (help) {
console.log()
log.info(help)
}
}
else log.success(`${JSON.stringify(res.ok, null, 2)}`)
if (global.loadedWallet && global.loadedWallet.keys && global.loadedWallet.keys.mpk) {
log.warn(`wallet MPK is being cached (MODE_ENV=${process.env.NODE_ENV}, RPC_MODE=${configWallet.get_RPC_MODE()})`)
}
//prompt.setPrompt('new>')
prompt.displayPrompt()
}, 1000) // https://github.com/nodejs/node/issues/11568 -- also, allow time for related reducer actions and their logs
}