UNPKG

@ethersphere/swarm-cli

Version:
136 lines (113 loc) 4.51 kB
import { Wallet } from '@ethereumjs/wallet' import { readFileSync } from 'fs' import { Argument, LeafCommand, Option } from 'furious-commander' import { IdentityType } from '../../service/identity/types' import { expectFile, getFieldOrNull, isPrivateKey, normalizePrivateKey } from '../../utils' import { CommandLineError } from '../../utils/error' import { Message } from '../../utils/message' import { createAndRunSpinner } from '../../utils/spinner' import { RootCommand } from '../root-command' import { VerbosityLevel } from '../root-command/command-log' export class Import extends RootCommand implements LeafCommand { public readonly name = 'import' public readonly description = 'Import private key or V3 wallet as a new identity' @Argument({ key: 'resource', required: true, description: 'Private key string or path to file with V3 Wallet or private key', autocompletePath: true, }) public resource!: string @Option({ key: 'name', alias: 'i', description: 'Name of the identity to be saved as', required: true }) public identityName!: string @Option({ key: 'password', alias: 'P', description: 'Password for the V3 wallet' }) public password!: string public async run(): Promise<void> { super.init() if (this.commandConfig.config.identities[this.identityName]) { throw new CommandLineError(Message.identityNameConflict(this.identityName)) } if (isPrivateKey(this.resource)) { await this.runImportOnPrivateKey() } else { expectFile(this.resource) this.resource = readFileSync(this.resource, 'utf-8') if (isPrivateKey(this.resource)) { await this.runImportOnPrivateKey() } else { if (!this.password) { this.console.log(Message.optionNotDefined('password')) this.password = await this.console.askForPassword(Message.existingV3Password()) } const spinner = createAndRunSpinner('Decrypting V3 wallet...', this.verbosity) try { const wallet: Wallet = await this.decryptV3Wallet(this.resource) spinner.text = 'Importing V3 wallet...' await this.saveWallet(wallet) } finally { spinner.stop() } this.console.log(`V3 Wallet imported as identity '${this.identityName}' successfully`) } } } private async runImportOnPrivateKey(): Promise<void> { if (await this.shouldConvertToV3Wallet()) { await this.convertPrivateKeyToV3Wallet() } else { const data = { wallet: { privateKey: this.resource, }, identityType: IdentityType.simple, } if (!this.commandConfig.saveIdentity(this.identityName, data)) { throw new CommandLineError(Message.identityNameConflictOption(this.identityName)) } this.console.log(`Private key imported as identity '${this.identityName}' successfully`) } } private async decryptV3Wallet(data: string): Promise<Wallet> { try { const wallet: Wallet = await Wallet.fromV3(data, this.password) return wallet } catch (error: unknown) { const message: string = getFieldOrNull(error, 'message') || 'unknown error' throw new CommandLineError(`Failed to decrypt wallet: ${message}`) } } private async convertPrivateKeyToV3Wallet(): Promise<void> { if (!this.password) { this.console.log(Message.optionNotDefined('password')) this.password = await this.console.askForPasswordWithConfirmation( Message.newV3Password(), Message.newV3PasswordConfirmation(), ) } const wallet = Wallet.fromPrivateKey(Buffer.from(normalizePrivateKey(this.resource), 'hex')) await this.saveWallet(wallet) this.console.log(`V3 Wallet imported as identity '${this.identityName}' successfully`) } private async saveWallet(wallet: Wallet): Promise<void> { const data = { wallet: await wallet.toV3(this.password), identityType: IdentityType.v3, } if (!this.commandConfig.saveIdentity(this.identityName, data)) { throw new CommandLineError(Message.identityNameConflict(this.identityName)) } } private async shouldConvertToV3Wallet(): Promise<boolean> { if (this.yes) { return false } if (this.password) { return true } if (this.verbosity !== VerbosityLevel.Quiet) { const answer = await this.console.confirmAndDelete('Convert private key to a secure V3 wallet?') return answer } return false } }