on aee35bb help sign in
bridge/src/views/Shards.js
212 lines 5.8 kB history raw
1import { Just, Nothing } from 'folktale/maybe';
2import React from 'react';
3import { Button } from '../components/old/Base';
4import {
5 InnerLabel,
6 ValidatedSigil,
7 PointInput,
8 ShardInput,
9 InputCaption,
10 Input,
11} from '../components/old/Base';
12import { H1, P } from '../components/old/Base';
13import * as kg from 'urbit-key-generation/dist';
14import * as ob from 'urbit-ob';
16import { ROUTE_NAMES } from '../lib/routeNames';
17import { withHistory } from '../store/history';
18import { urbitWalletFromTicket } from '../lib/wallet';
19import { compose } from '../lib/lib';
20import { withWallet } from '../store/wallet';
21import View from 'components/View';
23const placeholder = len => {
24 let bytes = window.crypto.getRandomValues(new Uint8Array(len));
25 let hex = bytes.reduce(
26 (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
27 ''
28 );
29 return ob.hex2patq(hex);
30};
32const SHARDS = {
33 SHARD1: Symbol('SHARD1'),
34 SHARD2: Symbol('SHARD2'),
35 SHARD3: Symbol('SHARD3'),
36};
38class Shards extends React.Component {
39 constructor(props) {
40 super(props);
42 this.state = {
43 shard1: '',
44 shard2: '',
45 shard3: '',
46 passphrase: '',
47 pointName: '',
48 };
50 this.pointPlaceholder = placeholder(4);
51 this.ticketPlaceholder = placeholder(16);
53 this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
54 this.handleShardInput = this.handleShardInput.bind(this);
55 this.handlePointNameInput = this.handlePointNameInput.bind(this);
56 }
58 handleShardInput(shard, input) {
59 if (shard === SHARDS.SHARD1) {
60 this.setState({ shard1: input });
61 } else if (shard === SHARDS.SHARD2) {
62 this.setState({ shard2: input });
63 } else if (shard === SHARDS.SHARD3) {
64 this.setState({ shard3: input });
65 }
66 }
68 handlePassphraseInput(passphrase) {
69 this.setState({ passphrase });
70 }
72 handlePointNameInput(pointName) {
73 if (pointName.length < 15) {
74 this.setState({ pointName });
75 }
76 }
78 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
79 const s1 = shard1 === '' ? undefined : shard1;
80 const s2 = shard2 === '' ? undefined : shard2;
81 const s3 = shard3 === '' ? undefined : shard3;
83 let ticket = undefined;
84 try {
85 ticket = kg.combine([s1, s2, s3]);
86 } catch (_) {
87 // do nothing
88 }
90 if (ticket !== undefined) {
91 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
92 this.props.setUrbitWallet(Just(uhdw));
93 }
94 }
96 render() {
97 const { history, wallet } = this.props;
98 const { shard1, shard2, shard3, pointName, passphrase } = this.state;
100 const phPoint = this.pointPlaceholder;
101 const phTick = this.ticketPlaceholder;
103 const shards = [shard1, shard2, shard3];
104 const ready = shards.filter(x => x !== '').length > 1;
106 return (
107 <View>
108 <H1>{'Authenticate'}</H1>
110 <P>
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 </P>
116 <PointInput
117 className="mono mt-8"
118 prop-size="lg"
119 prop-format="innerLabel"
120 type="text"
121 autoFocus
122 placeholder={`e.g. ${phPoint}`}
123 value={pointName}
124 onChange={this.handlePointNameInput}>
125 <InnerLabel>{'Point'}</InnerLabel>
126 <ValidatedSigil
127 className={'tr-0 mt-05 mr-0 abs'}
128 patp={pointName}
129 size={68}
130 margin={8}
131 />
132 </PointInput>
134 <ShardInput
135 className="mono mt-8"
136 prop-size="md"
137 prop-format="innerLabel"
138 type="text"
139 name="shard1"
140 placeholder={`e.g. ${phTick}`}
141 value={shard1}
142 onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
143 <InnerLabel>{'Shard 1'}</InnerLabel>
144 </ShardInput>
146 <ShardInput
147 className="mono mt-8"
148 prop-size="md"
149 prop-format="innerLabel"
150 type="text"
151 name="shard2"
152 placeholder={`e.g. ${phTick}`}
153 value={shard2}
154 onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
155 <InnerLabel>{'Shard 2'}</InnerLabel>
156 </ShardInput>
158 <ShardInput
159 className="mono mt-8"
160 prop-size="md"
161 prop-format="innerLabel"
162 type="text"
163 name="shard3"
164 placeholder={`e.g. ${phTick}`}
165 value={shard3}
166 onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
167 <InnerLabel>{'Shard 3'}</InnerLabel>
168 </ShardInput>
170 <InputCaption>
171 {'If your wallet requires a passphrase, you may enter it below.'}
172 </InputCaption>
174 <Input
175 className="pt-8"
176 prop-size="md"
177 prop-format="innerLabel"
178 name="passphrase"
179 type="password"
180 value={passphrase}
181 autocomplete="off"
182 onChange={this.handlePassphraseInput}>
183 <InnerLabel>{'Passphrase'}</InnerLabel>
184 </Input>
186 <Button
187 disabled={!ready}
188 className={'mt-8'}
189 prop-size={'lg wide'}
190 // prop-color={this.buttonTriState(wallet)}
191 onClick={() =>
192 this.walletFromShards(shard1, shard2, shard3, pointName, passphrase)
193 }>
194 {'Unlock Wallet →'}
195 </Button>
197 <Button
198 className={'mt-4'}
199 prop-size={'xl wide'}
200 disabled={Nothing.hasInstance(wallet)}
201 onClick={() => history.popAndPush(ROUTE_NAMES.POINTS)}>
202 {'Continue →'}
203 </Button>
204 </View>
205 );
206 }
209export default compose(
210 withHistory,
211 withWallet
212)(Shards);