Skip to content

Commit

Permalink
chore: signup with wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
apotdevin committed Jun 20, 2024
1 parent 68b23c1 commit 5fe15c9
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 62 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"graphql": "^16.8.2",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",
"lwk_wasm": "^0.6.2",
"lwk_wasm": "^0.6.3",
"next": "^14.2.4",
"next-qrcode": "^2.5.1",
"next-themes": "^0.3.0",
Expand Down
82 changes: 57 additions & 25 deletions src/components/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { ApolloError, useApolloClient } from '@apollo/client';
import { zodResolver } from '@hookform/resolvers/zod';
import { Loader2 } from 'lucide-react';
import Link from 'next/link';
Expand All @@ -18,7 +19,13 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useSignUpMutation } from '@/graphql/mutations/__generated__/signUp.generated';
import {
SignUpDocument,
SignUpMutation,
SignUpMutationVariables,
} from '@/graphql/mutations/__generated__/signUp.generated';
import { WalletAccountType, WalletType } from '@/graphql/types';
import { toWithError } from '@/utils/async';
import { handleApolloError } from '@/utils/error';
import {
evaluatePasswordStrength,
Expand Down Expand Up @@ -62,24 +69,11 @@ const FormSchema = z
export function SignUpForm() {
const { toast } = useToast();

const workerRef = useRef<Worker>();
const client = useApolloClient();

const [loading, setLoading] = useState(false);
const workerRef = useRef<Worker>();

const [signUp] = useSignUpMutation({
onCompleted: () => {
window.location.href = ROUTES.app.home;
},
onError: err => {
const messages = handleApolloError(err);

toast({
variant: 'destructive',
title: 'Error logging in.',
description: messages.join(', '),
});
},
});
const [loading, setLoading] = useState(true);

const form = useForm<z.infer<typeof FormSchema>>({
reValidateMode: 'onChange',
Expand Down Expand Up @@ -120,20 +114,58 @@ export function SignUpForm() {
new URL('../workers/account/account.ts', import.meta.url)
);

workerRef.current.onmessage = event => {
workerRef.current.onmessage = async event => {
const message: WorkerResponse = event.data;

switch (message.type) {
case 'create':
signUp({ variables: { input: message.payload } });
case 'loaded':
setLoading(false);
break;

default:
console.error('Unhandled message type:', event.data.type);
case 'create':
const { wallet, ...userInfo } = message.payload;

const [, error] = await toWithError(
client.mutate<SignUpMutation, SignUpMutationVariables>({
mutation: SignUpDocument,
variables: {
input: {
...userInfo,
wallet: {
secp256k1_key_pair: wallet.secp256k1_key_pair,
details: {
type: WalletType.ClientGenerated,
protected_mnemonic: wallet.protectedMnemonic,
},
accounts: [
{
type: WalletAccountType.Liquid,
liquid_descriptor: wallet.liquidDescriptor,
},
],
},
},
},
})
);

if (error) {
const messages = handleApolloError(error as ApolloError);

toast({
variant: 'destructive',
title: 'Error creating account.',
description: messages.join(', '),
});

setLoading(false);
return;
}

window.location.href = ROUTES.app.home;

break;
}

setLoading(false);
};

workerRef.current.onerror = error => {
Expand All @@ -144,7 +176,7 @@ export function SignUpForm() {
return () => {
if (workerRef.current) workerRef.current.terminate();
};
}, [signUp]);
}, [client, toast]);

return (
<Card>
Expand Down
2 changes: 2 additions & 0 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ export type RefreshToken = {
};

export type RefreshWalletInput = {
full_scan?: InputMaybe<Scalars['Boolean']['input']>;
wallet_id: Scalars['String']['input'];
};

Expand Down Expand Up @@ -323,6 +324,7 @@ export type SignUpInput = {
password_hint?: InputMaybe<Scalars['String']['input']>;
protected_symmetric_key: Scalars['String']['input'];
secp256k1_key_pair: Secp256k1KeyPairInput;
wallet?: InputMaybe<CreateWalletInput>;
};

export type SimpleWallet = {
Expand Down
6 changes: 6 additions & 0 deletions src/workers/account/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
secp256k1GenerateProtectedKeyPair,
} from '@/utils/crypto';

import { createNewWallet } from '../crypto';
import {
CreateAccountResult,
GenerateMasterKeyAndHashResult,
Expand All @@ -24,6 +25,8 @@ async function generateAccount(
const { publicKey, protectedPrivateKey } =
secp256k1GenerateProtectedKeyPair(masterKey);

const wallet = await createNewWallet(masterKey);

return {
email,
master_password_hash: masterPasswordHash,
Expand All @@ -33,6 +36,7 @@ async function generateAccount(
public_key: publicKey,
protected_private_key: protectedPrivateKey,
},
wallet,
};
}

Expand Down Expand Up @@ -95,3 +99,5 @@ self.onmessage = async e => {
break;
}
};

self.postMessage({ type: 'loaded' });
6 changes: 5 additions & 1 deletion src/workers/account/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CryptoNewWalletPayload } from '../crypto/types';

export type CreateAccount = {
email: string;
password: string;
Expand Down Expand Up @@ -28,6 +30,7 @@ export type CreateAccountResult = {
public_key: string;
protected_private_key: string;
};
wallet: CryptoNewWalletPayload;
};

export type GenerateMasterKeyAndHashResult = {
Expand All @@ -43,4 +46,5 @@ export type WorkerResponse =
| {
type: 'generateMaster';
payload: GenerateMasterKeyAndHashResult;
};
}
| { type: 'loaded' };
37 changes: 37 additions & 0 deletions src/workers/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Mnemonic, Network, Signer } from 'lwk_wasm';

import {
generateNewMnemonic,
secp256k1GenerateProtectedKeyPair,
} from '@/utils/crypto';

import { CryptoNewWalletPayload } from './crypto/types';

export const generateLiquidDescriptor = async (mnemonic: string) => {
const network = Network.mainnet();

const signer = new Signer(new Mnemonic(mnemonic), network);
const wolletDescriptor = signer.wpkhSlip77Descriptor().toString();

return wolletDescriptor;
};

export const createNewWallet = async (
masterKey: string
): Promise<CryptoNewWalletPayload> => {
const { mnemonic, protectedMnemonic } = generateNewMnemonic(masterKey);

const { publicKey, protectedPrivateKey } =
secp256k1GenerateProtectedKeyPair(masterKey);

const liquidDescriptor = await generateLiquidDescriptor(mnemonic);

return {
protectedMnemonic: protectedMnemonic,
liquidDescriptor,
secp256k1_key_pair: {
public_key: publicKey,
protected_private_key: protectedPrivateKey,
},
};
};
26 changes: 3 additions & 23 deletions src/workers/crypto/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,15 @@ import { nip44 } from 'nostr-tools';
import { toWithError } from '@/utils/async';
import {
bufToHex,
generateNewMnemonic,
hexToUint8Array,
restoreMnemonic,
secp256k1GenerateProtectedKeyPair,
} from '@/utils/crypto';
import { decryptMessage, encryptMessage } from '@/utils/nostr';

import { createNewWallet, generateLiquidDescriptor } from '../crypto';
import { CryptoWorkerMessage, CryptoWorkerResponse } from './types';

const generateLiquidDescriptor = async (mnemonic: string) => {
const network = Network.mainnet();

const signer = new Signer(new Mnemonic(mnemonic), network);
const wolletDescriptor = signer.wpkhSlip77Descriptor().toString();

return wolletDescriptor;
};

const signPset = (mnemonic: string, descriptor: string, pset: string) => {
const network = Network.mainnet();

Expand All @@ -54,23 +45,12 @@ self.onmessage = async e => {
switch (message.type) {
case 'newWallet': {
const { masterKey } = message.payload;
const { mnemonic, protectedMnemonic } = generateNewMnemonic(masterKey);

const { publicKey, protectedPrivateKey } =
secp256k1GenerateProtectedKeyPair(masterKey);

const liquidDescriptor = await generateLiquidDescriptor(mnemonic);
const payload = await createNewWallet(masterKey);

const response: CryptoWorkerResponse = {
type: 'newWallet',
payload: {
protectedMnemonic: protectedMnemonic,
liquidDescriptor,
secp256k1_key_pair: {
public_key: publicKey,
protected_private_key: protectedPrivateKey,
},
},
payload,
};

self.postMessage(response);
Expand Down
18 changes: 10 additions & 8 deletions src/workers/crypto/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,19 @@ export type CryptoWorkerMessage =
};
};

export type CryptoNewWalletPayload = {
protectedMnemonic: string;
liquidDescriptor: string;
secp256k1_key_pair: {
public_key: string;
protected_private_key: string;
};
};

export type CryptoWorkerResponse =
| {
type: 'newWallet';
payload: {
protectedMnemonic: string;
liquidDescriptor: string;
secp256k1_key_pair: {
public_key: string;
protected_private_key: string;
};
};
payload: CryptoNewWalletPayload;
}
| {
type: 'signPset';
Expand Down

0 comments on commit 5fe15c9

Please sign in to comment.