@@ -8,7 +8,9 @@ import { RequiredInput, InnerLabel } from '../components/Base'
88import StatelessTransaction from '../components/StatelessTransaction'
99import { BRIDGE_ERROR } from '../lib/error'
1010import { ROUTE_NAMES } from '../lib/router'
11−import { attemptSeedDerivation } from '../lib/keys'
11+import { attemptSeedDerivation, genKey } from '../lib/keys'
12+
13+import saveAs from 'file-saver'
1214
1315import { WALLET_NAMES } from '../lib/wallet'
1416
@@ -39,6 +41,7 @@ class SetKeys extends React.Component {
3941 auth: '',
4042 encr: '',
4143 networkSeed: '',
44+ nondeterministicSeed: false,
4245 point: point,
4346 cryptoSuiteVersion: 1,
4447 continuity: false,
@@ -122,22 +125,38 @@ class SetKeys extends React.Component {
122125 })
123126 }
124127
128+ //TODO use web3.utils.randomHex when it gets fixed, see web3.js#1490
129+ randomHex(len) {
130+ let hex = "";
131+
132+ for (var i = 0; i < len; i++) {
133+ hex = hex + ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
134+ 'C', 'D', 'E', 'F'][Math.floor(Math.random() * 16)]
135+ }
136+
137+ return hex;
138+ }
139+
125140 async deriveSeed() {
126141 const next = true
127− const seed = await attemptSeedDerivation(next, this.props)
142+ let seed = await attemptSeedDerivation(next, this.props)
143+ let nondeterministicSeed = false;
144+
145+ if (seed.getOrElse('') === '') {
146+ seed = Maybe.Just(this.randomHex(64));
147+ nondeterministicSeed = true;
148+ }
149+
128150 this.setState({
129− networkSeed: seed.getOrElse('')
151+ networkSeed: seed.getOrElse(''),
152+ nondeterministicSeed: nondeterministicSeed
130153 })
131154 }
132155
133−
134−
135156 handleNetworkSeedInput(networkSeed) {
136157 this.setState({ networkSeed })
137158 }
138159
139−
140−
141160 handleCreateUnsignedTxn() {
142161 const txn = this.createUnsignedTxn()
143162 this.setState({ txn })
@@ -222,23 +241,42 @@ class SetKeys extends React.Component {
222241 })
223242 }
224243
244+ downloadKeyfile(networkSeed) {
245+ const { pointCache } = this.props
246+ const { pointCursor } = this.props
225247
248+ const point = pointCursor.matchWith({
249+ Just: (pt) => pt.value,
250+ Nothing: () => {
251+ throw BRIDGE_ERROR.MISSING_POINT
252+ }
253+ })
254+
255+ const pointDetails =
256+ point in pointCache
257+ ? pointCache[point]
258+ : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
259+
260+ const revision = parseInt(pointDetails.keyRevisionNumber)
261+ const keyfile = genKey(networkSeed, point, revision)
262+ let blob = new Blob([keyfile], {type:"text/plain;charset=utf-8"});
263+ saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`)
264+ }
226265
227266 handleSubmit(){
228267 const { props, state } = this
229268 sendSignedTransaction(props.web3.value, state.stx)
230269 .then(sent => {
270+ props.setNetworkSeedCache(this.state.networkSeed)
231271 props.setTxnHashCursor(sent)
232272 props.popRoute()
233− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
273+ props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, {promptKeyfile: true})
234274 })
235275 .catch(err => {
236276 this.setState({ txError: err.map(val => val.merge()) })
237277 })
238278 }
239279
240−
241−
242280 createUnsignedTxn() {
243281 const { state, props } = this
244282
@@ -299,16 +337,6 @@ class SetKeys extends React.Component {
299337 ? props.pointCache[state.point]
300338 : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
301339
302− const isManagementMnemonic =
303− this.state.isManagementSeed &&
304− props.walletType === WALLET_NAMES.MNEMONIC
305−
306− const isMasterTicket =
307− props.walletType === WALLET_NAMES.TICKET ||
308− props.walletType === WALLET_NAMES.SHARD
309−
310− const networkSeedDisabled = isManagementMnemonic || isMasterTicket;
311−
312340 return (
313341 <Row>
314342 <Col>
@@ -316,19 +344,22 @@ class SetKeys extends React.Component {
316344 { 'Set Network Keys For ' } <code>{ `${ob.patp(state.point)}` }</code>
317345 </H1>
318346
319− <P>
347+ <P className="mt-10">
320348 {
321− `Please enter a network seed for generating and setting your public
322− network authentication and encryption keys. Your network seed
323− must be a string of 64 characters (containing 0-9, A-Z, a-z).`
349+ `Set new authentication and encryption keys for your Arvo ship.`
324350 }
325351 </P>
326− <P>
327− {
328− `If you've authenticated with a master ticket or management proxy
329− mnemonic, a seed will be generated for you automatically.`
352+
353+ { state.nondeterministicSeed &&
354+ <Warning>
355+ <h3 className={'mb-2'}>{'Warning'}</h3>
356+ {
357+ `Your network seed could not be derived automatically. We've
358+ generated a random one for you, so you must download your Arvo
359+ keyfile during this session after setting your keys.`
360+ }
361+ </Warning>
330362 }
331− </P>
332363
333364 { pointDetails.keyRevisionNumber === '0'
334365 ? <Warning>
@@ -340,17 +371,6 @@ class SetKeys extends React.Component {
340371 </Warning>
341372 : <div /> }
342373
343− <RequiredInput
344− className='mono'
345− prop-size='lg'
346− prop-format='innerLabel'
347− autoFocus
348− disabled= { networkSeedDisabled }
349− value={ state.networkSeed }
350− onChange={ this.handleNetworkSeedInput }>
351− <InnerLabel>{ 'Network seed' }</InnerLabel>
352− </RequiredInput>
353−
354374 <StatelessTransaction
355375 // Upper scope
356376 web3={props.web3}