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