on 87fa1a8 help sign in
bridge/src/lib/useKeyfileGenerator.ts
157 lines 4.1 kB history raw
1import { useCallback, useMemo, useEffect, useState } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import saveAs from 'file-saver';
4import ob from 'urbit-ob';
6import { usePointCache } from 'store/pointCache';
7import { useWallet } from 'store/wallet';
8import { usePointCursor } from 'store/pointCursor';
10import * as need from 'lib/need';
11import { generateCode } from 'lib/networkCode';
13import {
14 attemptNetworkSeedDerivation,
15 keysMatchChain,
16 deriveNetworkKeys,
17 compileNetworkKey,
18} from './keys';
19import useCurrentPermissions from './useCurrentPermissions';
20import { convertToInt } from './convertToInt';
21import { stripSigPrefix } from 'form/formatters';
23// overrideable defaults:
24// seed - derived from wallet, mnemonic, or token (if posisble)
25// point - the current pointCursor
26interface useKeyfileGeneratorArgs {
27 seed?: string;
28 point?: number;
31export default function useKeyfileGenerator({
32 seed,
33 point,
34}: useKeyfileGeneratorArgs) {
35 const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
36 const { pointCursor }: any = usePointCursor();
37 const { syncDetails, getDetails }: any = usePointCache();
39 const [notice, setNotice] = useState('Deriving networking keys...');
40 const [downloaded, setDownloaded] = useState(false);
41 const [generating, setGenerating] = useState(true);
42 const [keyfile, setKeyfile] = useState<boolean | string>(false);
43 const [code, setCode] = useState(false);
45 const _point = point || need.point(pointCursor);
46 useEffect(() => {
47 syncDetails(_point);
48 }, [_point, syncDetails]);
50 const details = getDetails(_point);
52 const networkRevision = details.matchWith({
53 Just: ({ value }) => convertToInt(value.keyRevisionNumber, 10),
54 Nothing: () => 0,
55 });
56 const { isOwner, isManagementProxy } = useCurrentPermissions();
58 const hasNetworkKeys = networkRevision > 0;
59 const available =
60 (isOwner || isManagementProxy) && hasNetworkKeys && !!keyfile;
62 const generate = useCallback(async () => {
63 if (!hasNetworkKeys) {
64 setGenerating(false);
65 setNotice('Network keys not yet set.');
66 console.log(
67 `no networking keys available for revision ${networkRevision}`
68 );
69 return;
70 }
72 const _details = need.details(details);
74 const networkSeed = seed
75 ? Just(seed)
76 : await attemptNetworkSeedDerivation({
77 urbitWallet,
78 wallet,
79 authMnemonic,
80 details: _details,
81 authToken,
82 point: _point,
83 revision: networkRevision,
84 });
86 if (Nothing.hasInstance(networkSeed)) {
87 setGenerating(false);
88 setNotice(
89 'Custom or nondeterministic networking keys cannot be re-downloaded.'
90 );
91 console.log(`seed is nondeterminable for revision ${networkRevision}`);
92 return;
93 }
95 const _networkSeed = networkSeed.value;
97 const pair = deriveNetworkKeys(_networkSeed);
99 if (!keysMatchChain(pair, _details)) {
100 setGenerating(false);
101 setNotice('Derived networking keys do not match on-chain details.');
102 console.log(`keys do not match details for revision ${networkRevision}`);
103 return;
104 }
106 setNotice('');
107 setCode(generateCode(pair));
108 setKeyfile(compileNetworkKey(pair, _point, networkRevision));
109 setGenerating(false);
110 }, [
111 networkRevision,
112 hasNetworkKeys,
113 seed,
114 urbitWallet,
115 wallet,
116 authMnemonic,
117 setCode,
118 details,
119 _point,
120 authToken,
121 ]);
123 const filename = useMemo(() => {
124 return `${stripSigPrefix(ob.patp(_point))}-${networkRevision}.key`;
125 }, [_point, networkRevision]);
127 const download = useCallback(() => {
128 if (typeof keyfile !== 'string') {
129 return;
130 }
132 saveAs(
133 new Blob([keyfile], {
134 type: 'text/plain;charset=utf-8',
135 }),
136 filename
137 );
138 setDownloaded(true);
139 }, [filename, keyfile]);
141 useEffect(() => {
142 generate();
143 }, [generate]);
145 const values = {
146 generating,
147 available,
148 downloaded,
149 download,
150 filename,
151 notice,
152 keyfile,
153 code,
154 };
156 return { ...values, bind: values };