Skip to content

Commit

Permalink
feat: add status command and pass json files as well as addresses
Browse files Browse the repository at this point in the history
This release adds a new status command to print a network status from grpc nodes.  It also adds the capacity to pass a JSON wallet file to existing commands that previously only accepted a Q- address (thanks for the suggestion fr1t2!).
  • Loading branch information
jplomas authored Aug 10, 2019
2 parents 2efecb9 + 82cff75 commit 85c4f89
Show file tree
Hide file tree
Showing 8 changed files with 593 additions and 123 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@theqrl/cli",
"description": "QRL CLI functions",
"version": "1.1.0",
"author": "JP Lomas <jp@qrl.foundation> The QRL Foundation",
"author": "JP Lomas <jp@theqrl.org>, The QRL Foundation",
"bin": {
"qrl-cli": "./bin/run"
},
Expand All @@ -17,11 +17,13 @@
"axios": "^0.19.0",
"bech32": "^1.1.3",
"bignumber.js": "^9.0.0",
"cli-ux": "^5.3.1",
"crypto": "^1.0.1",
"crypto-js": "^3.1.9-1",
"grpc": "^1.22.2",
"grpc-kit": "^0.2.0",
"kleur": "^3.0.3",
"moment": "^2.24.0",
"ora": "^3.4.0",
"qrcode-terminal": "^0.12.0",
"qrllib": "^1.0.4",
Expand Down Expand Up @@ -68,6 +70,7 @@
"posttest": "eslint .",
"prepack": "oclif-dev manifest && oclif-dev readme",
"test": "nyc mocha --forbid-only -t 50000 \"test/**/*.test.js\"",
"test-ots": "nyc mocha --forbid-only -t 50000 \"test/commands/ots.test.js\"",
"version": "oclif-dev readme && git add README.md",
"cov": "nyc report --reporter=text-lcov > coverage.lcov && ./node_modules/.bin/codecov -t $CODECOV_TOKEN",
"make-binaries": "pkg . -t node8-macos-x64 -o dist/macos/qrl-cli && pkg . -t node8-win-x64 -o dist/win/qrl-cli && pkg . -t node8-linux-x64 -o dist/linux/qrl-cli"
Expand Down
63 changes: 58 additions & 5 deletions src/commands/balance.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint new-cap: 0 */
/* eslint new-cap: 0, max-depth: 0 */
const {Command, flags} = require('@oclif/command')
const {red, green} = require('kleur')
const {red, green, white} = require('kleur')
const ora = require('ora')
const validateQrlAddress = require('@theqrl/validate-qrl-address')
const axios = require('axios')
const BigNumber = require('bignumber.js')
const fs = require('fs')
const aes256 = require('aes256')
const {cli} = require('cli-ux')

const shorPerQuanta = 10 ** 9

Expand All @@ -16,14 +19,63 @@ const GetBalance = async function (address, api) {
})
}

const openWalletFile = function (path) {
const contents = fs.readFileSync(path)
return JSON.parse(contents)[0]
}

class Balance extends Command {
async run() {
const {args, flags} = this.parse(Balance)
const address = args.address
let address = args.address
if (!validateQrlAddress.hexString(address).result) {
this.log(`${red('⨉')} Unable to get a balance: invalid QRL address`)
this.exit(1)
// not a valid address - is it a file?
let isFile = false
let isValidFile = false
const path = address
try {
if (fs.existsSync(path)) {
isFile = true
}
} catch (error) {
this.log(`${red('⨉')} Unable to get a balance: invalid QRL address/wallet file`)
this.exit(1)
}
if (isFile === false) {
this.log(`${red('⨉')} Unable to get a balance: invalid QRL address/wallet file`)
this.exit(1)
} else {
const walletJson = openWalletFile(path)
try {
if (walletJson.encrypted === false) {
isValidFile = true
address = walletJson.address
}
if (walletJson.encrypted === true) {
let password = ''
if (flags.password) {
password = flags.password
} else {
password = await cli.prompt('Enter password for wallet file', {type: 'hide'})
}
address = aes256.decrypt(password, walletJson.address)
if (validateQrlAddress.hexString(address).result) {
isValidFile = true
} else {
this.log(`${red('⨉')} Unable to open wallet file: invalid password`)
this.exit(1)
}
}
} catch (error) {
this.exit(1)
}
}
if (isValidFile === false) {
this.log(`${red('⨉')} Unable to get a balance: invalid QRL address/wallet file`)
this.exit(1)
}
}
this.log(white().bgBlack(address))
const spinner = ora({text: 'Fetching balance from API...'}).start()
let api = ''
if (flags.api) {
Expand Down Expand Up @@ -65,6 +117,7 @@ Balance.flags = {
shor: flags.boolean({char: 's', default: false, description: 'reports the balance in Shor'}),
quanta: flags.boolean({char: 'q', default: false, description: 'reports the balance in Quanta'}),
api: flags.string({char: 'a', required: false, description: 'api endpoint (for custom QRL network deployments)'}),
password: flags.string({char: 'p', required: false, description: 'wallet file password'}),
}

module.exports = {Balance}
62 changes: 55 additions & 7 deletions src/commands/ots.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint new-cap: 0 */
/* eslint new-cap: 0, max-depth: 0 */
const {Command, flags} = require('@oclif/command')
const {red, white} = require('kleur')
const ora = require('ora')
Expand All @@ -9,6 +9,8 @@ const tmp = require('tmp')
const fs = require('fs')
const util = require('util')
const CryptoJS = require('crypto-js')
const aes256 = require('aes256')
const {cli} = require('cli-ux')
const {QRLPROTO_SHA256} = require('../get-qrl-proto-shasum')
const protoLoader = require('@grpc/proto-loader')
const PROTO_PATH = `${__dirname}/../../src/qrlbase.proto`
Expand All @@ -27,6 +29,11 @@ const clientGetNodeInfo = client => {
})
}

const openWalletFile = function (path) {
const contents = fs.readFileSync(path)
return JSON.parse(contents)[0]
}

let qrlClient = null

async function checkProtoHash(file) {
Expand Down Expand Up @@ -101,10 +108,53 @@ async function loadGrpcProto(protofile, endpoint) {
class OTSKey extends Command {
async run() {
const {args, flags} = this.parse(OTSKey)
const address = args.address
let address = args.address
if (!validateQrlAddress.hexString(address).result) {
this.log(`${red('⨉')} Unable to get a OTS: invalid QRL address`)
this.exit(1)
// not a valid address - is it a file?
let isFile = false
let isValidFile = false
const path = address
try {
if (fs.existsSync(path)) {
isFile = true
}
} catch (error) {
this.log(`${red('⨉')} Unable to get OTS: invalid QRL address/wallet file`)
this.exit(1)
}
if (isFile === false) {
this.log(`${red('⨉')} Unable to get OTS: invalid QRL address/wallet file`)
this.exit(1)
} else {
const walletJson = openWalletFile(path)
try {
if (walletJson.encrypted === false) {
isValidFile = true
address = walletJson.address
}
if (walletJson.encrypted === true) {
let password = ''
if (flags.password) {
password = flags.password
} else {
password = await cli.prompt('Enter password for wallet file', {type: 'hide'})
}
address = aes256.decrypt(password, walletJson.address)
if (validateQrlAddress.hexString(address).result) {
isValidFile = true
} else {
this.log(`${red('⨉')} Unable to open wallet file: invalid password`)
this.exit(1)
}
}
} catch (error) {
this.exit(1)
}
}
if (isValidFile === false) {
this.log(`${red('⨉')} Unable to get a balance: invalid QRL address/wallet file`)
this.exit(1)
}
}
let grpcEndpoint = 'testnet-4.automated.theqrl.org:19009'
let network = 'Testnet'
Expand Down Expand Up @@ -136,11 +186,8 @@ class OTSKey extends Command {
await qrlClient.GetOTS(request, async (error, response) => {
if (error) {
this.log(`${red('⨉')} Unable to read next unused OTS key`)
this.exit(1)
}
// console.log('response:', response)
spinner.succeed(`Next unused OTS key: ${response.next_unused_ots_index}`)
this.exit(0)
})
})
}
Expand All @@ -163,6 +210,7 @@ OTSKey.flags = {
testnet: flags.boolean({char: 't', default: false, description: 'queries testnet for the OTS state'}),
mainnet: flags.boolean({char: 'm', default: false, description: 'queries mainnet for the OTS state'}),
grpc: flags.string({char: 'g', required: false, description: 'advanced: grcp endpoint (for devnet/custom QRL network deployments)'}),
password: flags.string({char: 'p', required: false, description: 'wallet file password'}),
}

module.exports = {OTSKey}
70 changes: 63 additions & 7 deletions src/commands/receive.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,69 @@
/* eslint new-cap: 0 */
const {Command} = require('@oclif/command')
const {red} = require('kleur')
/* eslint new-cap: 0, max-depth: 0 */
const {Command, flags} = require('@oclif/command')
const {red, white} = require('kleur')
const validateQrlAddress = require('@theqrl/validate-qrl-address')
const qrcode = require('qrcode-terminal')
const aes256 = require('aes256')
const {cli} = require('cli-ux')
const fs = require('fs')

const openWalletFile = function (path) {
const contents = fs.readFileSync(path)
return JSON.parse(contents)[0]
}

class Receive extends Command {
async run() {
const {args} = this.parse(Receive)
const address = args.address
const {args, flags} = this.parse(Receive)
let address = args.address
if (!validateQrlAddress.hexString(address).result) {
this.log(`${red('⨉')} Invalid QRL address`)
this.exit(1)
// not a valid address - is it a file?
let isFile = false
let isValidFile = false
const path = address
try {
if (fs.existsSync(path)) {
isFile = true
}
} catch (error) {
this.log(`${red('⨉')} Invalid QRL address/wallet file`)
this.exit(1)
}
if (isFile === false) {
this.log(`${red('⨉')} Invalid QRL address/wallet file`)
this.exit(1)
} else {
const walletJson = openWalletFile(path)
try {
if (walletJson.encrypted === false) {
isValidFile = true
address = walletJson.address
}
if (walletJson.encrypted === true) {
let password = ''
if (flags.password) {
password = flags.password
} else {
password = await cli.prompt('Enter password for wallet file', {type: 'hide'})
}
address = aes256.decrypt(password, walletJson.address)
if (validateQrlAddress.hexString(address).result) {
isValidFile = true
} else {
this.log(`${red('⨉')} Unable to open wallet file: invalid password`)
this.exit(1)
}
}
} catch (error) {
this.exit(1)
}
}
if (isValidFile === false) {
this.log(`${red('⨉')} Invalid QRL address/wallet file`)
this.exit(1)
}
}
this.log(white().bgBlack(address))
this.log(qrcode.generate(address))
}
}
Expand All @@ -29,4 +81,8 @@ Receive.args = [
},
]

Receive.flags = {
password: flags.string({char: 'p', required: false, description: 'wallet file password'}),
}

module.exports = {Receive}
Loading

0 comments on commit 85c4f89

Please sign in to comment.