UNPKG

@chorus-one/signer-local

Version:

Local signer for Chorus One SDK that utilizes a BIP39 mnemonic for signing operations

126 lines (125 loc) 5.16 kB
import { KeyType } from '@chorus-one/signer'; import { nopLogger } from '@chorus-one/utils'; import { mnemonicToSeed } from 'bip39'; import { deriveKey as deriveEd25519, sign as signEd25519 } from './ed25519'; import { deriveKey as deriveSecp256k1, sign as signSecp256k1 } from './secp256k1'; /** * The LocalSigner in the Chorus One SDK is a specialized implementation of the Signer interface that utilizes a `BIP39` * mnemonic for signing operations. * * This signer is ideal for local environments where you need a straightforward and * secure method to generate and manage cryptographic keys from mnemonic phrases. */ export class LocalSigner { config; accounts; addressDerivationFn; mnemonicToSeedFn; seedToKeypairFn; logger; /** * Constructs a new LocalSigner. * * @param params - The parameters required to initialize the LocalSigner * @param params.mnemonic - A string containing your `BIP39` mnemonic phrase * @param params.keyType - An enum specifying the signing key type (e.g. SECP256K1, ED25519) * @param params.accounts - An array of account objects, each containing an HD path * @param params.addressDerivationFn - A function that derives the address from the public key * @param params.logger - (Optional) A logger to use for logging messages, i.e `console` * * @returns A new instance of LocalSigner. */ constructor(params) { const { addressDerivationFn, ...config } = params; this.config = config; this.accounts = new Map(); this.logger = params.logger ?? nopLogger; this.addressDerivationFn = addressDerivationFn; this.mnemonicToSeedFn = params.mnemonicToSeedFn ?? mnemonicToSeed; this.seedToKeypairFn = params.seedToKeypairFn; } /** * Initializes the signer, performing any necessary setup or configuration. * @returns A promise that resolves once the initialization is complete. */ async init() { const seed = await this.mnemonicToSeedFn(this.config.mnemonic); await Promise.all(this.config.accounts.map(async (account) => { let key; switch (this.config.keyType) { case undefined: case KeyType.SECP256K1: { const fn = this.seedToKeypairFn ?? deriveSecp256k1; key = await fn(seed, account.hdPath); break; } case KeyType.ED25519: { const fn = this.seedToKeypairFn ?? deriveEd25519; key = await fn(seed, account.hdPath); break; } default: throw new Error('unsupported key type'); } const publicKey = key.publicKey; const privateKey = key.privateKey; const derivedAddresses = await this.addressDerivationFn(key.publicKey, account.hdPath); derivedAddresses.forEach((address) => { this.accounts.set(address.toLowerCase(), { hdPath: account.hdPath, privateKey, publicKey }); }); })); } /** * Signs the provided data using the private key associated with the signer's address. * * @param signerAddress - The address of the signer * @param signerData - The data to be signed, which can be a raw message or custom data * * @returns A promise that resolves to an object containing the signature and public key. */ async sign(signerAddress, signerData) { this.logger.info(`signing data from address: ${signerAddress}`); if (signerData.message === undefined) { throw new Error('missing message to sign'); } const content = signerData.message; let sig; switch (this.config.keyType) { case undefined: case KeyType.SECP256K1: sig = await signSecp256k1(content, this.getPrivateKey(signerAddress)); break; case KeyType.ED25519: sig = await signEd25519(content, this.getPrivateKey(signerAddress)); break; default: throw new Error('unsupported key type'); } return { sig, pk: await this.getPublicKey(signerAddress) }; } /** * Retrieves the public key associated with the signer's address. * * @param address - The address of the signer * * @returns A promise that resolves to a Uint8Array representing the public key. */ async getPublicKey(address) { const account = this.accounts.get(address.toLowerCase()); if (account === undefined) { throw new Error(`no public key found for address: ${address}`); } return account.publicKey; } getPrivateKey(address) { const account = this.accounts.get(address.toLowerCase()); if (account === undefined) { throw new Error(`no private key found for address: ${address}`); } return account.privateKey; } }