diff --git a/packages/e2e/src/factories.ts b/packages/e2e/src/factories.ts index bc904dc040e..b0fd426077f 100644 --- a/packages/e2e/src/factories.ts +++ b/packages/e2e/src/factories.ts @@ -355,7 +355,16 @@ export const getWallet = async (props: GetWalletProps) => { ? () => Promise.resolve(keyAgent) : await keyManagementFactory.create(env.KEY_MANAGEMENT_PROVIDER, keyManagementParams, logger), createWallet: async (asyncKeyAgent: AsyncKeyAgent) => - new PersonalWallet({ name, polling }, { ...providers, keyAgent: asyncKeyAgent, logger, stores }), + new PersonalWallet( + { name, polling }, + { + ...providers, + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), + logger, + stores, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) + } + ), logger }); 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 59b4baafee5..8b48b8d4a9f 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 @@ -26,6 +26,7 @@ import { waitForWalletStateSettle, walletVariables } from '../../../src'; +import { util } from '@cardano-sdk/key-management'; // Example call that creates 5000 wallets in 10 minutes: // VIRTUAL_USERS_GENERATE_DURATION=600 VIRTUAL_USERS_COUNT=5000 yarn load-test-custom:wallet-init @@ -84,7 +85,15 @@ const createWallet = async (accountIndex: number): Promise => { measurementUtil.addMeasureMarker(MeasureTarget.keyAgent, accountIndex); measurementUtil.addStartMarker(MeasureTarget.wallet, accountIndex); - const wallet = new PersonalWallet({ name: `Wallet ${accountIndex}` }, { ...providers, keyAgent, logger }); + const wallet = new PersonalWallet( + { name: `Wallet ${accountIndex}` }, + { + ...providers, + addressManager: util.createBip32Ed25519AddressManager(keyAgent), + logger, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) + } + ); walletUtil.initialize(wallet); return wallet; }; diff --git a/packages/e2e/test/local-network/register-pool.test.ts b/packages/e2e/test/local-network/register-pool.test.ts index dae7fddebd7..7ed9befa675 100644 --- a/packages/e2e/test/local-network/register-pool.test.ts +++ b/packages/e2e/test/local-network/register-pool.test.ts @@ -3,6 +3,7 @@ import { Cardano } from '@cardano-sdk/core'; import { KeyAgentFactoryProps, TestWallet, + bip32Ed25519Factory, getEnv, getWallet, submitCertificate, @@ -41,6 +42,7 @@ const wallet2Params: KeyAgentFactoryProps = { describe('local-network/register-pool', () => { let wallet1: TestWallet; let wallet2: TestWallet; + let bip32Ed25519: Crypto.Bip32Ed25519; beforeAll(async () => { jest.setTimeout(180_000); @@ -64,6 +66,7 @@ describe('local-network/register-pool', () => { await waitForWalletStateSettle(wallet1.wallet); await waitForWalletStateSettle(wallet2.wallet); + bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger); }); afterAll(() => { @@ -76,18 +79,17 @@ describe('local-network/register-pool', () => { await walletReady(wallet); - const poolKeyAgent = wallet.keyAgent; + const poolAddressManager = wallet.addressManager; - const poolPubKey = await poolKeyAgent.derivePublicKey({ + const poolPubKey = await poolAddressManager.derivePublicKey({ index: 0, role: KeyRole.External }); - const bip32Ed25519 = await poolKeyAgent.getBip32Ed25519(); const poolKeyHash = await bip32Ed25519.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( - await poolKeyAgent.deriveAddress( + await poolAddressManager.deriveAddress( { index: 0, type: AddressType.External @@ -165,18 +167,17 @@ describe('local-network/register-pool', () => { await walletReady(wallet); - const poolKeyAgent = wallet.keyAgent; + const poolAddressManager = wallet.addressManager; - const poolPubKey = await poolKeyAgent.derivePublicKey({ + const poolPubKey = await poolAddressManager.derivePublicKey({ index: 0, role: KeyRole.External }); - const bip32Ed25519 = await poolKeyAgent.getBip32Ed25519(); const poolKeyHash = await bip32Ed25519.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( - await poolKeyAgent.deriveAddress( + await poolAddressManager.deriveAddress( { index: 0, type: AddressType.External diff --git a/packages/e2e/test/long-running/cache-invalidation.test.ts b/packages/e2e/test/long-running/cache-invalidation.test.ts index 4e2ce167195..5ba31439c97 100644 --- a/packages/e2e/test/long-running/cache-invalidation.test.ts +++ b/packages/e2e/test/long-running/cache-invalidation.test.ts @@ -1,8 +1,10 @@ +import * as Crypto from '@cardano-sdk/crypto'; import { AddressType, KeyRole } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { KeyAgentFactoryProps, TestWallet, + bip32Ed25519Factory, getEnv, getTxConfirmationEpoch, getWallet, @@ -23,6 +25,7 @@ const vrf = Cardano.VrfVkHex('2ee5a4c423224bb9c42107fc18a60556d6a83cec1d9dd37a71 describe('cache invalidation', () => { let testProviderServer: Docker.Container; let wallet1: TestWallet; + let bip32Ed25519: Crypto.Bip32Ed25519; beforeAll(async () => { const port = await getRandomPort(); @@ -90,6 +93,7 @@ describe('cache invalidation', () => { }); await waitForWalletStateSettle(wallet1.wallet); + bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger); }); afterAll(async () => { @@ -103,18 +107,17 @@ describe('cache invalidation', () => { await walletReady(wallet); - const poolKeyAgent = wallet.keyAgent; + const poolAddressManager = wallet.addressManager; - const poolPubKey = await poolKeyAgent.derivePublicKey({ + const poolPubKey = await poolAddressManager.derivePublicKey({ index: 0, role: KeyRole.External }); - const bip32Ed25519 = await poolKeyAgent.getBip32Ed25519(); const poolKeyHash = await bip32Ed25519.getPubKeyHash(poolPubKey); const poolId = Cardano.PoolId.fromKeyHash(poolKeyHash); const poolRewardAccount = ( - await poolKeyAgent.deriveAddress( + await poolAddressManager.deriveAddress( { index: 0, type: AddressType.External diff --git a/packages/e2e/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts b/packages/e2e/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts index ada06992565..cd191584112 100644 --- a/packages/e2e/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts +++ b/packages/e2e/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts @@ -6,7 +6,15 @@ import { MultiSigWallet } from './MultiSigWallet'; import { Observable, filter, firstValueFrom, map, take } from 'rxjs'; import { PersonalWallet } from '@cardano-sdk/wallet'; import { TrackerSubject } from '@cardano-sdk/util-rxjs'; -import { createStandaloneKeyAgent, getEnv, getWallet, waitForEpoch, walletReady, walletVariables } from '../../../src'; +import { + bip32Ed25519Factory, + createStandaloneKeyAgent, + getEnv, + getWallet, + waitForEpoch, + walletReady, + walletVariables +} from '../../../src'; import { isNotNil } from '@cardano-sdk/util'; import { logger } from '@cardano-sdk/util-dev'; @@ -52,14 +60,10 @@ const fundMultiSigWallet = async (sendingWallet: PersonalWallet, address: Cardan await sendingWallet.submitTx(signedTx); }; -const getKeyAgent = async (mnemonics: string, faucetWallet: PersonalWallet) => { +const getKeyAgent = async (mnemonics: string, faucetWallet: PersonalWallet, bip32Ed25519: Crypto.Bip32Ed25519) => { const genesis = await firstValueFrom(faucetWallet.genesisParameters$); - const keyAgent = await createStandaloneKeyAgent( - mnemonics.split(' '), - genesis, - await faucetWallet.keyAgent.getBip32Ed25519() - ); + const keyAgent = await createStandaloneKeyAgent(mnemonics.split(' '), genesis, bip32Ed25519); const pubKey = await keyAgent.derivePublicKey(DERIVATION_PATH); @@ -139,9 +143,15 @@ describe('multi signature wallet', () => { beforeAll(async () => { await initializeFaucet(); - ({ keyAgent: aliceKeyAgent, pubKey: alicePubKey } = await getKeyAgent(aliceMnemonics, faucetWallet)); - ({ keyAgent: bobKeyAgent, pubKey: bobPubKey } = await getKeyAgent(bobMnemonics, faucetWallet)); - ({ keyAgent: charlotteKeyAgent, pubKey: charlottePubKey } = await getKeyAgent(charlotteMnemonics, faucetWallet)); + const bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger); + + ({ keyAgent: aliceKeyAgent, pubKey: alicePubKey } = await getKeyAgent(aliceMnemonics, faucetWallet, bip32Ed25519)); + ({ keyAgent: bobKeyAgent, pubKey: bobPubKey } = await getKeyAgent(bobMnemonics, faucetWallet, bip32Ed25519)); + ({ keyAgent: charlotteKeyAgent, pubKey: charlottePubKey } = await getKeyAgent( + charlotteMnemonics, + faucetWallet, + bip32Ed25519 + )); faucetAddress = (await firstValueFrom(faucetWallet.addresses$))[0].address; diff --git a/packages/e2e/test/wallet/PersonalWallet/delegation.test.ts b/packages/e2e/test/wallet/PersonalWallet/delegation.test.ts index cd8dd562fd6..35117e1a2f1 100644 --- a/packages/e2e/test/wallet/PersonalWallet/delegation.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/delegation.test.ts @@ -1,10 +1,12 @@ /* eslint-disable max-statements */ +import * as Crypto from '@cardano-sdk/crypto'; import { BigIntMath } from '@cardano-sdk/util'; import { Cardano } from '@cardano-sdk/core'; import { ObservableWallet, PersonalWallet } from '@cardano-sdk/wallet'; import { TX_TIMEOUT_DEFAULT, TestWallet, + bip32Ed25519Factory, firstValueFromTimed, getEnv, getWallet, @@ -27,6 +29,7 @@ const getWalletStateSnapshot = async (wallet: ObservableWallet) => { const utxoTotal = await firstValueFrom(wallet.utxo.total$); const utxoAvailable = await firstValueFrom(wallet.utxo.available$); const rewardsBalance = await firstValueFrom(wallet.balance.rewardAccounts.rewards$); + return { balance: { available: balanceAvailable, deposit, total: balanceTotal }, epoch: epoch.epochNo, @@ -54,6 +57,7 @@ const waitForTx = async (wallet: ObservableWallet, hash: Cardano.TransactionId) describe('PersonalWallet/delegation', () => { let wallet1: TestWallet; let wallet2: TestWallet; + let bip32Ed25519: Crypto.Bip32Ed25519; beforeAll(async () => { jest.setTimeout(180_000); @@ -61,6 +65,7 @@ describe('PersonalWallet/delegation', () => { wallet2 = await getWallet({ env, idx: 1, logger, name: 'Test Wallet 2', polling: { interval: 500 } }); await Promise.all([waitForWalletStateSettle(wallet1.wallet), waitForWalletStateSettle(wallet2.wallet)]); + bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger); }); afterAll(() => { @@ -164,9 +169,7 @@ describe('PersonalWallet/delegation', () => { expect(tx1ConfirmedState.rewardAccount.delegatee?.currentEpoch?.id).toEqual(poolId); } - const stakeKeyHash = await ( - await sourceWallet.keyAgent.getBip32Ed25519() - ).getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey); + const stakeKeyHash = await bip32Ed25519.getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey); expect(stakeKeyHash).toEqual(Cardano.RewardAccount.toHash(tx1ConfirmedState.rewardAccount.address)); expect(tx1ConfirmedState.publicStakeKey.keyStatus).toBe(Cardano.StakeKeyStatus.Registered); diff --git a/packages/e2e/test/wallet/PersonalWallet/handle.test.ts b/packages/e2e/test/wallet/PersonalWallet/handle.test.ts index 231ee233f98..c9916b6d486 100644 --- a/packages/e2e/test/wallet/PersonalWallet/handle.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/handle.test.ts @@ -3,6 +3,7 @@ import { Cardano, metadatum } from '@cardano-sdk/core'; import { KeyAgent, TransactionSigner } from '@cardano-sdk/key-management'; import { PersonalWallet } from '@cardano-sdk/wallet'; import { + bip32Ed25519Factory, burnTokens, coinsRequiredByHandleMint, createHandleMetadata, @@ -49,7 +50,7 @@ describe('Ada handle', () => { keyAgent = await createStandaloneKeyAgent( env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '), await firstValueFrom(wallet.genesisParameters$), - await wallet.keyAgent.getBip32Ed25519() + await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger) ); ({ policyScript, policySigner, policyId } = await createHandlePolicy(keyAgent)); const handleProviderPolicyId = await getHandlePolicyId( diff --git a/packages/e2e/test/wallet/PersonalWallet/mint.test.ts b/packages/e2e/test/wallet/PersonalWallet/mint.test.ts index 8916e649569..fa2f9996d5d 100644 --- a/packages/e2e/test/wallet/PersonalWallet/mint.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/mint.test.ts @@ -3,6 +3,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet'; import { InitializeTxProps } from '@cardano-sdk/tx-construction'; import { KeyRole, util } from '@cardano-sdk/key-management'; import { + bip32Ed25519Factory, burnTokens, createStandaloneKeyAgent, getEnv, @@ -36,7 +37,7 @@ describe('PersonalWallet/mint', () => { const aliceKeyAgent = await createStandaloneKeyAgent( env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '), genesis, - await wallet.keyAgent.getBip32Ed25519() + await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger) ); const derivationPath = { diff --git a/packages/e2e/test/wallet/PersonalWallet/multiAddress.test.ts b/packages/e2e/test/wallet/PersonalWallet/multiAddress.test.ts index 14d50f24c79..14227acf4c0 100644 --- a/packages/e2e/test/wallet/PersonalWallet/multiAddress.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/multiAddress.test.ts @@ -3,6 +3,7 @@ import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { KeyAgentFactoryProps, + bip32Ed25519Factory, createStandaloneKeyAgent, firstValueFromTimed, getWallet, @@ -42,7 +43,7 @@ describe('PersonalWallet/multiAddress', () => { const multiAddressKeyAgent = await createStandaloneKeyAgent( mnemonics, genesis, - await wallet.keyAgent.getBip32Ed25519() + await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger) ); let txBuilder = wallet.createTxBuilder(); diff --git a/packages/e2e/test/wallet/PersonalWallet/multisignature.test.ts b/packages/e2e/test/wallet/PersonalWallet/multisignature.test.ts index 22e94fe2e58..60c04060291 100644 --- a/packages/e2e/test/wallet/PersonalWallet/multisignature.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/multisignature.test.ts @@ -4,6 +4,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet'; import { InitializeTxProps } from '@cardano-sdk/tx-construction'; import { KeyRole, util } from '@cardano-sdk/key-management'; import { + bip32Ed25519Factory, burnTokens, createStandaloneKeyAgent, getEnv, @@ -34,15 +35,16 @@ describe('PersonalWallet/multisignature', () => { const genesis = await firstValueFrom(wallet.genesisParameters$); + const bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger); const aliceKeyAgent = await createStandaloneKeyAgent( env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '), genesis, - await wallet.keyAgent.getBip32Ed25519() + bip32Ed25519 ); const bobKeyAgent = await createStandaloneKeyAgent( env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '), genesis, - await wallet.keyAgent.getBip32Ed25519() + bip32Ed25519 ); const aliceDerivationPath = { diff --git a/packages/e2e/test/wallet/PersonalWallet/nft.test.ts b/packages/e2e/test/wallet/PersonalWallet/nft.test.ts index f8cb905cd7b..5ff7c8e4c07 100644 --- a/packages/e2e/test/wallet/PersonalWallet/nft.test.ts +++ b/packages/e2e/test/wallet/PersonalWallet/nft.test.ts @@ -4,6 +4,7 @@ import { Assets, FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet'; import { InitializeTxProps } from '@cardano-sdk/tx-construction'; import { KeyRole, TransactionSigner, util } from '@cardano-sdk/key-management'; import { + bip32Ed25519Factory, burnTokens, createStandaloneKeyAgent, firstValueFromTimed, @@ -60,7 +61,7 @@ describe('PersonalWallet.assets/nft', () => { const keyAgent = await createStandaloneKeyAgent( env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '), genesis, - await wallet.keyAgent.getBip32Ed25519() + await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger) ); const derivationPath = { diff --git a/packages/key-management/src/cip8/cip30signData.ts b/packages/key-management/src/cip8/cip30signData.ts index f1e6e4f04b4..696bcca43c8 100644 --- a/packages/key-management/src/cip8/cip30signData.ts +++ b/packages/key-management/src/cip8/cip30signData.ts @@ -13,16 +13,22 @@ import { ProtectedHeaderMap, SigStructure } from '@emurgo/cardano-message-signing-nodejs'; -import { AsyncKeyAgent, KeyRole } from '../types'; +import { + Bip32Ed25519AddressManager, + Bip32Ed25519Witnesser, + DREP_KEY_DERIVATION_PATH, + STAKE_KEY_DERIVATION_PATH +} from '../util'; import { Cardano, util } from '@cardano-sdk/core'; import { Cip30DataSignature } from '@cardano-sdk/dapp-connector'; import { ComposableError, HexBlob } from '@cardano-sdk/util'; import { CoseLabel } from './util'; -import { DREP_KEY_DERIVATION_PATH, STAKE_KEY_DERIVATION_PATH } from '../util'; +import { KeyRole } from '../types'; import { filter, firstValueFrom } from 'rxjs'; export interface Cip30SignDataRequest { - keyAgent: AsyncKeyAgent; + addressManager: Bip32Ed25519AddressManager; + witnesser: Bip32Ed25519Witnesser; signWith: Cardano.PaymentAddress | Cardano.RewardAccount | Cardano.DRepID; payload: HexBlob; } @@ -51,7 +57,7 @@ const getAddressBytes = (signWith: Cardano.PaymentAddress | Cardano.RewardAccoun const getDerivationPath = async ( signWith: Cardano.PaymentAddress | Cardano.RewardAccount | Cardano.DRepID, - keyAgent: AsyncKeyAgent + addressManager: Bip32Ed25519AddressManager ) => { if (Cardano.DRepID.isValid(signWith)) { return DREP_KEY_DERIVATION_PATH; @@ -60,7 +66,7 @@ const getDerivationPath = async ( const isRewardAccount = signWith.startsWith('stake'); const knownAddresses = await firstValueFrom( - keyAgent.knownAddresses$.pipe(filter((addresses) => addresses.length > 0)) + addressManager.knownAddresses$.pipe(filter((addresses) => addresses.length > 0)) ); if (isRewardAccount) { @@ -90,12 +96,12 @@ const createSigStructureHeaders = (addressBytes: Uint8Array) => { }; const signSigStructure = ( - keyAgent: AsyncKeyAgent, + witnesser: Bip32Ed25519Witnesser, derivationPath: AccountKeyDerivationPath, sigStructure: SigStructure ) => { try { - return keyAgent.signBlob(derivationPath, util.bytesToHex(sigStructure.to_bytes())); + return witnesser.signBlob(derivationPath, util.bytesToHex(sigStructure.to_bytes())); } catch (error) { throw new Cip30DataSignError(Cip30DataSignErrorCode.UserDeclined, 'Failed to sign', error); } @@ -117,7 +123,8 @@ const createCoseKey = (addressBytes: Uint8Array, publicKey: Crypto.Ed25519Public * @throws {Cip30DataSignError} */ export const cip30signData = async ({ - keyAgent, + addressManager, + witnesser, signWith, payload }: Cip30SignDataRequest): Promise => { @@ -125,7 +132,7 @@ export const cip30signData = async ({ throw new Cip30DataSignError(Cip30DataSignErrorCode.AddressNotPK, 'Invalid address'); } const addressBytes = getAddressBytes(signWith); - const derivationPath = await getDerivationPath(signWith, keyAgent); + const derivationPath = await getDerivationPath(signWith, addressManager); const builder = COSESign1Builder.new( Headers.new(ProtectedHeaderMap.new(createSigStructureHeaders(addressBytes)), HeaderMap.new()), @@ -133,7 +140,7 @@ export const cip30signData = async ({ false ); const sigStructure = builder.make_data_to_sign(); - const { signature, publicKey } = await signSigStructure(keyAgent, derivationPath, sigStructure); + const { signature, publicKey } = await signSigStructure(witnesser, derivationPath, sigStructure); const coseSign1 = builder.build(Buffer.from(signature, 'hex')); const coseKey = createCoseKey(addressBytes, publicKey); diff --git a/packages/key-management/src/types.ts b/packages/key-management/src/types.ts index 4f4900ab99a..67c18259467 100644 --- a/packages/key-management/src/types.ts +++ b/packages/key-management/src/types.ts @@ -201,3 +201,34 @@ export interface TransactionSigner { */ sign(tx: Cardano.TxBodyWithHash): Promise; } + +export type WitnessOptions = SignTransactionOptions; + +/** Interface for an entity capable of generating witness data for a transaction. */ +export interface Witnesser { + /** + * Generates the witness data for a given transaction. + * + * @param txInternals The transaction body along with its hash for which the witness data is to be generated. + * @param options Optional additional parameters that may influence how the witness data is generated. + * @returns A promise that resolves to the generated witness data for the transaction. + */ + witness(txInternals: Cardano.TxBodyWithHash, options?: WitnessOptions): Promise; + + /** + * @throws AuthenticationError + */ + signBlob(derivationPath: AccountKeyDerivationPath, blob: HexBlob): Promise; +} + +/** Interface for managing blockchain addresses. */ +export interface AddressManager { + knownAddresses$: Observable; + + /** + * Sets or updates the list of known addresses managed by this instance. + * + * @param addresses An array of grouped addresses to be managed. + */ + setKnownAddresses(addresses: GroupedAddress[]): Promise; +} diff --git a/packages/key-management/src/util/createAddressManager.ts b/packages/key-management/src/util/createAddressManager.ts new file mode 100644 index 00000000000..2eb7d7fe0a6 --- /dev/null +++ b/packages/key-management/src/util/createAddressManager.ts @@ -0,0 +1,53 @@ +import * as Crypto from '@cardano-sdk/crypto'; +import { + AccountAddressDerivationPath, + AccountKeyDerivationPath, + AddressManager, + AsyncKeyAgent, + GroupedAddress +} from '../types'; +import { Observable } from 'rxjs'; + +/** An address manager that uses a {@link AsyncKeyAgent} to derive addresses. */ +export class Bip32Ed25519AddressManager implements AddressManager { + knownAddresses$: Observable; + #keyAgent: AsyncKeyAgent; + + /** + * Initializes a new instance of the Bip32Ed25519AddressManager class. + * + * @param keyAgent The key agent that will be used to derive addresses. + */ + constructor(keyAgent: AsyncKeyAgent) { + this.#keyAgent = keyAgent; + this.knownAddresses$ = keyAgent.knownAddresses$; + } + + async setKnownAddresses(addresses: GroupedAddress[]): Promise { + return this.#keyAgent.setKnownAddresses(addresses); + } + + async derivePublicKey(derivationPath: AccountKeyDerivationPath): Promise { + return this.#keyAgent.derivePublicKey(derivationPath); + } + + async deriveAddress( + paymentKeyDerivationPath: AccountAddressDerivationPath, + stakeKeyDerivationIndex: number, + pure?: boolean + ): Promise { + return this.#keyAgent.deriveAddress(paymentKeyDerivationPath, stakeKeyDerivationIndex, pure); + } + + shutdown(): void { + this.#keyAgent.shutdown(); + } +} + +/** + * Creates a new instance of the Bip32Ed25519AddressManager class. + * + * @param keyAgent The key agent that will be used to derive addresses. + */ +export const createBip32Ed25519AddressManager = (keyAgent: AsyncKeyAgent): Bip32Ed25519AddressManager => + new Bip32Ed25519AddressManager(keyAgent); diff --git a/packages/key-management/src/util/createWitnesser.ts b/packages/key-management/src/util/createWitnesser.ts new file mode 100644 index 00000000000..863b4ed76d0 --- /dev/null +++ b/packages/key-management/src/util/createWitnesser.ts @@ -0,0 +1,23 @@ +import { AccountKeyDerivationPath, AsyncKeyAgent, SignBlobResult, WitnessOptions, Witnesser } from '../types'; +import { Cardano } from '@cardano-sdk/core'; +import { HexBlob } from '@cardano-sdk/util'; + +/** A witnesser that uses a {@link KeyAgent} to generate witness data for a transaction. */ +export class Bip32Ed25519Witnesser implements Witnesser { + #keyAgent: AsyncKeyAgent; + + constructor(keyAgent: AsyncKeyAgent) { + this.#keyAgent = keyAgent; + } + + async witness(txInternals: Cardano.TxBodyWithHash, options?: WitnessOptions): Promise { + return { signatures: await this.#keyAgent.signTransaction(txInternals, options) }; + } + + async signBlob(derivationPath: AccountKeyDerivationPath, blob: HexBlob): Promise { + return this.#keyAgent.signBlob(derivationPath, blob); + } +} + +export const createBip32Ed25519Witnesser = (keyAgent: AsyncKeyAgent): Bip32Ed25519Witnesser => + new Bip32Ed25519Witnesser(keyAgent); diff --git a/packages/key-management/src/util/ensureStakeKeys.ts b/packages/key-management/src/util/ensureStakeKeys.ts index 9e2044f21b4..50dd1796f75 100644 --- a/packages/key-management/src/util/ensureStakeKeys.ts +++ b/packages/key-management/src/util/ensureStakeKeys.ts @@ -1,11 +1,12 @@ -import { AddressType, AsyncKeyAgent, KeyRole } from '../types'; +import { AddressType, KeyRole } from '../types'; +import { Bip32Ed25519AddressManager } from './createAddressManager'; import { Cardano } from '@cardano-sdk/core'; import { Logger } from 'ts-log'; import { firstValueFrom } from 'rxjs'; export interface EnsureStakeKeysParams { /** Key agent to use */ - keyAgent: AsyncKeyAgent; + addressManager: Bip32Ed25519AddressManager; /** Requested number of stake keys */ count: number; /** The payment key index to use when more stake keys are needed */ @@ -15,12 +16,12 @@ export interface EnsureStakeKeysParams { /** Given a count, checks if enough stake keys exist and derives more if needed. Returns all reward accounts */ export const ensureStakeKeys = async ({ - keyAgent, + addressManager, count, logger, paymentKeyIndex: index = 0 }: EnsureStakeKeysParams): Promise => { - const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); + const knownAddresses = await firstValueFrom(addressManager.knownAddresses$); const stakeKeys = new Map( knownAddresses .filter( @@ -35,7 +36,7 @@ export const ensureStakeKeys = async ({ // Need more stake keys for the portfolio for (let stakeKeyIdx = 0; stakeKeys.size < count; stakeKeyIdx++) { if (!stakeKeys.has(stakeKeyIdx)) { - const address = await keyAgent.deriveAddress({ index, type: AddressType.External }, stakeKeyIdx); + const address = await addressManager.deriveAddress({ index, type: AddressType.External }, stakeKeyIdx); logger.debug( `No derivation with stake key index ${stakeKeyIdx} exists. Derived a new stake key ${address.rewardAccount}.` ); diff --git a/packages/key-management/src/util/index.ts b/packages/key-management/src/util/index.ts index 8c8e91354b1..a7421c31f6f 100644 --- a/packages/key-management/src/util/index.ts +++ b/packages/key-management/src/util/index.ts @@ -5,3 +5,5 @@ export * from './ownSignatureKeyPaths'; export * from './stubSignTransaction'; export * from './KeyAgentTransactionSigner'; export * from './ensureStakeKeys'; +export * from './createAddressManager'; +export * from './createWitnesser'; diff --git a/packages/key-management/test/cip8/cip30signData.test.ts b/packages/key-management/test/cip8/cip30signData.test.ts index 0fc7fd2dfc9..ca049c6c04a 100644 --- a/packages/key-management/test/cip8/cip30signData.test.ts +++ b/packages/key-management/test/cip8/cip30signData.test.ts @@ -1,5 +1,13 @@ import * as Crypto from '@cardano-sdk/crypto'; -import { AddressType, AsyncKeyAgent, GroupedAddress, KeyAgent, KeyRole, cip8 } from '../../src'; +import { + AddressType, + AsyncKeyAgent, + GroupedAddress, + KeyAgent, + util as KeyManagementUtil, + KeyRole, + cip8 +} from '../../src'; import { COSEKey, COSESign1, SigStructure } from '@emurgo/cardano-message-signing-nodejs'; import { Cardano, util } from '@cardano-sdk/core'; import { CoseLabel } from '../../src/cip8/util'; @@ -22,9 +30,10 @@ describe('cip30signData', () => { const signAndDecode = async (signWith: Cardano.PaymentAddress | Cardano.RewardAccount) => { const dataSignature = await cip8.cip30signData({ - keyAgent: asyncKeyAgent, + addressManager: KeyManagementUtil.createBip32Ed25519AddressManager(asyncKeyAgent), payload: HexBlob('abc123'), - signWith + signWith, + witnesser: KeyManagementUtil.createBip32Ed25519Witnesser(asyncKeyAgent) }); const coseKey = COSEKey.from_bytes(Buffer.from(dataSignature.key, 'hex')); diff --git a/packages/key-management/test/util/createAddressManager.test.ts b/packages/key-management/test/util/createAddressManager.test.ts new file mode 100644 index 00000000000..1e8b2ae32e8 --- /dev/null +++ b/packages/key-management/test/util/createAddressManager.test.ts @@ -0,0 +1,50 @@ +import * as Crypto from '@cardano-sdk/crypto'; +import { AsyncKeyAgent, InMemoryKeyAgent, KeyRole, util } from '../../src'; +import { Cardano } from '@cardano-sdk/core'; +import { dummyLogger } from 'ts-log'; + +describe('createBip32Ed25519AddressManager', () => { + let asyncKeyAgent: AsyncKeyAgent; + let addressManager: util.Bip32Ed25519AddressManager; + let inputResolver: jest.Mocked; + const addressDerivationPath = { index: 0, type: 0 }; + + beforeEach(async () => { + const mnemonicWords = util.generateMnemonicWords(); + const getPassphrase = jest.fn().mockResolvedValue(Buffer.from('password')); + inputResolver = { resolveInput: jest.fn() }; + const keyAgent = await InMemoryKeyAgent.fromBip39MnemonicWords( + { + chainId: Cardano.ChainIds.Preview, + getPassphrase, + mnemonicWords + }, + { bip32Ed25519: new Crypto.SodiumBip32Ed25519(), inputResolver, logger: dummyLogger } + ); + asyncKeyAgent = util.createAsyncKeyAgent(keyAgent); + addressManager = util.createBip32Ed25519AddressManager(asyncKeyAgent); + }); + + it('deriveAddress is unchanged', async () => { + await expect(asyncKeyAgent.deriveAddress(addressDerivationPath, 0)).resolves.toEqual( + await addressManager.deriveAddress(addressDerivationPath, 0) + ); + }); + + it('derivePublicKey is unchanged', async () => { + await expect(asyncKeyAgent.derivePublicKey({ index: 0, role: KeyRole.External })).resolves.toEqual( + await addressManager.derivePublicKey({ index: 0, role: KeyRole.External }) + ); + }); + + it('stops emitting addresses$ after shutdown', (done) => { + addressManager.shutdown(); + addressManager.knownAddresses$.subscribe({ + complete: done, + next: () => { + throw new Error('Should not emit'); + } + }); + void addressManager.deriveAddress(addressDerivationPath, 0); + }); +}); diff --git a/packages/key-management/test/util/createWitnesser.test.ts b/packages/key-management/test/util/createWitnesser.test.ts new file mode 100644 index 00000000000..62e1dc1fdfe --- /dev/null +++ b/packages/key-management/test/util/createWitnesser.test.ts @@ -0,0 +1,51 @@ +import * as Crypto from '@cardano-sdk/crypto'; +import { AsyncKeyAgent, InMemoryKeyAgent, Witnesser, util } from '../../src'; +import { Cardano } from '@cardano-sdk/core'; +import { HexBlob } from '@cardano-sdk/util'; +import { dummyLogger } from 'ts-log'; + +describe('createBip32Ed25519Witnesser', () => { + let asyncKeyAgent: AsyncKeyAgent; + let witnesser: Witnesser; + let inputResolver: jest.Mocked; + + beforeEach(async () => { + const mnemonicWords = util.generateMnemonicWords(); + const getPassphrase = jest.fn().mockResolvedValue(Buffer.from('password')); + inputResolver = { resolveInput: jest.fn() }; + const keyAgent = await InMemoryKeyAgent.fromBip39MnemonicWords( + { + chainId: Cardano.ChainIds.Preview, + getPassphrase, + mnemonicWords + }, + { bip32Ed25519: new Crypto.SodiumBip32Ed25519(), inputResolver, logger: dummyLogger } + ); + asyncKeyAgent = util.createAsyncKeyAgent(keyAgent); + witnesser = util.createBip32Ed25519Witnesser(asyncKeyAgent); + }); + + it('signBlob is unchanged', async () => { + const keyDerivationPath = { index: 0, role: 0 }; + const blob = HexBlob('abc123'); + + await expect(asyncKeyAgent.signBlob(keyDerivationPath, blob)).resolves.toEqual( + await witnesser.signBlob(keyDerivationPath, blob) + ); + }); + + it('signTransaction is unchanged', async () => { + inputResolver.resolveInput.mockResolvedValue(null); + + const txInternals = { + body: { fee: 20_000n, inputs: [], outputs: [], validityInterval: {} } as Cardano.HydratedTxBody, + hash: Cardano.TransactionId('8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec') + }; + + await expect(asyncKeyAgent.signTransaction(txInternals)).resolves.toEqual( + ( + await witnesser.witness(txInternals) + ).signatures + ); + }); +}); diff --git a/packages/key-management/test/util/ensureStakeKeys.test.ts b/packages/key-management/test/util/ensureStakeKeys.test.ts index f42555c3cb3..0d45bb10c2d 100644 --- a/packages/key-management/test/util/ensureStakeKeys.test.ts +++ b/packages/key-management/test/util/ensureStakeKeys.test.ts @@ -26,7 +26,11 @@ describe('ensureStakeKeys', () => { }); it('can derive one stake key', async () => { - const newRewardAccounts = await util.ensureStakeKeys({ count: 1, keyAgent, logger }); + const newRewardAccounts = await util.ensureStakeKeys({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), + count: 1, + logger + }); const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); expect(knownAddresses.length).toBe(1); expect(knownAddresses.map(({ rewardAccount }) => rewardAccount)).toEqual(newRewardAccounts); @@ -36,7 +40,11 @@ describe('ensureStakeKeys', () => { await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 1); - await util.ensureStakeKeys({ count: 2, keyAgent, logger }); + await util.ensureStakeKeys({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), + count: 2, + logger + }); const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); expect(knownAddresses.length).toBe(2); @@ -46,7 +54,7 @@ describe('ensureStakeKeys', () => { await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 2); - await util.ensureStakeKeys({ count: 4, keyAgent, logger }); + await util.ensureStakeKeys({ addressManager: util.createBip32Ed25519AddressManager(keyAgent), count: 4, logger }); const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); const stakeKeyIndices = knownAddresses.map(({ stakeKeyDerivationPath }) => stakeKeyDerivationPath?.index).sort(); @@ -57,7 +65,12 @@ describe('ensureStakeKeys', () => { await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 2); - await util.ensureStakeKeys({ count: 4, keyAgent, logger, paymentKeyIndex: 1 }); + await util.ensureStakeKeys({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), + count: 4, + logger, + paymentKeyIndex: 1 + }); const stakeKeyIndicesPaymentKey1 = (await firstValueFrom(keyAgent.knownAddresses$)) .filter(({ index }) => index === 1) @@ -67,7 +80,7 @@ describe('ensureStakeKeys', () => { it('can handle request of 0 stake keys', async () => { await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); - await util.ensureStakeKeys({ count: 0, keyAgent, logger }); + await util.ensureStakeKeys({ addressManager: util.createBip32Ed25519AddressManager(keyAgent), count: 0, logger }); const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); expect(knownAddresses.length).toBe(1); @@ -75,7 +88,9 @@ describe('ensureStakeKeys', () => { it('returns all reward accounts', async () => { await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); - await expect(util.ensureStakeKeys({ count: 2, keyAgent, logger })).resolves.toHaveLength(2); + await expect( + util.ensureStakeKeys({ addressManager: util.createBip32Ed25519AddressManager(keyAgent), count: 2, logger }) + ).resolves.toHaveLength(2); }); it('takes into account addresses with multiple stake keys and payment keys', async () => { @@ -86,7 +101,7 @@ describe('ensureStakeKeys', () => { keyAgent.deriveAddress({ index: 1, type: AddressType.External }, 2) ]); - await util.ensureStakeKeys({ count: 5, keyAgent, logger }); + await util.ensureStakeKeys({ addressManager: util.createBip32Ed25519AddressManager(keyAgent), count: 5, logger }); const knownAddresses = await firstValueFrom(keyAgent.knownAddresses$); expect(knownAddresses.length).toBe(6); diff --git a/packages/tx-construction/src/tx-builder/TxBuilder.ts b/packages/tx-construction/src/tx-builder/TxBuilder.ts index 095b2d721ed..284de630f31 100644 --- a/packages/tx-construction/src/tx-builder/TxBuilder.ts +++ b/packages/tx-construction/src/tx-builder/TxBuilder.ts @@ -27,7 +27,7 @@ import { Logger } from 'ts-log'; import { OutputBuilderValidator, TxOutputBuilder } from './OutputBuilder'; import { RewardAccountWithPoolId } from '../types'; import { coldObservableProvider } from '@cardano-sdk/util-rxjs'; -import { contextLogger, deepEquals, patchObject } from '@cardano-sdk/util'; +import { contextLogger, deepEquals } from '@cardano-sdk/util'; import { createOutputValidator } from '../output-validation'; import { filter, firstValueFrom, lastValueFrom } from 'rxjs'; import { finalizeTx } from './finalizeTx'; @@ -104,12 +104,7 @@ export class GenericTxBuilder implements TxBuilder { createOutputValidator({ protocolParameters: dependencies.txBuilderProviders.protocolParameters }); - this.#dependencies = { - ...dependencies, - keyAgent: patchObject(dependencies.keyAgent, { - knownAddresses$: dependencies.keyAgent.knownAddresses$.pipe(filter((addresses) => addresses.length > 0)) - }) - }; + this.#dependencies = dependencies; this.#logger = dependencies.logger; this.#handleProvider = dependencies.handleProvider; this.#handleResolutions = []; @@ -204,7 +199,9 @@ export class GenericTxBuilder implements TxBuilder { await this.#validateOutputs(); // Take a snapshot of returned properties, // so that they don't change while `initializeTx` is resolving - const ownAddresses = await firstValueFrom(this.#dependencies.keyAgent.knownAddresses$); + const ownAddresses = await firstValueFrom( + this.#dependencies.addressManager.knownAddresses$.pipe(filter((addresses) => addresses.length > 0)) + ); const registeredRewardAccounts = (await this.#dependencies.txBuilderProviders.rewardAccounts()).filter( (acct) => acct.keyStatus === Cardano.StakeKeyStatus.Registered || @@ -230,7 +227,7 @@ export class GenericTxBuilder implements TxBuilder { // If the wallet is currently delegating to several pools, and all delegations are being removed, // then the funds will be concentrated back into a single address. if (rewardAccountsWithWeights.size === 0) { - const firstAddress = await this.#dependencies.keyAgent.deriveAddress( + const firstAddress = await this.#dependencies.addressManager.deriveAddress( { index: 0, type: AddressType.External }, 0 ); @@ -302,8 +299,8 @@ export class GenericTxBuilder implements TxBuilder { let newRewardAccounts: Cardano.RewardAccount[] = []; if (this.#requestedPortfolio) { newRewardAccounts = await util.ensureStakeKeys({ + addressManager: this.#dependencies.addressManager, count: this.#requestedPortfolio.length, - keyAgent: this.#dependencies.keyAgent, logger: contextLogger(this.#logger, 'getOrCreateRewardAccounts') }); } diff --git a/packages/tx-construction/src/tx-builder/finalizeTx.ts b/packages/tx-construction/src/tx-builder/finalizeTx.ts index 71416effd75..7e18593c933 100644 --- a/packages/tx-construction/src/tx-builder/finalizeTx.ts +++ b/packages/tx-construction/src/tx-builder/finalizeTx.ts @@ -1,22 +1,23 @@ +import { Cardano, TxCBOR } from '@cardano-sdk/core'; +import { FinalizeTxDependencies, SignedTx, TxContext } from './types'; import { - AsyncKeyAgent, SignTransactionOptions, TransactionSigner, + Witnesser, util as keyManagementUtil } from '@cardano-sdk/key-management'; -import { Cardano, TxCBOR } from '@cardano-sdk/core'; -import { FinalizeTxDependencies, SignedTx, TxContext } from './types'; import { filter, firstValueFrom } from 'rxjs'; const getSignatures = async ( - keyAgent: AsyncKeyAgent, + addressManager: keyManagementUtil.Bip32Ed25519AddressManager, + witnesser: Witnesser, txInternals: Cardano.TxBodyWithHash, extraSigners?: TransactionSigner[], signingOptions?: SignTransactionOptions ) => { // Wait until the async key agent has at least one known addresses. - await firstValueFrom(keyAgent.knownAddresses$.pipe(filter((addresses) => addresses.length > 0))); - const signatures: Cardano.Signatures = await keyAgent.signTransaction(txInternals, signingOptions); + await firstValueFrom(addressManager.knownAddresses$.pipe(filter((addresses) => addresses.length > 0))); + const { signatures } = await witnesser.witness(txInternals, signingOptions); if (extraSigners) { for (const extraSigner of extraSigners) { @@ -31,19 +32,19 @@ const getSignatures = async ( export const finalizeTx = async ( tx: Cardano.TxBodyWithHash, { ownAddresses, witness, signingOptions, auxiliaryData, isValid, handleResolutions }: TxContext, - { inputResolver, keyAgent }: FinalizeTxDependencies, + { inputResolver, addressManager, witnesser }: FinalizeTxDependencies, stubSign = false ): Promise => { const signatures = stubSign ? await keyManagementUtil.stubSignTransaction({ - dRepPublicKey: await keyAgent.derivePublicKey(keyManagementUtil.DREP_KEY_DERIVATION_PATH), + dRepPublicKey: await addressManager.derivePublicKey(keyManagementUtil.DREP_KEY_DERIVATION_PATH), extraSigners: witness?.extraSigners, inputResolver, knownAddresses: ownAddresses, signTransactionOptions: signingOptions, txBody: tx.body }) - : await getSignatures(keyAgent, tx, witness?.extraSigners, signingOptions); + : await getSignatures(addressManager, witnesser, tx, witness?.extraSigners, signingOptions); const transaction = { auxiliaryData, diff --git a/packages/tx-construction/src/tx-builder/initializeTx.ts b/packages/tx-construction/src/tx-builder/initializeTx.ts index eabd7371b58..3895745c238 100644 --- a/packages/tx-construction/src/tx-builder/initializeTx.ts +++ b/packages/tx-construction/src/tx-builder/initializeTx.ts @@ -11,13 +11,13 @@ import { firstValueFrom } from 'rxjs'; export const initializeTx = async ( props: InitializeTxProps, - { txBuilderProviders, inputSelector, inputResolver, keyAgent, logger }: TxBuilderDependencies + { txBuilderProviders, inputSelector, inputResolver, addressManager, witnesser, logger }: TxBuilderDependencies ): Promise => { const [tip, genesisParameters, protocolParameters, addresses, rewardAccounts, utxo] = await Promise.all([ txBuilderProviders.tip(), txBuilderProviders.genesisParameters(), txBuilderProviders.protocolParameters(), - firstValueFrom(keyAgent.knownAddresses$), + firstValueFrom(addressManager.knownAddresses$), txBuilderProviders.rewardAccounts(), txBuilderProviders.utxoAvailable() ]); @@ -25,7 +25,7 @@ export const initializeTx = async ( inputSelector = inputSelector ?? roundRobinRandomImprove({ - changeAddressResolver: new StaticChangeAddressResolver(() => firstValueFrom(keyAgent.knownAddresses$)) + changeAddressResolver: new StaticChangeAddressResolver(() => firstValueFrom(addressManager.knownAddresses$)) }); const validityInterval = ensureValidityInterval(tip.slot, genesisParameters, props.options?.validityInterval); @@ -63,7 +63,7 @@ export const initializeTx = async ( signingOptions: props.signingOptions, witness: props.witness }, - { inputResolver, keyAgent }, + { addressManager, inputResolver, witnesser }, true ); return tx; diff --git a/packages/tx-construction/src/tx-builder/types.ts b/packages/tx-construction/src/tx-builder/types.ts index 6553cf1a484..5d3a43f3dde 100644 --- a/packages/tx-construction/src/tx-builder/types.ts +++ b/packages/tx-construction/src/tx-builder/types.ts @@ -3,7 +3,13 @@ import { CustomError } from 'ts-custom-error'; import { InputSelectionError, InputSelector, SelectionSkeleton } from '@cardano-sdk/input-selection'; -import { AsyncKeyAgent, GroupedAddress, SignTransactionOptions, TransactionSigner } from '@cardano-sdk/key-management'; +import { + GroupedAddress, + SignTransactionOptions, + TransactionSigner, + Witnesser, + util +} from '@cardano-sdk/key-management'; import { Hash32ByteBase16 } from '@cardano-sdk/crypto'; import { InitializeTxWitness, TxBuilderProviders } from '../types'; import { Logger } from 'ts-log'; @@ -264,11 +270,12 @@ export interface TxBuilder { export interface TxBuilderDependencies { inputSelector?: InputSelector; inputResolver: Cardano.InputResolver; - keyAgent: AsyncKeyAgent; + addressManager: util.Bip32Ed25519AddressManager; + witnesser: Witnesser; txBuilderProviders: TxBuilderProviders; logger: Logger; outputValidator?: OutputBuilderValidator; handleProvider?: HandleProvider; } -export type FinalizeTxDependencies = Pick; +export type FinalizeTxDependencies = Pick; diff --git a/packages/tx-construction/test/tx-builder/TxBuilder.bootstrap.test.ts b/packages/tx-construction/test/tx-builder/TxBuilder.bootstrap.test.ts index b5a4b36c0d6..31ac7e61de5 100644 --- a/packages/tx-construction/test/tx-builder/TxBuilder.bootstrap.test.ts +++ b/packages/tx-construction/test/tx-builder/TxBuilder.bootstrap.test.ts @@ -1,5 +1,5 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import { AddressType } from '@cardano-sdk/key-management'; +import { AddressType, util } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { GenericTxBuilder, OutputValidation } from '../../src'; import { StubKeyAgent, mockProviders as mocks } from '@cardano-sdk/util-dev'; @@ -34,12 +34,14 @@ describe('TxBuilder bootstrap', () => { const outputValidator = { validateOutput: jest.fn().mockResolvedValue({ coinMissing: 0n } as OutputValidation) }; + const builderParams = { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), inputResolver, - keyAgent, logger: dummyLogger, outputValidator, - txBuilderProviders + txBuilderProviders, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) }; const txBuilder = new GenericTxBuilder(builderParams); diff --git a/packages/tx-construction/test/tx-builder/TxBuilder.test.ts b/packages/tx-construction/test/tx-builder/TxBuilder.test.ts index d8bc9cddf88..fa176e22a18 100644 --- a/packages/tx-construction/test/tx-builder/TxBuilder.test.ts +++ b/packages/tx-construction/test/tx-builder/TxBuilder.test.ts @@ -91,12 +91,14 @@ describe('GenericTxBuilder', () => { validateOutput: jest.fn().mockResolvedValue({ coinMissing: 0n } as OutputValidation) }; + const asyncKeyAgent = util.createAsyncKeyAgent(keyAgent); const builderParams = { + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), inputResolver, - keyAgent: util.createAsyncKeyAgent(keyAgent), logger: dummyLogger, outputValidator, - txBuilderProviders + txBuilderProviders, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) }; txBuilder = new GenericTxBuilder({ diff --git a/packages/tx-construction/test/tx-builder/TxBuilderDelegatePortfolio.test.ts b/packages/tx-construction/test/tx-builder/TxBuilderDelegatePortfolio.test.ts index 2e79fab1e51..83b53c819ab 100644 --- a/packages/tx-construction/test/tx-builder/TxBuilderDelegatePortfolio.test.ts +++ b/packages/tx-construction/test/tx-builder/TxBuilderDelegatePortfolio.test.ts @@ -98,14 +98,16 @@ const createTxBuilder = async ({ const outputValidator = { validateOutput: jest.fn().mockResolvedValue({ coinMissing: 0n } as OutputValidation) }; + const asyncKeyAgent = util.createAsyncKeyAgent(keyAgent); return { groupedAddresses, txBuilder: new GenericTxBuilder({ + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), inputResolver, - keyAgent: util.createAsyncKeyAgent(keyAgent), logger: dummyLogger, outputValidator, - txBuilderProviders + txBuilderProviders, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) }), txBuilderProviders }; diff --git a/packages/wallet/src/PersonalWallet/PersonalWallet.ts b/packages/wallet/src/PersonalWallet/PersonalWallet.ts index 4d94cac08f0..f4b9d2e9cda 100644 --- a/packages/wallet/src/PersonalWallet/PersonalWallet.ts +++ b/packages/wallet/src/PersonalWallet/PersonalWallet.ts @@ -65,7 +65,6 @@ import { SyncStatus, WalletNetworkInfoProvider } from '../types'; -import { AsyncKeyAgent, GroupedAddress, cip8, util as keyManagementUtil } from '@cardano-sdk/key-management'; import { BehaviorObservable, TrackerSubject, coldObservableProvider } from '@cardano-sdk/util-rxjs'; import { BehaviorSubject, @@ -98,6 +97,7 @@ import { finalizeTx, initializeTx } from '@cardano-sdk/tx-construction'; +import { GroupedAddress, Witnesser, cip8, util as keyManagementUtil } from '@cardano-sdk/key-management'; import { Logger } from 'ts-log'; import { PubStakeKeyAndStatus, createPublicStakeKeysTracker } from '../services/PublicStakeKeysTracker'; import { RetryBackoffConfig } from 'backoff-rxjs'; @@ -114,7 +114,8 @@ export interface PersonalWalletProps { } export interface PersonalWalletDependencies { - readonly keyAgent: AsyncKeyAgent; + readonly witnesser: Witnesser; + readonly addressManager: keyManagementUtil.Bip32Ed25519AddressManager; readonly txSubmitProvider: TxSubmitProvider; readonly stakePoolProvider: StakePoolProvider; readonly assetProvider: AssetProvider; @@ -199,7 +200,8 @@ export class PersonalWallet implements ObservableWallet { #addressDiscovery: AddressDiscovery; #submittingPromises: Partial>> = {}; - readonly keyAgent: AsyncKeyAgent; + readonly witnesser: Witnesser; + readonly addressManager: keyManagementUtil.Bip32Ed25519AddressManager; readonly currentEpoch$: TrackerSubject; readonly txSubmitProvider: TxSubmitProvider; readonly utxoProvider: TrackedUtxoProvider; @@ -245,7 +247,8 @@ export class PersonalWallet implements ObservableWallet { { txSubmitProvider, stakePoolProvider, - keyAgent, + witnesser, + addressManager, assetProvider, handleProvider, networkInfoProvider, @@ -284,7 +287,8 @@ export class PersonalWallet implements ObservableWallet { { consideredOutOfSyncAfter } ); - this.keyAgent = keyAgent; + this.addressManager = addressManager; + this.witnesser = witnesser; this.fatalError$ = new Subject(); @@ -299,7 +303,7 @@ export class PersonalWallet implements ObservableWallet { this.addresses$ = new TrackerSubject( concat( stores.addresses.get(), - keyAgent.knownAddresses$.pipe( + this.addressManager.knownAddresses$.pipe( distinctUntilChanged(groupedAddressesEquals), tap( // derive addresses if none available @@ -311,7 +315,7 @@ export class PersonalWallet implements ObservableWallet { coldObservableProvider({ cancel$, onFatalError, - provider: () => this.#addressDiscovery.discover(keyAgent), + provider: () => this.#addressDiscovery.discover(this.addressManager), retryBackoffConfig }) ).catch(() => this.#logger.error('Failed to complete the address discovery process')); @@ -452,7 +456,7 @@ export class PersonalWallet implements ObservableWallet { this.delegation = createDelegationTracker({ epoch$, eraSummaries$, - knownAddresses$: this.keyAgent.knownAddresses$, + knownAddresses$: this.addressManager.knownAddresses$, logger: contextLogger(this.#logger, 'delegation'), onFatalError, retryBackoffConfig, @@ -481,8 +485,8 @@ export class PersonalWallet implements ObservableWallet { }); this.publicStakeKeys$ = createPublicStakeKeysTracker({ + addressManager: this.addressManager, addresses$: this.addresses$, - keyAgent: this.keyAgent, rewardAccounts$: this.delegation.rewardAccounts$ }); @@ -535,7 +539,7 @@ export class PersonalWallet implements ObservableWallet { const { tx: signedTx } = await finalizeTx( tx, { ...rest, ownAddresses: await firstValueFrom(this.addresses$) }, - { inputResolver: this.util, keyAgent: this.keyAgent }, + { addressManager: this.addressManager, inputResolver: this.util, witnesser: this.witnesser }, stubSign ); return signedTx; @@ -622,7 +626,14 @@ export class PersonalWallet implements ObservableWallet { } signData(props: SignDataProps): Promise { - return cip8.cip30signData({ keyAgent: this.keyAgent, ...props }); + return cip8.cip30signData({ + // TODO: signData probably needs to be refactored out of the wallet and supported as a stand alone util + // as this operation doesnt require any of the wallet state. Also this operation can only be performed + // by Bip32Ed25519 type of wallets. + addressManager: this.addressManager, + witnesser: this.witnesser as keyManagementUtil.Bip32Ed25519Witnesser, + ...props + }); } sync() { this.#tip$.sync(); @@ -642,7 +653,7 @@ export class PersonalWallet implements ObservableWallet { this.utxoProvider.stats.shutdown(); this.rewardsProvider.stats.shutdown(); this.chainHistoryProvider.stats.shutdown(); - this.keyAgent.shutdown(); + this.addressManager.shutdown(); this.currentEpoch$.complete(); this.delegation.shutdown(); this.assetInfo$.complete(); @@ -677,10 +688,10 @@ export class PersonalWallet implements ObservableWallet { */ getTxBuilderDependencies(): TxBuilderDependencies { return { + addressManager: this.addressManager, handleProvider: this.handleProvider, inputResolver: this.util, inputSelector: this.#inputSelector, - keyAgent: this.keyAgent, logger: this.#logger, outputValidator: this.util, txBuilderProviders: { @@ -689,7 +700,8 @@ export class PersonalWallet implements ObservableWallet { rewardAccounts: () => this.#firstValueFromSettled(this.delegation.rewardAccounts$), tip: () => this.#firstValueFromSettled(this.tip$), utxoAvailable: () => this.#firstValueFromSettled(this.utxo.available$) - } + }, + witnesser: this.witnesser }; } @@ -710,7 +722,7 @@ export class PersonalWallet implements ObservableWallet { async getPubDRepKey(): Promise { if (!this.drepPubKey) { try { - this.drepPubKey = await this.keyAgent.derivePublicKey(keyManagementUtil.DREP_KEY_DERIVATION_PATH); + this.drepPubKey = await this.addressManager.derivePublicKey(keyManagementUtil.DREP_KEY_DERIVATION_PATH); } catch (error) { this.#logger.error(error); throw error; diff --git a/packages/wallet/src/services/AddressDiscovery/HDSequentialDiscovery.ts b/packages/wallet/src/services/AddressDiscovery/HDSequentialDiscovery.ts index 1f56c8908f0..7a04540f522 100644 --- a/packages/wallet/src/services/AddressDiscovery/HDSequentialDiscovery.ts +++ b/packages/wallet/src/services/AddressDiscovery/HDSequentialDiscovery.ts @@ -1,4 +1,4 @@ -import { AccountAddressDerivationPath, AddressType, AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; +import { AccountAddressDerivationPath, AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; import { AddressDiscovery } from '../types'; import { ChainHistoryProvider } from '@cardano-sdk/core'; import uniqBy from 'lodash/uniqBy'; @@ -26,14 +26,14 @@ const addressHasTx = async (address: GroupedAddress, chainHistoryProvider: Chain /** * Search for all base addresses composed with the given payment and stake credentials. * - * @param keyAgent The key agent controlling the root key to be used to derive the addresses to be discovered. + * @param manager The address manager to be used to derive the addresses to be discovered. * @param chainHistoryProvider The chain history provider. * @param lookAheadCount Number down the derivation chain to be searched for. * @param getDeriveAddressArgs Callback that retrieves the derivation path arguments. * @returns A promise that will be resolved into a GroupedAddress list containing the discovered addresses. */ const discoverAddresses = async ( - keyAgent: AsyncKeyAgent, + manager: util.Bip32Ed25519AddressManager, chainHistoryProvider: ChainHistoryProvider, lookAheadCount: number, getDeriveAddressArgs: ( @@ -52,13 +52,13 @@ const discoverAddresses = async ( const externalAddressArgs = getDeriveAddressArgs(currentIndex, AddressType.External); const internalAddressArgs = getDeriveAddressArgs(currentIndex, AddressType.Internal); - const externalAddress = await keyAgent.deriveAddress( + const externalAddress = await manager.deriveAddress( externalAddressArgs.paymentKeyDerivationPath, externalAddressArgs.stakeKeyDerivationIndex, true ); - const internalAddress = await keyAgent.deriveAddress( + const internalAddress = await manager.deriveAddress( internalAddressArgs.paymentKeyDerivationPath, internalAddressArgs.stakeKeyDerivationIndex, true @@ -111,18 +111,18 @@ export class HDSequentialDiscovery implements AddressDiscovery { * This method performs a look-ahead search of 'n' addresses in the HD wallet using the chain history and * the given key agent. The discovered addresses are returned as a list. * - * @param keyAgent The key agent controlling the root key to be used to derive the addresses to be discovered. + * @param manager The address manager be used to derive the addresses to be discovered. * @returns A promise that will be resolved into a GroupedAddress list containing the discovered addresses. */ - public async discover(keyAgent: AsyncKeyAgent): Promise { - const firstAddresses = [await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0, true)]; - const firstInternalAddress = await keyAgent.deriveAddress({ index: 0, type: AddressType.Internal }, 0, true); + public async discover(manager: util.Bip32Ed25519AddressManager): Promise { + const firstAddresses = [await manager.deriveAddress({ index: 0, type: AddressType.External }, 0, true)]; + const firstInternalAddress = await manager.deriveAddress({ index: 0, type: AddressType.Internal }, 0, true); if (await addressHasTx(firstInternalAddress, this.#chainHistoryProvider)) { firstAddresses.push(firstInternalAddress); } const stakeKeyAddresses = await discoverAddresses( - keyAgent, + manager, this.#chainHistoryProvider, STAKE_KEY_INDEX_LOOKAHEAD, (currentIndex, type) => ({ @@ -136,7 +136,7 @@ export class HDSequentialDiscovery implements AddressDiscovery { ); const paymentKeyAddresses = await discoverAddresses( - keyAgent, + manager, this.#chainHistoryProvider, this.#lookAheadCount, (currentIndex, type) => ({ @@ -154,7 +154,7 @@ export class HDSequentialDiscovery implements AddressDiscovery { // We need to make sure the addresses are sorted since the wallet assumes that the first address // in the list is the change address (payment cred 0 and stake cred 0). addresses.sort((a, b) => a.index - b.index || a.stakeKeyDerivationPath!.index - b.stakeKeyDerivationPath!.index); - await keyAgent.setKnownAddresses(addresses); + await manager.setKnownAddresses(addresses); return addresses; } diff --git a/packages/wallet/src/services/AddressDiscovery/SingleAddressDiscovery.ts b/packages/wallet/src/services/AddressDiscovery/SingleAddressDiscovery.ts index 5cc4e32bacc..8748b631945 100644 --- a/packages/wallet/src/services/AddressDiscovery/SingleAddressDiscovery.ts +++ b/packages/wallet/src/services/AddressDiscovery/SingleAddressDiscovery.ts @@ -1,13 +1,13 @@ import { AddressDiscovery } from '../types'; -import { AddressType, AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; /** * Discovers the first address in the derivation chain (both payment and stake credentials) without looking at the * chain history. */ export class SingleAddressDiscovery implements AddressDiscovery { - public async discover(keyAgent: AsyncKeyAgent): Promise { - const address = await keyAgent.deriveAddress({ index: 0, type: AddressType.External }, 0); + public async discover(manager: util.Bip32Ed25519AddressManager): Promise { + const address = await manager.deriveAddress({ index: 0, type: AddressType.External }, 0); return [address]; } } diff --git a/packages/wallet/src/services/PublicStakeKeysTracker.ts b/packages/wallet/src/services/PublicStakeKeysTracker.ts index df8fdc3ca5b..0a7486c7688 100644 --- a/packages/wallet/src/services/PublicStakeKeysTracker.ts +++ b/packages/wallet/src/services/PublicStakeKeysTracker.ts @@ -1,4 +1,4 @@ -import { AccountKeyDerivationPath, AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; +import { AccountKeyDerivationPath, GroupedAddress, util } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { Ed25519PublicKeyHex } from '@cardano-sdk/crypto'; import { Observable, defaultIfEmpty, distinctUntilChanged, forkJoin, from, map, mergeMap, switchMap } from 'rxjs'; @@ -8,7 +8,7 @@ import { deepEquals } from '@cardano-sdk/util'; export interface CreatePubStakeKeysTrackerProps { addresses$: Observable; rewardAccounts$: Observable; - keyAgent: AsyncKeyAgent; + addressManager: util.Bip32Ed25519AddressManager; } export interface PubStakeKeyAndStatus { @@ -43,7 +43,7 @@ const withStakeKeyDerivationPaths = export const createPublicStakeKeysTracker = ({ addresses$, rewardAccounts$, - keyAgent + addressManager }: CreatePubStakeKeysTrackerProps) => new TrackerSubject( rewardAccounts$.pipe( @@ -51,7 +51,7 @@ export const createPublicStakeKeysTracker = ({ mergeMap((derivationPathsAndStatus) => forkJoin( derivationPathsAndStatus.map(({ stakeKeyDerivationPath, keyStatus }) => - from(keyAgent.derivePublicKey(stakeKeyDerivationPath)).pipe( + from(addressManager.derivePublicKey(stakeKeyDerivationPath)).pipe( map((publicStakeKey) => ({ keyStatus, publicStakeKey })) ) ) diff --git a/packages/wallet/src/services/types.ts b/packages/wallet/src/services/types.ts index dcc958a7a82..1be2bfd56f6 100644 --- a/packages/wallet/src/services/types.ts +++ b/packages/wallet/src/services/types.ts @@ -1,5 +1,5 @@ -import { AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; import { Cardano, CardanoNodeErrors, Reward, TxCBOR } from '@cardano-sdk/core'; +import { GroupedAddress, util } from '@cardano-sdk/key-management'; import { Observable } from 'rxjs'; import { Percent } from '@cardano-sdk/util'; import { SignedTx } from '@cardano-sdk/tx-construction'; @@ -41,10 +41,10 @@ export interface AddressDiscovery { /** * Discover used addresses in the HD wallet. * - * @param keyAgent The key agent controlling the root key to be used to derive the addresses to be discovered. + * @param addressManager The address manager to be used to derive the addresses to be discovered. * @returns A promise that will be resolved into a GroupedAddress list containing the discovered addresses. */ - discover(keyAgent: AsyncKeyAgent): Promise; + discover(addressManager: util.Bip32Ed25519AddressManager): Promise; } export type Milliseconds = number; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 8ec5827e470..450e6843f6c 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -18,7 +18,7 @@ import { Shutdown } from '@cardano-sdk/util'; export type Assets = Map; -export type SignDataProps = Omit; +export type SignDataProps = Omit; export interface SyncStatus extends Shutdown { /** diff --git a/packages/wallet/test/PersonalWallet/load.test.ts b/packages/wallet/test/PersonalWallet/load.test.ts index 653ea2aa2b4..8701aac509e 100644 --- a/packages/wallet/test/PersonalWallet/load.test.ts +++ b/packages/wallet/test/PersonalWallet/load.test.ts @@ -11,7 +11,7 @@ import { PollingConfig, setupWallet } from '../../src'; -import { AddressType, AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, AsyncKeyAgent, GroupedAddress, util } from '@cardano-sdk/key-management'; import { AssetId, createStubStakePoolProvider, @@ -75,13 +75,13 @@ export class MockAddressDiscovery implements AddressDiscovery { this.#resolveAfterAttempts = resolveAfterAttempts; } - public async discover(keyAgent: AsyncKeyAgent): Promise { + public async discover(addressManager: util.Bip32Ed25519AddressManager): Promise { if (this.#currentAttempt <= this.#resolveAfterAttempts) { ++this.#currentAttempt; throw new Error('An error occurred during the discovery process.'); } - await keyAgent.setKnownAddresses(this.#addresses); + await addressManager.setKnownAddresses(this.#addresses); return this.#addresses; } } @@ -132,18 +132,19 @@ const createWallet = async (props: CreateWalletProps) => { { name, polling: props.pollingConfig }, { addressDiscovery: props?.addressDiscovery, + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, connectionStatusTracker$, handleProvider, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, stores: props.stores, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ); }, @@ -158,7 +159,8 @@ const assertWalletProperties = async ( expectedRewardsHistory = flatten([...mocks.rewardsHistory.values()]), expectHandles?: boolean ) => { - expect(wallet.keyAgent).toBeTruthy(); + expect(wallet.addressManager).toBeTruthy(); + expect(wallet.witnesser).toBeTruthy(); // name expect(wallet.name).toBe(name); // utxo diff --git a/packages/wallet/test/PersonalWallet/methods.test.ts b/packages/wallet/test/PersonalWallet/methods.test.ts index 281dacbfc2e..5a244c46280 100644 --- a/packages/wallet/test/PersonalWallet/methods.test.ts +++ b/packages/wallet/test/PersonalWallet/methods.test.ts @@ -1,6 +1,6 @@ /* eslint-disable unicorn/consistent-destructuring, sonarjs/no-duplicate-string, @typescript-eslint/no-floating-promises, promise/no-nesting, promise/always-return */ import * as Crypto from '@cardano-sdk/crypto'; -import { AddressType, AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, AsyncKeyAgent, GroupedAddress, util } from '@cardano-sdk/key-management'; import { AssetId, StubKeyAgent, createStubStakePoolProvider, mockProviders as mocks } from '@cardano-sdk/util-dev'; import { BehaviorSubject, Subscription, firstValueFrom, skip } from 'rxjs'; import { Cardano, CardanoNodeErrors, ProviderError, ProviderFailure, TxCBOR } from '@cardano-sdk/core'; @@ -83,16 +83,17 @@ describe('PersonalWallet methods', () => { new PersonalWallet( { name: 'Test Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, handleProvider, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ), logger @@ -226,16 +227,17 @@ describe('PersonalWallet methods', () => { new PersonalWallet( { name: 'Stub Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider: mocks.mockAssetProvider(), chainHistoryProvider: mockChainHistoryProvider(), handleProvider: mocks.mockHandleProvider(), - keyAgent, logger, networkInfoProvider: mocks.mockNetworkInfoProvider(), rewardsProvider: mockRewardsProvider(), stakePoolProvider: createStubStakePoolProvider(), txSubmitProvider: mocks.mockTxSubmitProvider(), - utxoProvider: mocks.mockUtxoProvider() + utxoProvider: mocks.mockUtxoProvider(), + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ), logger @@ -542,15 +544,16 @@ describe('PersonalWallet methods', () => { new PersonalWallet( { name: 'Test Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider: mocks.mockAssetProvider(), chainHistoryProvider: mockChainHistoryProvider(), - keyAgent, logger, networkInfoProvider: mocks.mockNetworkInfoProvider(), rewardsProvider: mockRewardsProvider(), stakePoolProvider: mocks.mockStakePoolsProvider(), txSubmitProvider: mocks.mockTxSubmitProvider(), - utxoProvider: mocks.mockUtxoProvider() + utxoProvider: mocks.mockUtxoProvider(), + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ), logger diff --git a/packages/wallet/test/PersonalWallet/rollback.test.ts b/packages/wallet/test/PersonalWallet/rollback.test.ts index 2241c6b6438..f94ebdcf7b7 100644 --- a/packages/wallet/test/PersonalWallet/rollback.test.ts +++ b/packages/wallet/test/PersonalWallet/rollback.test.ts @@ -1,5 +1,5 @@ import * as Crypto from '@cardano-sdk/crypto'; -import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; import { Cardano, ChainHistoryProvider, @@ -61,17 +61,18 @@ const createWallet = async (stores: WalletStores, providers: Providers, pollingC return new PersonalWallet( { name, polling: pollingConfig }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, connectionStatusTracker$, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, stores, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ); }, diff --git a/packages/wallet/test/PersonalWallet/shutdown.test.ts b/packages/wallet/test/PersonalWallet/shutdown.test.ts index b77a0a58934..4e9b96750f2 100644 --- a/packages/wallet/test/PersonalWallet/shutdown.test.ts +++ b/packages/wallet/test/PersonalWallet/shutdown.test.ts @@ -1,7 +1,7 @@ /* eslint-disable max-statements */ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as Crypto from '@cardano-sdk/crypto'; -import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; import { AssetId, createStubStakePoolProvider, @@ -79,17 +79,18 @@ const createWallet = async ( return new PersonalWallet( { name, polling: pollingConfig }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, connectionStatusTracker$, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, stores, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ); }, @@ -103,7 +104,8 @@ const assertWalletProperties = async ( expectedDelegateeId: Cardano.PoolId | undefined, expectedRewardsHistory = flatten([...mocks.rewardsHistory.values()]) ) => { - expect(wallet.keyAgent).toBeTruthy(); + expect(wallet.addressManager).toBeTruthy(); + expect(wallet.witnesser).toBeTruthy(); // name expect(wallet.name).toBe(name); // utxo diff --git a/packages/wallet/test/hardware/ledger/LedgerKeyAgent.integration.test.ts b/packages/wallet/test/hardware/ledger/LedgerKeyAgent.integration.test.ts index 1ae9b0aed3e..298e7ba6a7d 100644 --- a/packages/wallet/test/hardware/ledger/LedgerKeyAgent.integration.test.ts +++ b/packages/wallet/test/hardware/ledger/LedgerKeyAgent.integration.test.ts @@ -29,15 +29,16 @@ const createWallet = async (keyAgent: KeyAgent) => { return new PersonalWallet( { name: 'Wallet1' }, { + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), assetProvider, chainHistoryProvider, - keyAgent: asyncKeyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) } ); }; diff --git a/packages/wallet/test/hardware/ledger/LedgerKeyAgent.test.ts b/packages/wallet/test/hardware/ledger/LedgerKeyAgent.test.ts index 17ec08558bb..ba1a1a99efd 100644 --- a/packages/wallet/test/hardware/ledger/LedgerKeyAgent.test.ts +++ b/packages/wallet/test/hardware/ledger/LedgerKeyAgent.test.ts @@ -44,15 +44,16 @@ describe('LedgerKeyAgent', () => { return new PersonalWallet( { name: 'HW Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), assetProvider, chainHistoryProvider, - keyAgent: asyncKeyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) } ); }, diff --git a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.integration.test.ts b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.integration.test.ts index cfd7e2c6e01..83b197fd3dd 100644 --- a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.integration.test.ts +++ b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.integration.test.ts @@ -28,15 +28,16 @@ const createWallet = async (keyAgent: KeyAgent) => { return new PersonalWallet( { name: 'Wallet1' }, { + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), assetProvider, chainHistoryProvider, - keyAgent: asyncKeyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) } ); }; diff --git a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts index d31e422358f..51b2ebb41f9 100644 --- a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts +++ b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts @@ -50,15 +50,16 @@ describe('TrezorKeyAgent', () => { return new PersonalWallet( { name: 'HW Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(asyncKeyAgent), assetProvider, chainHistoryProvider, - keyAgent: asyncKeyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent) } ); }, diff --git a/packages/wallet/test/integration/CustomObservableWallet.test.ts b/packages/wallet/test/integration/CustomObservableWallet.test.ts index 40f05ec8144..ce0be7cc4af 100644 --- a/packages/wallet/test/integration/CustomObservableWallet.test.ts +++ b/packages/wallet/test/integration/CustomObservableWallet.test.ts @@ -2,7 +2,7 @@ /* eslint-disable sonarjs/no-extra-arguments */ /* eslint-disable unicorn/consistent-function-scoping */ import { Cardano, Serialization } from '@cardano-sdk/core'; -import { GroupedAddress } from '@cardano-sdk/key-management'; +import { GroupedAddress, util } from '@cardano-sdk/key-management'; import { ObservableWallet, PersonalWallet } from '../../src'; import { OutputValidator, @@ -43,15 +43,16 @@ describe('CustomObservableWallet', () => { const extensionWallet: LaceObservableWallet = new PersonalWallet( { name: 'Extension Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(await testAsyncKeyAgent()), assetProvider: mocks.mockAssetProvider(), chainHistoryProvider: mocks.mockChainHistoryProvider(), - keyAgent: await testAsyncKeyAgent(), logger, networkInfoProvider: mocks.mockNetworkInfoProvider(), rewardsProvider: mocks.mockRewardsProvider(), stakePoolProvider: createStubStakePoolProvider(), txSubmitProvider: mocks.mockTxSubmitProvider(), - utxoProvider: mocks.mockUtxoProvider() + utxoProvider: mocks.mockUtxoProvider(), + witnesser: util.createBip32Ed25519Witnesser(await testAsyncKeyAgent()) } ); extensionWallet; diff --git a/packages/wallet/test/integration/cip30mapping.test.ts b/packages/wallet/test/integration/cip30mapping.test.ts index d02c5f91fc8..293b80f2e1f 100644 --- a/packages/wallet/test/integration/cip30mapping.test.ts +++ b/packages/wallet/test/integration/cip30mapping.test.ts @@ -12,7 +12,7 @@ import { TxSignError, WalletApi } from '@cardano-sdk/dapp-connector'; -import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management'; import { AssetId, createStubStakePoolProvider, mockProviders as mocks } from '@cardano-sdk/util-dev'; import { CallbackConfirmation, GetCollateralCallbackParams } from '../../src/cip30'; import { Cardano, CardanoNodeErrors, Serialization, TxCBOR, coalesceValueQuantities } from '@cardano-sdk/core'; @@ -683,15 +683,16 @@ describe('cip30', () => { new PersonalWallet( { name: 'Test Wallet' }, { + addressManager: util.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ), logger diff --git a/packages/wallet/test/integration/util.ts b/packages/wallet/test/integration/util.ts index 5f0c8f01342..8a74b347ca4 100644 --- a/packages/wallet/test/integration/util.ts +++ b/packages/wallet/test/integration/util.ts @@ -4,6 +4,7 @@ import { WalletStores } from '../../src/persistence'; import { createStubStakePoolProvider, mockProviders } from '@cardano-sdk/util-dev'; import { dummyLogger as logger } from 'ts-log'; import { testAsyncKeyAgent } from '../../../key-management/test/mocks'; +import { util } from '@cardano-sdk/key-management'; const { mockAssetProvider, @@ -39,9 +40,10 @@ export const createWallet = async (stores?: WalletStores, providers: Providers = { ...createDefaultProviders(), ...providers, - keyAgent, + addressManager: util.createBip32Ed25519AddressManager(keyAgent), logger, - stores + stores, + witnesser: util.createBip32Ed25519Witnesser(keyAgent) } ), logger diff --git a/packages/wallet/test/services/PublicStakeKeysTracker.test.ts b/packages/wallet/test/services/PublicStakeKeysTracker.test.ts index 92e9e9b4541..966cac3a79d 100644 --- a/packages/wallet/test/services/PublicStakeKeysTracker.test.ts +++ b/packages/wallet/test/services/PublicStakeKeysTracker.test.ts @@ -1,4 +1,4 @@ -import { AccountKeyDerivationPath, AsyncKeyAgent, GroupedAddress, KeyRole } from '@cardano-sdk/key-management'; +import { AccountKeyDerivationPath, AsyncKeyAgent, GroupedAddress, KeyRole, util } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { ObservableWallet } from '../../src'; import { PubStakeKeyAndStatus, createPublicStakeKeysTracker } from '../../src/services/PublicStakeKeysTracker'; @@ -77,8 +77,8 @@ describe('PublicStakeKeysTracker', () => { const rewardAccounts$ = of([]); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); @@ -91,8 +91,8 @@ describe('PublicStakeKeysTracker', () => { const rewardAccounts$ = of(rewardAccounts); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); @@ -117,8 +117,8 @@ describe('PublicStakeKeysTracker', () => { const rewardAccounts$ = of(rewardAccounts); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); @@ -136,8 +136,8 @@ describe('PublicStakeKeysTracker', () => { const rewardAccounts$ = from([[rewardAccounts[0]], rewardAccounts]); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); @@ -157,8 +157,8 @@ describe('PublicStakeKeysTracker', () => { const rewardAccounts$ = of(rewardAccounts); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); @@ -180,8 +180,8 @@ describe('PublicStakeKeysTracker', () => { ); const stakePubKeys$ = createPublicStakeKeysTracker({ + addressManager: util.createBip32Ed25519AddressManager(keyAgent), addresses$, - keyAgent, rewardAccounts$ }); diff --git a/packages/wallet/test/services/WalletUtil.test.ts b/packages/wallet/test/services/WalletUtil.test.ts index 318c17cefae..4ef4a80652a 100644 --- a/packages/wallet/test/services/WalletUtil.test.ts +++ b/packages/wallet/test/services/WalletUtil.test.ts @@ -1,5 +1,5 @@ import * as Crypto from '@cardano-sdk/crypto'; -import { AddressType, GroupedAddress } from '@cardano-sdk/key-management'; +import { AddressType, GroupedAddress, util as KeyManagementUtil } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { PersonalWallet, @@ -113,15 +113,16 @@ describe('WalletUtil', () => { new PersonalWallet( { name: 'Test Wallet' }, { + addressManager: KeyManagementUtil.createBip32Ed25519AddressManager(keyAgent), assetProvider, chainHistoryProvider, - keyAgent, logger, networkInfoProvider, rewardsProvider, stakePoolProvider, txSubmitProvider, - utxoProvider + utxoProvider, + witnesser: KeyManagementUtil.createBip32Ed25519Witnesser(keyAgent) } ), logger diff --git a/packages/wallet/test/services/addressDiscovery/HDSequentialDiscovery.test.ts b/packages/wallet/test/services/addressDiscovery/HDSequentialDiscovery.test.ts index 2a8d38bc4c0..586b1f6f1f9 100644 --- a/packages/wallet/test/services/addressDiscovery/HDSequentialDiscovery.test.ts +++ b/packages/wallet/test/services/addressDiscovery/HDSequentialDiscovery.test.ts @@ -1,4 +1,4 @@ -import { AddressType, AsyncKeyAgent, KeyRole } from '@cardano-sdk/key-management'; +import { AddressType, AsyncKeyAgent, KeyRole, util } from '@cardano-sdk/key-management'; import { Cardano } from '@cardano-sdk/core'; import { HDSequentialDiscovery } from '../../../src'; import { @@ -33,7 +33,7 @@ describe('HDSequentialDiscovery', () => { 25 ); - const addresses = await discovery.discover(mockKeyAgent); + const addresses = await discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent)); expect(addresses.length).toEqual(5); expect(addresses[0]).toEqual({ @@ -108,7 +108,7 @@ describe('HDSequentialDiscovery', () => { it('derives exactly 1 address when no used addresses are found', async () => { const discovery = new HDSequentialDiscovery(mockAlwaysEmptyChainHistoryProvider, 25); - const addresses = await discovery.discover(mockKeyAgent); + const addresses = await discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent)); expect(addresses).toHaveLength(1); expect(await firstValueFrom(mockKeyAgent.knownAddresses$)).toHaveLength(1); }); @@ -130,7 +130,7 @@ describe('HDSequentialDiscovery', () => { 25 ); - const addresses = await discovery.discover(mockKeyAgent); + const addresses = await discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent)); // 5 payment key + 4 stake keys combined with payment index 0 (the first address overlaps in both sets). expect(addresses.length).toEqual(8); @@ -158,7 +158,7 @@ describe('HDSequentialDiscovery', () => { it('return all discovered addresses', async () => { const discovery = new HDSequentialDiscovery(mockChainHistoryProvider, 25); - const addresses = await discovery.discover(mockKeyAgent); + const addresses = await discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent)); expect(addresses.length).toEqual(50); @@ -213,7 +213,7 @@ describe('HDSequentialDiscovery', () => { await mockKeyAgent.setKnownAddresses([knownAddress]); const discovery = new HDSequentialDiscovery(mockAlwaysFailChainHistoryProvider, 25); - await expect(discovery.discover(mockKeyAgent)).rejects.toThrow(); + await expect(discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent))).rejects.toThrow(); const knownAddresses = await firstValueFrom(mockKeyAgent.knownAddresses$); expect(knownAddresses).toEqual([knownAddress]); diff --git a/packages/wallet/test/services/addressDiscovery/SingleAddressDiscovery.test.ts b/packages/wallet/test/services/addressDiscovery/SingleAddressDiscovery.test.ts index c265b64a396..8a03a4a7657 100644 --- a/packages/wallet/test/services/addressDiscovery/SingleAddressDiscovery.test.ts +++ b/packages/wallet/test/services/addressDiscovery/SingleAddressDiscovery.test.ts @@ -1,4 +1,4 @@ -import { AsyncKeyAgent, KeyRole } from '@cardano-sdk/key-management'; +import { AsyncKeyAgent, KeyRole, util } from '@cardano-sdk/key-management'; import { SingleAddressDiscovery } from '../../../src'; import { prepareMockKeyAgentWithData } from './mockData'; @@ -12,7 +12,7 @@ describe('SingleAddressDiscovery', () => { it('return the first derived address', async () => { const discovery = new SingleAddressDiscovery(); - const addresses = await discovery.discover(mockKeyAgent); + const addresses = await discovery.discover(util.createBip32Ed25519AddressManager(mockKeyAgent)); expect(addresses.length).toEqual(1); expect(addresses[0]).toEqual({