Skip to content

Commit

Permalink
fix(key-management): the InMemoryKeyAgent now correctly takes into ac…
Browse files Browse the repository at this point in the history
…count the requiredSigners field
  • Loading branch information
AngelCastilloB committed Jun 2, 2023
1 parent 8e857ec commit 2071885
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
45 changes: 44 additions & 1 deletion packages/key-management/src/util/ownSignatureKeyPaths.ts
@@ -1,8 +1,11 @@
import * as Crypto from '@cardano-sdk/crypto';
import { AccountKeyDerivationPath, GroupedAddress } from '../types';
import { Cardano } from '@cardano-sdk/core';
import { isNotNil } from '@cardano-sdk/util';
import isEqual from 'lodash/isEqual';
import uniq from 'lodash/uniq';
import uniqBy from 'lodash/uniqBy';
import uniqWith from 'lodash/uniqWith';

/**
* Gets whether any certificate in the provided certificate list requires a stake key signature.
Expand Down Expand Up @@ -59,6 +62,39 @@ const getStakingKeyPaths = (
return paths;
};

/**
* Search for the key hashes provided in the requiredSigners in our current set of known addresses.
*
* @param groupedAddresses The known grouped addresses.
* @param keyHashes The list of required signers key hashes (or undefined if none).
* @returns A set of derivation paths if any of the key hashes is found.
*/
const getRequiredSignersKeyPaths = (
groupedAddresses: GroupedAddress[],
keyHashes?: Crypto.Ed25519KeyHashHex[]
): Set<AccountKeyDerivationPath> => {
const paths: Set<AccountKeyDerivationPath> = new Set();

if (!keyHashes) return paths;

for (const keyHash of keyHashes) {
for (const address of groupedAddresses) {
const paymentCredential = Cardano.Address.fromBech32(address.address)?.asBase()?.getPaymentCredential().hash;
const stakingCredential = Cardano.RewardAccount.toHash(address.rewardAccount);

if (paymentCredential && paymentCredential.toString() === keyHash) {
paths.add({ index: address.index, role: Number(address.type) });
}

if (stakingCredential && address.stakeKeyDerivationPath && stakingCredential.toString() === keyHash) {
paths.add(address.stakeKeyDerivationPath);
}
}
}

return paths;
};

/**
* Assumes that a single staking key is used for all addresses (index=0)
*
Expand All @@ -82,5 +118,12 @@ export const ownSignatureKeyPaths = async (
).filter(isNotNil)
).map(({ type, index }) => ({ index, role: Number(type) }));

return [...paymentKeyPaths, ...getStakingKeyPaths(knownAddresses, txBody)];
return uniqWith(
[
...paymentKeyPaths,
...getStakingKeyPaths(knownAddresses, txBody),
...getRequiredSignersKeyPaths(knownAddresses, txBody.requiredExtraSignatures)
],
isEqual
);
};
43 changes: 43 additions & 0 deletions packages/key-management/test/util/ownSignaturePaths.test.ts
@@ -1,4 +1,5 @@
/* eslint-disable sonarjs/no-duplicate-string */
import * as Crypto from '@cardano-sdk/crypto';
import { AccountKeyDerivationPath, AddressType, GroupedAddress, KeyRole, util } from '../../src';
import { Cardano } from '@cardano-sdk/core';
import { txOut } from '../../../tx-construction/test/testData';
Expand Down Expand Up @@ -309,4 +310,46 @@ describe('KeyManagement.util.ownSignaturePaths', () => {
}
]);
});

it('returns the derivation path of a known payment credential key hash present in the requiredSigners field', async () => {
const paymentAddress = Cardano.PaymentAddress(
'addr1qxdtr6wjx3kr7jlrvrfzhrh8w44qx9krcxhvu3e79zr7497tpmpxjfyhk3vwg6qjezjmlg5nr5dzm9j6nxyns28vsy8stu5lh6'
);
const paymentHash = Crypto.Ed25519KeyHashHex('9ab1e9d2346c3f4be360d22b8ee7756a0316c3c1aece473e2887ea97');

const txBody = {
inputs: [{}, {}, {}],
requiredExtraSignatures: [paymentHash]
} as Cardano.TxBody;
const knownAddresses = [
createGroupedAddress(paymentAddress, ownRewardAccount, AddressType.External, 100, stakeKeyPath)
];

const resolveInput = jest.fn().mockReturnValueOnce(null);
expect(await util.ownSignatureKeyPaths(txBody, knownAddresses, { resolveInput })).toEqual([
{
index: 100,
role: KeyRole.External
}
]);
});

it('returns the derivation path of a known stake credential key hash present in the requiredSigners field', async () => {
const rewardAccount = Cardano.RewardAccount('stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr');
const stakeKeyHash = Cardano.RewardAccount.toHash(rewardAccount);

const txBody = {
inputs: [{}, {}, {}],
requiredExtraSignatures: [stakeKeyHash]
} as Cardano.TxBody;
const knownAddresses = [createGroupedAddress(address1, rewardAccount, AddressType.External, 0, stakeKeyPath)];
const resolveInput = jest.fn().mockReturnValue(null);

expect(await util.ownSignatureKeyPaths(txBody, knownAddresses, { resolveInput })).toEqual([
{
index: 0,
role: KeyRole.Stake
}
]);
});
});

0 comments on commit 2071885

Please sign in to comment.