Skip to content

Commit

Permalink
fix: add reference app + mediator live mode (#177)
Browse files Browse the repository at this point in the history
Signed-off-by: Francisco Javier Ribó Labrador <elribonazo@gmail.com>
  • Loading branch information
elribonazo committed May 2, 2024
1 parent d8d7281 commit 14dd42d
Show file tree
Hide file tree
Showing 42 changed files with 2,562 additions and 767 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
audit_level: moderate
create_issues: false
github_token: ${{ secrets.GITHUB_TOKEN }}
production_flag: true

- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@v1.0.23
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Browser NextJS
```bash
cd demos/next
npm i
npm run build # becuase Error: ENOENT: no such file or directory, open '/.../atala-prism-wallet-sdk-ts/demos/next/.next/BUILD_ID']
npm run start
```

Expand Down
348 changes: 266 additions & 82 deletions demos/next/package-lock.json

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions demos/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,22 @@
"lint": "next lint"
},
"dependencies": {
"@atala/prism-wallet-sdk": "../..",
"@pluto-encrypted/database": "^1.2.14",
"@pluto-encrypted/inmemory": "^1.3.11",
"@atala/prism-wallet-sdk": "../../",
"@pluto-encrypted/database": "^1.15.5",
"@pluto-encrypted/indexdb": "^1.12.2",
"@pluto-encrypted/schemas": "^1.3.5",
"fs": "^0.0.1-security",
"@noble/hashes": "^1.3.3",
"jose": "^5.2.0",
"jotai": "^2.6.1",
"next": "14.0.4",
"path": "^0.12.7",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"react-redux": "^7.2.6",
"redux": "^4.1.2",
"redux-thunk": "^2.4.0",
"@reduxjs/toolkit": "^1.9.5"
},
"devDependencies": {
"@types/node": "^20",
Expand Down
258 changes: 258 additions & 0 deletions demos/next/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import { addRxPlugin } from "rxdb";
import { RxDBDevModePlugin } from "rxdb/plugins/dev-mode";

addRxPlugin(RxDBDevModePlugin);

import { AnyAction, ThunkDispatch, createAsyncThunk } from "@reduxjs/toolkit";
import SDK from "@atala/prism-wallet-sdk";
import { Database } from "@pluto-encrypted/database";
import { sha512 } from '@noble/hashes/sha512'
import { RootState, reduxActions } from "@/reducers/app";
import { DBNAME } from "@/utils/types";
import IndexDB from '@pluto-encrypted/indexdb'
import {
getDefaultCollections,
DIDCollection,
DIDPairCollection,
MediatorCollection,
PrivateKeyColletion,
CredentialCollection,
CredentialRequestMetadataCollection,
LinkSecretColletion,
MessageColletion
} from "@pluto-encrypted/schemas";

const Agent = SDK.Agent;
const BasicMessage = SDK.BasicMessage;
const OfferCredential = SDK.OfferCredential;
const ListenerKey = SDK.ListenerKey;
const IssueCredential = SDK.IssueCredential;
const RequestPresentation = SDK.RequestPresentation;


export const acceptPresentationRequest = createAsyncThunk<
any,
{
agent: SDK.Agent,
message: SDK.Domain.Message,
credential: SDK.Domain.Credential
}
>("acceptPresentationRequest", async (options, api) => {
try {
const { agent, message, credential } = options;
const requestPresentation = RequestPresentation.fromMessage(message);
try {
const presentation = await agent.createPresentationForRequestProof(requestPresentation, credential);
await agent.sendMessage(presentation.makeMessage());
} catch (err) {
console.log("continue after err", err);
}
return api.fulfillWithValue(null);
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

export const rejectPresentationRequest = createAsyncThunk<
any,
{
message: SDK.Domain.Message,
pluto: SDK.Domain.Pluto
}
>("rejectPresentationRequest", async (options, api) => {
try {
const { message, pluto } = options;
const requestPresentation = RequestPresentation.fromMessage(message);
const storedMessage = await pluto.getCollection("messages").findOne({
selector: {
id: {
$eq: message.id
}
}
}).exec();
await storedMessage?.remove()
return api.fulfillWithValue(requestPresentation);
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

export const rejectCredentialOffer = createAsyncThunk<
any,
{
message: SDK.Domain.Message,
pluto: SDK.Domain.Pluto
}
>("rejectCredentialOffer", async (options, api) => {
try {
const { message, pluto } = options;
const credentialOffer = OfferCredential.fromMessage(message);
const storedMessage = await pluto.getCollection("messages").findOne({
selector: {
id: {
$eq: message.id
}
}
}).exec();
await storedMessage?.remove()
return api.fulfillWithValue(credentialOffer);
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

export const acceptCredentialOffer = createAsyncThunk<
any,
{
agent: SDK.Agent,
message: SDK.Domain.Message
}
>("acceptCredentialOffer", async (options, api) => {
try {
const { agent, message } = options;
const credentialOffer = OfferCredential.fromMessage(message);
const requestCredential = await agent.prepareRequestCredentialWithIssuer(credentialOffer);
try {
const requestMessage = requestCredential.makeMessage()
await agent.sendMessage(requestMessage);
} catch (err) {
console.log("continue after err", err);
}
return api.fulfillWithValue(null);
} catch (err) {
return api.rejectWithValue(err as Error);
}
})


async function handleMessages(
options: {
dispatch: ThunkDispatch<unknown, unknown, AnyAction>,
agent: SDK.Agent,
},
newMessages: SDK.Domain.Message[]
) {
const { agent, dispatch } = options;
const issuedCredentials = newMessages.filter((message) => message.piuri === "https://didcomm.org/issue-credential/3.0/issue-credential");
if (issuedCredentials.length) {
for (const issuedCredential of issuedCredentials) {
const issueCredential = IssueCredential.fromMessage(issuedCredential);
await agent.processIssuedCredentialMessage(issueCredential);
}
}
dispatch(
reduxActions.messageSuccess(
newMessages
)
)
}

export const stopAgent = createAsyncThunk<
{ agent: SDK.Agent },
{ agent: SDK.Agent }
>("stopAgent", async (options, api) => {
try {
const { agent } = options
agent.removeListener(ListenerKey.MESSAGE, handleMessages.bind({}, { dispatch: api.dispatch, agent }));
await agent.stop()
return api.fulfillWithValue({ agent })
} catch (err) {
return api.rejectWithValue(err as Error);
}
})


export const startAgent = createAsyncThunk<
{ agent: SDK.Agent },
{ agent: SDK.Agent }
>("startAgent", async (options, api) => {
try {
const { agent } = options;
agent.addListener(ListenerKey.MESSAGE, handleMessages.bind({}, { dispatch: api.dispatch, agent }));
await agent.start()

const mediator = agent.currentMediatorDID;
if (!mediator) {
throw new Error("Mediator not available");
}

const secondaryDID = await agent.createNewPeerDID(
[],
true
);

const testMessage = new BasicMessage(
{ content: "Test Message" },
secondaryDID,
secondaryDID
).makeMessage();

try {
await agent.sendMessage(testMessage);
} catch (err) {
console.log("Safe to ignore, mediator returns null on successfully receiving the message, unpack fails.");
}

return api.fulfillWithValue({ agent })
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

export const initAgent = createAsyncThunk<
{ agent: SDK.Agent },
{
mediatorDID: SDK.Domain.DID,
pluto: SDK.Domain.Pluto
}
>("initAgent", async (options, api) => {
try {
const { mediatorDID, pluto } = options;
const agent = await Agent.initialize({ mediatorDID, pluto });
return api.fulfillWithValue({ agent })
} catch (err) {
return api.rejectWithValue(err as Error);
}
})

export const connectDatabase = createAsyncThunk<
{
db: SDK.Domain.Pluto
},
{
encryptionKey: Uint8Array,
},
{ state: { app: RootState } }
>("connectDatabase", async (options, api) => {
try {
const hashedPassword = sha512(options.encryptionKey)
const db = await Database.createEncrypted<{
dids: DIDCollection;
didpairs: DIDPairCollection;
mediators: MediatorCollection;
privatekeys: PrivateKeyColletion;
credentials: CredentialCollection;
credentialrequestmetadatas: CredentialRequestMetadataCollection;
linksecrets: LinkSecretColletion;
messages: MessageColletion;
}>(
{
name: DBNAME,
encryptionKey: hashedPassword,
storage: IndexDB,
collections: getDefaultCollections()
}
);
const messages = await db.getAllMessages()
const connections = await db.getAllDidPairs()
const credentials = await db.getAllCredentials();
api.dispatch(
reduxActions.dbPreload(
{ messages, connections, credentials }
)
);
return api.fulfillWithValue({ db });
} catch (err) {
return api.rejectWithValue(err as Error);
}
});
13 changes: 13 additions & 0 deletions demos/next/src/actions/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export enum DBConnection {
success = "dbConnectionSuccess",
failure = "dbConnectionFailure",
ongoing = "dbConnectionOngoing",
}

export enum DBPreload {
complete = "dbPreload",
}

export enum Message {
success = "messageSuccess"
}
4 changes: 0 additions & 4 deletions demos/next/src/app/App.css

This file was deleted.

8 changes: 2 additions & 6 deletions demos/next/src/app/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ import React from "react";
export const Box: React.FC<{ children: React.ReactNode | React.ReactNode[] }> = (props) => {
return (
<div
style={{
borderRadius: 10,
border: "1px solid lightgray",
padding: 20,
margin: 20,
}}
className="w-full mt-5 p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
>
{props.children}
</div>

);
}
9 changes: 3 additions & 6 deletions demos/next/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
background: linear-gradient(to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb));
}
Loading

0 comments on commit 14dd42d

Please sign in to comment.