-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
refactor(utxo-bin): improve yargs argument parsing
- Loading branch information
Showing
11 changed files
with
288 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './parseString'; | ||
export * from './walletKeys'; | ||
export * from './parseNetwork'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import * as utxolib from '@bitgo/utxo-lib'; | ||
|
||
export function getNetworkForName(name: string): utxolib.Network { | ||
const network = utxolib.networks[name as utxolib.NetworkName]; | ||
if (!network) { | ||
throw new Error(`invalid network ${name}`); | ||
} | ||
return network; | ||
} | ||
|
||
export function getNetwork(argv: { network: string }): utxolib.Network { | ||
return getNetworkForName(argv.network); | ||
} | ||
|
||
type DemandOption<T> = T & { demandOption: true }; | ||
|
||
type NetworkOption<TDefault> = { | ||
type: 'string'; | ||
description: string; | ||
default: TDefault; | ||
coerce: (arg: string) => utxolib.Network; | ||
}; | ||
|
||
export function getNetworkOptions(defaultValue?: string): { | ||
network: NetworkOption<typeof defaultValue>; | ||
} { | ||
return { | ||
network: { | ||
type: 'string', | ||
description: 'network name', | ||
default: defaultValue, | ||
coerce: getNetworkForName, | ||
}, | ||
}; | ||
} | ||
|
||
export function getNetworkOptionsDemand(defaultValue?: string): { | ||
network: DemandOption<NetworkOption<typeof defaultValue>>; | ||
} { | ||
return { | ||
network: { ...getNetworkOptions(defaultValue).network, demandOption: true }, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import * as process from 'process'; | ||
import * as fs from 'fs'; | ||
|
||
import clipboardy from 'clipboardy-cjs'; | ||
|
||
type Format = 'hex' | 'base64'; | ||
export function stringToBuffer(data: string, format: Format | Format[]): Buffer { | ||
if (typeof format !== 'string') { | ||
for (const f of format) { | ||
try { | ||
return stringToBuffer(data, f); | ||
} catch (err) { | ||
// ignore, try next | ||
} | ||
} | ||
throw new Error(`could not parse data, formats: ${format}`); | ||
} | ||
|
||
// strip all whitespace | ||
data = data.replace(/\s*/g, ''); | ||
|
||
if (format === 'hex') { | ||
data = data.toLowerCase(); | ||
} | ||
|
||
const buf = Buffer.from(data, format); | ||
// make sure there were no decoding errors | ||
if (buf.toString(format) !== data) { | ||
throw new Error(`invalid ${format}`); | ||
} | ||
return buf; | ||
} | ||
|
||
export const readStringOptions = { | ||
path: { type: 'string', nargs: 1, default: '' }, | ||
stdin: { type: 'boolean', default: false }, | ||
data: { | ||
type: 'string', | ||
description: 'hex or base64', | ||
alias: 'hex', | ||
}, | ||
clipboard: { type: 'boolean', default: false }, | ||
} as const; | ||
|
||
export type ReadStringOptions = { | ||
clipboard?: boolean; | ||
path?: string; | ||
data?: string; | ||
stdin: boolean; | ||
}; | ||
|
||
/** | ||
* Reads from stdin until Ctrl-D is pressed. | ||
*/ | ||
export async function readStdin(): Promise<string> { | ||
/* | ||
* High-performance implementation of reading from stdin. | ||
* Standard readline is extremely slow for long lines. | ||
*/ | ||
return new Promise((resolve, reject) => { | ||
// Using readline is not an option because it is extremely slow for long lines. | ||
// By enabling raw mode, we can read more than 4096 bytes, but it requires manual Ctrl-C/Ctrl-D handling | ||
if (!process.stdin.setRawMode) { | ||
throw new Error('stdin is not a tty'); | ||
} | ||
process.stdin.setRawMode(true); | ||
const buf: Buffer[] = []; | ||
|
||
process.stdin.on('data', (chunk) => { | ||
if (chunk[0] === 0x03) { | ||
// Ctrl-C | ||
process.exit(130); | ||
} | ||
if (chunk[0] === 0x04) { | ||
// Ctrl-D | ||
process.stdin.emit('end'); | ||
return; | ||
} | ||
buf.push(chunk); | ||
process.stdout.write(chunk); | ||
}); | ||
|
||
process.stdin.on('end', () => { | ||
resolve(Buffer.concat(buf).toString('utf8')); | ||
}); | ||
|
||
process.stdin.on('error', (err) => { | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* @param argv | ||
* @param input - optional input data. If set, this function just ensures that nothing else is set. | ||
* @return string from specified source | ||
*/ | ||
export async function argToString(argv: ReadStringOptions, input?: string): Promise<string | undefined> { | ||
if (argv.stdin || argv.path === '-') { | ||
if (input) { | ||
throw new Error(`conflicting arguments`); | ||
} | ||
console.log('Reading from stdin. Please paste hex-encoded transaction data.'); | ||
console.log('After inserting data, press Ctrl-D to finish. Press Ctrl-C to cancel.'); | ||
if (process.stdin.isTTY) { | ||
input = await readStdin(); | ||
} else { | ||
input = await fs.promises.readFile('/dev/stdin', 'utf8'); | ||
} | ||
} | ||
|
||
if (argv.clipboard) { | ||
if (input) { | ||
throw new Error(`conflicting arguments`); | ||
} | ||
input = await clipboardy.read(); | ||
} | ||
|
||
if (argv.path) { | ||
if (input) { | ||
throw new Error(`conflicting arguments`); | ||
} | ||
input = (await fs.promises.readFile(argv.path, 'utf8')).toString(); | ||
} | ||
|
||
if (argv.data) { | ||
if (input) { | ||
throw new Error(`conflicting arguments`); | ||
} | ||
input = argv.data; | ||
} | ||
|
||
return input; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import * as assert from 'assert'; | ||
import * as utxolib from '@bitgo/utxo-lib'; | ||
|
||
export function isWalletKeyName(name: string): name is utxolib.bitgo.KeyName { | ||
return name === 'user' || name === 'backup' || name === 'bitgo'; | ||
} | ||
|
||
export const keyOptions = { | ||
userKey: { type: 'string', demandOption: true }, | ||
userKeyPrefix: { type: 'string', default: '0/0' }, | ||
backupKey: { type: 'string', demandOption: true }, | ||
backupKeyPrefix: { type: 'string', default: '0/0' }, | ||
bitgoKey: { type: 'string', demandOption: true }, | ||
bitgoKeyPrefix: { type: 'string', default: '0/0' }, | ||
} as const; | ||
|
||
export type KeyOptions = { | ||
userKey: string; | ||
userKeyPrefix?: string; | ||
backupKey: string; | ||
backupKeyPrefix?: string; | ||
bitgoKey: string; | ||
bitgoKeyPrefix?: string; | ||
}; | ||
|
||
export function getRootWalletKeys(argv: KeyOptions): utxolib.bitgo.RootWalletKeys { | ||
const xpubs = [argv.userKey, argv.backupKey, argv.bitgoKey].map((k) => utxolib.bip32.fromBase58(k)); | ||
assert(utxolib.bitgo.isTriple(xpubs)); | ||
return new utxolib.bitgo.RootWalletKeys(xpubs, [ | ||
argv.userKeyPrefix ?? utxolib.bitgo.RootWalletKeys.defaultPrefix, | ||
argv.backupKeyPrefix ?? utxolib.bitgo.RootWalletKeys.defaultPrefix, | ||
argv.bitgoKeyPrefix ?? utxolib.bitgo.RootWalletKeys.defaultPrefix, | ||
]); | ||
} |
Oops, something went wrong.