Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:cardano-foundation/cf-identity-w…
Browse files Browse the repository at this point in the history
…allet into feature/app-offline-mode
  • Loading branch information
Sotatek-HocNguyena committed May 3, 2024
2 parents ad051d3 + 8fe19e7 commit 1a3bc27
Show file tree
Hide file tree
Showing 9 changed files with 1,674 additions and 71 deletions.
1,507 changes: 1,441 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@capacitor/splash-screen": "^5.0.0",
"@capacitor/status-bar": "^5.0.7",
"@dcspark/cardano-multiplatform-lib-browser": "^3.1.2",
"@fabianbormann/cardano-peer-connect": "^1.2.15",
"@ionic/react": "^7.5.4",
"@ionic/react-router": "^7.5.4",
"@ionic/storage": "^3.0.6",
Expand Down
4 changes: 4 additions & 0 deletions src/assets/icon-only.ts

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions src/core/agent/services/identifierService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { v4 as uuidv4 } from "uuid";
import { Signer } from "signify-ts";
import {
CreateIdentifierResult,
IdentifierDetails,
Expand All @@ -23,6 +24,8 @@ class IdentifierService extends AgentService {
"There's no exchange message for the given SAID";
static readonly FAILED_TO_ROTATE_AID =
"Failed to rotate AID, operation not completing...";
static readonly FAILED_TO_OBTAIN_KEY_MANAGER =
"Failed to obtain key manager for given AID";

async getIdentifiers(getArchived = false): Promise<IdentifierShortDetails[]> {
const identifiers: IdentifierShortDetails[] = [];
Expand Down Expand Up @@ -146,6 +149,24 @@ class IdentifierService extends AgentService {
});
}

async getSigner(identifier: string): Promise<Signer> {
const metadata = await this.identifierStorage.getIdentifierMetadata(
identifier
);
this.validIdentifierMetadata(metadata);

const aid = await this.signifyClient
.identifiers()
.get(metadata.signifyName);

const manager = this.signifyClient.manager;
if (manager) {
return (await manager.get(aid)).signers[0];
} else {
throw new Error(IdentifierService.FAILED_TO_OBTAIN_KEY_MANAGER);
}
}

@OnlineOnly
async syncKeriaIdentifiers() {
const { aids: signifyIdentifiers } = await this.signifyClient
Expand Down
102 changes: 102 additions & 0 deletions src/core/cardano/walletConnect/identityWalletConnect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Buffer } from "buffer";
import {
Paginate,
Cip30DataSignature,
IWalletInfo,
} from "@fabianbormann/cardano-peer-connect/dist/src/types";
import { CardanoPeerConnect } from "@fabianbormann/cardano-peer-connect";
import { Signer } from "signify-ts";
import { Agent } from "../../agent/agent";

class IdentityWalletConnect extends CardanoPeerConnect {
static readonly IDENTIFIER_ID_NOT_LOCATED =
"The id doesn't correspond with any stored identifier";
static readonly NO_IDENTIFIERS_STORED = "No stored identifiers";

getIdentifierOobi: () => Promise<string>;
sign: (identifier: string, payload: string) => Promise<string>;

signerCache: Map<string, Signer>;

constructor(
walletInfo: IWalletInfo,
seed: string | null,
announce: string[],
discoverySeed?: string | null
) {
super(walletInfo, {
seed: seed,
announce: announce,
discoverySeed: discoverySeed,
logLevel: "info",
});

this.signerCache = new Map();

this.getIdentifierOobi = async (): Promise<string> => {
const identifiers = await Agent.agent.identifiers.getIdentifiers();
if (!(identifiers && identifiers.length > 0)) {
throw new Error(IdentityWalletConnect.NO_IDENTIFIERS_STORED);
}

return Agent.agent.connections.getOobi(identifiers[0].signifyName);
};

this.sign = async (
identifier: string,
payload: string
): Promise<string> => {
if (this.signerCache.get(identifier) === undefined) {
this.signerCache.set(
identifier,
await Agent.agent.identifiers.getSigner(identifier)
);
}
return this.signerCache.get(identifier)!.sign(Buffer.from(payload)).qb64;
};
}

protected getNetworkId(): Promise<number> {
throw new Error("Method not implemented.");
}
protected getUtxos(
amount?: string | undefined,
paginate?: Paginate | undefined
): Promise<string[] | null> {
throw new Error("Method not implemented.");
}
protected getCollateral(
params?: { amount?: string | undefined } | undefined
): Promise<string[] | null> {
throw new Error("Method not implemented.");
}
protected getBalance(): Promise<string> {
throw new Error("Method not implemented.");
}
protected getUsedAddresses(): Promise<string[]> {
throw new Error("Method not implemented.");
}
protected getUnusedAddresses(): Promise<string[]> {
throw new Error("Method not implemented.");
}
protected getChangeAddress(): Promise<string> {
throw new Error("Method not implemented.");
}
protected async getRewardAddresses(): Promise<string[]> {
throw new Error("Method not implemented.");
}
protected signTx(tx: string, partialSign: boolean): Promise<string> {
throw new Error("Method not implemented.");
}
protected async signData(
addr: string,
payload: string
): Promise<Cip30DataSignature> {
throw new Error("Method not implemented.");
}
protected submitTx(tx: string): Promise<string> {
throw new Error("Method not implemented.");
}
}

export { IdentityWalletConnect };
93 changes: 93 additions & 0 deletions src/core/cardano/walletConnect/peerConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { IConnectMessage } from "@fabianbormann/cardano-peer-connect/dist/src/types";
import { ExperimentalContainer } from "@fabianbormann/cardano-peer-connect";
import { SecureStorage } from "@aparajita/capacitor-secure-storage";
import { IdentityWalletConnect } from "./identityWalletConnect";
import { ExperimentalAPIFunctions } from "./peerConnection.types";
import packageInfo from "../../../../package.json";
import ICON_BASE64 from "../../../assets/icon-only";
import { KeyStoreKeys } from "../../storage";

class PeerConnection {
static readonly PEER_CONNECTION_START_PENDING =
"The PeerConnection.start() has not been called yet";

private walletInfo = {
address: "",
name: "idw_p2p",
icon: ICON_BASE64,
version: packageInfo.version,
requestAutoconnect: true,
};

private announce = [
"wss://tracker.openwebtorrent.com",
"wss://dev.tracker.cf-identity-wallet.metadata.dev.cf-deployments.org",
"wss://tracker.files.fm:7073/announce",
"ws://tracker.files.fm:7072/announce",
"wss://tracker.openwebtorrent.com:443/announce",
];

private identityWalletConnect: IdentityWalletConnect | undefined;
private connected = false;

async start() {
let meerkatSeed = null;

try {
meerkatSeed = (await SecureStorage.get(
KeyStoreKeys.MEERKAT_SEED
)) as string;
} catch {
meerkatSeed = null;
}

this.identityWalletConnect = new IdentityWalletConnect(
this.walletInfo,
meerkatSeed,
this.announce
);
this.identityWalletConnect.setOnConnect(
(connectMessage: IConnectMessage) => {
this.connected = true;
}
);

this.identityWalletConnect.setOnDisconnect(
(connectMessage: IConnectMessage) => {
this.connected = false;
}
);

this.identityWalletConnect.setEnableExperimentalApi(
new ExperimentalContainer<ExperimentalAPIFunctions>({
getIdentifierOobi: this.identityWalletConnect.getIdentifierOobi,
sign: this.identityWalletConnect.sign,
})
);
}

connectWithDApp(dAppIdentifier: string) {
if (this.identityWalletConnect === undefined) {
throw new Error(PeerConnection.PEER_CONNECTION_START_PENDING);
}

const seed = this.identityWalletConnect.connect(dAppIdentifier);
SecureStorage.set(KeyStoreKeys.MEERKAT_SEED, seed);
this.connected = true;
}

disconnectDApp(dAppIdentifier: string) {
if (this.identityWalletConnect === undefined) {
throw new Error(PeerConnection.PEER_CONNECTION_START_PENDING);
}

this.identityWalletConnect.disconnect(dAppIdentifier);
this.connected = false;
}

isConnected() {
return this.connected;
}
}

export { PeerConnection };
6 changes: 6 additions & 0 deletions src/core/cardano/walletConnect/peerConnection.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface ExperimentalAPIFunctions {
getIdentifierOobi: () => Promise<string>;
sign: (identifier: string, payload: string) => Promise<string>;
}

export type { ExperimentalAPIFunctions };
1 change: 1 addition & 0 deletions src/core/storage/secureStorage/secureStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum KeyStoreKeys {
IDENTITY_ROOT_XPRV_KEY = "identity-root-xprv-key",
APP_OP_PASSWORD = "app-operations-password",
SIGNIFY_BRAN = "signify-bran",
MEERKAT_SEED = "app-meerkat-seed",
}

class SecureStorage {
Expand Down
10 changes: 5 additions & 5 deletions src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";
import { TextDecoder, TextEncoder } from "util";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { TextDecoder, TextEncoder, ReadableStream } = require("node:util");

Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
});
Reflect.set(globalThis, "TextDecoder", TextDecoder);
Reflect.set(globalThis, "TextEncoder", TextEncoder);
Reflect.set(globalThis, "ReadableStream", { ...ReadableStream, prototype: {} });

0 comments on commit 1a3bc27

Please sign in to comment.