mcps-sdk-js
Version:
MCPS JavaScript SDK
327 lines (298 loc) • 8.81 kB
text/typescript
import { Client } from '../client';
import { Crypto } from '../utils';
import { SdkError } from '../errors';
import * as is from 'is_js';
import * as types from '../types';
/**
* This module allows you to manage your local tendermint keystore (wallets) for mcps.
*
* **NOTE:** You need to implement the [[KeyDAO]] Interface first.
*
* @category Modules
* @since v0.17
*/
export class Keys {
/** @hidden */
private client: Client;
/** @hidden */
constructor(client: Client) {
this.client = client;
}
/**
* Create a new key
*
* @param name Name of the key
* @param password Password for encrypting the keystore
* @param type Pubkey Type
* @returns Bech32 address and mnemonic
* @since v0.17
*/
add(
name: string,
password: string,
type:types.PubkeyType = types.PubkeyType.secp256k1
): { address: string; mnemonic: string } {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(password)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (!this.client.config.keyDAO.encrypt) {
throw new SdkError(`Encrypt method of KeyDAO not implemented`);
}
const exists = this.client.config.keyDAO.read(name);
if (exists) {
throw new SdkError(`Key with name '${name}' already exists`);
}
const mnemonic = Crypto.generateMnemonic();
const privKey = Crypto.getPrivateKeyFromMnemonic(mnemonic);
const pubKey = Crypto.getPublicKeyFromPrivateKey(privKey, type);
const address = Crypto.getAddressFromPublicKey(
pubKey,
this.client.config.bech32Prefix.AccAddr
);
const encryptedPrivKey = this.client.config.keyDAO.encrypt(
privKey,
password
);
// Save the key to app
this.client.config.keyDAO.write(name, {
address,
privKey: encryptedPrivKey,
});
return { address, mnemonic };
}
/**
* Recover a key
*
* @param name Name of the key
* @param password Password for encrypting the keystore
* @param mnemonic Mnemonic of the key
* @param type Pubkey Type
* @param index The bip44 address index (default: 0)
* @param derive Derive a private key using the default HD path (default: true)
* @param saltPassword A passphrase for generating the salt, according to bip39
* @returns Bech32 address
* @since v0.17
*/
recover(
name: string,
password: string,
mnemonic: string,
type:types.PubkeyType = types.PubkeyType.secp256k1,
index = 0,
derive = true,
saltPassword = '',
): string {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(password)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (is.empty(mnemonic)) {
throw new SdkError(`Mnemonic of the key can not be empty`);
}
if (!this.client.config.keyDAO.encrypt) {
throw new SdkError(`Encrypt method of KeyDAO not implemented`);
}
const exists = this.client.config.keyDAO.read(name);
if (exists) {
throw new SdkError(`Key with name '${name}' exists`);
}
const privKey = Crypto.getPrivateKeyFromMnemonic(
mnemonic,
index,
derive,
saltPassword
);
const pubKey = Crypto.getPublicKeyFromPrivateKey(privKey, type);
const address = Crypto.getAddressFromPublicKey(
pubKey,
this.client.config.bech32Prefix.AccAddr
);
const encryptedPrivKey = this.client.config.keyDAO.encrypt(
privKey,
password
);
// Save the key to app
this.client.config.keyDAO.write(name, {
address,
privKey: encryptedPrivKey,
});
return address;
}
/**
* Import a key from keystore
*
* @param name Name of the key
* @param password Password of the keystore
* @param keystore Keystore json or object
* @param type Pubkey Type
* @returns Bech32 address
* @since v0.17
*/
import(
name: string,
password: string,
keystore: string | types.Keystore,
type:types.PubkeyType = types.PubkeyType.secp256k1
): string {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(password)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (is.empty(keystore)) {
throw new SdkError(`Keystore can not be empty`);
}
if (!this.client.config.keyDAO.encrypt) {
throw new SdkError(`Encrypt method of KeyDAO not implemented`);
}
const exists = this.client.config.keyDAO.read(name);
if (exists) {
throw new SdkError(`Key with name '${name}' already exists`);
}
const privKey = Crypto.getPrivateKeyFromKeyStore(keystore, password);
const pubKey = Crypto.getPublicKeyFromPrivateKey(privKey, type);
const address = Crypto.getAddressFromPublicKey(
pubKey,
this.client.config.bech32Prefix.AccAddr
);
const encryptedPrivKey = this.client.config.keyDAO.encrypt(
privKey,
password
);
// Save the key to app
this.client.config.keyDAO.write(name, {
address,
privKey: encryptedPrivKey,
});
return address;
}
/**
* Import a PrivateKey
*
* @param name Name of the key
* @param password Password of the keystore
* @param privateKey privateKey hex
* @param type Pubkey Type
* @returns Bech32 address
* @since v0.17
*/
importPrivateKey(
name: string,
password: string,
privateKey: string,
type:types.PubkeyType = types.PubkeyType.secp256k1
): string {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(password)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (is.empty(privateKey)) {
throw new SdkError(`privateKey can not be empty`);
}
const exists = this.client.config.keyDAO.read(name);
if (exists) {
throw new SdkError(`Key with name '${name}' already exists`);
}
const pubKey = Crypto.getPublicKeyFromPrivateKey(privateKey, type);
const address = Crypto.getAddressFromPublicKey(
pubKey,
this.client.config.bech32Prefix.AccAddr
);
const encryptedPrivKey = this.client.config.keyDAO.encrypt!(
privateKey,
password
);
// Save the key to app
this.client.config.keyDAO.write(name, {
address,
privKey: encryptedPrivKey,
});
return address;
}
/**
* Export keystore of a key
*
* @param name Name of the key
* @param keyPassword Password of the key
* @param keystorePassword Password for encrypting the keystore
* @returns Keystore json
* @since v0.17
*/
export(name: string, keyPassword: string, keystorePassword: string): string {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(keyPassword)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (!this.client.config.keyDAO.decrypt) {
throw new SdkError(`Decrypt method of KeyDAO not implemented`);
}
const keyObj = this.client.config.keyDAO.read(name);
if (!keyObj) {
throw new SdkError(`Key with name '${name}' not found`);
}
const privKey = this.client.config.keyDAO.decrypt(
keyObj.privKey,
keyPassword
);
const keystore = Crypto.generateKeyStore(
privKey,
keystorePassword,
this.client.config.bech32Prefix.AccAddr
);
return JSON.stringify(keystore);
}
/**
* Delete a key
*
* @param name Name of the key
* @param password Password of the key
* @since v0.17
*/
delete(name: string, password: string) {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
if (is.empty(password)) {
throw new SdkError(`Password of the key can not be empty`);
}
if (!this.client.config.keyDAO.decrypt) {
throw new SdkError(`Decrypt method of KeyDAO not implemented`);
}
const keyObj = this.client.config.keyDAO.read(name);
if (!keyObj) {
throw new SdkError(`Key with name '${name}' not found`);
}
// Check keystore password
this.client.config.keyDAO.decrypt(keyObj.privKey, password);
// Delete the key from app
this.client.config.keyDAO.delete(name);
}
/**
* Gets address of a key
*
* @param name Name of the key
* @returns Bech32 address
* @since v0.17
*/
show(name: string) {
if (is.empty(name)) {
throw new SdkError(`Name of the key can not be empty`);
}
const keyObj = this.client.config.keyDAO.read(name);
if (!keyObj) {
throw new SdkError(`Key with name '${name}' not found`);
}
return keyObj.address;
}
// TODO: Ledger support
}