on e7ae14d help sign in
bridge/src/views/Admin/AdminNetworkingKeys.js
326 lines 8.5 kB history raw
1import React, { useCallback, useEffect, useState } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import { Grid, Text, H5, Flex } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
5import { randomHex } from 'web3-utils';
7import { usePointCursor } from 'store/pointCursor';
8import { usePointCache } from 'store/pointCache';
10import { useLocalRouter } from 'lib/LocalRouter';
11import * as need from 'lib/need';
12import {
13 segmentNetworkKey,
14 attemptNetworkSeedDerivation,
15 deriveNetworkKeys,
16} from 'lib/keys';
17import { formatDotsWithTime } from 'lib/dateFormat';
19import ViewHeader from 'components/ViewHeader';
20import MiniBackButton from 'components/MiniBackButton';
21import { BootArvoButton, ForwardButton } from 'components/Buttons';
22import FooterButton from 'components/FooterButton';
23import WarningBox from 'components/WarningBox';
24import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
25import useEthereumTransaction from 'lib/useEthereumTransaction';
26import { GAS_LIMITS, ZERO_KEY } from 'lib/constants';
27import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
28import { addHexPrefix } from 'lib/wallet';
29import { useNetwork } from 'store/network';
30import { useWallet } from 'store/wallet';
32const CRYPTO_SUITE_VERSION = 1;
34const chainKeyProp = name => d =>
35 d[name] === ZERO_KEY ? Nothing() : Just(d[name]);
37const renderNetworkKey = key => {
38 const segments = segmentNetworkKey(key);
40 if (!segments) {
41 return 'Unset';
42 }
44 return segments.join('\n');
45};
47function useSetKeys() {
48 const { urbitWallet, wallet, authMnemonic } = useWallet();
49 const { pointCursor } = usePointCursor();
50 const { syncOwnedPoint, getDetails } = usePointCache();
51 const { contracts } = useNetwork();
52 const _point = need.point(pointCursor);
53 const _contracts = need.contracts(contracts);
55 const [isDiscontinuity] = useState(false);
56 // TODO: set discontinuity with radio check?
57 const [ndNetworkSeed, setNdNetworkSeed] = useState();
59 const { construct, broadcasting, confirmed, bind } = useEthereumTransaction(
60 GAS_LIMITS.CONFIGURE_KEYS
61 );
63 // sync point details after success
64 useEffect(() => {
65 if (confirmed) {
66 syncOwnedPoint(_point);
67 }
68 }, [_point, confirmed, syncOwnedPoint]);
70 const setKeys = useCallback(async () => {
71 const details = need.details(getDetails(_point));
72 const networkRevision = parseInt(details.keyRevisionNumber, 10);
74 console.log('setting revision to ', networkRevision + 1);
76 const seed = await attemptNetworkSeedDerivation({
77 urbitWallet,
78 wallet,
79 authMnemonic,
80 details,
81 revision: networkRevision + 1,
82 });
84 const _networkSeed = seed.matchWith({
85 Nothing: () => {
86 const ndSeed = randomHex(64);
87 setNdNetworkSeed(ndSeed);
88 return ndSeed;
89 },
90 Just: p => p.value,
91 });
93 const pair = deriveNetworkKeys(_networkSeed);
95 const txn = azimuth.ecliptic.configureKeys(
96 _contracts,
97 _point,
98 addHexPrefix(pair.crypt.public),
99 addHexPrefix(pair.auth.public),
100 CRYPTO_SUITE_VERSION,
101 isDiscontinuity
102 );
104 construct(txn);
105 }, [
106 _contracts,
107 _point,
108 authMnemonic,
109 construct,
110 getDetails,
111 isDiscontinuity,
112 urbitWallet,
113 wallet,
114 ]);
116 return {
117 setKeys,
118 construct,
119 broadcasting,
120 confirmed,
121 ndNetworkSeed,
122 bind,
123 };
126export default function AdminNetworkingKeys() {
127 const { push, names, pop } = useLocalRouter();
128 const { pointCursor } = usePointCursor();
129 const { getDetails, getRekeyDate } = usePointCache();
131 const point = need.point(pointCursor);
132 const details = getDetails(point);
134 const hasKeys = details.matchWith({
135 Nothing: () => false,
136 Just: ({ value: details }) => parseInt(details.keyRevisionNumber, 10) > 0,
137 });
139 const [didRequestSetKeys, _setDidRequestSetKeys] = useState(false);
140 const {
141 setKeys,
142 broadcasting,
143 confirmed,
144 ndNetworkSeed,
145 bind,
146 } = useSetKeys();
148 const setDidRequestSetKeys = useCallback(() => {
149 setKeys();
150 _setDidRequestSetKeys(true);
151 }, [setKeys, _setDidRequestSetKeys]);
153 const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
155 const renderTitle = () => {
156 if (confirmed) {
157 return 'Networking keys are now set. Download your Keyfile to authenticate Arvo.';
158 }
160 if (broadcasting) {
161 return 'Setting Network Keys...';
162 }
164 if (!hasKeys) {
165 return 'Networking keys have not yet been set.';
166 }
168 return 'Networking';
169 };
171 const renderButton = () => {
172 if (!didRequestSetKeys) {
173 return (
174 <Grid.Item
175 full
176 as={ForwardButton}
177 solid
178 onClick={() => setDidRequestSetKeys(true)}>
179 {hasKeys ? 'Reset' : 'Set'} Networking Keys
180 </Grid.Item>
181 );
182 }
184 if (confirmed) {
185 return (
186 <Grid.Item
187 full
188 as={DownloadKeyfileButton}
189 solid
190 networkSeed={ndNetworkSeed}
191 />
192 );
193 }
195 return null;
196 };
198 const renderNetworkKeySection = (title, key) => (
199 <>
200 <Grid.Item full as={H5} className="mt3 gray4">
201 {title}
202 </Grid.Item>
203 {key.matchWith({
204 Nothing: () => (
205 <Grid.Item full as="code" className="f5 gray4">
206 Unset
207 </Grid.Item>
208 ),
209 Just: ({ value: key }) => (
210 <>
211 <Grid.Item full as="code" className="f5 wrap">
212 0x
213 </Grid.Item>
214 <Grid.Item full as="code" className="f5 wrap">
215 {renderNetworkKey(key)}
216 </Grid.Item>
217 </>
218 ),
219 })}
220 </>
221 );
223 const renderDetail = (title, value) => (
224 <Flex.Item as={Flex} col>
225 <Flex.Item as={H5} className="gray4">
226 {title}
227 </Flex.Item>
228 {value.matchWith({
229 Nothing: () => (
230 <Flex.Item as={Text} className="f5 gray4">
231 Unset
232 </Flex.Item>
233 ),
234 Just: ({ value }) => <Flex.Item as={Text}>{value}</Flex.Item>,
235 })}
236 </Flex.Item>
237 );
239 const renderDetails = () => {
240 return (
241 <>
242 {renderNetworkKeySection(
243 'Authentication',
244 details.chain(chainKeyProp('authenticationKey'))
245 )}
246 {renderNetworkKeySection(
247 'Encryption',
248 details.chain(chainKeyProp('encryptionKey'))
249 )}
250 <Grid.Item full as={Flex} row justify="between" className="mt3">
251 {renderDetail('Revision', details.map(d => d.keyRevisionNumber))}
252 {renderDetail('Continuity Era', details.map(d => d.continuityNumber))}
253 {renderDetail(
254 'Crypto Suite Ver.',
255 details.map(d => d.cryptoSuiteVersion)
256 )}
257 </Grid.Item>
258 {hasKeys && (
259 <Grid.Item full as={Flex} row justify="between" className="mt3">
260 {renderDetail(
261 'Last Set',
262 getRekeyDate(point).map(date =>
263 date.matchWith({
264 Ok: r => formatDotsWithTime(r.value),
265 Error: () => Nothing(),
266 })
267 )
268 )}
269 </Grid.Item>
270 )}
271 </>
272 );
273 };
275 return (
276 <>
277 <Grid>
278 <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
280 <Grid.Item full as={ViewHeader}>
281 {renderTitle()}
282 </Grid.Item>
284 {!didRequestSetKeys && (
285 <Grid.Item full as={Text} className="mb3">
286 {hasKeys
287 ? 'Here are your public keys that authenticate your Arvo.'
288 : 'Set your networking keys to authenticate with Arvo.'}
289 </Grid.Item>
290 )}
292 {confirmed && (
293 <Grid.Item full as={WarningBox} className="mb3">
294 You need this keyfile to authenticate with Arvo.
295 </Grid.Item>
296 )}
298 {renderButton()}
300 {didRequestSetKeys && (
301 <Grid.Item
302 full
303 as={InlineEthereumTransaction}
304 {...bind}
305 onReturn={() => pop()}
306 />
307 )}
309 {!didRequestSetKeys && renderDetails()}
311 {confirmed && (
312 <>
313 <Grid.Item full as={BootArvoButton} disabled />
314 <Grid.Divider />
315 </>
316 )}
317 </Grid>
319 {!didRequestSetKeys && (
320 <FooterButton onClick={goRelocate} disabled>
321 Relocate
322 </FooterButton>
323 )}
324 </>
325 );