on 616e621 help sign in
bridge/src/views/Shards.js
205 lines 5.8 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, Form } 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 <Form>
120 <PointInput
121 className='mono mt-8'
122 prop-size='lg'
123 prop-format='innerLabel'
124 type='text'
125 autoFocus
126 placeholder={ `e.g. ${phPoint}` }
127 value={ pointName }
128 onChange={ this.handlePointNameInput }>
129 <InnerLabel>{ 'Point' }</InnerLabel>
130 <ValidatedSigil
131 className={'tr-0 mt-05 mr-0 abs'}
132 patp={pointName}
133 size={68}
134 margin={8} />
135 </PointInput>
138 <ShardInput
139 className='mono mt-8'
140 prop-size='md'
141 prop-format='innerLabel'
142 type='text'
143 name='shard1'
144 placeholder={ `e.g. ${phTick}` }
145 value={ shard1 }
146 onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
147 <InnerLabel>{ 'Shard 1' }</InnerLabel>
148 </ShardInput>
150 <ShardInput
151 className='mono mt-8'
152 prop-size='md'
153 prop-format='innerLabel'
154 type='text'
155 name='shard2'
156 placeholder={ `e.g. ${phTick}` }
157 value={ shard2 }
158 onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
159 <InnerLabel>{ 'Shard 2' }</InnerLabel>
160 </ShardInput>
162 <ShardInput
163 className='mono mt-8'
164 prop-size='md'
165 prop-format='innerLabel'
166 type='text'
167 name='shard3'
168 placeholder={ `e.g. ${phTick}` }
169 value={ shard3 }
170 onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
171 <InnerLabel>{ 'Shard 3' }</InnerLabel>
172 </ShardInput>
174 <Button
175 disabled={ !ready }
176 className={'mt-8'}
177 prop-size={'lg wide'}
178 // prop-color={this.buttonTriState(wallet)}
179 onClick={() =>
180 this.walletFromShards(shard1, shard2, shard3, pointName)
181 }>
182 {'Unlock Wallet →'}
183 </Button>
185 <Button
186 className={'mt-4'}
187 prop-size={'xl wide'}
188 disabled={ Maybe.Nothing.hasInstance(wallet) }
189 onClick={ () => {
190 popRoute()
191 pushRoute(ROUTE_NAMES.SHIPS)
192 }
193 }
194 >
195 { 'Continue →' }
196 </Button>
198 </Form>
199 </Col>
200 </Row>
201 )
202 }
205export default Shards