on 05c1914 help sign in
bridge/src/views/Shards.js
202 lines 5.6 kB history raw
1import Maybe from 'folktale/maybe'
2import React from 'react'
3import { Button } from '../components/Base'
4import { InnerLabel, ValidatedSigil, PointInput, ShardInput } from '../components/Base'
5import { Row, Col, H1, P } from '../components/Base'
6import * as kg from '../../node_modules/urbit-key-generation/dist/index'
7import * as ob from 'urbit-ob'
9import { ROUTE_NAMES } from '../lib/router'
10import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet'
12const placeholder = (len) => {
13 let bytes = window.crypto.getRandomValues(new Uint8Array(len))
14 let hex = bytes.reduce((acc, byt) =>
15 acc + byt.toString(16).padStart(2, '0'),
16 ''
17 )
18 return ob.hex2patq(hex)
21const SHARDS = {
22 SHARD1: Symbol('SHARD1'),
23 SHARD2: Symbol('SHARD2'),
24 SHARD3: Symbol('SHARD3')
27class Shards extends React.Component {
29 constructor(props) {
30 super(props)
32 this.state = {
33 shard1: '',
34 shard2: '',
35 shard3: '',
36 pointName: ''
37 }
39 this.pointPlaceholder = placeholder(4)
40 this.ticketPlaceholder = placeholder(16)
42 this.handleShardInput = this.handleShardInput.bind(this)
43 this.handlePointNameInput = this.handlePointNameInput.bind(this)
44 }
46 handleShardInput(shard, input) {
47 if (shard === SHARDS.SHARD1) {
48 this.setState({ shard1: input })
49 } else if (shard === SHARDS.SHARD2) {
50 this.setState({ shard2: input })
51 } else if (shard === SHARDS.SHARD3) {
52 this.setState({ shard3: input })
53 }
54 }
56 handlePointNameInput(pointName) {
57 if (pointName.length < 15) {
58 this.setState({ pointName })
59 }
60 }
62 // buttonTriState = (wallet) => {
63 // if (wallet.isNothing()) return 'blue'
64 // if (wallet === false) return 'yellow'
65 // if (Maybe.Nothing.hasInstance(wallet)) return 'green'
66 // }
68 async walletFromShards(shard1, shard2, shard3, pointName) {
69 const { setWallet, setUrbitWallet } = this.props
71 const s1 = shard1 === '' ? undefined : shard1
72 const s2 = shard2 === '' ? undefined : shard2
73 const s3 = shard3 === '' ? undefined : shard3
75 let ticket = undefined
76 try {
77 ticket = kg.combine([s1, s2, s3])
78 } catch(_) {
79 // do nothing
80 }
82 if (ticket !== undefined) {
83 const urbitWallet = await kg.generateWallet({
84 ticket: ticket,
85 ship: ob.patp2dec(pointName)
86 })
87 const mnemonic = urbitWallet.ownership.seed
88 const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH)
89 setWallet(wallet)
90 setUrbitWallet(Maybe.Just(urbitWallet))
91 }
92 }
94 render() {
95 const { popRoute, pushRoute, wallet } = this.props
96 const { shard1, shard2, shard3, pointName } = this.state
98 const phPoint = this.pointPlaceholder
99 const phTick = this.ticketPlaceholder
101 const shards = [shard1, shard2, shard3]
102 const ready = shards.filter(x => x !== '').length > 1
104 return (
105 <Row>
106 <Col>
107 <H1>{ 'Authenticate' }</H1>
109 <P>
110 {
111 `Enter your point and at least two of your three Urbit master
112 ticket shards here. The index of the input field needs to
113 match the index of the shard.`
114 }
116 </P>
118 <PointInput
119 className='mono mt-8'
120 prop-size='lg'
121 prop-format='innerLabel'
122 type='text'
123 autoFocus
124 placeholder={ `e.g. ${phPoint}` }
125 value={ pointName }
126 onChange={ this.handlePointNameInput }>
127 <InnerLabel>{ 'Point' }</InnerLabel>
128 <ValidatedSigil
129 className={'tr-0 mt-05 mr-0 abs'}
130 patp={pointName}
131 size={68}
132 margin={8} />
133 </PointInput>
136 <ShardInput
137 className='mono mt-8'
138 prop-size='md'
139 prop-format='innerLabel'
140 type='text'
141 name='shard1'
142 placeholder={ `e.g. ${phTick}` }
143 value={ shard1 }
144 onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
145 <InnerLabel>{ 'Shard 1' }</InnerLabel>
146 </ShardInput>
148 <ShardInput
149 className='mono mt-8'
150 prop-size='md'
151 prop-format='innerLabel'
152 type='text'
153 name='shard2'
154 placeholder={ `e.g. ${phTick}` }
155 value={ shard2 }
156 onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
157 <InnerLabel>{ 'Shard 2' }</InnerLabel>
158 </ShardInput>
160 <ShardInput
161 className='mono mt-8'
162 prop-size='md'
163 prop-format='innerLabel'
164 type='text'
165 name='shard3'
166 placeholder={ `e.g. ${phTick}` }
167 value={ shard3 }
168 onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
169 <InnerLabel>{ 'Shard 3' }</InnerLabel>
170 </ShardInput>
172 <Button
173 disabled={ !ready }
174 className={'mt-8'}
175 prop-size={'lg wide'}
176 // prop-color={this.buttonTriState(wallet)}
177 onClick={() =>
178 this.walletFromShards(shard1, shard2, shard3, pointName)
179 }>
180 {'Unlock Wallet →'}
181 </Button>
183 <Button
184 className={'mt-4'}
185 prop-size={'xl wide'}
186 disabled={ Maybe.Nothing.hasInstance(wallet) }
187 onClick={ () => {
188 popRoute()
189 pushRoute(ROUTE_NAMES.SHIPS)
190 }
191 }
192 >
193 { 'Continue →' }
194 </Button>
196 </Col>
197 </Row>
198 )
199 }
202export default Shards