Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): multisig receive acdc #590

Merged
merged 36 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1e44923
feat: create multi-sig oobi
Sotatek-HocNguyena Jun 28, 2024
4cc5a0e
Merge branch 'develop' of
Sotatek-HocNguyena Jul 1, 2024
d71ce44
feat: multisig admit credential
Sotatek-HocNguyena Jul 3, 2024
8864913
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 3, 2024
a4e5723
feat: creat multi-sig oobi
Sotatek-HocNguyena Jul 3, 2024
1ea9e22
feat: creat multi-sig oobi
Sotatek-HocNguyena Jul 3, 2024
574f9d4
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 4, 2024
6df90b3
feat: auto trigger end role authorization when the multisig is ready
Sotatek-HocNguyena Jul 11, 2024
12ffda5
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 11, 2024
8f0a6bc
refactor: get recp and ourAid outside the loop
Sotatek-HocNguyena Jul 11, 2024
9fe136a
Merge branch 'feat/create-multi-sig-oobi' of github.com:cardano-found…
Sotatek-HocNguyena Jul 11, 2024
71d24dd
refactor: only join the authorization when the route is add end role
Sotatek-HocNguyena Jul 12, 2024
0c8ea8a
Merge branch 'feat/create-multi-sig-oobi' of github.com:cardano-found…
Sotatek-HocNguyena Jul 15, 2024
d666472
refactor: remove id tag from identifierMetadata
Sotatek-HocNguyena Jul 15, 2024
dc12c10
Merge branch 'feat/create-multi-sig-oobi' of github.com:cardano-found…
Sotatek-HocNguyena Jul 15, 2024
6d71b37
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 15, 2024
e0f5573
feat: add authorizedEids to multisig records
Sotatek-HocNguyena Jul 15, 2024
fc61379
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 15, 2024
349e959
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 16, 2024
3924710
Merge branch 'feat/create-multi-sig-oobi' of github.com:cardano-found…
Sotatek-HocNguyena Jul 16, 2024
5094e6c
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 17, 2024
d05764a
update: remove authorizedEids in identifier metadata
Sotatek-HocNguyena Jul 18, 2024
a5faa20
revert: remove admitMultisig
Sotatek-HocNguyena Jul 18, 2024
c7fb6d7
refactor: mark the notification after join the authorization
Sotatek-HocNguyena Jul 18, 2024
7457aeb
feat: multisig receive acdc
Sotatek-HocNguyena Jul 19, 2024
9cfb5c8
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Sotatek-HocNguyena Jul 19, 2024
464b314
Merge branch 'develop' into feat/multisig-receive-acdc
Sotatek-BaoHoanga Jul 30, 2024
381554e
Merge branch 'develop' into feat/multisig-receive-acdc
Sotatek-BaoHoanga Jul 31, 2024
463aa67
feat: update receive an ACDC to a multi-sig identifier
Sotatek-BaoHoanga Jul 31, 2024
da9f963
Merge branch 'develop' into feat/multisig-receive-acdc
Sotatek-BaoHoanga Aug 2, 2024
3f97ec7
fix: update unittest for multisigAdmit
Sotatek-BaoHoanga Aug 2, 2024
55dbdff
feat: update notification for multisig receive acdc
Sotatek-BaoHoanga Aug 5, 2024
4345be6
fix: update unittest ReceiveCredentialMultisig
Sotatek-BaoHoanga Aug 5, 2024
03ffe00
Merge branch 'develop' into feat/multisig-receive-acdc
Sotatek-BaoHoanga Aug 7, 2024
14c66b5
fix: resolve comment
Sotatek-BaoHoanga Aug 7, 2024
daaf5e7
fix: update parameter of multisigAdmit and unittest multisigService
Sotatek-BaoHoanga Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions src/core/agent/services/ipexCommunicationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ jest.mock("../../../core/agent/agent", () => ({
deleteNotificationMock(id),
addPendingOperationToQueue: jest.fn(),
},
multiSigs: {
multisigAdmit: jest.fn().mockResolvedValue({ name: "opName" }),
},
},
},
}));
Expand Down Expand Up @@ -750,4 +753,204 @@ describe("Ipex communication service of agent", () => {
ipexCommunicationService.getIpexApplyDetails(noti)
).rejects.toThrowError(Agent.KERIA_CONNECTION_BROKEN);
});

test("can accept ACDC from multisig exn", async () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValue(true);
const id = "uuid";

signifyClient.exchanges = jest.fn().mockReturnValue({
get: jest
.fn()
.mockImplementationOnce(() =>
Promise.resolve({
exn: {
d: "ECWyfhUctyCCoxZG-PU7MFPWkw5H2--TMC9v_tbZjjBv",
i: "ECa8C3YyqT9khmn0MnLUJKQTCNmiB6tr74uNUX_Y-r2y",
p: "",
dt: "2024-07-31T02:45:28.535000+00:00",
r: "/multisig/exn",
a: {
gid: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
},
e: {
exn: {
d: "EPcCdp9JRd5wgCVs7hmzB0JsbuqHhYU3ggShqR2QDpbH",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
p: "EDm8iNyZ9I3P93jb0lFtL6DJD-4Mtd2zw1ADFOoEQAqw",
dt: "2024-07-31T02:45:25.998000+00:00",
r: "/ipex/admit",
a: {
m: "",
},
e: {},
},
d: "EFHb7hpsDWdBdZyMVhQR6kJN6j9DddAAS-_pQQhq-yZ6",
},
},
})
)
.mockImplementationOnce(() =>
Promise.resolve({
exn: {
v: "KERI10JSON000514_",
t: "exn",
d: "EDm8iNyZ9I3P93jb0lFtL6DJD-4Mtd2zw1ADFOoEQAqw",
i: "EKhebhdg6jOqK7ZgY-cFpx6rozpUave8llE2B15ioNHi",
p: "",
dt: "2024-07-31T02:45:17.288000+00:00",
r: "/ipex/grant",
q: {},
a: {
m: "",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
},
e: {
acdc: {
v: "ACDC10JSON00018d_",
d: "EJvvnAZruVSfvPZjzGwyTR3RQApoK7228du0c8flDcaF",
i: "EKhebhdg6jOqK7ZgY-cFpx6rozpUave8llE2B15ioNHi",
ri: "EDXcY9Jsg32LgZ8S5QuHNi3ZF5U01_kU3FakVUMCbGG3",
s: "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao",
a: {
d: "EKoEogKtVuRAkoFs43CLPTwSAUuo3TQsmnKI86ef2Usb",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
dt: "2024-07-31T02:45:16.860000+00:00",
LEI: "New 310724",
},
},
d: "EK3-ZPPv8JYVAIK8pq1SfQxvHlsKmwdpqxqO1kcP_ajv",
},
},
})
),
});

identifierStorage.getIdentifierMetadata = jest.fn().mockResolvedValue({
signifyName: "holder",
});

schemaGetMock.mockResolvedValue({ title: "title" });
credentialListMock.mockResolvedValue([
{
sad: {
d: "id",
},
},
]);
credentialStorage.getCredentialMetadata = jest.fn().mockResolvedValue({
id: "id",
});

await ipexCommunicationService.acceptAcdcFromMultisigExn(id);
expect(Agent.agent.multiSigs.multisigAdmit).toBeCalledTimes(1);
expect(operationPendingStorage.save).toBeCalledWith({
id: "opName",
recordType: OperationPendingRecordType.ExchangeReceiveCredential,
});
expect(
Agent.agent.signifyNotifications.addPendingOperationToQueue
).toBeCalledTimes(1);
expect(deleteNotificationMock).toBeCalledWith(id);
});

test("cannot accept ACDC from multisig exn if the notification is missing in the DB", async () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValueOnce(true);
const id = "not-found-id";
notificationStorage.findById.mockResolvedValueOnce(null);

await expect(
ipexCommunicationService.acceptAcdcFromMultisigExn(id)
).rejects.toThrowError(
`${IpexCommunicationService.NOTIFICATION_NOT_FOUND} ${id}`
);
});

test("cannot accept ACDC from multisig exn if identifier is not locally stored", async () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValueOnce(true);
const id = "uuid";

notificationStorage.findById = jest.fn().mockResolvedValue({
id,
createdAt: new Date("2024-04-29T11:01:04.903Z"),
a: {
d: "saidForUuid",
},
});

signifyClient.exchanges = jest.fn().mockReturnValue({
get: jest
.fn()
.mockImplementationOnce(() =>
Promise.resolve({
exn: {
d: "ECWyfhUctyCCoxZG-PU7MFPWkw5H2--TMC9v_tbZjjBv",
i: "ECa8C3YyqT9khmn0MnLUJKQTCNmiB6tr74uNUX_Y-r2y",
p: "",
dt: "2024-07-31T02:45:28.535000+00:00",
r: "/multisig/exn",
a: {
gid: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
},
e: {
exn: {
d: "EPcCdp9JRd5wgCVs7hmzB0JsbuqHhYU3ggShqR2QDpbH",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
p: "EDm8iNyZ9I3P93jb0lFtL6DJD-4Mtd2zw1ADFOoEQAqw",
dt: "2024-07-31T02:45:25.998000+00:00",
r: "/ipex/admit",
a: {
m: "",
},
e: {},
},
d: "EFHb7hpsDWdBdZyMVhQR6kJN6j9DddAAS-_pQQhq-yZ6",
},
},
})
)
.mockImplementationOnce(() =>
Promise.resolve({
exn: {
v: "KERI10JSON000514_",
t: "exn",
d: "EDm8iNyZ9I3P93jb0lFtL6DJD-4Mtd2zw1ADFOoEQAqw",
i: "EKhebhdg6jOqK7ZgY-cFpx6rozpUave8llE2B15ioNHi",
p: "",
dt: "2024-07-31T02:45:17.288000+00:00",
r: "/ipex/grant",
q: {},
a: {
m: "",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
},
e: {
acdc: {
v: "ACDC10JSON00018d_",
d: "EJvvnAZruVSfvPZjzGwyTR3RQApoK7228du0c8flDcaF",
i: "EKhebhdg6jOqK7ZgY-cFpx6rozpUave8llE2B15ioNHi",
ri: "EDXcY9Jsg32LgZ8S5QuHNi3ZF5U01_kU3FakVUMCbGG3",
s: "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao",
a: {
d: "EKoEogKtVuRAkoFs43CLPTwSAUuo3TQsmnKI86ef2Usb",
i: "EJgTVgwvxuY2pGAcuAcE_-77SA0wGRsvWGlaH8z_YP2f",
dt: "2024-07-31T02:45:16.860000+00:00",
LEI: "New 310724",
},
},
d: "EK3-ZPPv8JYVAIK8pq1SfQxvHlsKmwdpqxqO1kcP_ajv",
},
},
})
),
});

identifierStorage.getIdentifierMetadata = jest
.fn()
.mockResolvedValue(undefined);

await expect(
ipexCommunicationService.acceptAcdcFromMultisigExn(id)
).rejects.toThrowError(IpexCommunicationService.ISSUEE_NOT_FOUND_LOCALLY);
expect(deleteNotificationMock).not.toBeCalledWith(id);
});
});
85 changes: 79 additions & 6 deletions src/core/agent/services/ipexCommunicationService.ts
iFergal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,22 @@ class IpexCommunicationService extends AgentService {
(key) => exn.exn.e.acdc.e?.[key]?.s
);

const op = await this.admitIpex(
notifRecord.a.d as string,
holder.signifyName,
exn.exn.i,
[exn.exn.e.acdc.s, ...chainedSchemaSaids]
);
let op: Operation;
if (holder.multisigManageAid) {
op = await Agent.agent.multiSigs.multisigAdmit(
holder.signifyName,
notifRecord.a.d as string,
[exn.exn.e.acdc.s, ...chainedSchemaSaids]
);
} else {
op = await this.admitIpex(
notifRecord.a.d as string,
holder.signifyName,
exn.exn.i,
[exn.exn.e.acdc.s, ...chainedSchemaSaids]
);
}

const pendingOperation = await this.operationPendingStorage.save({
id: op.name,
recordType: OperationPendingRecordType.ExchangeReceiveCredential,
Expand Down Expand Up @@ -394,6 +404,69 @@ class IpexCommunicationService extends AgentService {
historyType,
});
}

@OnlineOnly
async acceptAcdcFromMultisigExn(id: string): Promise<void> {
const notifRecord = await this.getNotificationRecordById(id);
const exn = await this.props.signifyClient
.exchanges()
.get(notifRecord.a.d as string);

const multisigExn = exn?.exn?.e?.exn;
const previousExnGrantMsg = await this.props.signifyClient
.exchanges()
.get(exn?.exn.e.exn.p);

const holder = await this.identifierStorage.getIdentifierMetadata(
exn.exn.e.exn.i
);

if (!holder) {
throw new Error(IpexCommunicationService.ISSUEE_NOT_FOUND_LOCALLY);
}

const credentialId = previousExnGrantMsg.exn.e.acdc.d;
const connectionId = previousExnGrantMsg.exn.i;

const schemaSaid = previousExnGrantMsg.exn.e.acdc.s;
const allSchemaSaids = Object.keys(
previousExnGrantMsg.exn.e.acdc?.e || {}
).map((key) => previousExnGrantMsg.exn.e.acdc.e?.[key]?.s);
allSchemaSaids.push(schemaSaid);

const op = await Agent.agent.multiSigs.multisigAdmit(
holder.signifyName,
previousExnGrantMsg.exn.d as string,
allSchemaSaids,
multisigExn
);

const schema = await this.props.signifyClient.schemas().get(schemaSaid);
await this.saveAcdcMetadataRecord(
previousExnGrantMsg.exn.e.acdc.d,
previousExnGrantMsg.exn.e.acdc.a.dt,
connectionId,
schema.title,
connectionId
);

this.props.eventService.emit<AcdcStateChangedEvent>({
type: AcdcEventTypes.AcdcStateChanged,
payload: {
credentialId,
status: CredentialStatus.PENDING,
},
});

const pendingOperation = await this.operationPendingStorage.save({
id: op.name,
recordType: OperationPendingRecordType.ExchangeReceiveCredential,
});
Agent.agent.signifyNotifications.addPendingOperationToQueue(
pendingOperation
);
Agent.agent.signifyNotifications.deleteNotificationRecordById(id);
}
}

export { IpexCommunicationService };
Loading
Loading