From cf73dafe40391757c8a14c1c133088fa22de4dea Mon Sep 17 00:00:00 2001 From: Angel Castillo Date: Tue, 31 Jan 2023 02:51:12 +0000 Subject: [PATCH] refactor!: the key-management package now uses the new Ed25519 cryptographic provider --- packages/e2e/.env.example | 10 +-- packages/e2e/src/environment.ts | 4 +- packages/e2e/src/factories.ts | 9 +++ packages/e2e/src/scripts/mnemonic.ts | 7 +- packages/e2e/src/util/StubKeyAgent.ts | 4 + .../wallet-init/wallet-init.test.ts | 4 +- .../test/local-network/register-pool.test.ts | 4 +- .../long-running/cache-invalidation.test.ts | 3 +- packages/e2e/test/util.ts | 10 ++- .../wallet/SingleAddressWallet/mint.test.ts | 10 ++- .../multisignature.test.ts | 18 +++-- .../wallet/SingleAddressWallet/nft.test.ts | 10 ++- .../e2e/test/web-extension/extension/ui.ts | 3 +- .../test/integration/cip36KeyAgents.test.ts | 5 +- packages/key-management/package.json | 1 + .../key-management/src/InMemoryKeyAgent.ts | 43 +++++----- packages/key-management/src/KeyAgentBase.ts | 79 +++++++++++++------ packages/key-management/src/types.ts | 3 + .../src/util/createAsyncKeyAgent.ts | 1 + packages/key-management/src/util/key.ts | 39 +++++---- .../test/InMemoryKeyAgent.test.ts | 51 ++++++------ .../key-management/test/KeyAgentBase.test.ts | 27 +++---- .../test/mocks/mockKeyAgentDependencies.ts | 2 + .../test/restoreKeyAgent.test.ts | 6 +- .../test/util/createAsyncKeyAgent.test.ts | 3 +- packages/wallet/src/setupWallet.ts | 7 +- .../test/SingleAddressWallet/load.test.ts | 2 + .../test/SingleAddressWallet/methods.test.ts | 2 + .../test/SingleAddressWallet/rollback.test.ts | 1 + .../test/SingleAddressWallet/shutdown.test.ts | 2 + .../LedgerKeyAgent.integration.test.ts | 3 + .../test/hardware/LedgerKeyAgent.test.ts | 2 + .../TrezorKeyAgent.integration.test.ts | 3 + .../test/hardware/TrezorKeyAgent.test.ts | 2 + packages/wallet/test/integration/util.ts | 2 + packages/wallet/test/setupWallet.test.ts | 6 +- packages/web-extension/src/keyAgent/util.ts | 1 + yarn.lock | 1 + 38 files changed, 238 insertions(+), 152 deletions(-) diff --git a/packages/e2e/.env.example b/packages/e2e/.env.example index aa362a57091..d846f09d4d9 100644 --- a/packages/e2e/.env.example +++ b/packages/e2e/.env.example @@ -7,11 +7,11 @@ FAUCET_PROVIDER_PARAMS='{"baseUrl":"http://localhost:8090/v2","mnemonic":"fire m # Key management setup - required by getWallet KEY_MANAGEMENT_PROVIDER=inMemory -KEY_MANAGEMENT_PARAMS='{"accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"vacant violin soft weird deliver render brief always monitor general maid smart jelly core drastic erode echo there clump dizzy card filter option defense"}' -#KEY_MANAGEMENT_PARAMS='{"accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"slab gorilla reflect display cage aim silver add own arrange crew start female bitter menu inner combine exit swallow bamboo midnight wealth culture picnic"}' -#KEY_MANAGEMENT_PARAMS='{"accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"decorate survey empower stairs pledge humble social leisure baby wrap grief exact monster rug dash kiss perfect select science light frame play swallow day"}' -#KEY_MANAGEMENT_PARAMS='{"accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"phrase raw learn suspect inmate powder combine apology regular hero gain chronic fruit ritual short screen goddess odor keen creek brand today kit machine"}' -#KEY_MANAGEMENT_PARAMS='{"accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"salon zoo engage submit smile frost later decide wing sight chaos renew lizard rely canal coral scene hobby scare step bus leaf tobacco slice"}' +KEY_MANAGEMENT_PARAMS='{"cryptoProvider": "CML", "accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"vacant violin soft weird deliver render brief always monitor general maid smart jelly core drastic erode echo there clump dizzy card filter option defense"}' +#KEY_MANAGEMENT_PARAMS='{"cryptoProvider": "CML", "accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"slab gorilla reflect display cage aim silver add own arrange crew start female bitter menu inner combine exit swallow bamboo midnight wealth culture picnic"}' +#KEY_MANAGEMENT_PARAMS='{"cryptoProvider": "CML", "accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"decorate survey empower stairs pledge humble social leisure baby wrap grief exact monster rug dash kiss perfect select science light frame play swallow day"}' +#KEY_MANAGEMENT_PARAMS='{"cryptoProvider": "CML", "accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"phrase raw learn suspect inmate powder combine apology regular hero gain chronic fruit ritual short screen goddess odor keen creek brand today kit machine"}' +#KEY_MANAGEMENT_PARAMS='{"cryptoProvider": "CML", "accountIndex": 0, "chainId":{"networkId": 0, "networkMagic": 888}, "password":"some_password","mnemonic":"salon zoo engage submit smile frost later decide wing sight chaos renew lizard rely canal coral scene hobby scare step bus leaf tobacco slice"}' # Providers setup - required by getWallet ASSET_PROVIDER=http diff --git a/packages/e2e/src/environment.ts b/packages/e2e/src/environment.ts index a0c68044ae1..dfff31ce2ad 100644 --- a/packages/e2e/src/environment.ts +++ b/packages/e2e/src/environment.ts @@ -7,6 +7,7 @@ export interface KeyManagementParams { mnemonic: string; chainId: Cardano.ChainId; password: string; + cryptoProvider: string; } export interface ProviderParams { @@ -33,10 +34,11 @@ const keyManagementParams = makeValidator((value) => properties: { accountIndex: { minimum: 0, type: 'integer' }, chainId: { $ref: '/ChainId' }, + cryptoProvider: { type: 'string' }, mnemonic: { type: 'string' }, password: { type: 'string' } }, - required: ['accountIndex', 'mnemonic', 'chainId', 'password'], + required: ['accountIndex', 'mnemonic', 'chainId', 'password', 'cryptoProvider'], type: 'object' }, { diff --git a/packages/e2e/src/factories.ts b/packages/e2e/src/factories.ts index b793a33ca14..492b03e07fd 100644 --- a/packages/e2e/src/factories.ts +++ b/packages/e2e/src/factories.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Crypto from '@cardano-sdk/crypto'; import { AssetProvider, Cardano, @@ -66,8 +67,14 @@ export const rewardsProviderFactory = new ProviderFactory(); export const txSubmitProviderFactory = new ProviderFactory(); export const utxoProviderFactory = new ProviderFactory(); export const stakePoolProviderFactory = new ProviderFactory(); +export const cryptoProviderFactory = new ProviderFactory(); + +// Crypto Providers + +cryptoProviderFactory.register('CML', async () => new Crypto.CmlEd25519Provider()); // Faucet providers + faucetProviderFactory.register('cardano-wallet', CardanoWalletFaucetProvider.create); // Asset providers @@ -305,12 +312,14 @@ export const getWallet = async (props: GetWalletProps) => { const envKeyParams = customKeyParams ? customKeyParams : env.KEY_MANAGEMENT_PARAMS; const keyManagementParams = { ...envKeyParams, ...(idx === undefined ? {} : { accountIndex: idx }) }; + const cryptoProvider = await cryptoProviderFactory.create(env.KEY_MANAGEMENT_PROVIDER.cryptoProvider, null, logger); const { wallet } = await setupWallet({ createKeyAgent: keyAgent ? () => Promise.resolve(keyAgent) : await keyManagementFactory.create(env.KEY_MANAGEMENT_PROVIDER, keyManagementParams, logger), createWallet: async (asyncKeyAgent: AsyncKeyAgent) => new SingleAddressWallet({ name, polling }, { ...providers, keyAgent: asyncKeyAgent, logger, stores }), + cryptoProvider, logger }); diff --git a/packages/e2e/src/scripts/mnemonic.ts b/packages/e2e/src/scripts/mnemonic.ts index 7de07830b02..e90d68fae59 100644 --- a/packages/e2e/src/scripts/mnemonic.ts +++ b/packages/e2e/src/scripts/mnemonic.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-floating-promises */ +import * as Crypto from '@cardano-sdk/crypto'; import { AddressType, InMemoryKeyAgent, util } from '@cardano-sdk/key-management'; import { localNetworkChainId } from '../util'; @@ -17,7 +18,11 @@ import { localNetworkChainId } from '../util'; getPassword: async () => Buffer.from(''), mnemonicWords: mnemonicArray }, - { inputResolver: { resolveInputAddress: async () => null }, logger: console } + { + cryptoProvider: new Crypto.CmlEd25519Provider(), + inputResolver: { resolveInputAddress: async () => null }, + logger: console + } ); const derivedAddress = await keyAgentFromMnemonic.deriveAddress({ diff --git a/packages/e2e/src/util/StubKeyAgent.ts b/packages/e2e/src/util/StubKeyAgent.ts index 1c206b7fc86..a8456a91d2b 100644 --- a/packages/e2e/src/util/StubKeyAgent.ts +++ b/packages/e2e/src/util/StubKeyAgent.ts @@ -21,6 +21,10 @@ export class StubKeyAgent implements KeyAgent { return this.#knownAddresses; } + get cryptoProvider(): Crypto.Ed25519Provider { + throw new NotImplementedError('cryptoProvider'); + } + get chainId(): Cardano.ChainId { throw new NotImplementedError('chainId'); } diff --git a/packages/e2e/test/load-test-custom/wallet-init/wallet-init.test.ts b/packages/e2e/test/load-test-custom/wallet-init/wallet-init.test.ts index f73c599a2d9..19ecd512680 100644 --- a/packages/e2e/test/load-test-custom/wallet-init/wallet-init.test.ts +++ b/packages/e2e/test/load-test-custom/wallet-init/wallet-init.test.ts @@ -14,6 +14,7 @@ import { MeasurementUtil, assetProviderFactory, chainHistoryProviderFactory, + cryptoProviderFactory, getEnv, getLoadTestScheduler, keyManagementFactory, @@ -70,8 +71,9 @@ const getKeyAgent = async (accountIndex: number) => { { ...env.KEY_MANAGEMENT_PARAMS, accountIndex }, logger ); + const cryptoProvider = await cryptoProviderFactory.create(env.KEY_MANAGEMENT_PARAMS.cryptoProvider, null, logger); const walletUtil = createLazyWalletUtil(); - const keyAgent = await createKeyAgent({ inputResolver: walletUtil, logger }); + const keyAgent = await createKeyAgent({ cryptoProvider, inputResolver: walletUtil, logger }); return { keyAgent, walletUtil }; }; diff --git a/packages/e2e/test/local-network/register-pool.test.ts b/packages/e2e/test/local-network/register-pool.test.ts index 3cd9da8b453..570153803ee 100644 --- a/packages/e2e/test/local-network/register-pool.test.ts +++ b/packages/e2e/test/local-network/register-pool.test.ts @@ -1,5 +1,4 @@ /* eslint-disable max-statements */ -import * as Crypto from '@cardano-sdk/crypto'; import { Cardano } from '@cardano-sdk/core'; import { KeyAgentFactoryProps, TestWallet, getEnv, getWallet, walletVariables } from '../../src'; import { logger } from '@cardano-sdk/util-dev'; @@ -33,7 +32,6 @@ const wallet2Params: KeyAgentFactoryProps = { describe('local-network/register-pool', () => { let wallet1: TestWallet; let wallet2: TestWallet; - const cryptoProvider = new Crypto.CmlEd25519Provider(); beforeAll(async () => { jest.setTimeout(180_000); @@ -76,6 +74,7 @@ describe('local-network/register-pool', () => { role: KeyRole.External }); + const cryptoProvider = await poolKeyAgent.getCryptoProvider(); const poolKeyHash = await cryptoProvider.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( @@ -157,6 +156,7 @@ describe('local-network/register-pool', () => { role: KeyRole.External }); + const cryptoProvider = await poolKeyAgent.getCryptoProvider(); const poolKeyHash = await cryptoProvider.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( diff --git a/packages/e2e/test/long-running/cache-invalidation.test.ts b/packages/e2e/test/long-running/cache-invalidation.test.ts index 51ea09dd263..91b26414404 100644 --- a/packages/e2e/test/long-running/cache-invalidation.test.ts +++ b/packages/e2e/test/long-running/cache-invalidation.test.ts @@ -1,5 +1,4 @@ /* eslint-disable max-statements */ -import * as Crypto from '@cardano-sdk/crypto'; import { AddressType, KeyRole } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { KeyAgentFactoryProps, TestWallet, getEnv, getWallet, walletVariables } from '../../src'; @@ -26,7 +25,6 @@ const wallet1Params: KeyAgentFactoryProps = { describe('cache invalidation', () => { let wallet1: TestWallet; - const cryptoProvider = new Crypto.CmlEd25519Provider(); beforeAll(async () => { jest.setTimeout(180_000); @@ -57,6 +55,7 @@ describe('cache invalidation', () => { role: KeyRole.External }); + const cryptoProvider = await poolKeyAgent.getCryptoProvider(); const poolKeyHash = await cryptoProvider.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( diff --git a/packages/e2e/test/util.ts b/packages/e2e/test/util.ts index 96e7ccb03bf..d5833b04c20 100644 --- a/packages/e2e/test/util.ts +++ b/packages/e2e/test/util.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Crypto from '@cardano-sdk/crypto'; import * as envalid from 'envalid'; import { Cardano, createSlotEpochCalc } from '@cardano-sdk/core'; import { @@ -239,13 +240,18 @@ export const submitCertificate = async (certificate: Cardano.Certificate, wallet * * @param mnemonics The random set of mnemonics. * @param genesis Network genesis parameters + * @param cryptoProvider The Ed25519 cryptographic provider. */ -export const createStandaloneKeyAgent = async (mnemonics: string[], genesis: Cardano.CompactGenesis) => +export const createStandaloneKeyAgent = async ( + mnemonics: string[], + genesis: Cardano.CompactGenesis, + cryptoProvider: Crypto.Ed25519Provider +) => await InMemoryKeyAgent.fromBip39MnemonicWords( { chainId: genesis, getPassword: async () => Buffer.from(''), mnemonicWords: mnemonics }, - { inputResolver: { resolveInputAddress: async () => null }, logger } + { cryptoProvider, inputResolver: { resolveInputAddress: async () => null }, logger } ); diff --git a/packages/e2e/test/wallet/SingleAddressWallet/mint.test.ts b/packages/e2e/test/wallet/SingleAddressWallet/mint.test.ts index 06b82958f79..5b2b94aa849 100644 --- a/packages/e2e/test/wallet/SingleAddressWallet/mint.test.ts +++ b/packages/e2e/test/wallet/SingleAddressWallet/mint.test.ts @@ -1,5 +1,4 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import * as Crypto from '@cardano-sdk/crypto'; import { Cardano, nativeScriptPolicyId } from '@cardano-sdk/core'; import { KeyRole, util } from '@cardano-sdk/key-management'; import { SingleAddressWallet } from '@cardano-sdk/wallet'; @@ -13,7 +12,6 @@ const logger = createLogger(); describe('SingleAddressWallet/mint', () => { let wallet: SingleAddressWallet; - const cryptoProvider = new Crypto.CmlEd25519Provider(); afterAll(() => { wallet.shutdown(); @@ -26,7 +24,11 @@ describe('SingleAddressWallet/mint', () => { const genesis = await firstValueFrom(wallet.genesisParameters$); - const aliceKeyAgent = await createStandaloneKeyAgent(util.generateMnemonicWords(), genesis); + const aliceKeyAgent = await createStandaloneKeyAgent( + util.generateMnemonicWords(), + genesis, + await wallet.keyAgent.getCryptoProvider() + ); const derivationPath = { index: 0, @@ -34,7 +36,7 @@ describe('SingleAddressWallet/mint', () => { }; const alicePubKey = await aliceKeyAgent.derivePublicKey(derivationPath); - const aliceKeyHash = await cryptoProvider.getPubKeyHash(alicePubKey); + const aliceKeyHash = await aliceKeyAgent.cryptoProvider.getPubKeyHash(alicePubKey); const alicePolicySigner = new util.KeyAgentTransactionSigner(aliceKeyAgent, derivationPath); diff --git a/packages/e2e/test/wallet/SingleAddressWallet/multisignature.test.ts b/packages/e2e/test/wallet/SingleAddressWallet/multisignature.test.ts index c282f62d943..4afc02576f4 100644 --- a/packages/e2e/test/wallet/SingleAddressWallet/multisignature.test.ts +++ b/packages/e2e/test/wallet/SingleAddressWallet/multisignature.test.ts @@ -1,5 +1,4 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import * as Crypto from '@cardano-sdk/crypto'; import { Cardano, nativeScriptPolicyId } from '@cardano-sdk/core'; import { KeyRole, util } from '@cardano-sdk/key-management'; import { SingleAddressWallet } from '@cardano-sdk/wallet'; @@ -13,7 +12,6 @@ const logger = createLogger(); describe('SingleAddressWallet/multisignature', () => { let wallet: SingleAddressWallet; - const cryptoProvider = new Crypto.CmlEd25519Provider(); const assetName = '3030303030'; afterAll(() => { @@ -27,8 +25,16 @@ describe('SingleAddressWallet/multisignature', () => { const genesis = await firstValueFrom(wallet.genesisParameters$); - const aliceKeyAgent = await createStandaloneKeyAgent(util.generateMnemonicWords(), genesis); - const bobKeyAgent = await createStandaloneKeyAgent(util.generateMnemonicWords(), genesis); + const aliceKeyAgent = await createStandaloneKeyAgent( + util.generateMnemonicWords(), + genesis, + await wallet.keyAgent.getCryptoProvider() + ); + const bobKeyAgent = await createStandaloneKeyAgent( + util.generateMnemonicWords(), + genesis, + await wallet.keyAgent.getCryptoProvider() + ); const derivationPath = { index: 0, @@ -36,10 +42,10 @@ describe('SingleAddressWallet/multisignature', () => { }; const alicePubKey = await aliceKeyAgent.derivePublicKey(derivationPath); - const aliceKeyHash = await cryptoProvider.getPubKeyHash(alicePubKey); + const aliceKeyHash = await aliceKeyAgent.cryptoProvider.getPubKeyHash(alicePubKey); const bobPubKey = await bobKeyAgent.derivePublicKey(derivationPath); - const bobKeyHash = await cryptoProvider.getPubKeyHash(bobPubKey); + const bobKeyHash = await bobKeyAgent.cryptoProvider.getPubKeyHash(bobPubKey); const alicePolicySigner = new util.KeyAgentTransactionSigner(aliceKeyAgent, derivationPath); const bobPolicySigner = new util.KeyAgentTransactionSigner(bobKeyAgent, derivationPath); diff --git a/packages/e2e/test/wallet/SingleAddressWallet/nft.test.ts b/packages/e2e/test/wallet/SingleAddressWallet/nft.test.ts index 41297ec1c56..72339c76380 100644 --- a/packages/e2e/test/wallet/SingleAddressWallet/nft.test.ts +++ b/packages/e2e/test/wallet/SingleAddressWallet/nft.test.ts @@ -1,5 +1,4 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import * as Crypto from '@cardano-sdk/crypto'; import { Cardano, metadatum, nativeScriptPolicyId } from '@cardano-sdk/core'; import { KeyRole, TransactionSigner, util } from '@cardano-sdk/key-management'; import { SingleAddressWallet } from '@cardano-sdk/wallet'; @@ -23,7 +22,6 @@ describe('SingleAddressWallet.assets/nft', () => { let assetIds: Cardano.AssetId[]; let fingerprints: Cardano.AssetFingerprint[]; const assetNames = ['4e46542d66696c6573', '4e46542d303031', '4e46542d303032']; - const cryptoProvider = new Crypto.CmlEd25519Provider(); let walletAddress: Cardano.Address; beforeAll(async () => { @@ -33,14 +31,18 @@ describe('SingleAddressWallet.assets/nft', () => { const genesis = await firstValueFrom(wallet.genesisParameters$); - const keyAgent = await createStandaloneKeyAgent(util.generateMnemonicWords(), genesis); + const keyAgent = await createStandaloneKeyAgent( + util.generateMnemonicWords(), + genesis, + await wallet.keyAgent.getCryptoProvider() + ); const pubKey = await keyAgent.derivePublicKey({ index: 0, role: KeyRole.External }); - const keyHash = await cryptoProvider.getPubKeyHash(pubKey); + const keyHash = await keyAgent.cryptoProvider.getPubKeyHash(pubKey); policySigner = new util.KeyAgentTransactionSigner(keyAgent, { index: 0, diff --git a/packages/e2e/test/web-extension/extension/ui.ts b/packages/e2e/test/web-extension/extension/ui.ts index 7ba1a4309c7..318fb2f745e 100644 --- a/packages/e2e/test/web-extension/extension/ui.ts +++ b/packages/e2e/test/web-extension/extension/ui.ts @@ -9,7 +9,7 @@ import { exposeApi } from '@cardano-sdk/web-extension'; import { adaPriceServiceChannel, getObservableWalletName, userPromptServiceChannel, walletName } from './const'; -import { keyManagementFactory } from '../../../src'; +import { cryptoProviderFactory, keyManagementFactory } from '../../../src'; import { combineLatest, firstValueFrom, of } from 'rxjs'; import { runtime } from 'webextension-polyfill'; @@ -126,6 +126,7 @@ const createWallet = async (accountIndex: number) => { ) )(dependencies), createWallet: async () => wallet, + cryptoProvider: await cryptoProviderFactory.create(env.KEY_MANAGEMENT_PARAMS.cryptoProvider, null, logger), logger }); diff --git a/packages/governance/test/integration/cip36KeyAgents.test.ts b/packages/governance/test/integration/cip36KeyAgents.test.ts index 28b6bc50cbf..738ccecbcbb 100644 --- a/packages/governance/test/integration/cip36KeyAgents.test.ts +++ b/packages/governance/test/integration/cip36KeyAgents.test.ts @@ -23,14 +23,15 @@ describe('cip36', () => { // - it can also be a separate key agent, then it would be using a different seedphrase, // but right now it's the only way to support voting when stake key is controlled by a HW device. const votingKeyAgent: InMemoryKeyAgent = (await testKeyAgent()) as unknown as InMemoryKeyAgent; - votingKeyPair = util.toEd25519KeyPair( + votingKeyPair = await util.toEd25519KeyPair( await votingKeyAgent.exportExtendedKeyPair([ cip36.VotingKeyDerivationPath.PURPOSE, cip36.VotingKeyDerivationPath.COIN_TYPE, walletKeyAgent.accountIndex, // using same account index as wallet's key agent here 0, // chain as per cip36 0 // address_index as per cip36 - ]) + ]), + votingKeyAgent.cryptoProvider ); }); diff --git a/packages/key-management/package.json b/packages/key-management/package.json index e960d8ff5a9..8a4cc215687 100644 --- a/packages/key-management/package.json +++ b/packages/key-management/package.json @@ -64,6 +64,7 @@ "@cardano-sdk/crypto": "^0.1.0", "@cardano-sdk/dapp-connector": "^0.6.1", "@cardano-sdk/util": "^0.7.0", + "@cardano-sdk/util-dev": "^0.6.0", "@emurgo/cardano-message-signing-nodejs": "^1.0.1", "@ledgerhq/hw-transport": "^6.27.2", "@ledgerhq/hw-transport-node-hid-noevents": "^6.27.2", diff --git a/packages/key-management/src/InMemoryKeyAgent.ts b/packages/key-management/src/InMemoryKeyAgent.ts index 1311c42a8b3..01039288ff6 100644 --- a/packages/key-management/src/InMemoryKeyAgent.ts +++ b/packages/key-management/src/InMemoryKeyAgent.ts @@ -11,7 +11,7 @@ import { SignBlobResult, SignTransactionOptions } from './types'; -import { CML, Cardano } from '@cardano-sdk/core'; +import { Cardano } from '@cardano-sdk/core'; import { KeyAgentBase } from './KeyAgentBase'; import { deriveAccountPrivateKey, @@ -31,7 +31,7 @@ export interface InMemoryKeyAgentProps extends Omit { const rootPrivateKey = await this.#decryptRootPrivateKey(); - const accountKey = deriveAccountPrivateKey({ + const accountKey = await deriveAccountPrivateKey({ accountIndex: this.accountIndex, + cryptoProvider: this.cryptoProvider, rootPrivateKey }); - const signingKey = accountKey.derive(type).derive(index).to_raw_key(); - const signature = Crypto.Ed25519SignatureHex(signingKey.sign(Buffer.from(blob, 'hex')).to_hex()); - const publicKey = Crypto.Ed25519PublicKeyHex(Buffer.from(signingKey.to_public().as_bytes()).toString('hex')); + const bip32SigningKey = await this.cryptoProvider.derivePrivateKey(accountKey, [type, index]); + const signingKey = await this.cryptoProvider.getRawPrivateKey(bip32SigningKey); + const signature = await this.cryptoProvider.sign(signingKey, Buffer.from(blob, 'hex')); + const publicKey = await this.cryptoProvider.getPublicKey(signingKey); return { publicKey, signature }; } @@ -72,8 +74,7 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent { // eslint-disable-next-line max-len // https://github.com/Emurgo/cardano-serialization-lib/blob/f817a033ade7a2255591d7c6444fa4f9ffbcf061/rust/src/chain_crypto/derive.rs#L30-L38 async exportRootPrivateKey(): Promise { - const rootPrivateKey = await this.#decryptRootPrivateKey(true); - return Crypto.Bip32PrivateKeyHex(Buffer.from(rootPrivateKey.as_bytes()).toString('hex')); + return await this.#decryptRootPrivateKey(true); } /** @@ -84,7 +85,7 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent { chainId, getPassword, mnemonicWords, - mnemonic2ndFactorPassphrase = Buffer.from(''), + mnemonic2ndFactorPassphrase = '', accountIndex = 0 }: FromBip39MnemonicWordsProps, dependencies: KeyAgentDependencies @@ -93,17 +94,16 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent { const validMnemonic = validateMnemonic(mnemonic); if (!validMnemonic) throw new errors.InvalidMnemonicError(); const entropy = Buffer.from(mnemonicWordsToEntropy(mnemonicWords), 'hex'); - const rootPrivateKey = CML.Bip32PrivateKey.from_bip39_entropy(entropy, mnemonic2ndFactorPassphrase); + const rootPrivateKey = await dependencies.cryptoProvider.fromBip39Entropy(entropy, mnemonic2ndFactorPassphrase); const password = await getPasswordRethrowTypedError(getPassword); - const encryptedRootPrivateKey = await emip3encrypt(rootPrivateKey.as_bytes(), password); - const accountPrivateKey = deriveAccountPrivateKey({ + const encryptedRootPrivateKey = await emip3encrypt(Buffer.from(rootPrivateKey, 'hex'), password); + const accountPrivateKey = await deriveAccountPrivateKey({ accountIndex, + cryptoProvider: dependencies.cryptoProvider, rootPrivateKey }); - const extendedAccountPublicKey = Crypto.Bip32PublicKeyHex( - Buffer.from(accountPrivateKey.to_public().as_bytes()).toString('hex') - ); + const extendedAccountPublicKey = await dependencies.cryptoProvider.getBip32PublicKey(accountPrivateKey); return new InMemoryKeyAgent( { @@ -145,13 +145,12 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent { */ async exportExtendedKeyPair(derivationPath: number[]): Promise { const rootPrivateKey = await this.exportRootPrivateKey(); - let cslPrivateKey = CML.Bip32PrivateKey.from_bytes(Buffer.from(rootPrivateKey, 'hex')); - for (const val of derivationPath) { - cslPrivateKey = cslPrivateKey.derive(harden(val)); - } + const hardenedIndices = derivationPath.map((index: number) => harden(index)); + const childKey = await this.cryptoProvider.derivePrivateKey(rootPrivateKey, hardenedIndices); + return { - skey: Crypto.Bip32PrivateKeyHex(Buffer.from(cslPrivateKey.as_bytes()).toString('hex')), - vkey: Crypto.Bip32PublicKeyHex(Buffer.from(cslPrivateKey.to_public().as_bytes()).toString('hex')) + skey: childKey, + vkey: await this.cryptoProvider.getBip32PublicKey(childKey) }; } @@ -166,6 +165,6 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent { } catch (error) { throw new errors.AuthenticationError('Failed to decrypt root private key', error); } - return CML.Bip32PrivateKey.from_bytes(decryptedRootKeyBytes); + return Crypto.Bip32PrivateKeyHex(Buffer.from(decryptedRootKeyBytes).toString('hex')); } } diff --git a/packages/key-management/src/KeyAgentBase.ts b/packages/key-management/src/KeyAgentBase.ts index 8d7ae426017..da3ff56a01a 100644 --- a/packages/key-management/src/KeyAgentBase.ts +++ b/packages/key-management/src/KeyAgentBase.ts @@ -12,9 +12,11 @@ import { } from './types'; import { CML, Cardano } from '@cardano-sdk/core'; import { STAKE_KEY_DERIVATION_PATH } from './util'; +import { usingAutoFree } from '@cardano-sdk/util'; export abstract class KeyAgentBase implements KeyAgent { readonly #serializableData: SerializableKeyAgentData; + readonly #cryptoProvider: Crypto.Ed25519Provider; protected readonly inputResolver: Cardano.util.InputResolver; get knownAddresses(): GroupedAddress[] { @@ -35,6 +37,10 @@ export abstract class KeyAgentBase implements KeyAgent { get accountIndex(): number { return this.serializableData.accountIndex; } + get cryptoProvider(): Crypto.Ed25519Provider { + return this.#cryptoProvider; + } + abstract signBlob(derivationPath: AccountKeyDerivationPath, blob: Cardano.util.HexBlob): Promise; abstract exportRootPrivateKey(): Promise; abstract signTransaction( @@ -42,9 +48,10 @@ export abstract class KeyAgentBase implements KeyAgent { signTransactionOptions?: SignTransactionOptions ): Promise; - constructor(serializableData: SerializableKeyAgentData, { inputResolver }: KeyAgentDependencies) { + constructor(serializableData: SerializableKeyAgentData, { inputResolver, cryptoProvider }: KeyAgentDependencies) { this.#serializableData = serializableData; this.inputResolver = inputResolver; + this.#cryptoProvider = cryptoProvider; } /** @@ -53,44 +60,64 @@ export abstract class KeyAgentBase implements KeyAgent { async deriveAddress({ index, type }: AccountAddressDerivationPath): Promise { const knownAddress = this.knownAddresses.find((addr) => addr.type === type && addr.index === index); if (knownAddress) return knownAddress; - const derivedPublicPaymentKey = await this.deriveCmlPublicKey({ + const derivedPublicPaymentKey = await this.derivePublicKey({ index, role: type as unknown as KeyRole }); + const derivedPublicPaymentKeyHash = await this.#cryptoProvider.getPubKeyHash(derivedPublicPaymentKey); + // Possible optimization: memoize/cache stakeKeyCredential, because it's always the same - const publicStakeKey = await this.deriveCmlPublicKey(STAKE_KEY_DERIVATION_PATH); - const stakeKeyCredential = CML.StakeCredential.from_keyhash(publicStakeKey.hash()); + const publicStakeKey = await this.derivePublicKey(STAKE_KEY_DERIVATION_PATH); + const publicStakeKeyHash = await this.#cryptoProvider.getPubKeyHash(publicStakeKey); - const address = CML.BaseAddress.new( - this.chainId.networkId, - CML.StakeCredential.from_keyhash(derivedPublicPaymentKey.hash()), - stakeKeyCredential - ).to_address(); + const groupedAddress = usingAutoFree((scope) => { + const stakeKeyCredential = scope.manage( + CML.StakeCredential.from_keyhash( + scope.manage(CML.Ed25519KeyHash.from_bytes(Buffer.from(publicStakeKeyHash, 'hex'))) + ) + ); - const rewardAccount = CML.RewardAddress.new(this.chainId.networkId, stakeKeyCredential).to_address(); - const groupedAddress = { - accountIndex: this.accountIndex, - address: Cardano.Address(address.to_bech32()), - index, - networkId: this.chainId.networkId, - rewardAccount: Cardano.RewardAccount(rewardAccount.to_bech32()), - stakeKeyDerivationPath: STAKE_KEY_DERIVATION_PATH, - type - }; + const address = scope.manage( + scope + .manage( + CML.BaseAddress.new( + this.chainId.networkId, + scope.manage( + CML.StakeCredential.from_keyhash( + scope.manage(CML.Ed25519KeyHash.from_bytes(Buffer.from(derivedPublicPaymentKeyHash, 'hex'))) + ) + ), + stakeKeyCredential + ) + ) + .to_address() + ); + + const rewardAccount = scope.manage( + scope.manage(CML.RewardAddress.new(this.chainId.networkId, stakeKeyCredential)).to_address() + ); + return { + accountIndex: this.accountIndex, + address: Cardano.Address(address.to_bech32()), + index, + networkId: this.chainId.networkId, + rewardAccount: Cardano.RewardAccount(rewardAccount.to_bech32()), + stakeKeyDerivationPath: STAKE_KEY_DERIVATION_PATH, + type + }; + }); this.knownAddresses = [...this.knownAddresses, groupedAddress]; return groupedAddress; } async derivePublicKey(derivationPath: AccountKeyDerivationPath): Promise { - const cslPublicKey = await this.deriveCmlPublicKey(derivationPath); - return Crypto.Ed25519PublicKeyHex(Buffer.from(cslPublicKey.as_bytes()).toString('hex')); - } + const childKey = await this.#cryptoProvider.derivePublicKey(this.extendedAccountPublicKey, [ + derivationPath.role, + derivationPath.index + ]); - protected async deriveCmlPublicKey({ index, role: type }: AccountKeyDerivationPath): Promise { - const accountPublicKeyBytes = Buffer.from(this.extendedAccountPublicKey, 'hex'); - const accountPublicKey = CML.Bip32PublicKey.from_bytes(accountPublicKeyBytes); - return accountPublicKey.derive(type).derive(index).to_raw_key(); + return await this.#cryptoProvider.getRawPublicKey(childKey); } } diff --git a/packages/key-management/src/types.ts b/packages/key-management/src/types.ts index 76dbcea899f..7ee42724e41 100644 --- a/packages/key-management/src/types.ts +++ b/packages/key-management/src/types.ts @@ -67,6 +67,7 @@ export type BIP32Path = Array; export interface KeyAgentDependencies { inputResolver: Cardano.util.InputResolver; logger: Logger; + cryptoProvider: Crypto.Ed25519Provider; } export interface AccountAddressDerivationPath { @@ -148,6 +149,7 @@ export interface KeyAgent { get serializableData(): SerializableKeyAgentData; get knownAddresses(): GroupedAddress[]; get extendedAccountPublicKey(): Crypto.Bip32PublicKeyHex; + get cryptoProvider(): Crypto.Ed25519Provider; /** * @throws AuthenticationError */ @@ -173,6 +175,7 @@ export interface KeyAgent { export type AsyncKeyAgent = Pick & { knownAddresses$: Observable; getChainId(): Promise; + getCryptoProvider(): Promise; getExtendedAccountPublicKey(): Promise; } & Shutdown; diff --git a/packages/key-management/src/util/createAsyncKeyAgent.ts b/packages/key-management/src/util/createAsyncKeyAgent.ts index d23add2336c..a63c2712924 100644 --- a/packages/key-management/src/util/createAsyncKeyAgent.ts +++ b/packages/key-management/src/util/createAsyncKeyAgent.ts @@ -14,6 +14,7 @@ export const createAsyncKeyAgent = (keyAgent: KeyAgent, onShutdown?: () => void) }, derivePublicKey: keyAgent.derivePublicKey.bind(keyAgent), getChainId: () => Promise.resolve(keyAgent.chainId), + getCryptoProvider: () => Promise.resolve(keyAgent.cryptoProvider), getExtendedAccountPublicKey: () => Promise.resolve(keyAgent.extendedAccountPublicKey), knownAddresses$, shutdown() { diff --git a/packages/key-management/src/util/key.ts b/packages/key-management/src/util/key.ts index e659a91815a..c29fb738a8a 100644 --- a/packages/key-management/src/util/key.ts +++ b/packages/key-management/src/util/key.ts @@ -1,6 +1,5 @@ import * as Crypto from '@cardano-sdk/crypto'; import { AccountKeyDerivationPath, CardanoKeyConst, Ed25519KeyPair, KeyPair, KeyRole } from '../types'; -import { CML } from '@cardano-sdk/core'; export const harden = (num: number): number => 0x80_00_00_00 + num; @@ -9,29 +8,27 @@ export const STAKE_KEY_DERIVATION_PATH: AccountKeyDerivationPath = { role: KeyRole.Stake }; -export const toEd25519KeyPair = (bip32KeyPair: KeyPair): Ed25519KeyPair => { - const pubKeyBytes = Buffer.from(bip32KeyPair.vkey, 'hex'); - const cmlPubKey = CML.Bip32PublicKey.from_bytes(pubKeyBytes); - const vkey = Crypto.Ed25519PublicKeyHex(Buffer.from(cmlPubKey.to_raw_key().as_bytes()).toString('hex')); - const prvKeyBytes = Buffer.from(bip32KeyPair.skey, 'hex'); - const cmlPrvKey = CML.Bip32PrivateKey.from_bytes(prvKeyBytes); - const skey = Crypto.Ed25519PrivateExtendedKeyHex(Buffer.from(cmlPrvKey.to_raw_key().as_bytes()).toString('hex')); - return { - skey, - vkey - }; -}; +export const toEd25519KeyPair = async ( + bip32KeyPair: KeyPair, + provider: Crypto.Ed25519Provider +): Promise => ({ + skey: await provider.getRawPrivateKey(bip32KeyPair.skey), + vkey: await provider.getRawPublicKey(bip32KeyPair.vkey) +}); export interface DeriveAccountPrivateKeyProps { - rootPrivateKey: CML.Bip32PrivateKey; + rootPrivateKey: Crypto.Bip32PrivateKeyHex; accountIndex: number; + cryptoProvider: Crypto.Ed25519Provider; } -export const deriveAccountPrivateKey = ({ +export const deriveAccountPrivateKey = async ({ rootPrivateKey, - accountIndex -}: DeriveAccountPrivateKeyProps): CML.Bip32PrivateKey => - rootPrivateKey - .derive(harden(CardanoKeyConst.PURPOSE)) - .derive(harden(CardanoKeyConst.COIN_TYPE)) - .derive(harden(accountIndex)); + accountIndex, + cryptoProvider +}: DeriveAccountPrivateKeyProps): Promise => + await cryptoProvider.derivePrivateKey(rootPrivateKey, [ + harden(CardanoKeyConst.PURPOSE), + harden(CardanoKeyConst.COIN_TYPE), + harden(accountIndex) + ]); diff --git a/packages/key-management/test/InMemoryKeyAgent.test.ts b/packages/key-management/test/InMemoryKeyAgent.test.ts index e76045744b9..d9a660aad98 100644 --- a/packages/key-management/test/InMemoryKeyAgent.test.ts +++ b/packages/key-management/test/InMemoryKeyAgent.test.ts @@ -1,6 +1,6 @@ import * as Crypto from '@cardano-sdk/crypto'; import { AddressType, InMemoryKeyAgent, KeyRole, SerializableInMemoryKeyAgentData, util } from '../src'; -import { CML, Cardano } from '@cardano-sdk/core'; +import { Cardano } from '@cardano-sdk/core'; import { dummyLogger } from 'ts-log'; jest.mock('../src/util/ownSignatureKeyPaths'); @@ -11,6 +11,7 @@ describe('InMemoryKeyAgent', () => { let getPassword: jest.Mock; let inputResolver: jest.Mocked; let mnemonicWords: string[]; + const cryptoProvider = new Crypto.CmlEd25519Provider(); beforeEach(async () => { mnemonicWords = util.generateMnemonicWords(); @@ -22,7 +23,7 @@ describe('InMemoryKeyAgent', () => { getPassword, mnemonicWords }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); }); @@ -45,10 +46,10 @@ describe('InMemoryKeyAgent', () => { { chainId: Cardano.ChainIds.Preview, getPassword, - mnemonic2ndFactorPassphrase: Buffer.from('passphrase'), + mnemonic2ndFactorPassphrase: 'passphrase', mnemonicWords }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); expect(await saferKeyAgent.exportRootPrivateKey()).not.toEqual(await keyAgent.exportRootPrivateKey()); }); @@ -150,21 +151,17 @@ describe('InMemoryKeyAgent', () => { accountIndex: 0, chainId: Cardano.ChainIds.Preview, encryptedRootPrivateKeyBytes: [...Buffer.from(yoroiEncryptedRootPrivateKeyHex, 'hex')], - extendedAccountPublicKey: Crypto.Bip32PublicKeyHex( - Buffer.from( - util - .deriveAccountPrivateKey({ - accountIndex: 0, - rootPrivateKey: CML.Bip32PrivateKey.from_bytes(Buffer.from(yoroiRootPrivateKeyHex, 'hex')) - }) - .to_public() - .as_bytes() - ).toString('hex') + extendedAccountPublicKey: await cryptoProvider.getBip32PublicKey( + await util.deriveAccountPrivateKey({ + accountIndex: 0, + cryptoProvider, + rootPrivateKey: Crypto.Bip32PrivateKeyHex(yoroiRootPrivateKeyHex) + }) ), getPassword, knownAddresses: [] }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); const exportedPrivateKeyHex = await keyAgentFromEncryptedKey.exportRootPrivateKey(); expect(exportedPrivateKeyHex).toEqual(yoroiRootPrivateKeyHex); @@ -177,7 +174,7 @@ describe('InMemoryKeyAgent', () => { getPassword, mnemonicWords: yoroiMnemonic }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); const exportedPrivateKeyHex = await keyAgentFromMnemonic.exportRootPrivateKey(); expect(exportedPrivateKeyHex).toEqual(yoroiRootPrivateKeyHex); @@ -195,7 +192,7 @@ describe('InMemoryKeyAgent', () => { getPassword, mnemonicWords: michaelMnemonic }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); ownSignatureKeyPaths.mockResolvedValue([{ index: 0, type: KeyRole.External }]); @@ -249,22 +246,18 @@ describe('InMemoryKeyAgent', () => { accountIndex: 0, chainId: Cardano.ChainIds.Preview, encryptedRootPrivateKeyBytes: [...Buffer.from(daedelusEncryptedRootPrivateKeyHex, 'hex')], - extendedAccountPublicKey: Crypto.Bip32PublicKeyHex( - Buffer.from( - util - .deriveAccountPrivateKey({ - accountIndex: 0, - rootPrivateKey: CML.Bip32PrivateKey.from_bytes(Buffer.from(daedalusRootPrivateKeyHex, 'hex')) - }) - .to_public() - .as_bytes() - ).toString('hex') + extendedAccountPublicKey: await cryptoProvider.getBip32PublicKey( + await util.deriveAccountPrivateKey({ + accountIndex: 0, + cryptoProvider, + rootPrivateKey: Crypto.Bip32PrivateKeyHex(daedalusRootPrivateKeyHex) + }) ), // daedelus enforces min length of 10 getPassword: jest.fn().mockResolvedValue(Buffer.from('nMmys*X002')), knownAddresses: [] }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); const derivedAddress = await keyAgentFromEncryptedKey.deriveAddress({ index: 1, @@ -280,7 +273,7 @@ describe('InMemoryKeyAgent', () => { getPassword, mnemonicWords: daedelusMnemonic24 }, - { inputResolver, logger: dummyLogger } + { cryptoProvider, inputResolver, logger: dummyLogger } ); const derivedAddress = await keyAgentFromMnemonic.deriveAddress({ index: 1, diff --git a/packages/key-management/test/KeyAgentBase.test.ts b/packages/key-management/test/KeyAgentBase.test.ts index d310312429f..9f8da788961 100644 --- a/packages/key-management/test/KeyAgentBase.test.ts +++ b/packages/key-management/test/KeyAgentBase.test.ts @@ -1,21 +1,19 @@ /* eslint-disable sonarjs/no-duplicate-string */ import * as Crypto from '@cardano-sdk/crypto'; -import { - AccountKeyDerivationPath, - AddressType, - KeyAgentBase, - KeyAgentType, - KeyRole, - SerializableInMemoryKeyAgentData -} from '../src'; -import { CML, Cardano } from '@cardano-sdk/core'; +import { AddressType, KeyAgentBase, KeyAgentType, KeyRole, SerializableInMemoryKeyAgentData } from '../src'; +import { Cardano } from '@cardano-sdk/core'; import { dummyLogger } from 'ts-log'; const ACCOUNT_INDEX = 1; +const cryptoProvider = new Crypto.CmlEd25519Provider(); class MockKeyAgent extends KeyAgentBase { constructor(data: SerializableInMemoryKeyAgentData) { - super(data, { inputResolver: { resolveInputAddress: () => Promise.resolve(null) }, logger: dummyLogger }); + super(data, { + cryptoProvider, + inputResolver: { resolveInputAddress: () => Promise.resolve(null) }, + logger: dummyLogger + }); } serializableDataImpl = jest.fn(); @@ -24,15 +22,12 @@ class MockKeyAgent extends KeyAgentBase { signTransaction = jest.fn(); signVotingMetadata = jest.fn(); exportExtendedKeyPair = jest.fn(); - deriveCmlPublicKeyPublic(derivationPath: AccountKeyDerivationPath) { - return this.deriveCmlPublicKey(derivationPath); - } } describe('KeyAgentBase', () => { let keyAgent: MockKeyAgent; - beforeAll(() => { + beforeEach(() => { keyAgent = new MockKeyAgent({ __typename: KeyAgentType.InMemory, accountIndex: ACCOUNT_INDEX, @@ -80,8 +75,4 @@ describe('KeyAgentBase', () => { const stakePublicKey = await keyAgent.derivePublicKey({ index: 1, role: KeyRole.Stake }); expect(typeof stakePublicKey).toBe('string'); }); - - test('deriveCMLPublicKey', async () => { - expect(await keyAgent.deriveCmlPublicKeyPublic({ index: 0, role: KeyRole.External })).toBeInstanceOf(CML.PublicKey); - }); }); diff --git a/packages/key-management/test/mocks/mockKeyAgentDependencies.ts b/packages/key-management/test/mocks/mockKeyAgentDependencies.ts index f3489402a09..6690ff32cc0 100644 --- a/packages/key-management/test/mocks/mockKeyAgentDependencies.ts +++ b/packages/key-management/test/mocks/mockKeyAgentDependencies.ts @@ -1,7 +1,9 @@ +import { CmlEd25519Provider } from '@cardano-sdk/crypto'; import { KeyAgentDependencies } from '../../src/'; import { dummyLogger } from 'ts-log'; export const mockKeyAgentDependencies = (): jest.Mocked => ({ + cryptoProvider: new CmlEd25519Provider(), inputResolver: { resolveInputAddress: jest.fn().mockResolvedValue(null) }, diff --git a/packages/key-management/test/restoreKeyAgent.test.ts b/packages/key-management/test/restoreKeyAgent.test.ts index 5b25558dbea..3b0c098d471 100644 --- a/packages/key-management/test/restoreKeyAgent.test.ts +++ b/packages/key-management/test/restoreKeyAgent.test.ts @@ -16,7 +16,11 @@ import { STAKE_KEY_DERIVATION_PATH } from '../src/util'; import { dummyLogger } from 'ts-log'; describe('KeyManagement/restoreKeyAgent', () => { - const dependencies: KeyAgentDependencies = { inputResolver: { resolveInputAddress: jest.fn() }, logger: dummyLogger }; + const dependencies: KeyAgentDependencies = { + cryptoProvider: new Crypto.CmlEd25519Provider(), + inputResolver: { resolveInputAddress: jest.fn() }, + logger: dummyLogger + }; describe('InMemoryKeyAgent', () => { const encryptedRootPrivateKeyBytes = [ diff --git a/packages/key-management/test/util/createAsyncKeyAgent.test.ts b/packages/key-management/test/util/createAsyncKeyAgent.test.ts index a97d6635e42..5e73027e57a 100644 --- a/packages/key-management/test/util/createAsyncKeyAgent.test.ts +++ b/packages/key-management/test/util/createAsyncKeyAgent.test.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { AsyncKeyAgent, InMemoryKeyAgent, KeyAgent, util } from '../../src'; import { Cardano } from '@cardano-sdk/core'; import { dummyLogger } from 'ts-log'; @@ -19,7 +20,7 @@ describe('createAsyncKeyAgent maps KeyAgent to AsyncKeyAgent', () => { getPassword, mnemonicWords }, - { inputResolver, logger: dummyLogger } + { cryptoProvider: new Crypto.CmlEd25519Provider(), inputResolver, logger: dummyLogger } ); asyncKeyAgent = util.createAsyncKeyAgent(keyAgent); }); diff --git a/packages/wallet/src/setupWallet.ts b/packages/wallet/src/setupWallet.ts index 60962759006..cb3c5e5002f 100644 --- a/packages/wallet/src/setupWallet.ts +++ b/packages/wallet/src/setupWallet.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { AsyncKeyAgent, KeyAgentDependencies } from '@cardano-sdk/key-management'; import { Logger } from 'ts-log'; import { ObservableWallet } from './types'; @@ -7,6 +8,7 @@ export interface SetupWalletProps { createKeyAgent: (dependencies: KeyAgentDependencies) => Promise; createWallet: (keyAgent: TKeyAgent) => Promise; logger: Logger; + cryptoProvider: Crypto.Ed25519Provider; } /** @@ -19,10 +21,11 @@ export interface SetupWalletProps { export const setupWallet = async ({ createKeyAgent, createWallet, - logger + logger, + cryptoProvider }: SetupWalletProps) => { const walletUtil = createLazyWalletUtil(); - const keyAgent = await createKeyAgent({ inputResolver: walletUtil, logger }); + const keyAgent = await createKeyAgent({ cryptoProvider, inputResolver: walletUtil, logger }); const wallet = await createWallet(keyAgent); walletUtil.initialize(wallet); return { keyAgent, wallet, walletUtil: walletUtil as WalletUtil }; diff --git a/packages/wallet/test/SingleAddressWallet/load.test.ts b/packages/wallet/test/SingleAddressWallet/load.test.ts index 68962d10c84..14223e91e47 100644 --- a/packages/wallet/test/SingleAddressWallet/load.test.ts +++ b/packages/wallet/test/SingleAddressWallet/load.test.ts @@ -1,5 +1,6 @@ /* eslint-disable max-statements */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Crypto from '@cardano-sdk/crypto'; import * as mocks from '../mocks'; import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; import { @@ -87,6 +88,7 @@ const createWallet = async (stores: WalletStores, providers: Providers, pollingC } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); return wallet; diff --git a/packages/wallet/test/SingleAddressWallet/methods.test.ts b/packages/wallet/test/SingleAddressWallet/methods.test.ts index 001aabb2330..67e414ef894 100644 --- a/packages/wallet/test/SingleAddressWallet/methods.test.ts +++ b/packages/wallet/test/SingleAddressWallet/methods.test.ts @@ -1,4 +1,5 @@ /* eslint-disable max-len */ +import * as Crypto from '@cardano-sdk/crypto'; import * as mocks from '../mocks'; import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; import { AssetId, createStubStakePoolProvider } from '@cardano-sdk/util-dev'; @@ -74,6 +75,7 @@ describe('SingleAddressWallet methods', () => { utxoProvider } ), + cryptoProvider: new Crypto.CmlEd25519Provider(), logger })); await waitForWalletStateSettle(wallet); diff --git a/packages/wallet/test/SingleAddressWallet/rollback.test.ts b/packages/wallet/test/SingleAddressWallet/rollback.test.ts index 89e898878b5..09ef3bef4bc 100644 --- a/packages/wallet/test/SingleAddressWallet/rollback.test.ts +++ b/packages/wallet/test/SingleAddressWallet/rollback.test.ts @@ -75,6 +75,7 @@ const createWallet = async (stores: WalletStores, providers: Providers, pollingC } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); return wallet; diff --git a/packages/wallet/test/SingleAddressWallet/shutdown.test.ts b/packages/wallet/test/SingleAddressWallet/shutdown.test.ts index 4a7a9e66b8d..ca9d3bba577 100644 --- a/packages/wallet/test/SingleAddressWallet/shutdown.test.ts +++ b/packages/wallet/test/SingleAddressWallet/shutdown.test.ts @@ -1,5 +1,6 @@ /* eslint-disable max-statements */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Crypto from '@cardano-sdk/crypto'; import * as mocks from '../mocks'; import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; import { AssetId, createStubStakePoolProvider, somePartialStakePools } from '@cardano-sdk/util-dev'; @@ -88,6 +89,7 @@ const createWallet = async ( } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); return wallet; diff --git a/packages/wallet/test/hardware/LedgerKeyAgent.integration.test.ts b/packages/wallet/test/hardware/LedgerKeyAgent.integration.test.ts index 0c832a70336..55b50a9bb8b 100644 --- a/packages/wallet/test/hardware/LedgerKeyAgent.integration.test.ts +++ b/packages/wallet/test/hardware/LedgerKeyAgent.integration.test.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { Cardano } from '@cardano-sdk/core'; import { CommunicationType, KeyAgent, LedgerKeyAgent, restoreKeyAgent, util } from '@cardano-sdk/key-management'; import { ObservableWallet, SingleAddressWallet, setupWallet } from '../../src'; @@ -52,11 +53,13 @@ describe('LedgerKeyAgent+SingleAddressWallet', () => { dependencies ), createWallet, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); const { wallet: restoredWallet } = await setupWallet({ createKeyAgent: (dependencies) => restoreKeyAgent(freshKeyAgent.serializableData, dependencies), createWallet, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); expect(await getAddress(freshWallet)).toEqual(await getAddress(restoredWallet)); diff --git a/packages/wallet/test/hardware/LedgerKeyAgent.test.ts b/packages/wallet/test/hardware/LedgerKeyAgent.test.ts index c3b0a0032be..89ba79ca108 100644 --- a/packages/wallet/test/hardware/LedgerKeyAgent.test.ts +++ b/packages/wallet/test/hardware/LedgerKeyAgent.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Crypto from '@cardano-sdk/crypto'; import * as mocks from '../mocks'; import { AddressType, @@ -69,6 +70,7 @@ describe('LedgerKeyAgent', () => { } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger })); }); diff --git a/packages/wallet/test/hardware/TrezorKeyAgent.integration.test.ts b/packages/wallet/test/hardware/TrezorKeyAgent.integration.test.ts index f2180b6a7d0..166673b7e37 100644 --- a/packages/wallet/test/hardware/TrezorKeyAgent.integration.test.ts +++ b/packages/wallet/test/hardware/TrezorKeyAgent.integration.test.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { Cardano } from '@cardano-sdk/core'; import { CommunicationType, KeyAgent, TrezorKeyAgent, restoreKeyAgent, util } from '@cardano-sdk/key-management'; import { ObservableWallet, SingleAddressWallet, setupWallet } from '../../src'; @@ -58,11 +59,13 @@ describe('TrezorKeyAgent+SingleAddressWallet', () => { dependencies ), createWallet, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); const { wallet: restoredWallet } = await setupWallet({ createKeyAgent: (dependencies) => restoreKeyAgent(freshKeyAgent.serializableData, dependencies), createWallet, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); diff --git a/packages/wallet/test/hardware/TrezorKeyAgent.test.ts b/packages/wallet/test/hardware/TrezorKeyAgent.test.ts index 4fcab81e715..5efad0a42aa 100644 --- a/packages/wallet/test/hardware/TrezorKeyAgent.test.ts +++ b/packages/wallet/test/hardware/TrezorKeyAgent.test.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import * as mocks from '../mocks'; import { AddressType, @@ -73,6 +74,7 @@ describe('TrezorKeyAgent', () => { } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger })); }); diff --git a/packages/wallet/test/integration/util.ts b/packages/wallet/test/integration/util.ts index a63ae31839e..5f3875ff5ac 100644 --- a/packages/wallet/test/integration/util.ts +++ b/packages/wallet/test/integration/util.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { SingleAddressWallet, setupWallet } from '../../src'; import { WalletStores } from '../../src/persistence'; import { createStubStakePoolProvider } from '@cardano-sdk/util-dev'; @@ -39,5 +40,6 @@ export const createWallet = async (stores?: WalletStores) => } ); }, + cryptoProvider: new Crypto.CmlEd25519Provider(), logger }); diff --git a/packages/wallet/test/setupWallet.test.ts b/packages/wallet/test/setupWallet.test.ts index 6e237d99406..7d86cfad4c4 100644 --- a/packages/wallet/test/setupWallet.test.ts +++ b/packages/wallet/test/setupWallet.test.ts @@ -1,3 +1,4 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { logger } from '@cardano-sdk/util-dev'; import { setupWallet } from '../src'; @@ -5,6 +6,8 @@ jest.mock('../src/services/WalletUtil'); const { createLazyWalletUtil } = jest.requireMock('../src/services/WalletUtil'); describe('setupWallet', () => { + const cryptoProvider = new Crypto.CmlEd25519Provider(); + it('initializes WalletUtil with the wallet that is then used as InputResolver for KeyAgent', async () => { const initialize = jest.fn(); const walletUtil = { initialize }; @@ -19,11 +22,12 @@ describe('setupWallet', () => { await setupWallet({ createKeyAgent, createWallet, + cryptoProvider, logger }) ).toEqual({ keyAgent, wallet, walletUtil }); expect(initialize).toBeCalledWith(wallet); - expect(createKeyAgent).toBeCalledWith({ inputResolver: walletUtil, logger }); + expect(createKeyAgent).toBeCalledWith({ cryptoProvider, inputResolver: walletUtil, logger }); expect(createWallet).toBeCalledWith(keyAgent); }); }); diff --git a/packages/web-extension/src/keyAgent/util.ts b/packages/web-extension/src/keyAgent/util.ts index 90857f62cbe..e8b03ae0b0a 100644 --- a/packages/web-extension/src/keyAgent/util.ts +++ b/packages/web-extension/src/keyAgent/util.ts @@ -7,6 +7,7 @@ export const keyAgentProperties: RemoteApiProperties = { deriveAddress: RemoteApiPropertyType.MethodReturningPromise, derivePublicKey: RemoteApiPropertyType.MethodReturningPromise, getChainId: RemoteApiPropertyType.MethodReturningPromise, + getCryptoProvider: RemoteApiPropertyType.MethodReturningPromise, getExtendedAccountPublicKey: RemoteApiPropertyType.MethodReturningPromise, knownAddresses$: RemoteApiPropertyType.HotObservable, signBlob: RemoteApiPropertyType.MethodReturningPromise, diff --git a/yarn.lock b/yarn.lock index 36af90bf52e..02228b59c16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2672,6 +2672,7 @@ __metadata: "@cardano-sdk/crypto": ^0.1.0 "@cardano-sdk/dapp-connector": ^0.6.1 "@cardano-sdk/util": ^0.7.0 + "@cardano-sdk/util-dev": ^0.6.0 "@emurgo/cardano-message-signing-nodejs": ^1.0.1 "@ledgerhq/hw-transport": ^6.27.2 "@ledgerhq/hw-transport-node-hid-noevents": ^6.27.2