@@ -1,6 +1,14 @@
11import React, { useCallback, useEffect, useState } from 'react';
22import { Just, Nothing } from 'folktale/maybe';
3−import { Grid, Text, H5, Flex } from 'indigo-react';
3+import {
4+ Grid,
5+ Text,
6+ H5,
7+ Flex,
8+ ToggleInput,
9+ Input,
10+ CheckboxInput,
11+} from 'indigo-react';
412import * as azimuth from 'azimuth-js';
513import { randomHex } from 'web3-utils';
614
@@ -23,11 +31,12 @@ import { addHexPrefix } from 'lib/wallet';
2331
2432import ViewHeader from 'components/ViewHeader';
2533import MiniBackButton from 'components/MiniBackButton';
26−import { BootArvoButton, ForwardButton } from 'components/Buttons';
34+import { BootArvoButton } from 'components/Buttons';
2735import FooterButton from 'components/FooterButton';
2836import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
2937import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
3038import NoticeBox from 'components/NoticeBox';
39+import { useHexInput, useCheckboxInput } from 'lib/useInputs';
3140
3241const chainKeyProp = name => d =>
3342 d[name] === ZERO_KEY ? Nothing() : Just(d[name]);
@@ -50,13 +59,15 @@ function useSetKeys() {
5059 const _point = need.point(pointCursor);
5160 const _contracts = need.contracts(contracts);
5261
53− const [isDiscontinuity] = useState(false);
54− // TODO: set discontinuity with radio check?
5562 const [ndNetworkSeed, setNdNetworkSeed] = useState();
5663
57− const { construct, broadcasting, confirmed, bind } = useEthereumTransaction(
58− GAS_LIMITS.CONFIGURE_KEYS
59− );
64+ const {
65+ isDefaultState,
66+ construct: _construct,
67+ broadcasting,
68+ confirmed,
69+ bind,
70+ } = useEthereumTransaction(GAS_LIMITS.CONFIGURE_KEYS);
6071
6172 // sync point details after success
6273 useEffect(() => {
@@ -65,54 +76,56 @@ function useSetKeys() {
6576 }
6677 }, [_point, confirmed, syncOwnedPoint]);
6778
68− const setKeys = useCallback(async () => {
69− const details = need.details(getDetails(_point));
70− const networkRevision = parseInt(details.keyRevisionNumber, 10);
71−
72− console.log('setting revision to ', networkRevision + 1);
79+ const construct = useCallback(
80+ async (manualSeed, isDiscontinuity) => {
81+ const details = need.details(getDetails(_point));
82+ const networkRevision = parseInt(details.keyRevisionNumber, 10);
83+
84+ let _seed = manualSeed;
85+ if (!_seed) {
86+ const networkSeed = await attemptNetworkSeedDerivation({
87+ urbitWallet,
88+ wallet,
89+ authMnemonic,
90+ details,
91+ revision: networkRevision + 1,
92+ });
93+
94+ _seed = networkSeed.matchWith({
95+ Nothing: () => {
96+ const ndSeed = randomHex(64);
97+ setNdNetworkSeed(ndSeed);
98+ return ndSeed;
99+ },
100+ Just: p => p.value,
101+ });
102+ }
103+ const pair = deriveNetworkKeys(_seed);
104+
105+ const txn = azimuth.ecliptic.configureKeys(
106+ _contracts,
107+ _point,
108+ addHexPrefix(pair.crypt.public),
109+ addHexPrefix(pair.auth.public),
110+ CRYPTO_SUITE_VERSION,
111+ isDiscontinuity ? 1 : 0
112+ );
73113
74− const seed = await attemptNetworkSeedDerivation({
75− urbitWallet,
76− wallet,
77− authMnemonic,
78− details,
79− revision: networkRevision + 1,
80− });
81−
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− });
90−
91− const pair = deriveNetworkKeys(_networkSeed);
92−
93− const txn = azimuth.ecliptic.configureKeys(
114+ _construct(txn);
115+ },
116+ [
117+ _construct,
94118 _contracts,
95119 _point,
96− addHexPrefix(pair.crypt.public),
97− addHexPrefix(pair.auth.public),
98− CRYPTO_SUITE_VERSION,
99− isDiscontinuity
100− );
101−
102− construct(txn);
103− }, [
104− _contracts,
105− _point,
106− authMnemonic,
107− construct,
108− getDetails,
109− isDiscontinuity,
110− urbitWallet,
111− wallet,
112− ]);
120+ authMnemonic,
121+ getDetails,
122+ urbitWallet,
123+ wallet,
124+ ]
125+ );
113126
114127 return {
115− setKeys,
128+ isDefaultState,
116129 construct,
117130 broadcasting,
118131 confirmed,
@@ -134,19 +147,62 @@ export default function AdminNetworkingKeys() {
134147 Just: ({ value: details }) => parseInt(details.keyRevisionNumber, 10) > 0,
135148 });
136149
137− const [didRequestSetKeys, _setDidRequestSetKeys] = useState(false);
150+ const [showNetworkSeedInput, { data: showNetworkSeed }] = useCheckboxInput({
151+ name: 'shownetworkseed',
152+ label: 'Use Custom Network Seed',
153+ inverseLabel: 'Back to Derived Network Seed',
154+ initialValue: false,
155+ });
156+ const [
157+ networkSeedInput,
158+ { pass: validNetworkSeed, data: networkSeed },
159+ { setValue: setNetworkSeed },
160+ ] = useHexInput({
161+ name: 'networkseed',
162+ label: 'Network Seed (64 bytes)',
163+ length: 32, // 64 bytes
164+ });
165+
166+ const [
167+ discontinuityInput,
168+ { pass: validDiscontinuity, data: isDiscontinuity },
169+ ] = useCheckboxInput({
170+ name: 'discontinuity',
171+ label: 'Trigger New Continuity Era',
172+ length: 64,
173+ initialValue: false,
174+ });
175+
138176 const {
139− setKeys,
177+ isDefaultState,
178+ construct,
140179 broadcasting,
141180 confirmed,
142181 ndNetworkSeed,
143182 bind,
144183 } = useSetKeys();
145184
146− const setDidRequestSetKeys = useCallback(() => {
147− setKeys();
148− _setDidRequestSetKeys(true);
149− }, [setKeys, _setDidRequestSetKeys]);
185+ useEffect(() => {
186+ if (
187+ (!showNetworkSeed || (showNetworkSeed && validNetworkSeed)) &&
188+ validDiscontinuity
189+ ) {
190+ construct(networkSeed, isDiscontinuity);
191+ }
192+ }, [
193+ construct,
194+ isDiscontinuity,
195+ networkSeed,
196+ showNetworkSeed,
197+ validDiscontinuity,
198+ validNetworkSeed,
199+ ]);
200+
201+ useEffect(() => {
202+ if (!showNetworkSeed) {
203+ setNetworkSeed('');
204+ }
205+ }, [setNetworkSeed, showNetworkSeed]);
150206
151207 const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
152208
@@ -166,33 +222,6 @@ export default function AdminNetworkingKeys() {
166222 return 'Networking';
167223 };
168224
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− }
181−
182− if (confirmed) {
183− return (
184− <Grid.Item
185− full
186− as={DownloadKeyfileButton}
187− solid
188− networkSeed={ndNetworkSeed}
189− />
190− );
191− }
192−
193− return null;
194− };
195−
196225 const renderNetworkKeySection = (title, key) => (
197226 <>
198227 <Grid.Item full as={H5} className="mt3 gray4">
@@ -279,32 +308,52 @@ export default function AdminNetworkingKeys() {
279308 {renderTitle()}
280309 </Grid.Item>
281310
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− )}
311+ <Grid.Item full as={Text} className="mb3">
312+ {hasKeys
313+ ? 'Here are your public keys that authenticate your Arvo.'
314+ : 'Set your networking keys to authenticate with Arvo.'}
315+ </Grid.Item>
289316
290317 {confirmed && (
291− <Grid.Item full as={NoticeBox} className="mb3">
292− You need this keyfile to authenticate with Arvo.
293− </Grid.Item>
318+ <>
319+ <Grid.Item full as={NoticeBox} className="mb3">
320+ You need this keyfile to authenticate with Arvo.
321+ </Grid.Item>
322+ <Grid.Item
323+ full
324+ as={DownloadKeyfileButton}
325+ solid
326+ networkSeed={ndNetworkSeed}
327+ />
328+ </>
294329 )}
295330
296− {renderButton()}
297−
298− {didRequestSetKeys && (
299− <Grid.Item
300− full
301− as={InlineEthereumTransaction}
302− {...bind}
303− onReturn={() => pop()}
304− />
331+ {!confirmed && (
332+ <>
333+ <Grid.Item full as={ToggleInput} {...showNetworkSeedInput} />
334+ {showNetworkSeed && (
335+ <>
336+ <Grid.Item full as={NoticeBox} className="mb2">
337+ When using a custom network seed, you'll need to download your
338+ Arvo keyfile immediately after this transaction is
339+ confirmed—Multipass does not store your seed.
340+ </Grid.Item>
341+ <Grid.Item full as={Input} {...networkSeedInput} />
342+ </>
343+ )}
344+ <Grid.Item full as={CheckboxInput} {...discontinuityInput} />
345+ </>
305346 )}
306347
307− {!didRequestSetKeys && renderDetails()}
348+ <Grid.Item
349+ full
350+ as={InlineEthereumTransaction}
351+ {...bind}
352+ label={`${hasKeys ? 'Reset' : 'Set'} Networking Keys`}
353+ onReturn={() => pop()}
354+ />
355+
356+ {isDefaultState && renderDetails()}
308357
309358 {confirmed && (
310359 <>
@@ -314,7 +363,7 @@ export default function AdminNetworkingKeys() {
314363 )}
315364 </Grid>
316365
317− {!didRequestSetKeys && (
366+ {isDefaultState && (
318367 <FooterButton onClick={goRelocate} disabled>
319368 Relocate
320369 </FooterButton>