on master help sign in
Merge pull request #937 from urbit/t/895-keygen

keys: generate multikeys in Activate flow, single keys in Home view

commit11f9916ce3400bbc3d96cf83c40c762863128d35 authortomholford <16504501+tomholford@users.noreply.github.com> dateMon 7 Feb 2022, 14:50 (5 years ago) parent9f93263225800a
12 files changed+241−289
src/components/DownloadKeyfileButton.js+1−1 src/lib/useKeyfileGenerator.ts+168−51 src/lib/useMultikeyFileGenerator.ts−214 src/lib/useRoller.ts+1−6 src/urbit-key-generation.d.ts+56 src/views/Activate/MasterKeyDownload.tsx+4−5 src/views/Activate/useActivateFlowState.js+1−2 src/views/UrbitID/DownloadKeys.js+2−2 src/views/UrbitID/Home.tsx+2−2 src/views/UrbitOS/AccessCode.test.tsx+2−2 src/views/UrbitOS/Home.tsx+2−2 src/views/UrbitOS/NetworkKeys.tsx+2−2
src/components/DownloadKeyfileButton.js+1−1
@@ -4,7 +4,7 @@ import { B } from 'indigo-react';
44import { DownloadButton } from 'components/Buttons';
55
66export default function DownloadKeyfileButton({
7− // useMultikeyFileGenerator.bind
7+ // useKeyfileGenerator.bind
88 generating,
99 available,
1010 downloaded,
src/lib/useKeyfileGenerator.ts+168−51
@@ -1,49 +1,66 @@
11import { useCallback, useMemo, useEffect, useState } from 'react';
2−import { Just, Nothing } from 'folktale/maybe';
2+import { Just } from 'folktale/maybe';
33import saveAs from 'file-saver';
4−
54import { useWallet } from 'store/wallet';
6−import { useRollerStore } from 'store/rollerStore';
7−
5+import { EMPTY_POINT, useRollerStore } from 'store/rollerStore';
86import { generateCode } from 'lib/networkCode';
9−
107import {
118 attemptNetworkSeedDerivation,
9+ deriveNetworkSeedFromUrbitWallet,
1210 keysMatchChain,
1311 deriveNetworkKeys,
14− compileNetworkKey,
12+ compileMultiKey,
1513} from './keys';
1614import { stripSigPrefix } from 'form/formatters';
15+import useRoller from './useRoller';
16+import Point from './types/Point';
17+import { toL1Details } from './utils/point';
18+import { ETH_ZERO_ADDR } from './constants';
1719
18−// overrideable defaults:
19−// seed - derived from wallet, mnemonic, or token (if posisble)
20−// point - the current pointCursor
2120interface useKeyfileGeneratorArgs {
22− seed?: string;
23− point?: number;
21+ point?: Point;
22+ seeds: string[];
2423}
2524
2625/**
27− * @deprecated
28− * Use `useMultikeyFileGenerator` instead
26+ * The default Keyfile Generator. Can be used by itself, or composed with
27+ * other hooks (see `useActivationKeyfileGenerator` and
28+ * `useSingleKeyfileGenerator`).
29+ *
30+ * @param point Point - (optional) the Point for which to generate keys
31+ * @param seeds string[] - (required) one or more seeds consumed by deriveNetworkKeys
32+ *
2933 */
30−export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
31− const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
32− const { point } = useRollerStore();
33−
34+function useKeyfileGenerator({ point, seeds }: useKeyfileGeneratorArgs) {
35+ // exported keyfile generator properties
3436 const [notice, setNotice] = useState('Deriving networking keys...');
3537 const [downloaded, setDownloaded] = useState(false);
3638 const [generating, setGenerating] = useState(true);
3739 const [keyfile, setKeyfile] = useState<boolean | string>(false);
3840 const [code, setCode] = useState(false);
3941
40− const networkRevision = Number(point.keyRevisionNumber);
41− const canManage = point.isOwner || point.isManagementProxy;
42+ // resolved point
43+ const { point: storedPoint } = useRollerStore();
44+ const resolvedPoint = useMemo(() => {
45+ return point || storedPoint;
46+ }, [point, storedPoint]);
47+
48+ const networkRevision = useMemo(() => {
49+ return Number(resolvedPoint.keyRevisionNumber);
50+ }, [resolvedPoint]);
51+ const canManage = useMemo(() => {
52+ return resolvedPoint.isOwner || resolvedPoint.isManagementProxy;
53+ }, [resolvedPoint]);
4254
4355 const hasNetworkKeys = networkRevision > 0;
4456 const available = canManage && hasNetworkKeys && !!keyfile;
4557
4658 const generate = useCallback(async () => {
59+ // Point still loading ...
60+ if (resolvedPoint.value === EMPTY_POINT.value) {
61+ return;
62+ }
63+
4764 if (!hasNetworkKeys) {
4865 setGenerating(false);
4966 setNotice('Network keys not yet set.');
@@ -53,19 +70,9 @@ export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
5370 return;
5471 }
5572
56− const networkSeed = seed
57− ? Just(seed)
58− : await attemptNetworkSeedDerivation({
59− urbitWallet,
60− wallet,
61− authMnemonic,
62− details: point,
63− authToken,
64− point: point.value,
65− revision: networkRevision,
66− });
67−
68− if (Nothing.hasInstance(networkSeed)) {
73+ // seeds were either generated non-deterministically,
74+ // or still being derived ...
75+ if (seeds.length === 0) {
6976 setGenerating(false);
7077 setNotice(
7178 'Custom or nondeterministic networking keys cannot be re-downloaded.'
@@ -74,11 +81,10 @@ export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
7481 return;
7582 }
7683
77− const _networkSeed = networkSeed.value;
78−
79− const pair = deriveNetworkKeys(_networkSeed);
84+ const pairs = seeds.map(seed => deriveNetworkKeys(seed));
85+ const firstPair = pairs[0];
8086
81− if (!keysMatchChain(pair, point)) {
87+ if (!keysMatchChain(firstPair, resolvedPoint)) {
8288 setGenerating(false);
8389 setNotice('Derived networking keys do not match on-chain details.');
8490 console.log(`keys do not match details for revision ${networkRevision}`);
@@ -86,24 +92,21 @@ export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
8692 }
8793
8894 setNotice('');
89− setCode(generateCode(pair));
90− setKeyfile(compileNetworkKey(pair, point.value, networkRevision));
95+ setCode(generateCode(firstPair));
96+ setKeyfile(
97+ compileMultiKey(
98+ resolvedPoint.value,
99+ pairs.map((pair, k) => {
100+ return { revision: networkRevision + k, pair };
101+ })
102+ )
103+ );
91104 setGenerating(false);
92− }, [
93− networkRevision,
94− hasNetworkKeys,
95− seed,
96− urbitWallet,
97− wallet,
98− authMnemonic,
99− setCode,
100− point,
101− authToken,
102− ]);
105+ }, [hasNetworkKeys, seeds, resolvedPoint, networkRevision]);
103106
104107 const filename = useMemo(() => {
105− return `${stripSigPrefix(point.patp)}-${networkRevision}.key`;
106− }, [point, networkRevision]);
108+ return `${stripSigPrefix(resolvedPoint.patp)}-${networkRevision}.key`;
109+ }, [resolvedPoint, networkRevision]);
107110
108111 const download = useCallback(() => {
109112 if (typeof keyfile !== 'string') {
@@ -136,3 +139,117 @@ export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
136139
137140 return { ...values, bind: values };
138141}
142+
143+interface ActivationKeyfileGeneratorArgs {
144+ pointId: number;
145+ wallets: UrbitWallet[];
146+}
147+
148+/**
149+ * This Activation Keyfile Generator is used to derive a multikey during the
150+ * Activation Flow. At the time of generation, there is no Point yet set in
151+ * the Roller Store, so we need to look it up by Point ID (to retreive the
152+ * current key revision via API).
153+ *
154+ * @param pointId number - (required) the point ID for which to generate keys
155+ * @param wallets UrbitWallet[] - (required) for each wallet,
156+ * a seed + revision pair is generated and consumed by `compileMultiKey`.
157+ * It should be an array of [deterministic invite wallet, non-deterministic
158+ * master ticket wallet] (the sending and receiving wallets)
159+ */
160+export const useActivationKeyfileGenerator = ({
161+ pointId,
162+ wallets,
163+}: ActivationKeyfileGeneratorArgs) => {
164+ // resolve point
165+ const { api } = useRoller();
166+ const [point, setPoint] = useState<Point>(EMPTY_POINT);
167+
168+ const resolvePoint = useCallback(async () => {
169+ const resolved = new Point({
170+ value: pointId,
171+ details: toL1Details(await api.getPoint(pointId)),
172+ address: ETH_ZERO_ADDR,
173+ });
174+
175+ setPoint(resolved);
176+ }, [api, pointId]);
177+
178+ useEffect(() => {
179+ resolvePoint();
180+ }, [resolvePoint]);
181+
182+ // derive seeds from wallets
183+ const [seeds, setSeeds] = useState<string[]>([]);
184+ const deriveSeeds = useCallback(async () => {
185+ if (!('keyRevisionNumber' in point)) {
186+ return;
187+ }
188+
189+ const derivedSeeds = await Promise.all(
190+ wallets.map(async (w, i) => {
191+ return deriveNetworkSeedFromUrbitWallet(w, point.keyRevisionNumber + i);
192+ })
193+ );
194+
195+ setSeeds(
196+ derivedSeeds
197+ .map(maybeSeed => maybeSeed.getOrElse(null))
198+ .filter(s => s !== null)
199+ );
200+ }, [point, wallets]);
201+
202+ useEffect(() => {
203+ deriveSeeds();
204+ // eslint-disable-next-line react-hooks/exhaustive-deps
205+ }, [point]);
206+
207+ return useKeyfileGenerator({ point, seeds });
208+};
209+
210+interface SingleKeyfileGeneratorArgs {
211+ seed?: string;
212+}
213+
214+/**
215+ * This Single Keyfile Generator is used to derive network keys in multiple
216+ * views in the Bridge dashboard (Set Network Keys, Home > ID, Home > OS).
217+ * By default, it uses the logged in wallet's seed phrase.
218+ *
219+ * * @param seed string - (optional) default: provided by the logged in wallet
220+ */
221+export const useSingleKeyfileGenerator = ({
222+ seed,
223+}: SingleKeyfileGeneratorArgs) => {
224+ // use provided seed, or fallback to deriving from logged in wallet
225+ const { point } = useRollerStore();
226+ const { authMnemonic, authToken, urbitWallet, wallet }: any = useWallet();
227+ const [seeds, setSeeds] = useState<string[]>([]);
228+
229+ const deriveSeeds = useCallback(async () => {
230+ if (seed) {
231+ setSeeds([seed]);
232+ return;
233+ }
234+
235+ const derivedSeed = await attemptNetworkSeedDerivation({
236+ urbitWallet,
237+ wallet,
238+ authMnemonic,
239+ details: point,
240+ authToken,
241+ point: point.value,
242+ revision: point.keyRevisionNumber,
243+ });
244+
245+ if (Just.hasInstance(derivedSeed)) {
246+ setSeeds([derivedSeed.value]);
247+ }
248+ }, [authMnemonic, authToken, point, seed, urbitWallet, wallet]);
249+
250+ useEffect(() => {
251+ deriveSeeds();
252+ }, [deriveSeeds]);
253+
254+ return useKeyfileGenerator({ seeds });
255+};
src/lib/useMultikeyFileGenerator.tsdeleted−214
@@ -1,214 +0,0 @@
1−import * as need from 'lib/need';
2−import saveAs from 'file-saver';
3−import ob from 'urbit-ob';
4−import { Just, Nothing } from 'folktale/maybe';
5−
6−import { useCallback, useEffect, useMemo, useState } from 'react';
7−import { usePointCache } from 'store/pointCache';
8−import { usePointCursor } from 'store/pointCursor';
9−import { useWallet } from 'store/wallet';
10−import { deriveNetworkKeys } from 'urbit-key-generation';
11−import { convertToInt } from './convertToInt';
12−import {
13− attemptNetworkSeedDerivation,
14− compileMultiKey,
15− keysMatchChain,
16−} from './keys';
17−import { generateCode } from './networkCode';
18−import { L1Point } from './types/L1Point';
19−import useCurrentPermissions from './useCurrentPermissions';
20−import { stripSigPrefix } from 'form/formatters';
21−import { useRollerStore } from 'store/rollerStore';
22−
23−interface useMultikeyFileGeneratorArgs {
24− seed?: string;
25− point?: number;
26− seedWallet?: any;
27− inviteMasterTicketWallet?: any;
28−}
29−
30−/**
31− * defaults:
32− * - seed: derived from wallet, mnemonic, or token (if posisble)
33− * - point: the current pointCursor
34− */
35−export default function useMultikeyFileGenerator({
36− seed,
37− point,
38− seedWallet,
39− inviteMasterTicketWallet,
40−}: useMultikeyFileGeneratorArgs) {
41− const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
42− const { pointCursor }: any = usePointCursor();
43− const { syncDetails, getDetails }: any = usePointCache();
44− const { isOwner, isManagementProxy } = useCurrentPermissions();
45− const rollerStore = useRollerStore();
46−
47− const [notice, setNotice] = useState('Deriving networking keys...');
48− const [downloaded, setDownloaded] = useState(false);
49− const [generating, setGenerating] = useState(true);
50− const [keyfile, setKeyfile] = useState<boolean | string>(false);
51− const [code, setCode] = useState(false);
52−
53− const _point = useMemo(() => point || need.point(pointCursor), [
54− point,
55− pointCursor,
56− ]);
57− const details = getDetails(_point);
58− const _details: L1Point | null = useMemo(
59− () =>
60− rollerStore?.point?.isL2
61− ? rollerStore.point
62− : details.matchWith({
63− Just: (d: any) => need.details(d),
64− Nothing: () => null,
65− }),
66− [details, rollerStore.point]
67− );
68−
69− const currentRevision: number = useMemo(
70− () => convertToInt(_details?.keyRevisionNumber || 0, 10),
71− [_details]
72− );
73− const nextRevision = useMemo(() => currentRevision + 1, [currentRevision]);
74−
75− const pairFromRevision = useCallback(
76− async (revision: number, useInviteMasterTicketWallet = false) => {
77− if (!_details) {
78− return;
79− }
80−
81− // During the activation flow, we pass in the
82− // inviteMasterTicketWallet to derive the multikey
83− const derivationWallet =
84− useInviteMasterTicketWallet && inviteMasterTicketWallet
85− ? inviteMasterTicketWallet
86− : seedWallet || urbitWallet;
87−
88− const networkSeed = seed
89− ? Just(seed)
90− : await attemptNetworkSeedDerivation({
91− urbitWallet: derivationWallet,
92− wallet,
93− authMnemonic,
94− details: _details,
95− authToken,
96− point: _point,
97− revision: revision,
98− });
99−
100− if (Nothing.hasInstance(networkSeed)) {
101− setGenerating(false);
102− setNotice(
103− 'Custom or nondeterministic networking keys cannot be re-downloaded.'
104− );
105− console.log(`seed is nondeterminable for revision ${revision}`);
106− return;
107− }
108−
109− const _networkSeed = networkSeed.value;
110−
111− return deriveNetworkKeys(_networkSeed);
112− },
113− [
114− _details,
115− inviteMasterTicketWallet,
116− seedWallet,
117− urbitWallet,
118− seed,
119− wallet,
120− authMnemonic,
121− authToken,
122− _point,
123− ]
124− );
125−
126− const hasNetworkKeys = currentRevision > 0;
127− const available =
128− (isOwner || isManagementProxy) && hasNetworkKeys && !!keyfile;
129−
130− const generate = useCallback(async () => {
131− if (!hasNetworkKeys) {
132− setGenerating(false);
133− setNotice('Network keys not yet set.');
134− console.log(
135− `no networking keys available for revision ${currentRevision}`
136− );
137− return;
138− }
139−
140− const currentPair = await pairFromRevision(currentRevision);
141−
142− if (!currentPair || !keysMatchChain(currentPair, _details || {})) {
143− setGenerating(false);
144− setNotice('Derived networking keys do not match on-chain details.');
145− console.log(`keys do not match details for revision ${currentRevision}`);
146− return;
147− }
148−
149− // During the activation flow, we want to use the invite wallet for the next pair
150− const nextPair = await pairFromRevision(nextRevision, true);
151−
152− setNotice('');
153− setCode(generateCode(currentPair));
154− setKeyfile(
155− compileMultiKey(_point, [
156− {
157− revision: currentRevision,
158− pair: currentPair,
159− },
160− {
161− revision: nextRevision,
162− pair: nextPair,
163− },
164− ])
165− );
166− setGenerating(false);
167− }, [
168− _details,
169− _point,
170− currentRevision,
171− hasNetworkKeys,
172− nextRevision,
173− pairFromRevision,
174− ]);
175−
176− const filename = useMemo(() => {
177− return `${stripSigPrefix(ob.patp(_point))}-${currentRevision}.key`;
178− }, [_point, currentRevision]);
179−
180− const download = useCallback(() => {
181− if (typeof keyfile !== 'string') {
182− return;
183− }
184−
185− saveAs(
186− new Blob([keyfile], {
187− type: 'text/plain;charset=utf-8',
188− }),
189− filename
190− );
191− setDownloaded(true);
192− }, [filename, keyfile]);
193−
194− useEffect(() => {
195− syncDetails(_point);
196− }, [_point, syncDetails]);
197−
198− useEffect(() => {
199− generate();
200− }, [generate]);
201−
202− const output = {
203− generating,
204− available,
205− downloaded,
206− download,
207− filename,
208− notice,
209− keyfile,
210− code,
211− };
212−
213− return { ...output, bind: output };
214−}
src/lib/useRoller.ts+1−6
@@ -31,12 +31,7 @@ import {
3131 registerProxyAddress,
3232 isL2Spawn,
3333} from './utils/roller';
34−import {
35− ETH_ZERO_ADDR,
36− POINT_DOMINIONS,
37− ROLLER_HOSTS,
38− TEN_SECONDS,
39−} from './constants';
34+import { POINT_DOMINIONS, ROLLER_HOSTS, TEN_SECONDS } from './constants';
4035
4136import {
4237 Config,
src/urbit-key-generation.d.ts+56
@@ -1,3 +1,18 @@
1+type ValueOf<T> = T[keyof T];
2+
3+type CHILD_SEED_TYPES = {
4+ OWNERSHIP: 'ownership';
5+ TRANSFER: 'transfer';
6+ SPAWN: 'spawn';
7+ VOTING: 'voting';
8+ MANAGEMENT: 'management';
9+ NETWORK: 'network';
10+ BITCOIN_MAINNET: 'bitcoinMainnet';
11+ BITCOIN_TESTNET: 'bitcoinTestnet';
12+};
13+
14+type ChildSeedType = ValueOf<CHILD_SEED_TYPES>;
15+
116interface NetworkKeys {
217 crypt: {
318 private: string;
@@ -23,6 +38,8 @@ interface WalletNode {
2338 derivationPath: string;
2439}
2540
41+interface BitcoinWallet extends WalletNode {}
42+
2643interface WalletConfig {
2744 ticket: string;
2845 ship: number;
@@ -55,15 +72,54 @@ interface UrbitWallet {
5572 | {};
5673 voting?: WalletNode;
5774 spawn?: WalletNode;
75+ bitcoinTestnet: BitcoinWallet;
76+ bitcoinMainnet: BitcoinWallet;
5877}
5978
6079declare module 'urbit-key-generation' {
80+ const CHILD_SEED_TYPES: CHILD_SEED_TYPES;
81+ function addressFromSecp256k1Public(pub: string): string;
82+ function argon2u(entropy: Buffer, ship: number): Promise<Uint8Array>;
6183 function combine(shards: string[]): string;
84+ function deriveNetworkInfo(
85+ mnemonic: string,
86+ revision: number,
87+ passphrase?: string
88+ ): { type: 'network'; seed: string; keys: WalletNodeKeys };
6289 function deriveNetworkKeys(hex: string): NetworkKeys;
90+ function deriveNetworkSeed(
91+ mnemonic: string,
92+ passphrase: string,
93+ revision: number
94+ ): string;
95+ function deriveNode(
96+ master: Uint8Array,
97+ type: ChildSeedType,
98+ derivationPath: string,
99+ passphrase?: string
100+ ): WalletNode;
101+ function deriveNodeKeys(
102+ mnemonic: string,
103+ derivationPath: string,
104+ passphrase?: string
105+ ): WalletNodeKeys;
106+ function deriveNodeSeed(master: Uint8Array, type: ChildSeedType): string;
107+ function generateCode(pair: NetworkKeys, step: number): string;
108+ function generateKeyfile(
109+ pair: NetworkKeys,
110+ point: number,
111+ revision: number
112+ ): string;
113+ function generateOwnershipWallet({
114+ ticket,
115+ ship,
116+ passphrase,
117+ }: WalletConfig): UrbitWallet;
63118 function generateWallet({
64119 ticket,
65120 ship,
66121 passphrase,
67122 boot,
68123 }: WalletConfig): UrbitWallet;
124+ function shard(ticket: string): string[];
69125}
src/views/Activate/MasterKeyDownload.tsx+4−5
@@ -16,9 +16,9 @@ import { useLocalRouter } from 'lib/LocalRouter';
1616import PaperBuilder from 'components/PaperBuilder';
1717import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
1818import { timeout } from 'lib/timeout';
19−import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
2019import { downloadWallet } from 'lib/invite';
2120import View from 'components/View';
21+import { useActivationKeyfileGenerator } from 'lib/useKeyfileGenerator';
2222
2323const MasterKeyDownload = () => {
2424 const {
@@ -46,10 +46,9 @@ const MasterKeyDownload = () => {
4646 Just: (p: any) => p.value.toFixed(),
4747 });
4848
49− const { keyfile, filename } = useMultikeyFileGenerator({
50− point,
51− seedWallet: inviteWallet,
52− inviteMasterTicketWallet,
49+ const { keyfile, filename } = useActivationKeyfileGenerator({
50+ pointId: point,
51+ wallets: [inviteWallet.value, inviteMasterTicketWallet.value],
5352 });
5453
5554 // sync paper value to activation state
src/views/Activate/useActivateFlowState.js+1−2
@@ -17,14 +17,13 @@ export default function useActivateFlowState() {
1717 // it is for this wallet
1818 // type UrbitWallet
1919 const [inviteMasterTicketWallet, setInviteMasterTicketWallet] = useState(Nothing());
20− // The wallet generated deterministically during the invite flow;
20+ // The wallet generated deterministically during the invite creation flow;
2121 // it is re-generated when the user starts the Activate flow. It is used
2222 // to derive and confirm a shareable ticket that a user can activate a point with.
2323 // type UrbitWallet
2424 const [inviteWallet, setInviteWallet] = useState(Nothing());
2525 // This wallet is generated from the invite wallet's seed phrase.
2626 // It is used during the transfer phase of the invite flow to send transactions.
27− // useMultiKeyFileGenerator.
2827 // type bip32.BIP32Interface
2928 const [sendWallet, setSendWallet] = useState(Nothing());
3029
src/views/UrbitID/DownloadKeys.js+2−2
@@ -9,7 +9,7 @@ import { DownloadButton, RestartButton } from 'components/Buttons';
99import PaperBuilder from 'components/PaperBuilder';
1010
1111import { downloadWallet } from 'lib/invite';
12−import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
12+import { useSingleKeyfileGenerator } from 'lib/useKeyfileGenerator';
1313
1414import { useWallet } from 'store/wallet';
1515import { Grid, P } from 'indigo-react';
@@ -26,7 +26,7 @@ export default function AdminRedownload() {
2626 const _urbitWallet = need.wallet(urbitWallet);
2727 const point = need.point(pointCursor);
2828
29− const { keyfile, filename } = useMultikeyFileGenerator({});
29+ const { keyfile, filename } = useSingleKeyfileGenerator({});
3030
3131 const [paper, setPaper] = useState(Nothing());
3232 const [downloaded, setDownloaded] = useState(false);
src/views/UrbitID/Home.tsx+2−2
@@ -13,7 +13,7 @@ import {
1313} from 'lib/constants';
1414import { abbreviateAddress } from 'lib/utils/address';
1515import { downloadWallet } from 'lib/invite';
16−import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
16+import { useSingleKeyfileGenerator } from 'lib/useKeyfileGenerator';
1717
1818import { useWallet } from 'store/wallet';
1919import { useRollerStore } from 'store/rollerStore';
@@ -43,7 +43,7 @@ export default function UrbitIDHome() {
4343 return null;
4444 }
4545 }, [urbitWallet]);
46− const { keyfile, filename } = useMultikeyFileGenerator({});
46+ const { keyfile, filename } = useSingleKeyfileGenerator({});
4747 const [paper, setPaper] = useState(Nothing());
4848
4949 const downloadKeys = useCallback(() => {
src/views/UrbitOS/AccessCode.test.tsx+2−2
@@ -4,7 +4,7 @@ import AccessCode from './AccessCode';
44
55describe('AccessCode', () => {
66 describe('when code is not available', () => {
7− // Value is initialized as `false` in `useMultikeyFileGenerator`
7+ // Value is initialized as `false` in `useKeyfileGenerator`
88 const testCode = false;
99
1010 it('does not render the component', () => {
@@ -15,7 +15,7 @@ describe('AccessCode', () => {
1515 });
1616
1717 describe('when code is available', () => {
18− // Value has been loaded from `useMultikeyFileGenerator`
18+ // Value has been loaded from `useKeyfileGenerator`
1919 const testCode = 'foo-bar';
2020
2121 it('renders the component', () => {
src/views/UrbitOS/Home.tsx+2−2
@@ -9,7 +9,7 @@ import BodyPane from 'components/L2/Window/BodyPane';
99import { ReactComponent as KeyfileIcon } from 'assets/keyfile.svg';
1010
1111import { useLocalRouter } from 'lib/LocalRouter';
12−import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
12+import { useSingleKeyfileGenerator } from 'lib/useKeyfileGenerator';
1313import { Box, Button, Row } from '@tlon/indigo-react';
1414import CopiableWithTooltip from 'components/copiable/CopiableWithTooltip';
1515
@@ -42,7 +42,7 @@ export default function UrbitOSHome() {
4242 names,
4343 ]);
4444
45− const { code, download } = useMultikeyFileGenerator({});
45+ const { code, download } = useSingleKeyfileGenerator({});
4646
4747 return (
4848 <Window className="os-home">
src/views/UrbitOS/NetworkKeys.tsx+2−2
@@ -27,7 +27,7 @@ import {
2727import useEthereumTransaction from 'lib/useEthereumTransaction';
2828import { GAS_LIMITS, ONE_SECOND } from 'lib/constants';
2929import { addHexPrefix } from 'lib/utils/address';
30−import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
30+import { useSingleKeyfileGenerator } from 'lib/useKeyfileGenerator';
3131
3232import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
3333import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
@@ -66,7 +66,7 @@ function useSetKeys(
6666 generating: keyfileGenerating,
6767 filename,
6868 bind: keyfileBind,
69− } = useMultikeyFileGenerator({ seed: manualNetworkSeed });
69+ } = useSingleKeyfileGenerator({ seed: manualNetworkSeed });
7070
7171 const buildNetworkSeed = useCallback(
7272 async manualSeed => {