on master help sign in
Merge pull request #164 from urbit/feat/extract-network
23ed93bMatt Condon
src/bridge/Bridge.js+3−28
@@ -25,9 +25,7 @@ const kInitialNetworkType = isDevelopment
25 ? NETWORK_TYPES.LOCAL
25 ? NETWORK_TYPES.LOCAL
26 : NETWORK_TYPES.MAINNET;
26 : NETWORK_TYPES.MAINNET;
27
27
28−// NB(shrugs): toggle these variables to change the default local state.
28+// NB(shrugs): modify these variables to change the default local state.
29−// try not to commit changes to this line, but there shouldn't be a problem
30−// if you do because we'll never stub on a production build.
31const shouldStubLocal = false;
29const shouldStubLocal = false;
32const kIsStubbed = isDevelopment && shouldStubLocal;
30const kIsStubbed = isDevelopment && shouldStubLocal;
33const kInitialRoutes = kIsStubbed
31const kInitialRoutes = kIsStubbed
@@ -46,7 +44,7 @@ const kInitialWallet = kIsStubbed
46 )
44 )
47 : undefined;
45 : undefined;
48const kInitialMnemonic = kIsStubbed
46const kInitialMnemonic = kIsStubbed
49− ? process.env.REACT_APP_DEV_MNEMONIC
47+ ? Just(process.env.REACT_APP_DEV_MNEMONIC)
50 : Nothing();
48 : Nothing();
51const kInitialPointCursor = kIsStubbed ? Just(0) : Nothing();
49const kInitialPointCursor = kIsStubbed ? Just(0) : Nothing();
52
50
@@ -93,26 +91,7 @@ const AllProviders = nest([
93]);
91]);
94
92
95class Bridge extends React.Component {
93class Bridge extends React.Component {
96− constructor(props) {
97− super(props);
98−
99− this.state = {
100− networkSeedCache: null,
101− };
102−
103− this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this);
104− }
105−
106− setNetworkSeedCache(networkSeed, revision) {
107− this.setState({
108− networkSeedCache: networkSeed,
109− networkRevisionCache: revision,
110− });
111− }
112−
113 render() {
94 render() {
114− const { networkSeedCache, networkRevisionCache } = this.state;
115−
116 return (
95 return (
117 <AllProviders
96 <AllProviders
118 initialRoutes={kInitialRoutes}
97 initialRoutes={kInitialRoutes}
@@ -126,11 +105,7 @@ class Bridge extends React.Component {
126 <Header />
105 <Header />
127
106
128 <Row className={'row wrapper'}>
107 <Row className={'row wrapper'}>
129− <Router
108+ <Router />
130− networkSeedCache={networkSeedCache}
131− networkRevisionCache={networkRevisionCache}
132− setNetworkSeedCache={this.setNetworkSeedCache}
133− />
134
109
135 <div className={'push'} />
110 <div className={'push'} />
136 </Row>
111 </Row>
src/bridge/components/StatelessTransaction.js+6−2
@@ -242,11 +242,15 @@ class StatelessTransaction extends React.Component {
242 props.setTxnCursor(Just(Ok(txHash)));
242 props.setTxnCursor(Just(Ok(txHash)));
243
243
244 let routeData = {};
244 let routeData = {};
245− if (props.networkSeed) {
245+ if (props.newNetworkSeed) {
246− props.setNetworkSeedCache(props.networkSeed);
246+ props.setNetworkSeed(Just(props.newNetworkSeed));
247 routeData.promptKeyfile = true;
247 routeData.promptKeyfile = true;
248 }
248 }
249
249
250+ if (props.newRevision) {
251+ props.setNetworkRevision(Just(props.newRevision));
252+ }
253+
250 props.history.popAndPush(ROUTE_NAMES.SENT_TRANSACTION, routeData);
254 props.history.popAndPush(ROUTE_NAMES.SENT_TRANSACTION, routeData);
251 })
255 })
252 .catch(err => {
256 .catch(err => {
src/bridge/lib/invite.js+10−6
@@ -11,7 +11,7 @@ import saveAs from 'file-saver';
11
11
12import { sendSignedTransaction } from './txn';
12import { sendSignedTransaction } from './txn';
13import { BRIDGE_ERROR } from '../lib/error';
13import { BRIDGE_ERROR } from '../lib/error';
14−import { attemptSeedDerivation } from './keys';
14+import { attemptNetworkSeedDerivation } from './keys';
15import { addHexPrefix, WALLET_TYPES } from './wallet';
15import { addHexPrefix, WALLET_TYPES } from './wallet';
16
16
17const INVITE_STAGES = {
17const INVITE_STAGES = {
@@ -212,16 +212,20 @@ async function startTransactions(args) {
212
212
213 // configure networking public keys
213 // configure networking public keys
214 //TODO feels like more of this logic should live in a lib?
214 //TODO feels like more of this logic should live in a lib?
215− let networkSeed = await attemptSeedDerivation(true, {
215+ const seed = await attemptNetworkSeedDerivation(true, {
216 walletType: WALLET_TYPES.TICKET,
216 walletType: WALLET_TYPES.TICKET,
217 urbitWallet: Just(realWallet),
217 urbitWallet: Just(realWallet),
218 pointCursor: Just(point),
218 pointCursor: Just(point),
219 pointCache: { [point]: { keyRevisionNumber: 0 } },
219 pointCache: { [point]: { keyRevisionNumber: 0 } },
220 });
220 });
221− if (Nothing.hasInstance(networkSeed)) {
221+
222+ const networkSeed = seed.matchWith({
223+ Nothing: () => {
222− throw new Error('wtf network seed not derived');
224+ throw new Error('network seed not derived');
223− }
225+ },
224− networkSeed = networkSeed.value;
226+ Just: p => p.value,
227+ });
228+
225 const networkKeys = kg.deriveNetworkKeys(networkSeed);
229 const networkKeys = kg.deriveNetworkKeys(networkSeed);
226
230
227 let keysTx = azimuth.ecliptic.configureKeys(
231 let keysTx = azimuth.ecliptic.configureKeys(
src/bridge/lib/keys.js+9−7
@@ -55,7 +55,7 @@ const genKey = (networkSeed, point, revision) => {
55 const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex'); // '42' is curve parameter
55 const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex'); // '42' is curve parameter
56
56
57 const sed = noun.dwim(
57 const sed = noun.dwim(
58− noun.Atom.fromString(point),
58+ noun.Atom.fromInt(point),
59 noun.Atom.fromInt(revision),
59 noun.Atom.fromInt(revision),
60 noun.Atom.fromString(bnsec.toString()),
60 noun.Atom.fromString(bnsec.toString()),
61 noun.Atom.yes
61 noun.Atom.yes
@@ -66,7 +66,7 @@ const genKey = (networkSeed, point, revision) => {
66
66
67// 'next' refers to setting the next set of keys
67// 'next' refers to setting the next set of keys
68// if false, we use revision - 1
68// if false, we use revision - 1
69−const attemptSeedDerivation = async (next, args) => {
69+const attemptNetworkSeedDerivation = async (next, args) => {
70 const { walletType, urbitWallet, authMnemonic } = args;
70 const { walletType, urbitWallet, authMnemonic } = args;
71
71
72 // NB (jtobin):
72 // NB (jtobin):
@@ -115,15 +115,17 @@ const attemptSeedDerivation = async (next, args) => {
115 }
115 }
116 }
116 }
117
117
118− let networkSeed = Maybe.Nothing();
119−
120 if (managementSeed !== '') {
118 if (managementSeed !== '') {
121 const seed = await kg.deriveNetworkSeed(managementSeed, '', revision);
119 const seed = await kg.deriveNetworkSeed(managementSeed, '', revision);
122
120
121+ if (seed === '') {
122+ return Maybe.Nothing();
123+ }
124+
123− networkSeed = Maybe.Just(seed);
125+ return Maybe.Just(seed);
124 }
126 }
125
127
126− return networkSeed;
128+ return Maybe.Nothing();
127};
129};
128
130
129−export { genKey, attemptSeedDerivation };
131+export { genKey, attemptNetworkSeedDerivation };
src/bridge/store/pointCursor.js+1
@@ -3,6 +3,7 @@ import Maybe from 'folktale/maybe';
3
3
4export const PointCursorContext = createContext(null);
4export const PointCursorContext = createContext(null);
5
5
6+// pointCursor is Maybe<number>
6function _usePointCursor(initialPointCursor = Maybe.Nothing()) {
7function _usePointCursor(initialPointCursor = Maybe.Nothing()) {
7 const [pointCursor, setPointCursor] = useState(initialPointCursor);
8 const [pointCursor, setPointCursor] = useState(initialPointCursor);
8
9
src/bridge/store/wallet.js+7
@@ -33,6 +33,8 @@ function _useWallet(
33 const [wallet, _setWallet] = useState(initialWallet);
33 const [wallet, _setWallet] = useState(initialWallet);
34 const [urbitWallet, _setUrbitWallet] = useState(Maybe.Nothing());
34 const [urbitWallet, _setUrbitWallet] = useState(Maybe.Nothing());
35 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
35 const [authMnemonic, setAuthMnemonic] = useState(initialMnemonic);
36+ const [networkSeed, setNetworkSeed] = useState(Maybe.Nothing());
37+ const [networkRevision, setNetworkRevision] = useState(Maybe.Nothing());
36
38
37 const setWalletType = walletType => {
39 const setWalletType = walletType => {
38 if (!includes(WALLET_TYPES, walletType)) {
40 if (!includes(WALLET_TYPES, walletType)) {
@@ -89,6 +91,11 @@ function _useWallet(
89 setUrbitWallet,
91 setUrbitWallet,
90 authMnemonic,
92 authMnemonic,
91 setAuthMnemonic,
93 setAuthMnemonic,
94+ //
95+ networkSeed,
96+ setNetworkSeed,
97+ networkRevision,
98+ setNetworkRevision,
92 };
99 };
93}
100}
94
101
src/bridge/views/GenKeyfile.js+11−10
@@ -1,5 +1,4 @@
1import React from 'react';
1import React from 'react';
2−import Maybe from 'folktale/maybe';
3import * as need from '../lib/need';
2import * as need from '../lib/need';
4
3
5import { Button } from '../components/Base';
4import { Button } from '../components/Base';
@@ -9,7 +8,7 @@ import * as ob from 'urbit-ob';
9import * as kg from 'urbit-key-generation/dist/index';
8import * as kg from 'urbit-key-generation/dist/index';
10import saveAs from 'file-saver';
9import saveAs from 'file-saver';
11
10
12−import { attemptSeedDerivation, genKey } from '../lib/keys';
11+import { attemptNetworkSeedDerivation, genKey } from '../lib/keys';
13import { addHexPrefix } from '../lib/wallet';
12import { addHexPrefix } from '../lib/wallet';
14import { compose } from '../lib/lib';
13import { compose } from '../lib/lib';
15import { withWallet } from '../store/wallet';
14import { withWallet } from '../store/wallet';
@@ -33,9 +32,10 @@ class GenKeyfile extends React.Component {
33
32
34 // in case we did SetKeys earlier this session, make sure to generate the
33 // in case we did SetKeys earlier this session, make sure to generate the
35 // newer keyfile, rather than the one that will expire soon
34 // newer keyfile, rather than the one that will expire soon
36− const revision =
35+ const revision = this.props.networkRevision.matchWith({
37− this.props.networkRevisionCache ||
38− parseInt(pointDetails.keyRevisionNumber);
36+ Nothing: () => parseInt(pointDetails.keyRevisionNumber),
37+ Just: p => p.value,
38+ });
39
39
40 return {
40 return {
41 point,
41 point,
@@ -79,13 +79,14 @@ class GenKeyfile extends React.Component {
79
79
80 async deriveSeed() {
80 async deriveSeed() {
81 const next = false;
81 const next = false;
82− let seed = await attemptSeedDerivation(next, this.props);
83
82
84− if (seed.getOrElse('') === '' && this.props.networkSeedCache) {
83+ const seed = await attemptNetworkSeedDerivation(next, this.props);
85− seed = Maybe.Just(this.props.networkSeedCache);
86− }
87
84
85+ // either return the derived seed, the cached seed, or empty string
88− return seed.getOrElse('');
86+ return seed.matchWith({
87+ Nothing: () => this.props.networkSeed.getOrElse(''),
88+ Just: p => p.value,
89+ });
89 }
90 }
90
91
91 render() {
92 render() {
src/bridge/views/Points.js+7−28
@@ -84,7 +84,7 @@ class Points extends React.Component {
84 }
84 }
85
85
86 render() {
86 render() {
87− const { setPointCursor, history, pointCache } = this.props;
87+ const { history, pointCache } = this.props;
88
88
89 const {
89 const {
90 points,
90 points,
@@ -93,8 +93,8 @@ class Points extends React.Component {
93 voting,
93 voting,
94 spawning,
94 spawning,
95 loading,
95 loading,
96+ eclipticOwner,
96 } = this.state;
97 } = this.state;
97− const { eclipticOwner } = this.state;
98
98
99 const lookupPointButton = (
99 const lookupPointButton = (
100 <Row>
100 <Row>
@@ -143,11 +143,7 @@ class Points extends React.Component {
143 {`You own these points until the recipient accepts the incoming
143 {`You own these points until the recipient accepts the incoming
144 transfer. You may cancel the transfer until accepted.`}
144 transfer. You may cancel the transfer until accepted.`}
145 </P>
145 </P>
146− <PointList
147− setPointCursor={setPointCursor}
148− routeHandler={history.push}
146+ <PointList routeHandler={history.push} points={outgoing} />
149− points={outgoing}
150− />
151 </React.Fragment>
147 </React.Fragment>
152 ) : (
148 ) : (
153 <div />
149 <div />
@@ -161,11 +157,7 @@ class Points extends React.Component {
161 {`You do not own these points until you accept the incoming transfer.
157 {`You do not own these points until you accept the incoming transfer.
162 You may reject any incoming transfers.`}
158 You may reject any incoming transfers.`}
163 </P>
159 </P>
164− <PointList
165− setPointCursor={setPointCursor}
166− routeHandler={history.push}
160+ <PointList routeHandler={history.push} points={incoming} />
167− points={incoming}
168− />
169 </React.Fragment>
161 </React.Fragment>
170 ) : (
162 ) : (
171 <div />
163 <div />
@@ -179,11 +171,7 @@ class Points extends React.Component {
179 {`You can configure or set network keys and conduct sponsorship
171 {`You can configure or set network keys and conduct sponsorship
180 related operations for these points.`}
172 related operations for these points.`}
181 </P>
173 </P>
182− <PointList
183− setPointCursor={setPointCursor}
184− routeHandler={history.push}
174+ <PointList routeHandler={history.push} points={managing} />
185− points={managing}
186− />
187 </React.Fragment>
175 </React.Fragment>
188 ) : (
176 ) : (
189 <div />
177 <div />
@@ -197,11 +185,7 @@ class Points extends React.Component {
197 {`Since you are part of the Galactic Senate, you can cast votes on
185 {`Since you are part of the Galactic Senate, you can cast votes on
198 new Azimuth proposals on behalf of these points.`}
186 new Azimuth proposals on behalf of these points.`}
199 </P>
187 </P>
200− <PointList
201− setPointCursor={setPointCursor}
202− routeHandler={history.push}
188+ <PointList routeHandler={history.push} points={voting} />
203− points={voting}
204− />
205 </React.Fragment>
189 </React.Fragment>
206 ) : (
190 ) : (
207 <div />
191 <div />
@@ -212,11 +196,7 @@ class Points extends React.Component {
212 <React.Fragment>
196 <React.Fragment>
213 <H2>{'You Are a Spawn Proxy For'}</H2>
197 <H2>{'You Are a Spawn Proxy For'}</H2>
214 <P>{`You can create new child ships under these points.`}</P>
198 <P>{`You can create new child ships under these points.`}</P>
215− <PointList
216− setPointCursor={setPointCursor}
217− routeHandler={history.push}
199+ <PointList routeHandler={history.push} points={spawning} />
218− points={spawning}
219− />
220 </React.Fragment>
200 </React.Fragment>
221 ) : (
201 ) : (
222 <div />
202 <div />
@@ -241,7 +221,6 @@ class Points extends React.Component {
241 <H2>{'Your Points'}</H2>
221 <H2>{'Your Points'}</H2>
242
222
243 <PointList
223 <PointList
244− setPointCursor={setPointCursor}
245 routeHandler={history.push}
224 routeHandler={history.push}
246 points={points}
225 points={points}
247 loading={loading}
226 loading={loading}
src/bridge/views/SentTransaction.js−3
@@ -10,7 +10,6 @@ import { BRIDGE_ERROR, renderTxnError } from '../lib/error';
10import { NETWORK_TYPES } from '../lib/network';
10import { NETWORK_TYPES } from '../lib/network';
11import { useTxnConfirmations } from '../store/txnConfirmations';
11import { useTxnConfirmations } from '../store/txnConfirmations';
12import { useNetwork } from '../store/network';
12import { useNetwork } from '../store/network';
13−import { usePointCursor } from '../store/pointCursor';
14import { useTxnCursor } from '../store/txnCursor';
13import { useTxnCursor } from '../store/txnCursor';
15
14
16class Success extends React.Component {
15class Success extends React.Component {
@@ -115,7 +114,6 @@ const Failure = props => (
115function SentTransaction(props) {
114function SentTransaction(props) {
116 const history = useHistory();
115 const history = useHistory();
117 const { txnConfirmations } = useTxnConfirmations();
116 const { txnConfirmations } = useTxnConfirmations();
118− const { setPointCursor, pointCursor } = usePointCursor();
119 const { web3, networkType } = useNetwork();
117 const { web3, networkType } = useNetwork();
120 const { txnCursor } = useTxnCursor();
118 const { txnCursor } = useTxnCursor();
121
119
@@ -148,7 +146,6 @@ function SentTransaction(props) {
148 <Button
146 <Button
149 prop-type={'link'}
147 prop-type={'link'}
150 onClick={() => {
148 onClick={() => {
151− setPointCursor(pointCursor);
152 history.pop();
149 history.pop();
153 }}>
150 }}>
154 {'Ok →'}
151 {'Ok →'}
src/bridge/views/SetKeys.js+17−14
@@ -7,7 +7,7 @@ import * as kg from 'urbit-key-generation/dist/index';
7import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base';
7import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base';
8
8
9import StatelessTransaction from '../components/StatelessTransaction';
9import StatelessTransaction from '../components/StatelessTransaction';
10−import { attemptSeedDerivation } from '../lib/keys';
10+import { attemptNetworkSeedDerivation } from '../lib/keys';
11
11
12import { addHexPrefix } from '../lib/wallet';
12import { addHexPrefix } from '../lib/wallet';
13import { compose } from '../lib/lib';
13import { compose } from '../lib/lib';
@@ -26,7 +26,7 @@ class SetKeys extends React.Component {
26 this.state = {
26 this.state = {
27 auth: '',
27 auth: '',
28 encr: '',
28 encr: '',
29− networkSeed: '',
29+ newNetworkSeed: '',
30 nondeterministicSeed: false,
30 nondeterministicSeed: false,
31 point: point,
31 point: point,
32 cryptoSuiteVersion: 1,
32 cryptoSuiteVersion: 1,
@@ -93,17 +93,20 @@ class SetKeys extends React.Component {
93
93
94 async deriveSeed() {
94 async deriveSeed() {
95 const next = true;
95 const next = true;
96− let seed = await attemptSeedDerivation(next, this.props);
97 let nondeterministicSeed = false;
96 let nondeterministicSeed = false;
98
97
99− if (seed.getOrElse('') === '') {
98+ const seed = await attemptNetworkSeedDerivation(next, this.props);
99+ const newNetworkSeed = seed.matchWith({
100+ Nothing: () => {
101+ nondeterministicSeed = true;
100− seed = Maybe.Just(this.randomHex(64));
102+ return this.randomHex(64);
101− nondeterministicSeed = true;
103+ },
104+ Just: p => p.value,
102− }
105+ });
103
106
104 this.setState({
107 this.setState({
105− networkSeed: seed.getOrElse(''),
108+ newNetworkSeed,
106− nondeterministicSeed: nondeterministicSeed,
109+ nondeterministicSeed,
107 });
110 });
108 }
111 }
109
112
@@ -114,11 +117,12 @@ class SetKeys extends React.Component {
114
117
115 const validPoint = need.pointCursor(props);
118 const validPoint = need.pointCursor(props);
116
119
120+ // TODO: move this to a lib for validating things
117 const hexRegExp = /[0-9A-Fa-f]{64}/g;
121 const hexRegExp = /[0-9A-Fa-f]{64}/g;
118
122
119− if (hexRegExp.test(state.networkSeed)) {
123+ if (hexRegExp.test(state.newNetworkSeed)) {
120 // derive network keys
124 // derive network keys
121− const pair = kg.deriveNetworkKeys(state.networkSeed);
125+ const pair = kg.deriveNetworkKeys(state.newNetworkSeed);
122
126
123 const pencr = addHexPrefix(pair.crypt.public);
127 const pencr = addHexPrefix(pair.crypt.public);
124 const pauth = addHexPrefix(pair.auth.public);
128 const pauth = addHexPrefix(pair.auth.public);
@@ -142,7 +146,7 @@ class SetKeys extends React.Component {
142 const { props, state } = this;
146 const { props, state } = this;
143
147
144 const canGenerate =
148 const canGenerate =
145− state.networkSeed.length === 64 && state.networkSeed.length === 64;
149+ state.newNetworkSeed.length === 64 && state.newNetworkSeed.length === 64;
146
150
147 const pointDetails = need.fromPointCache(props, state.point);
151 const pointDetails = need.fromPointCache(props, state.point);
148
152
@@ -191,9 +195,8 @@ class SetKeys extends React.Component {
191 // Tx
195 // Tx
192 canGenerate={canGenerate}
196 canGenerate={canGenerate}
193 createUnsignedTxn={this.createUnsignedTxn}
197 createUnsignedTxn={this.createUnsignedTxn}
194− networkSeed={state.networkSeed}
198+ newNetworkSeed={state.newNetworkSeed}
195 newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
199 newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
196− setNetworkSeedCache={props.setNetworkSeedCache}
197 />
200 />
198 </Col>
201 </Col>
199 </Row>
202 </Row>
src/bridge/views/ViewPoint.js+2−1
@@ -71,7 +71,8 @@ class ViewPoint extends React.Component {
71 className={'mt-8'}
71 className={'mt-8'}
72 disabled={valid === false}
72 disabled={valid === false}
73 onClick={() => {
73 onClick={() => {
74− setPointCursor(Maybe.Just(ob.patp2dec(pointName)));
74+ setPointCursor(Maybe.Just(parseInt(ob.patp2dec(pointName), 10)));
75+ // ^ pointCursor expects native number type, not string
75 history.popAndPush(ROUTE_NAMES.SHIP);
76 history.popAndPush(ROUTE_NAMES.SHIP);
76 }}>
77 }}>
77 {'Continue →'}
78 {'Continue →'}