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