diff --git a/packages/core/src/CSL/coreToCsl/coreToCsl.ts b/packages/core/src/CSL/coreToCsl/coreToCsl.ts index e4f4a8b638c..3eaa2b56989 100644 --- a/packages/core/src/CSL/coreToCsl/coreToCsl.ts +++ b/packages/core/src/CSL/coreToCsl/coreToCsl.ts @@ -67,7 +67,7 @@ export const value = ({ coins, assets }: Cardano.Value): Value => { return result; }; -export const txIn = (core: Cardano.TxIn): TransactionInput => +export const txIn = (core: Cardano.NewTxIn): TransactionInput => TransactionInput.new(TransactionHash.from_bytes(Buffer.from(core.txId, 'hex')), core.index); export const txOut = (core: Cardano.TxOut): TransactionOutput => @@ -166,7 +166,7 @@ export const txAuxiliaryData = (auxiliaryData?: Cardano.AuxiliaryData): Auxiliar return result; }; -const txInputs = (coreInputs: Cardano.TxIn[]) => { +const txInputs = (coreInputs: Cardano.NewTxIn[]) => { const cslInputs = TransactionInputs.new(); for (const input of coreInputs) { cslInputs.add(txIn(input)); @@ -207,7 +207,7 @@ export const txBody = ( collaterals, requiredExtraSignatures, scriptIntegrityHash - }: Cardano.TxBodyAlonzo, + }: Cardano.NewTxBodyAlonzo, auxiliaryData?: Cardano.AuxiliaryData ): TransactionBody => { const cslOutputs = TransactionOutputs.new(); diff --git a/packages/core/src/CSL/cslToCore/cslToCore.ts b/packages/core/src/CSL/cslToCore/cslToCore.ts index 26c57f056d4..22feece9e07 100644 --- a/packages/core/src/CSL/cslToCore/cslToCore.ts +++ b/packages/core/src/CSL/cslToCore/cslToCore.ts @@ -54,8 +54,7 @@ export const value = (cslValue: CSL.Value): Cardano.Value => { return result; }; -export const txIn = (input: CSL.TransactionInput, address?: Cardano.Address): Cardano.TxIn => ({ - address, +export const txIn = (input: CSL.TransactionInput): Cardano.NewTxIn => ({ index: input.index(), txId: Cardano.TransactionId.fromHexBlob(util.bytesToHex(input.transaction_id().to_bytes())) }); @@ -77,10 +76,10 @@ export const txOutputs = (outputs: CSL.TransactionOutputs): Cardano.TxOut[] => { return result; }; -export const txInputs = (inputs: CSL.TransactionInputs, address?: Cardano.Address): Cardano.TxIn[] => { - const result: Cardano.TxIn[] = []; +export const txInputs = (inputs: CSL.TransactionInputs): Cardano.NewTxIn[] => { + const result: Cardano.NewTxIn[] = []; for (let i = 0; i < inputs.len(); i++) { - result.push(txIn(inputs.get(i), address)); + result.push(txIn(inputs.get(i))); } return result; }; @@ -118,7 +117,7 @@ export const txMint = (assets?: CSL.Mint): Cardano.TokenMap | undefined => { return assetMap; }; -export const txBody = (body: CSL.TransactionBody): Cardano.TxBodyAlonzo => { +export const txBody = (body: CSL.TransactionBody): Cardano.NewTxBodyAlonzo => { const cslScriptDataHash = body.script_data_hash(); const cslCollaterals = body.collateral(); diff --git a/packages/core/src/Cardano/types/Transaction.ts b/packages/core/src/Cardano/types/Transaction.ts index b36f183660c..5bbd2a9d353 100644 --- a/packages/core/src/Cardano/types/Transaction.ts +++ b/packages/core/src/Cardano/types/Transaction.ts @@ -47,6 +47,11 @@ export interface TxBodyAlonzo { requiredExtraSignatures?: Cardano.Ed25519KeyHash[]; } +export interface NewTxBodyAlonzo extends Omit { + inputs: Cardano.NewTxIn[]; + collaterals?: Cardano.NewTxIn[]; +} + /** * Implicit coin quantities used in the transaction */ @@ -81,15 +86,18 @@ export type Witness = Omit, 'redeemers' | 's redeemers?: Redeemer[]; signatures: Signatures; }; -export interface TxAlonzo { + +export interface NewTxAlonzo { id: TransactionId; + body: TBody; + witness: Witness; + auxiliaryData?: AuxiliaryData; +} + +export interface TxAlonzo extends NewTxAlonzo { index: number; blockHeader: PartialBlockHeader; body: TxBodyAlonzo; implicitCoin: ImplicitCoin; txSize: number; - witness: Witness; - auxiliaryData?: AuxiliaryData; } - -export type NewTxAlonzo = Omit; diff --git a/packages/core/src/Cardano/types/Utxo.ts b/packages/core/src/Cardano/types/Utxo.ts index f0701d05c60..c9d375dd60b 100644 --- a/packages/core/src/Cardano/types/Utxo.ts +++ b/packages/core/src/Cardano/types/Utxo.ts @@ -1,14 +1,13 @@ import { Address, Hash32ByteBase16, TransactionId } from '.'; import { Value } from './Value'; -export interface TxIn { +export interface NewTxIn { txId: TransactionId; index: number; - /** - * Might or might not be present based on source. - * Not present in serialized tx. - */ - address?: Address; +} + +export interface TxIn extends NewTxIn { + address: Address; } export interface TxOut { diff --git a/packages/core/test/CSL/coreToCsl.test.ts b/packages/core/test/CSL/coreToCsl.test.ts index 97ab77cc3ab..292de8e9fae 100644 --- a/packages/core/test/CSL/coreToCsl.test.ts +++ b/packages/core/test/CSL/coreToCsl.test.ts @@ -1,7 +1,7 @@ /* eslint-disable max-len */ import { Asset, CSL, Cardano, SerializationFailure, coreToCsl } from '../../src'; import { BigNum } from '@emurgo/cardano-serialization-lib-nodejs'; -import { signature, tx, txBody, txIn, txOut, valueCoinOnly, valueWithAssets, vkey } from './testData'; +import { signature, tx, txBody, txIn, txInWithAddress, txOut, valueCoinOnly, valueWithAssets, vkey } from './testData'; const txOutByron = { ...txOut, @@ -19,7 +19,7 @@ describe('coreToCsl', () => { expect(coreToCsl.txOut(txOutByron)).toBeInstanceOf(CSL.TransactionOutput); }); it('utxo', () => { - expect(coreToCsl.utxo([[txIn, txOut]])[0]).toBeInstanceOf(CSL.TransactionUnspentOutput); + expect(coreToCsl.utxo([[txInWithAddress, txOut]])[0]).toBeInstanceOf(CSL.TransactionUnspentOutput); }); describe('value', () => { it('coin only', () => { diff --git a/packages/core/test/CSL/testData.ts b/packages/core/test/CSL/testData.ts index 6e5da4d8975..e475879c462 100644 --- a/packages/core/test/CSL/testData.ts +++ b/packages/core/test/CSL/testData.ts @@ -63,7 +63,7 @@ export const txOut: Cardano.TxOut = { value: valueWithAssets }; -export const txBody: Cardano.TxBodyAlonzo = { +export const txBody: Cardano.NewTxBodyAlonzo = { certificates: [ { __typename: Cardano.CertificateType.PoolRetirement, diff --git a/packages/core/test/Cardano/types/TxSubmissionErrors.test.ts b/packages/core/test/Cardano/types/TxSubmissionErrors.test.ts index 1053b3dfa7a..c919d891fb5 100644 --- a/packages/core/test/Cardano/types/TxSubmissionErrors.test.ts +++ b/packages/core/test/Cardano/types/TxSubmissionErrors.test.ts @@ -1,6 +1,6 @@ import { Cardano } from '../../../src'; import { TxSubmission } from '@cardano-ogmios/client'; -import { TxSubmissionErrors } from '../../../dist/Cardano'; +import { TxSubmissionErrors } from '../../../src/Cardano'; describe('Cardano/types/TxSuubmissionErrors', () => { test('TxSubmissionError can be narrowed down with "instanceof"', () => { diff --git a/packages/core/test/Cardano/util/computeImplicitCoin.test.ts b/packages/core/test/Cardano/util/computeImplicitCoin.test.ts index eb2b46ad7f1..cb297902285 100644 --- a/packages/core/test/Cardano/util/computeImplicitCoin.test.ts +++ b/packages/core/test/Cardano/util/computeImplicitCoin.test.ts @@ -16,7 +16,6 @@ describe('Cardano.util.computeImplicitCoin', () => { }, { __typename: Cardano.CertificateType.StakeDelegation, - epoch: 500, poolId: Cardano.PoolId('pool1zuevzm3xlrhmwjw87ec38mzs02tlkwec9wxpgafcaykmwg7efhh'), stakeKeyHash } diff --git a/packages/core/test/Cardano/util/metadatum.test.ts b/packages/core/test/Cardano/util/metadatum.test.ts index 484f8d79b1e..6bd8b228d5c 100644 --- a/packages/core/test/Cardano/util/metadatum.test.ts +++ b/packages/core/test/Cardano/util/metadatum.test.ts @@ -1,4 +1,4 @@ -import { Cardano } from '@cardano-sdk/core'; +import { Cardano } from '../../../src'; describe('Cardano.util.metadatum', () => { describe('asMetadatumMap', () => { diff --git a/packages/wallet/src/KeyManagement/InMemoryKeyAgent.ts b/packages/wallet/src/KeyManagement/InMemoryKeyAgent.ts index d03ccc4bf09..b7c20c2f565 100644 --- a/packages/wallet/src/KeyManagement/InMemoryKeyAgent.ts +++ b/packages/wallet/src/KeyManagement/InMemoryKeyAgent.ts @@ -40,7 +40,7 @@ export class InMemoryKeyAgent extends KeyAgentBase { this.#getPassword = getPassword; } - async signBlob({ index, type }: AccountKeyDerivationPath, blob: Cardano.util.HexBlob): Promise { + async signBlob({ index, role: type }: AccountKeyDerivationPath, blob: Cardano.util.HexBlob): Promise { const rootPrivateKey = await this.#decryptRootPrivateKey(); const accountKey = deriveAccountPrivateKey(rootPrivateKey, this.accountIndex); const signingKey = accountKey.derive(type).derive(index).to_raw_key(); diff --git a/packages/wallet/src/KeyManagement/KeyAgentBase.ts b/packages/wallet/src/KeyManagement/KeyAgentBase.ts index 4b1ec8ac61c..3e0ec99aecf 100644 --- a/packages/wallet/src/KeyManagement/KeyAgentBase.ts +++ b/packages/wallet/src/KeyManagement/KeyAgentBase.ts @@ -3,16 +3,19 @@ import { AccountKeyDerivationPath, GroupedAddress, KeyAgent, - KeyType, + KeyRole, SerializableKeyAgentData, - SignBlobResult + SignBlobResult, + SignTransactionOptions } from './types'; import { CSL, Cardano, util } from '@cardano-sdk/core'; import { STAKE_KEY_DERIVATION_PATH, ownSignatureKeyPaths } from './util'; import { TxInternals } from '../Transaction'; +import { uniqBy } from 'lodash-es'; export abstract class KeyAgentBase implements KeyAgent { readonly #serializableData: SerializableKeyAgentData; + get knownAddresses(): GroupedAddress[] { return this.#serializableData.knownAddresses; } @@ -41,7 +44,7 @@ export abstract class KeyAgentBase implements KeyAgent { async deriveAddress({ index, type }: AccountAddressDerivationPath): Promise { const derivedPublicPaymentKey = await this.deriveCslPublicKey({ index, - type: type as unknown as KeyType + role: type as unknown as KeyRole }); // Possible optimization: memoize/cache stakeKeyCredential, because it's always the same @@ -67,14 +70,22 @@ export abstract class KeyAgentBase implements KeyAgent { return groupedAddress; } - async signTransaction({ body, hash }: TxInternals): Promise { + async signTransaction( + { body, hash }: TxInternals, + { inputAddressResolver, additionalKeyPaths = [] }: SignTransactionOptions + ): Promise { // Possible optimization is casting strings to OpaqueString types directly and skipping validation const blob = Cardano.util.HexBlob(hash.toString()); - const derivationPaths = ownSignatureKeyPaths(body, this.knownAddresses); + const derivationPaths = ownSignatureKeyPaths(body, this.knownAddresses, inputAddressResolver); + const keyPaths = uniqBy([...derivationPaths, ...additionalKeyPaths], ({ role, index }) => `${role}.${index}`); + // TODO: + // if (keyPaths.length === 0) { + // throw new ProofGenerationError(); + // } return new Map( await Promise.all( - derivationPaths.map(async ({ role, index }) => { - const { publicKey, signature } = await this.signBlob({ index, type: role }, blob); + keyPaths.map(async ({ role, index }) => { + const { publicKey, signature } = await this.signBlob({ index, role }, blob); return [publicKey, signature] as const; }) ) @@ -86,7 +97,7 @@ export abstract class KeyAgentBase implements KeyAgent { return Cardano.Ed25519PublicKey.fromHexBlob(util.bytesToHex(cslPublicKey.as_bytes())); } - protected async deriveCslPublicKey({ index, type }: AccountKeyDerivationPath): Promise { + protected async deriveCslPublicKey({ index, role: type }: AccountKeyDerivationPath): Promise { const accountPublicKeyBytes = Buffer.from(this.extendedAccountPublicKey, 'hex'); const accountPublicKey = CSL.Bip32PublicKey.from_bytes(accountPublicKeyBytes); return accountPublicKey.derive(type).derive(index).to_raw_key(); diff --git a/packages/wallet/src/KeyManagement/cip8/cip30signData.ts b/packages/wallet/src/KeyManagement/cip8/cip30signData.ts index e3c316718ad..d4ed92dd367 100644 --- a/packages/wallet/src/KeyManagement/cip8/cip30signData.ts +++ b/packages/wallet/src/KeyManagement/cip8/cip30signData.ts @@ -16,7 +16,7 @@ import { Cardano, parseCslAddress, util } from '@cardano-sdk/core'; import { Cip30DataSignature } from '@cardano-sdk/cip30'; import { CoseLabel } from './util'; import { CustomError } from 'ts-custom-error'; -import { KeyAgent, KeyType } from '../types'; +import { KeyAgent, KeyRole } from '../types'; import { STAKE_KEY_DERIVATION_PATH } from '../util'; export interface Cip30SignDataRequest { @@ -58,7 +58,7 @@ const getDerivationPath = (signWith: Cardano.Address | Cardano.RewardAccount, ke if (!knownAddress) { throw new Cip30DataSignError(Cip30DataSignErrorCode.ProofGeneration, 'Unknown address'); } - return { index: knownAddress.index, type: knownAddress.type as number as KeyType }; + return { index: knownAddress.index, role: knownAddress.type as number as KeyRole }; }; const createSigStructureHeaders = (addressBytes: Uint8Array) => { diff --git a/packages/wallet/src/KeyManagement/restoreKeyAgent.ts b/packages/wallet/src/KeyManagement/restoreKeyAgent.ts index cd14a3e76f0..a9e8f05506c 100644 --- a/packages/wallet/src/KeyManagement/restoreKeyAgent.ts +++ b/packages/wallet/src/KeyManagement/restoreKeyAgent.ts @@ -12,6 +12,7 @@ import { InMemoryKeyAgent } from './InMemoryKeyAgent'; import { InvalidSerializableDataError } from './errors'; import { LedgerKeyAgent } from './LedgerKeyAgent'; +// TODO: use this type as 2nd parameter of restoreKeyAgent export interface RestoreInMemoryKeyAgentProps { /** * Required for InMemoryKeyAgent diff --git a/packages/wallet/src/KeyManagement/types.ts b/packages/wallet/src/KeyManagement/types.ts index abc15e867c7..1fd212ce518 100644 --- a/packages/wallet/src/KeyManagement/types.ts +++ b/packages/wallet/src/KeyManagement/types.ts @@ -13,14 +13,14 @@ export enum KeyAgentType { Ledger = 'Ledger' } -export enum KeyType { +export enum KeyRole { External = 0, Internal = 1, Stake = 2 } export interface AccountKeyDerivationPath { - type: KeyType; + role: KeyRole; index: number; } /** Internal = change address & External = receipt address */ @@ -89,6 +89,13 @@ export type TransportType = TransportWebHID | TransportNodeHid; */ export type GetPassword = (noCache?: true) => Promise; +export type InputAddressResolver = (txIn: Cardano.NewTxIn) => Cardano.Address | null; + +export interface SignTransactionOptions { + inputAddressResolver: InputAddressResolver; + additionalKeyPaths?: AccountKeyDerivationPath[]; +} + export interface KeyAgent { get networkId(): Cardano.NetworkId; get accountIndex(): number; @@ -102,7 +109,7 @@ export interface KeyAgent { /** * @throws AuthenticationError */ - signTransaction(txInternals: TxInternals): Promise; + signTransaction(txInternals: TxInternals, options: SignTransactionOptions): Promise; /** * @throws AuthenticationError */ diff --git a/packages/wallet/src/KeyManagement/util/key.ts b/packages/wallet/src/KeyManagement/util/key.ts index 8091853803b..9d36b29f668 100644 --- a/packages/wallet/src/KeyManagement/util/key.ts +++ b/packages/wallet/src/KeyManagement/util/key.ts @@ -1,11 +1,11 @@ -import { AccountKeyDerivationPath, KeyType } from '../types'; +import { AccountKeyDerivationPath, KeyRole } from '../types'; import { CSL } from '@cardano-sdk/core'; export const harden = (num: number): number => 0x80_00_00_00 + num; export const STAKE_KEY_DERIVATION_PATH: AccountKeyDerivationPath = { index: 0, - type: KeyType.Stake + role: KeyRole.Stake }; export const deriveAccountPrivateKey = (rootPrivateKey: CSL.Bip32PrivateKey, accountIndex: number) => diff --git a/packages/wallet/src/KeyManagement/util/ownSignatureKeyPaths.ts b/packages/wallet/src/KeyManagement/util/ownSignatureKeyPaths.ts index 70193a41b6e..a0f907c6f62 100644 --- a/packages/wallet/src/KeyManagement/util/ownSignatureKeyPaths.ts +++ b/packages/wallet/src/KeyManagement/util/ownSignatureKeyPaths.ts @@ -1,27 +1,29 @@ +import { AccountKeyDerivationPath, GroupedAddress, InputAddressResolver, KeyRole } from '../types'; import { Cardano, util } from '@cardano-sdk/core'; -import { GroupedAddress, KeyType } from '../types'; import { uniq } from 'lodash-es'; -export interface PartialDerivationPath { - role: KeyType; - index: number; -} - /** * Assumes that a single staking key is used for all addresses (index=0) * - * @returns {PartialDerivationPath[]} derivation paths for keys to sign transaction with + * @returns {AccountKeyDerivationPath[]} derivation paths for keys to sign transaction with */ export const ownSignatureKeyPaths = ( - txBody: Cardano.TxBodyAlonzo, - knownAddresses: GroupedAddress[] -): PartialDerivationPath[] => { + txBody: Cardano.NewTxBodyAlonzo, + knownAddresses: GroupedAddress[], + resolveInputAddress: InputAddressResolver +): AccountKeyDerivationPath[] => { const paymentKeyPaths = uniq( - txBody.inputs.map((input) => knownAddresses.find(({ address }) => address === input.address)).filter(util.isNotNil) + txBody.inputs + .map((input) => { + const ownAddress = resolveInputAddress(input); + if (!ownAddress) return null; + return knownAddresses.find(({ address }) => address === ownAddress); + }) + .filter(util.isNotNil) ).map(({ type, index }) => ({ index, role: Number(type) })); const isStakingKeySignatureRequired = txBody.certificates?.length; if (isStakingKeySignatureRequired) { - return [...paymentKeyPaths, { index: 0, role: KeyType.Stake }]; + return [...paymentKeyPaths, { index: 0, role: KeyRole.Stake }]; } return paymentKeyPaths; }; diff --git a/packages/wallet/src/KeyManagement/util/stubSignTransaction.ts b/packages/wallet/src/KeyManagement/util/stubSignTransaction.ts index 35e39708d55..3a3d5d49b6b 100644 --- a/packages/wallet/src/KeyManagement/util/stubSignTransaction.ts +++ b/packages/wallet/src/KeyManagement/util/stubSignTransaction.ts @@ -1,16 +1,17 @@ import { Cardano } from '@cardano-sdk/core'; -import { GroupedAddress } from '../types'; +import { GroupedAddress, InputAddressResolver } from '../types'; import { ownSignatureKeyPaths } from './ownSignatureKeyPaths'; const randomHexChar = () => Math.floor(Math.random() * 16).toString(16); const randomPublicKey = () => Cardano.Ed25519PublicKey(Array.from({ length: 64 }).map(randomHexChar).join('')); export const stubSignTransaction = ( - txBody: Cardano.TxBodyAlonzo, - knownAddresses: GroupedAddress[] + txBody: Cardano.NewTxBodyAlonzo, + knownAddresses: GroupedAddress[], + inputAddressResolver: InputAddressResolver ): Cardano.Signatures => new Map( - ownSignatureKeyPaths(txBody, knownAddresses).map(() => [ + ownSignatureKeyPaths(txBody, knownAddresses, inputAddressResolver).map(() => [ randomPublicKey(), Cardano.Ed25519Signature( // eslint-disable-next-line max-len diff --git a/packages/wallet/src/SingleAddressWallet.ts b/packages/wallet/src/SingleAddressWallet.ts index f81314a8671..5467edd002b 100644 --- a/packages/wallet/src/SingleAddressWallet.ts +++ b/packages/wallet/src/SingleAddressWallet.ts @@ -1,4 +1,10 @@ -import { AddressType, GroupedAddress, KeyAgent, util as keyManagementUtil } from './KeyManagement'; +import { + AddressType, + GroupedAddress, + InputAddressResolver, + KeyAgent, + util as keyManagementUtil +} from './KeyManagement'; import { AssetProvider, BigIntMath, @@ -48,7 +54,8 @@ import { createTransactionsTracker, createUtxoTracker, distinctBlock, - distinctEpoch + distinctEpoch, + txInEquals } from './services'; import { Cip30DataSignature } from '@cardano-sdk/cip30'; import { @@ -111,6 +118,7 @@ export class SingleAddressWallet implements Wallet { readonly assets$: TrackerSubject; readonly syncStatus: SyncStatus; readonly name: string; + readonly inputAddressResolver: InputAddressResolver; constructor( { @@ -228,6 +236,8 @@ export class SingleAddressWallet implements Wallet { }), stores.assets ); + this.inputAddressResolver = (input) => + this.utxo.available$.value?.find(([txIn]) => txInEquals(txIn, input))?.[1].address || null; } async validateInitializeTxProps(props: InitializeTxProps): Promise { @@ -266,8 +276,10 @@ export class SingleAddressWallet implements Wallet { stubSign = false ): Promise { const signatures = stubSign - ? keyManagementUtil.stubSignTransaction(tx.body, this.keyAgent.knownAddresses) - : await this.keyAgent.signTransaction(tx); + ? keyManagementUtil.stubSignTransaction(tx.body, this.keyAgent.knownAddresses, this.inputAddressResolver) + : await this.keyAgent.signTransaction(tx, { + inputAddressResolver: this.inputAddressResolver + }); return { auxiliaryData, body: tx.body, diff --git a/packages/wallet/src/Transaction/createTransactionInternals.ts b/packages/wallet/src/Transaction/createTransactionInternals.ts index a3e0b143a30..4f691315a9b 100644 --- a/packages/wallet/src/Transaction/createTransactionInternals.ts +++ b/packages/wallet/src/Transaction/createTransactionInternals.ts @@ -3,7 +3,7 @@ import { SelectionResult } from '@cardano-sdk/cip2'; export type TxInternals = { hash: Cardano.TransactionId; - body: Cardano.TxBodyAlonzo; + body: Cardano.NewTxBodyAlonzo; }; export type CreateTxInternalsProps = { diff --git a/packages/wallet/src/cip30.ts b/packages/wallet/src/cip30.ts index 11b725cb541..d92592e7155 100644 --- a/packages/wallet/src/cip30.ts +++ b/packages/wallet/src/cip30.ts @@ -134,10 +134,13 @@ export const createWalletApi = ( const txDecoded = CSL.TransactionBody.from_bytes(Buffer.from(tx, 'hex')); const hash = Cardano.TransactionId(Buffer.from(CSL.hash_transaction(txDecoded).to_bytes()).toString('hex')); const coreTx = cslToCore.txBody(txDecoded); - const witnessSet = await wallet.keyAgent.signTransaction({ - body: coreTx, - hash - }); + const witnessSet = await wallet.keyAgent.signTransaction( + { + body: coreTx, + hash + }, + { inputAddressResolver: wallet.inputAddressResolver } + ); const cslWitnessSet = coreToCsl.witnessSet(witnessSet); diff --git a/packages/wallet/src/services/DelegationTracker/DelegationTracker.ts b/packages/wallet/src/services/DelegationTracker/DelegationTracker.ts index 70266cb49c2..19939be116d 100644 --- a/packages/wallet/src/services/DelegationTracker/DelegationTracker.ts +++ b/packages/wallet/src/services/DelegationTracker/DelegationTracker.ts @@ -25,7 +25,7 @@ import { transactionsWithCertificates } from './transactionCertificates'; export const createBlockEpochProvider = (walletProvider: WalletProvider, retryBackoffConfig: RetryBackoffConfig) => (blockHashes: Cardano.BlockId[]) => - coldObservableProvider(() => walletProvider.queryBlocksByHashes(blockHashes), retryBackoffConfig).pipe( + coldObservableProvider(() => walletProvider.blocksByHashes(blockHashes), retryBackoffConfig).pipe( map((blocks) => blocks.map(({ epoch }) => epoch)) ); diff --git a/packages/wallet/src/services/ProviderTracker/TrackedWalletProvider.ts b/packages/wallet/src/services/ProviderTracker/TrackedWalletProvider.ts index d5ed3d6aa55..05db697f91e 100644 --- a/packages/wallet/src/services/ProviderTracker/TrackedWalletProvider.ts +++ b/packages/wallet/src/services/ProviderTracker/TrackedWalletProvider.ts @@ -52,9 +52,9 @@ export class TrackedWalletProvider extends ProviderTracker implements WalletProv readonly ledgerTip: WalletProvider['ledgerTip']; readonly networkInfo: WalletProvider['networkInfo']; readonly utxoDelegationAndRewards: WalletProvider['utxoDelegationAndRewards']; - readonly queryTransactionsByAddresses: WalletProvider['queryTransactionsByAddresses']; - readonly queryTransactionsByHashes: WalletProvider['queryTransactionsByHashes']; - readonly queryBlocksByHashes: WalletProvider['queryBlocksByHashes']; + readonly transactionsByAddresses: WalletProvider['transactionsByAddresses']; + readonly transactionsByHashes: WalletProvider['transactionsByHashes']; + readonly blocksByHashes: WalletProvider['blocksByHashes']; readonly currentWalletProtocolParameters: WalletProvider['currentWalletProtocolParameters']; readonly genesisParameters: WalletProvider['genesisParameters']; readonly rewardsHistory: WalletProvider['rewardsHistory']; @@ -74,15 +74,15 @@ export class TrackedWalletProvider extends ProviderTracker implements WalletProv () => walletProvider.utxoDelegationAndRewards(addresses, rewardAccount), this.stats.utxoDelegationAndRewards$ ); - this.queryTransactionsByAddresses = (addresses) => + this.transactionsByAddresses = (addresses) => this.trackedCall( - () => walletProvider.queryTransactionsByAddresses(addresses), + () => walletProvider.transactionsByAddresses(addresses), this.stats.queryTransactionsByAddresses$ ); - this.queryTransactionsByHashes = (hashes) => - this.trackedCall(() => walletProvider.queryTransactionsByHashes(hashes), this.stats.queryTransactionsByHashes$); - this.queryBlocksByHashes = (hashes) => - this.trackedCall(() => walletProvider.queryBlocksByHashes(hashes), this.stats.queryBlocksByHashes$); + this.transactionsByHashes = (hashes) => + this.trackedCall(() => walletProvider.transactionsByHashes(hashes), this.stats.queryTransactionsByHashes$); + this.blocksByHashes = (hashes) => + this.trackedCall(() => walletProvider.blocksByHashes(hashes), this.stats.queryBlocksByHashes$); this.currentWalletProtocolParameters = () => this.trackedCall(walletProvider.currentWalletProtocolParameters, this.stats.currentWalletProtocolParameters$); this.genesisParameters = () => this.trackedCall(walletProvider.genesisParameters, this.stats.genesisParameters$); diff --git a/packages/wallet/src/services/TransactionsTracker.ts b/packages/wallet/src/services/TransactionsTracker.ts index ca2ad312c5b..6561a7cd5b9 100644 --- a/packages/wallet/src/services/TransactionsTracker.ts +++ b/packages/wallet/src/services/TransactionsTracker.ts @@ -71,7 +71,7 @@ export const createAddressTransactionsProvider = ( while (true) { const lastStoredTransaction: Cardano.TxAlonzo | undefined = localTransactions[localTransactions.length - 1]; - const newTransactions = await walletProvider.queryTransactionsByAddresses( + const newTransactions = await walletProvider.transactionsByAddresses( addresses, lastStoredTransaction?.blockHeader.blockNo ); diff --git a/packages/wallet/src/services/createNftMetadataProvider.ts b/packages/wallet/src/services/createNftMetadataProvider.ts index 2ae416722fe..4510afe8542 100644 --- a/packages/wallet/src/services/createNftMetadataProvider.ts +++ b/packages/wallet/src/services/createNftMetadataProvider.ts @@ -21,7 +21,7 @@ export const createNftMetadataProvider = map((transactions) => transactions.find((tx) => tx.id === latestMintTxId)), mergeMap((localTx) => // Use local transaction if available, otherwise fetch from WalletProvider - localTx ? of(localTx) : from(walletProvider.queryTransactionsByHashes([latestMintTxId]).then(([tx]) => tx)) + localTx ? of(localTx) : from(walletProvider.transactionsByHashes([latestMintTxId]).then(([tx]) => tx)) ), map(({ auxiliaryData }) => Asset.util.metadatumToCip25(asset, auxiliaryData?.body.blob)) ) diff --git a/packages/wallet/src/services/util/equals.ts b/packages/wallet/src/services/util/equals.ts index 618a8333990..bebf4a9c360 100644 --- a/packages/wallet/src/services/util/equals.ts +++ b/packages/wallet/src/services/util/equals.ts @@ -14,5 +14,7 @@ export const txEquals = (a: Cardano.TxAlonzo, b: Cardano.TxAlonzo) => a.id === b export const transactionsEquals = (a: Cardano.TxAlonzo[], b: Cardano.TxAlonzo[]) => arrayEquals(a, b, txEquals); +export const txInEquals = (a: Cardano.NewTxIn, b: Cardano.NewTxIn) => a.txId === b.txId && a.index === b.index; + export const utxoEquals = (a: Cardano.Utxo[], b: Cardano.Utxo[]) => - arrayEquals(a, b, ([aTxIn], [bTxIn]) => aTxIn.txId === bTxIn.txId && aTxIn.index === bTxIn.index); + arrayEquals(a, b, ([aTxIn], [bTxIn]) => txInEquals(aTxIn, bTxIn)); diff --git a/packages/wallet/test/KeyManagement/InMemoryKeyAgent.test.ts b/packages/wallet/test/KeyManagement/InMemoryKeyAgent.test.ts index 75e3c684459..87220b440a1 100644 --- a/packages/wallet/test/KeyManagement/InMemoryKeyAgent.test.ts +++ b/packages/wallet/test/KeyManagement/InMemoryKeyAgent.test.ts @@ -67,7 +67,7 @@ describe('InMemoryKeyAgent', () => { test('signBlob', async () => { const { publicKey, signature } = await keyAgent.signBlob( - { index: 0, type: KeyManagement.KeyType.Internal }, + { index: 0, role: KeyManagement.KeyRole.Internal }, Cardano.util.HexBlob('abc123') ); expect(typeof publicKey).toBe('string'); diff --git a/packages/wallet/test/KeyManagement/KeyAgentBase.test.ts b/packages/wallet/test/KeyManagement/KeyAgentBase.test.ts index dedd4793bd7..7096d34febb 100644 --- a/packages/wallet/test/KeyManagement/KeyAgentBase.test.ts +++ b/packages/wallet/test/KeyManagement/KeyAgentBase.test.ts @@ -70,25 +70,30 @@ describe('KeyAgentBase', () => { .mockResolvedValueOnce({ publicKey: 'key1', signature: 'signature1' }) .mockResolvedValueOnce({ publicKey: 'key2', signature: 'signature2' }); const body = {} as unknown as Cardano.TxBodyAlonzo; - const witnessSet = await keyAgent.signTransaction({ - body, - hash: Cardano.TransactionId('8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec') - }); + const inputAddressResolver = {} as KeyManagement.InputAddressResolver; + const options = { inputAddressResolver } as KeyManagement.SignTransactionOptions; + const witnessSet = await keyAgent.signTransaction( + { + body, + hash: Cardano.TransactionId('8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec') + }, + options + ); expect(keyAgent.signBlob).toBeCalledTimes(2); - expect(ownSignatureKeyPaths).toBeCalledWith(body, keyAgent.knownAddresses); + expect(ownSignatureKeyPaths).toBeCalledWith(body, keyAgent.knownAddresses, inputAddressResolver); expect(witnessSet.size).toBe(2); expect(typeof [...witnessSet.values()][0]).toBe('string'); }); test('derivePublicKey', async () => { - const externalPublicKey = await keyAgent.derivePublicKey({ index: 1, type: KeyManagement.KeyType.External }); + const externalPublicKey = await keyAgent.derivePublicKey({ index: 1, role: KeyManagement.KeyRole.External }); expect(typeof externalPublicKey).toBe('string'); - const stakePublicKey = await keyAgent.derivePublicKey({ index: 1, type: KeyManagement.KeyType.Stake }); + const stakePublicKey = await keyAgent.derivePublicKey({ index: 1, role: KeyManagement.KeyRole.Stake }); expect(typeof stakePublicKey).toBe('string'); }); test('deriveCslPublicKey', async () => { - expect(await keyAgent.deriveCslPublicKeyPublic({ index: 0, type: KeyManagement.KeyType.External })).toBeInstanceOf( + expect(await keyAgent.deriveCslPublicKeyPublic({ index: 0, role: KeyManagement.KeyRole.External })).toBeInstanceOf( CSL.PublicKey ); }); diff --git a/packages/wallet/test/KeyManagement/cip8/cip30signData.test.ts b/packages/wallet/test/KeyManagement/cip8/cip30signData.test.ts index fe48cd3fa10..a3e578bc2dd 100644 --- a/packages/wallet/test/KeyManagement/cip8/cip30signData.test.ts +++ b/packages/wallet/test/KeyManagement/cip8/cip30signData.test.ts @@ -1,5 +1,5 @@ import { Address, Ed25519Signature, PublicKey } from '@emurgo/cardano-serialization-lib-nodejs'; -import { AddressType, KeyAgent, KeyType } from '../../../src/KeyManagement'; +import { AddressType, KeyAgent, KeyRole } from '../../../src/KeyManagement'; import { COSEKey, COSESign1, SigStructure } from '@emurgo/cardano-message-signing-nodejs'; import { Cardano, util } from '@cardano-sdk/core'; import { CoseLabel } from '../../../src/KeyManagement/cip8/util'; @@ -54,7 +54,7 @@ describe('cip30signData', () => { expect(publicKeyHex).toEqual( await keyAgent.derivePublicKey({ index: addressDerivationPath.index, - type: addressDerivationPath.type as number + role: addressDerivationPath.type as number }) ); }); @@ -68,7 +68,7 @@ describe('cip30signData', () => { expect(publicKeyHex).toEqual( await keyAgent.derivePublicKey({ index: 0, - type: KeyType.Stake + role: KeyRole.Stake }) ); }); diff --git a/packages/wallet/test/KeyManagement/util/ownSignaturePaths.test.ts b/packages/wallet/test/KeyManagement/util/ownSignaturePaths.test.ts index bb505bad119..6f9e8555488 100644 --- a/packages/wallet/test/KeyManagement/util/ownSignaturePaths.test.ts +++ b/packages/wallet/test/KeyManagement/util/ownSignaturePaths.test.ts @@ -1,11 +1,6 @@ -import { AddressType, GroupedAddress, KeyType, util } from '../../../src/KeyManagement'; +import { AddressType, GroupedAddress, KeyRole, util } from '../../../src/KeyManagement'; import { Cardano } from '@cardano-sdk/core'; -const createAddressInput = (address: Cardano.Address) => - ({ - address - } as Cardano.TxIn); - const createGroupedAddress = (address: Cardano.Address, type: AddressType, index: number): GroupedAddress => ({ address, @@ -23,23 +18,28 @@ describe('KeyManagement.util.ownSignaturePaths', () => { ); const txBody = { certificates: [{ __typename: Cardano.CertificateType.StakeKeyRegistration }], - inputs: [address1, address2, address1].map(createAddressInput) - } as Cardano.TxBodyAlonzo; + inputs: [{}, {}, {}] + } as Cardano.NewTxBodyAlonzo; const knownAddresses = [address1, address2].map((address, index) => createGroupedAddress(address, AddressType.External, index) ); - expect(util.ownSignatureKeyPaths(txBody, knownAddresses)).toEqual([ + const resolveInput = jest + .fn() + .mockReturnValueOnce(address1) + .mockReturnValueOnce(address2) + .mockReturnValueOnce(address1); + expect(util.ownSignatureKeyPaths(txBody, knownAddresses, resolveInput)).toEqual([ { index: 0, - role: KeyType.External + role: KeyRole.External }, { index: 1, - role: KeyType.External + role: KeyRole.External }, { index: 0, - role: KeyType.Stake + role: KeyRole.Stake } ]); }); diff --git a/packages/wallet/test/KeyManagement/util/stubSignTransaction.test.ts b/packages/wallet/test/KeyManagement/util/stubSignTransaction.test.ts index 20723374f28..88914d9f4c5 100644 --- a/packages/wallet/test/KeyManagement/util/stubSignTransaction.test.ts +++ b/packages/wallet/test/KeyManagement/util/stubSignTransaction.test.ts @@ -1,5 +1,5 @@ import { Cardano } from '@cardano-sdk/core'; -import { GroupedAddress } from '../../../src/KeyManagement'; +import { GroupedAddress, InputAddressResolver } from '../../../src/KeyManagement'; import { stubSignTransaction } from '../../../src/KeyManagement/util'; jest.mock('../../../src/KeyManagement/util/ownSignatureKeyPaths'); @@ -7,11 +7,12 @@ const { ownSignatureKeyPaths } = jest.requireMock('../../../src/KeyManagement/ut describe('KeyManagement.util.stubSignTransaction', () => { it('returns as many signatures as number of keys returned by ownSignaturePaths', () => { + const inputAddressResolver = {} as InputAddressResolver; // not called const txBody = {} as Cardano.TxBodyAlonzo; const knownAddresses = [{} as GroupedAddress]; ownSignatureKeyPaths.mockReturnValueOnce([{}]).mockReturnValueOnce([{}, {}]); - expect(stubSignTransaction(txBody, knownAddresses).size).toBe(1); - expect(stubSignTransaction(txBody, knownAddresses).size).toBe(2); - expect(ownSignatureKeyPaths).toBeCalledWith(txBody, knownAddresses); + expect(stubSignTransaction(txBody, knownAddresses, inputAddressResolver).size).toBe(1); + expect(stubSignTransaction(txBody, knownAddresses, inputAddressResolver).size).toBe(2); + expect(ownSignatureKeyPaths).toBeCalledWith(txBody, knownAddresses, inputAddressResolver); }); }); diff --git a/packages/wallet/test/SingleAddressWallet/load.test.ts b/packages/wallet/test/SingleAddressWallet/load.test.ts index f3d64f0be20..a60b900a6b7 100644 --- a/packages/wallet/test/SingleAddressWallet/load.test.ts +++ b/packages/wallet/test/SingleAddressWallet/load.test.ts @@ -42,7 +42,7 @@ const createWallet = async (stores: WalletStores, walletProvider: WalletProvider }; const assertWalletProperties = async ( - wallet: Wallet, + wallet: SingleAddressWallet, expectedDelegateeId: Cardano.PoolId | undefined, expectedRewardsHistory = flatten([...mocks.rewardsHistory.values()]) ) => { @@ -93,6 +93,8 @@ const assertWalletProperties = async ( expect(await firstValueFrom(wallet.assets$)).toEqual(new Map([[AssetId.TSLA, mocks.asset]])); // timeSettings$ expect(await firstValueFrom(wallet.timeSettings$)).toEqual(testnetTimeSettings); + // inputAddressResolver + expect(typeof wallet.inputAddressResolver).toBe('function'); }; const assertWalletProperties2 = async (wallet: Wallet) => { diff --git a/packages/wallet/test/SingleAddressWallet/methods.test.ts b/packages/wallet/test/SingleAddressWallet/methods.test.ts index a2bad40de57..99268bbc3c8 100644 --- a/packages/wallet/test/SingleAddressWallet/methods.test.ts +++ b/packages/wallet/test/SingleAddressWallet/methods.test.ts @@ -4,6 +4,8 @@ import { AssetId, createStubStakePoolSearchProvider, createStubTimeSettingsProvi import { Cardano, testnetTimeSettings } from '@cardano-sdk/core'; import { KeyManagement, SingleAddressWallet } from '../../src'; import { firstValueFrom, skip } from 'rxjs'; +import { utxo } from '../mocks'; +import { waitForWalletStateSettle } from '../util'; jest.mock('../../src/KeyManagement/cip8/cip30signData'); const { cip30signData } = jest.requireMock('../../src/KeyManagement/cip8/cip30signData'); @@ -35,6 +37,7 @@ describe('SingleAddressWallet methods', () => { { assetProvider, keyAgent, stakePoolSearchProvider, timeSettingsProvider, txSubmitProvider, walletProvider } ); keyAgent.knownAddresses.push(groupedAddress); + await waitForWalletStateSettle(wallet); }); afterEach(() => { @@ -60,6 +63,7 @@ describe('SingleAddressWallet methods', () => { } ]; const props = { + inputs: new Set([utxo[1][0]]), outputs: new Set(outputs) }; diff --git a/packages/wallet/test/Transaction/createTransactionInternals.test.ts b/packages/wallet/test/Transaction/createTransactionInternals.test.ts index dc47072dc32..4c414fe0249 100644 --- a/packages/wallet/test/Transaction/createTransactionInternals.test.ts +++ b/packages/wallet/test/Transaction/createTransactionInternals.test.ts @@ -56,7 +56,7 @@ describe('Transaction.createTransactionInternals', () => { const txInternals = await createSimpleTransactionInternals(); expect(txInternals.body.outputs).toHaveLength(2); expect(typeof txInternals.body.fee).toBe('bigint'); - expect(typeof txInternals.body.inputs[0].address).toBe('string'); + expect(typeof txInternals.body.inputs[0].txId).toBe('string'); expect(typeof txInternals.hash).toBe('string'); }); }); diff --git a/packages/wallet/test/mocks/mockWalletProvider.ts b/packages/wallet/test/mocks/mockWalletProvider.ts index 0feda07bfb7..ef8fc1cb043 100644 --- a/packages/wallet/test/mocks/mockWalletProvider.ts +++ b/packages/wallet/test/mocks/mockWalletProvider.ts @@ -98,7 +98,6 @@ export const queryTransactionsResult: Cardano.TxAlonzo[] = [ }, { __typename: Cardano.CertificateType.StakeDelegation, - epoch: currentEpoch.number - 10, poolId: somePartialStakePools[0].id, stakeKeyHash } diff --git a/packages/wallet/test/services/UtxoTracker.test.ts b/packages/wallet/test/services/UtxoTracker.test.ts index fe454477dd1..7fe98effc53 100644 --- a/packages/wallet/test/services/UtxoTracker.test.ts +++ b/packages/wallet/test/services/UtxoTracker.test.ts @@ -23,7 +23,7 @@ describe('createUtxoTracker', () => { body: { inputs: [utxo[0][0]] } - } as Cardano.NewTxAlonzo + } as unknown as Cardano.NewTxAlonzo ] }); const utxoTracker = createUtxoTracker(