@@ -1,434 +0,0 @@
1−import React, { useCallback, useMemo, useRef, useState } from 'react';
2−import { Just, Nothing } from 'folktale/maybe';
3−import { Grid, Text, H5, Flex, ToggleInput, CheckboxInput } from 'indigo-react';
4−import * as azimuth from 'azimuth-js';
5−import ob from 'urbit-ob';
6−import { randomHex } from 'web3-utils';
7−
8−import { usePointCursor } from 'store/pointCursor';
9−import { usePointCache } from 'store/pointCache';
10−import { useNetwork } from 'store/network';
11−import { useWallet } from 'store/wallet';
12−
13−import { useLocalRouter } from 'lib/LocalRouter';
14−import * as need from 'lib/need';
15−import {
16− segmentNetworkKey,
17− attemptNetworkSeedDerivation,
18− deriveNetworkKeys,
19− CRYPTO_SUITE_VERSION,
20− CURVE_ZERO_ADDR,
21−} from 'lib/keys';
22−import { formatDotsWithTime } from 'lib/dateFormat';
23−import useEthereumTransaction from 'lib/useEthereumTransaction';
24−import { GAS_LIMITS } from 'lib/constants';
25−import { addHexPrefix } from 'lib/wallet';
26−import useKeyfileGenerator from 'lib/useKeyfileGenerator';
27−
28−import ViewHeader from 'components/ViewHeader';
29−import { BootUrbitOSButton } from 'components/Buttons';
30−import FooterButton from 'components/FooterButton';
31−import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
32−import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
33−import NoticeBox from 'components/NoticeBox';
34−
35−import { HexInput } from 'form/Inputs';
36−import {
37− composeValidator,
38− buildCheckboxValidator,
39− buildHexValidator,
40−} from 'form/validators';
41−import BridgeForm from 'form/BridgeForm';
42−import Condition from 'form/Condition';
43−import FormError from 'form/FormError';
44−import convertToInt from 'lib/convertToInt';
45−
46−const chainKeyProp = name => d =>
47− d[name] === CURVE_ZERO_ADDR ? Nothing() : Just(d[name]);
48−
49−const renderNetworkKey = key => {
50− const segments = segmentNetworkKey(key);
51−
52− if (!segments) {
53− return 'Unset';
54− }
55−
56− return segments.join('\n');
57−};
58−
59−function useSetKeys() {
60− const { urbitWallet, wallet, authMnemonic } = useWallet();
61− const { pointCursor } = usePointCursor();
62− const { syncDetails, syncRekeyDate, getDetails } = usePointCache();
63− const { contracts } = useNetwork();
64−
65− const _point = need.point(pointCursor);
66− const _contracts = need.contracts(contracts);
67− const _details = need.details(getDetails(_point));
68−
69− const networkRevision = convertToInt(_details.keyRevisionNumber, 10);
70− const randomSeed = useRef();
71−
72− // NOTE: nd = 'nondeterministic' (can also be a 'manual' seed)
73− const [ndNetworkSeed, setNdNetworkSeed] = useState();
74−
75− const {
76− available: keyfileAvailable,
77− generating: keyfileGenerating,
78− filename,
79− bind: keyfileBind,
80− } = useKeyfileGenerator(ndNetworkSeed);
81−
82− const buildNetworkSeed = useCallback(
83− async manualSeed => {
84− if (manualSeed !== undefined) {
85− setNdNetworkSeed(manualSeed);
86− return manualSeed;
87− } else {
88− const newNetworkRevision = networkRevision + 1;
89− console.log(`deriving seed with revision ${newNetworkRevision}`);
90−
91− const networkSeed = await attemptNetworkSeedDerivation({
92− urbitWallet,
93− wallet,
94− authMnemonic,
95− details: _details,
96− revision: newNetworkRevision,
97− });
98−
99− if (Just.hasInstance(networkSeed)) {
100− return networkSeed.value;
101− }
102−
103− randomSeed.current = randomSeed.current || randomHex(32); // 32 bytes
104− setNdNetworkSeed(randomSeed.current);
105−
106− return randomSeed.current;
107− }
108− },
109− [_details, authMnemonic, networkRevision, urbitWallet, wallet]
110− );
111−
112− const { completed: _completed, ...rest } = useEthereumTransaction(
113− useCallback(
114− async (manualSeed, isDiscontinuity) => {
115− const seed = await buildNetworkSeed(manualSeed);
116− const pair = deriveNetworkKeys(seed);
117−
118− return azimuth.ecliptic.configureKeys(
119− _contracts,
120− _point,
121− addHexPrefix(pair.crypt.public),
122− addHexPrefix(pair.auth.public),
123− CRYPTO_SUITE_VERSION,
124− isDiscontinuity
125− );
126− },
127− [_contracts, _point, buildNetworkSeed]
128− ),
129− useCallback(
130− () => Promise.all([syncDetails(_point), syncRekeyDate(_point)]),
131− [_point, syncDetails, syncRekeyDate]
132− ),
133− GAS_LIMITS.CONFIGURE_KEYS
134− );
135−
136− // only treat the transaction as completed once we also have keys to download
137− const completed = _completed && keyfileAvailable && !keyfileGenerating;
138−
139− return {
140− completed,
141− ndNetworkSeed,
142− filename,
143− keyfileBind,
144− ...rest,
145− };
146−}
147−
148−export default function AdminNetworkingKeys() {
149− const { push, names, pop } = useLocalRouter();
150− const { pointCursor } = usePointCursor();
151− const { getDetails, getRekeyDate } = usePointCache();
152−
153− const point = need.point(pointCursor);
154− const details = getDetails(point);
155−
156− const hasKeys = details.matchWith({
157− Nothing: () => false,
158− Just: ({ value: details }) => parseInt(details.keyRevisionNumber, 10) > 0,
159− // we actually don't mind the default NaN behavior of parseInt here,
160− // since NaN > 0 === false and that's a reasonable result
161− });
162−
163− const [usedDiscontinuity, setUsedDiscontinuity] = useState(false);
164−
165− const {
166− isDefaultState,
167− construct,
168− unconstruct,
169− broadcasting,
170− completed,
171− inputsLocked,
172− filename,
173− bind,
174− keyfileBind,
175− } = useSetKeys();
176−
177− const validateForm = useCallback((values, errors) => {
178− if (values.useNetworkSeed && errors.networkSeed) {
179− return errors;
180− }
181− }, []);
182−
183− const validate = useMemo(
184− () =>
185− composeValidator(
186− {
187− useNetworkSeed: buildCheckboxValidator(),
188− networkSeed: buildHexValidator(64), // 64 chars
189− useDiscontinuity: buildCheckboxValidator(),
190− },
191− validateForm
192− ),
193− [validateForm]
194− );
195−
196− const onValues = useCallback(
197− ({ valid, values, form }) => {
198− if (valid) {
199− construct(
200− values.useNetworkSeed ? values.networkSeed : undefined,
201− values.useDiscontinuity
202− );
203− setUsedDiscontinuity(values.useDiscontinuity);
204− } else {
205− unconstruct();
206− }
207−
208− if (!values.useNetworkSeed && values.networkSeed) {
209− form.change('networkSeed', '');
210− }
211− },
212− [construct, unconstruct, setUsedDiscontinuity]
213− );
214−
215− const initialValues = useMemo(
216− () => ({
217− useNetworkSeed: false,
218− useDiscontinuity: false,
219− }),
220− []
221− );
222−
223− const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
224−
225− const usageMessage = useMemo(() => {
226− const need = 'You need this keyfile to authenticate with Arvo.';
227− const boot = (
228− <>
229− boot fresh with{' '}
230− <code>
231− urbit -w {ob.patp(point).slice(1)} -k {filename}
232− </code>
233− </>
234− );
235− if (usedDiscontinuity) {
236− return (
237− <>
238− {need} Since you broke continuity, (re)move your old files, and {boot}
239− </>
240− );
241− } else {
242− return (
243− <>
244− {need} If you have already booted Arvo, run{' '}
245− <code>|rekey '0wkeyfile.contents'</code>. Otherwise, {boot}
246− </>
247− );
248− }
249− }, [usedDiscontinuity, filename, point]);
250−
251− const renderTitle = () => {
252− if (completed) {
253− return 'Networking keys are now set. Download your Keyfile to authenticate Arvo.';
254− }
255−
256− if (broadcasting) {
257− return 'Setting Network Keys...';
258− }
259−
260− if (!hasKeys) {
261− return 'Networking keys have not yet been set.';
262− }
263−
264− return 'Networking';
265− };
266−
267− const renderNetworkKeySection = (title, key) => (
268− <>
269− <Grid.Item full as={H5} className="mt3 gray4">
270− {title}
271− </Grid.Item>
272− {key.matchWith({
273− Nothing: () => (
274− <Grid.Item full as="code" className="f5 mono gray4">
275− Unset
276− </Grid.Item>
277− ),
278− Just: ({ value: key }) => (
279− <>
280− <Grid.Item full as="code" className="f5 mono wrap">
281− 0x
282− </Grid.Item>
283− <Grid.Item full as="code" className="f5 mono wrap">
284− {renderNetworkKey(key)}
285− </Grid.Item>
286− </>
287− ),
288− })}
289− </>
290− );
291−
292− const renderDetail = (title, value) => (
293− <Flex.Item as={Flex} col>
294− <Flex.Item as={H5} className="gray4">
295− {title}
296− </Flex.Item>
297− {value.matchWith({
298− Nothing: () => (
299− <Flex.Item as={Text} className="f5 gray4">
300− Unset
301− </Flex.Item>
302− ),
303− Just: ({ value }) => <Flex.Item as={Text}>{value}</Flex.Item>,
304− })}
305− </Flex.Item>
306− );
307−
308− const renderDetails = () => {
309− return (
310− <>
311− {renderNetworkKeySection(
312− 'Authentication',
313− details.chain(chainKeyProp('authenticationKey'))
314− )}
315− {renderNetworkKeySection(
316− 'Encryption',
317− details.chain(chainKeyProp('encryptionKey'))
318− )}
319− <Grid.Item full as={Flex} row justify="between" className="mt3">
320− {renderDetail('Revision', details.map(d => d.keyRevisionNumber))}
321− {renderDetail('Continuity Era', details.map(d => d.continuityNumber))}
322− {renderDetail(
323− 'Crypto Suite Ver.',
324− details.map(d => d.cryptoSuiteVersion)
325− )}
326− </Grid.Item>
327− {hasKeys && (
328− <Grid.Item full as={Flex} row justify="between" className="mt3">
329− {renderDetail(
330− 'Last Set',
331− getRekeyDate(point).map(date =>
332− date.matchWith({
333− Ok: r => formatDotsWithTime(r.value),
334− Error: () => Nothing(),
335− })
336− )
337− )}
338− </Grid.Item>
339− )}
340− </>
341− );
342− };
343−
344− return (
345− <>
346− <Grid>
347− <Grid.Item full as={ViewHeader}>
348− {renderTitle()}
349− </Grid.Item>
350−
351− <Grid.Item full as={Text} className="mb3">
352− {hasKeys
353− ? 'Here are your public keys that authenticate your Arvo.'
354− : 'Set your networking keys to authenticate with Arvo.'}
355− </Grid.Item>
356−
357− {completed && (
358− <>
359− <Grid.Item full as={NoticeBox} className="mb3">
360− {usageMessage}
361− </Grid.Item>
362− <Grid.Item full as={DownloadKeyfileButton} solid {...keyfileBind} />
363− </>
364− )}
365−
366− {!completed && (
367− <BridgeForm
368− validate={validate}
369− onValues={onValues}
370− initialValues={initialValues}>
371− {() => (
372− <>
373− <Grid.Item
374− full
375− as={ToggleInput}
376− name="useNetworkSeed"
377− label="Use Custom Network Seed"
378− inverseLabel="Back to Derived Network Seed"
379− disabled={inputsLocked}
380− />
381− <Condition when="useNetworkSeed" is={true}>
382− <Grid.Item full as={NoticeBox} className="mb2">
383− When using a custom network seed, you'll need to download
384− your Arvo keyfile immediately after this transaction is
385− Bridge does not store your seed.
386− </Grid.Item>
387− <Grid.Item
388− full
389− as={HexInput}
390− name="networkSeed"
391− label="Network Seed (32 bytes)"
392− disabled={inputsLocked}
393− />
394− </Condition>
395− <Grid.Item
396− full
397− as={CheckboxInput}
398− name="useDiscontinuity"
399− label="Breach Continuity"
400− disabled={inputsLocked}
401− />
402−
403− <Grid.Item full as={FormError} />
404−
405− <Grid.Item
406− full
407− as={InlineEthereumTransaction}
408− {...bind}
409− label={`${hasKeys ? 'Reset' : 'Set'} Networking Keys`}
410− onReturn={() => pop()}
411− />
412− </>
413− )}
414− </BridgeForm>
415− )}
416−
417− {isDefaultState && renderDetails()}
418−
419− {completed && (
420− <>
421− <Grid.Item full as={BootUrbitOSButton} />
422− <Grid.Divider />
423− </>
424− )}
425− </Grid>
426−
427− {isDefaultState && (
428− <FooterButton onClick={goRelocate} disabled>
429− Relocate
430− </FooterButton>
431− )}
432− </>
433− );
434−}