on master help sign in
Merged
commitb6a375630b7238665d469874d44bdbfd6355c21f authorChris Johnson <chris@cjohnson.io> dateMon 29 Apr 2019, 08:45 (8 years ago) parentaf5c22660b0fce
6 files changed+337−105
src/bridge/views/InviteWallet.js+226 src/bridge/views/InvitesSend.js+3−33 src/bridge/views/Point.js+28−7 src/bridge/views/Point/Actions.js+11−2 src/walletgen/lib/lib.js+65 src/walletgen/views/Generate.js+4−63
src/bridge/views/InviteWallet.jsnew file+226
@@ -0,0 +1,226 @@
1+import { Just, Nothing } from 'folktale/maybe'
2+import React from 'react'
3+import { pour } from 'sigil-js'
4+import * as ob from 'urbit-ob'
5+import * as azimuth from 'azimuth-js'
6+
7+import { addressFromSecp256k1Public, EthereumWallet } from '../lib/wallet'
8+import { ROUTE_NAMES } from '../lib/router'
9+
10+import PointList from '../components/PointList'
11+import ReactSVGComponents from '../components/ReactSVGComponents'
12+import KeysAndMetadata from './Point/KeysAndMetadata'
13+import Actions from './Point/Actions'
14+import { BRIDGE_ERROR } from '../lib/error'
15+import { Row, Col, Warning, Button, H1, H3 } from '../components/Base'
16+
17+// for wallet generation
18+import * as wg from '../../walletgen/lib/lib'
19+import PaperCollateralRenderer from 'PaperCollateralRenderer'
20+import JSZip from 'jszip'
21+import saveAs from 'file-saver'
22+
23+class InviteWallet extends React.Component {
24+
25+ constructor(props) {
26+ super(props)
27+
28+ this.state = {
29+ loaded: false,
30+ point: Nothing(),
31+ wallet: Nothing(),
32+ paper: Nothing(),
33+ downloaded: false
34+ }
35+
36+ this.download = this.download.bind(this)
37+ this.proceed = this.proceed.bind(this)
38+ }
39+
40+ componentDidMount() {
41+ const { web3, wallet, contracts, setPointCursor } = this.props
42+
43+ web3.chain(_ =>
44+ contracts.chain(ctrcs =>
45+ wallet.chain(async wal => {
46+ const addr = addressFromSecp256k1Public(wal.publicKey);
47+ console.log('fetching incoming for', addr)
48+ const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr)
49+ console.log('incoming', incoming)
50+
51+ let point = Nothing();
52+ if (incoming.length > 0) {
53+ let pointNum = parseInt(incoming[0]);
54+ point = Just(pointNum);
55+ this.generateWallet(pointNum);
56+ if (incoming.length > 1) {
57+ //TODO notify user "...and others / ticket reusable"
58+ }
59+ }
60+ this.setState({loaded: true, point: point})
61+ })))
62+ }
63+
64+ componentDidUpdate(prevProps) {
65+ //
66+ }
67+
68+ async generateWallet(point) {
69+ const ticket = await wg.makeTicket(point);
70+ const wallet = await wg.generateWallet(point, ticket);
71+ this.setState({ wallet: Just(wallet) });
72+ }
73+
74+ download() {
75+ const zip = new JSZip();
76+ //TODO the categories here aren't explained in bridge at all...
77+
78+ const bin0 = this.state.paper.filter(item => item.bin === '0');
79+ const bin1 = this.state.paper.filter(item => item.bin === '1');
80+ const bin2 = this.state.paper.filter(item => item.bin === '2');
81+ const bin3 = this.state.paper.filter(item => item.bin === '3');
82+ const bin4 = this.state.paper.filter(item => item.bin === '4');
83+
84+ const bin0Folder = zip.folder("0. Public");
85+ const bin1Folder = zip.folder("1. Very High Friction Custody");
86+ const bin2Folder = zip.folder("2. High Friction Custody");
87+ const bin3Folder = zip.folder("3. Medium Friction Custody");
88+ const bin4Folder = zip.folder("4. Low Friction Custody");
89+
90+ bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png))
91+ bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
92+ bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
93+ bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
94+ bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
95+
96+ zip.generateAsync({type:"blob"}).then((content) => {
97+ saveAs(content, 'urbit-wallet.zip');
98+ this.setState({ downloaded: true });
99+ });
100+ }
101+
102+ proceed() {
103+ this.props.pushRoute(ROUTE_NAMES.INVITE_VERIFY, {
104+ ticket: this.state.wallet.value.ticket,
105+ point: parseInt(this.state.point.value)
106+ });
107+ }
108+
109+ render() {
110+
111+ const { loaded, point, wallet, paper, downloaded } = this.state;
112+
113+ let loading;
114+ if (!loaded) {
115+ loading = ( <div>{ 'Loading...' }</div> );
116+ }
117+
118+ let balanceWarning;
119+ if (loaded && Nothing.hasInstance(point)) {
120+ balanceWarning = (
121+ <Warning>
122+ <h3 className={'mb-2'}>{'Warning'}</h3>
123+ {
124+ 'This invite has no claimable balance.'
125+ }
126+ </Warning>
127+ );
128+ }
129+
130+ let pointOverview;
131+ if (loaded && Just.hasInstance(point)) {
132+ const name = ob.patp(point.value);
133+ const sigil = pour({
134+ patp: name,
135+ renderer: ReactSVGComponents,
136+ size: 256
137+ });
138+ pointOverview = (
139+ //TODO Passport display component
140+ <>
141+ <div className={'mt-12 pt-6'}>
142+ { sigil }
143+ </div>
144+ <H3><code>{ name }</code></H3>
145+ </>
146+ );
147+ }
148+
149+ let downloadButtonContents = 'Download wallet';
150+ if ( Nothing.hasInstance(this.state.wallet) ||
151+ Nothing.hasInstance(this.state.paper) ) {
152+ downloadButtonContents = (
153+ <>
154+ <span className="btn-spinner"></span>
155+ {'Generating wallet...'}
156+ </>
157+ );
158+ }
159+
160+ let downloadButton;
161+ let nextButton;
162+ if (loaded) {
163+ downloadButton = (
164+ <Button
165+ className={'mt-8'}
166+ prop-size={'lg wide'}
167+ disabled={( Nothing.hasInstance(this.state.wallet) ||
168+ Nothing.hasInstance(this.state.paper) )}
169+ onClick={this.download}
170+ >
171+ <span className="relative">
172+ { downloadButtonContents }
173+ </span>
174+ </Button>
175+ );
176+
177+ nextButton = (
178+ <Button
179+ className={'mt-8'}
180+ prop-size={'lg wide'}
181+ disabled={!this.state.downloaded}
182+ onClick={this.proceed}
183+ >
184+ {'Proceed'}
185+ </Button>
186+ );
187+ }
188+
189+ let paperRenderer;
190+ if ( Just.hasInstance(this.state.wallet) &&
191+ Nothing.hasInstance(this.state.paper) ) {
192+ console.log('rendering for', point.value)
193+ paperRenderer = (
194+ <PaperCollateralRenderer
195+ wallet={{[point.value]: wallet.value}}
196+ className={'extremely-hidden'}
197+ callback={data => {
198+ console.log('got paper', data);
199+ this.setState({isRendering: false, paper: data})
200+ }}
201+ mode={'REGISTRATION'} />
202+ );
203+ }
204+
205+ return (
206+ <Row>
207+ <Col>
208+
209+ { loading }
210+ { balanceWarning }
211+ { pointOverview }
212+
213+ <p>{'TODO copy. you need a wallet to receive the point into. download it below. also explanation of wallet files/storage'}</p>
214+
215+ { downloadButton }
216+ { nextButton }
217+
218+ { paperRenderer }
219+
220+ </Col>
221+ </Row>
222+ )
223+ }
224+}
225+
226+export default InviteWallet
src/bridge/views/InvitesSend.js+3−33
@@ -23,10 +23,7 @@ import { Row, Col, Warning, Button, H1, H3,
2323import StatelessTransaction from '../components/StatelessTransaction'
2424
2525// for wallet generation
26−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
27−import * as more from 'more-entropy'
28−import lodash from 'lodash'
29−import { PLANET_ENTROPY_BITS } from '../../walletgen/lib/constants'
26+import * as wg from '../../walletgen/lib/lib'
3027
3128// for transaction generation and signing
3229import {
@@ -101,7 +98,6 @@ class InvitesSend extends React.Component {
10198 );
10299 invited = invited.map(async point => {
103100 const active = await azimuth.azimuth.isActive(this.contracts, point);
104− console.log('point active', point, active);
105101 const res = {point: Number(point), active:active};
106102 return res;
107103 });
@@ -123,35 +119,9 @@ class InvitesSend extends React.Component {
123119 return res;
124120 }
125121
126− //TODO pulled from walletgen/views/Generate, put into lib
127122 async generateWallet() {
128− const makeTicket = () => {
129−
130− const bits = PLANET_ENTROPY_BITS
131−
132− const bytes = bits / 8
133− const some = new Uint8Array(bytes)
134− window.crypto.getRandomValues(some)
135−
136− const gen = new more.Generator()
137−
138− return new Promise((resolve, reject) => {
139− gen.generate(bits, result => {
140− const chunked = lodash.chunk(result, 2)
141− const desired = chunked.slice(0, bytes) // only take required entropy
142− const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
143− const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
144− const buf = Buffer.from(entropy)
145− const patq = ob.hex2patq(buf.toString('hex'))
146− resolve(patq)
147− reject('Entropy generation failed')
148− })
149− })
150− };
151−
152− const ticket = await makeTicket(0x10000); // planet-sized ticket
153− const wallet = await kg.generateWallet({ticket, ship:0});
154− console.log('found ticket', ticket, wallet, wallet.ownership.keys.address);
123+ const ticket = await wg.makeTicket(0x10000); // planet-sized ticket
124+ const wallet = await wg.generateWallet(0, ticket, false);
155125 this.setState({inviteWallet:
156126 Just({ticket, owner:wallet.ownership.keys.address})
157127 });
src/bridge/views/Point.js+28−7
@@ -49,9 +49,12 @@ class Point extends React.Component {
4949 contracts.chain(ctrcs =>
5050 pointCursor.chain(point => {
5151 azimuth.azimuth.getPoint(ctrcs, point)
52− .then(details => addToPointCache({ [point]: details }))
53− this.updateSpawned(ctrcs, point)
54− })))
52+ .then(details => addToPointCache({ [point]: details }));
53+ const prefix = azimuth.azimuth.getPrefix(point);
54+ azimuth.azimuth.getPoint(ctrcs, prefix)
55+ .then(details => addToPointCache({ [prefix]: details }));
56+ this.updateSpawned(ctrcs, point);
57+ })));
5558 }
5659
5760 updateSpawned = contracts => {
@@ -69,9 +72,10 @@ class Point extends React.Component {
6972
7073 render() {
7174
72− const { web3, popRoute, pushRoute, wallet } = this.props
73− const { setPointCursor } = this.props
74− const { pointCursor, pointCache } = this.props
75+ const {
76+ web3, popRoute, pushRoute, wallet, contracts,
77+ setPointCursor, pointCursor, pointCache
78+ } = this.props;
7579
7680 const { spawned } = this.state
7781
@@ -88,6 +92,16 @@ class Point extends React.Component {
8892 ? Just(pointCache[point])
8993 : Nothing()
9094
95+ const prefix = azimuth.azimuth.getPrefix(point);
96+ const prefixDetails =
97+ prefix in pointCache
98+ ? Just(pointCache[prefix])
99+ : Nothing();
100+ const delegatedSending = contracts.matchWith({
101+ Nothing: () => '0x',
102+ Just: (c) => c.value.delegatedSending.address
103+ })
104+
91105 const name = ob.patp(point)
92106
93107 const sigil = pour({
@@ -129,7 +143,14 @@ class Point extends React.Component {
129143 <H1><code>{ name }</code></H1>
130144 {
131145 authenticated
132− ? <Actions pushRoute={ pushRoute } online={ online } point={ point } pointDetails={ pointDetails } wallet={ wallet }/>
146+ ? <Actions
147+ pushRoute={ pushRoute }
148+ online={ online }
149+ wallet={ wallet }
150+ delegatedSending={ delegatedSending }
151+ point={ point }
152+ pointDetails={ pointDetails }
153+ prefixDetails={ prefixDetails } />
133154 : null
134155 }
135156
src/bridge/views/Point/Actions.js+11−2
@@ -17,7 +17,10 @@ const isStar = point =>
1717 (azimuth.getPointSize(point) === azimuth.PointSize.Star);
1818
1919const Actions = (props) => {
20− const { pushRoute, online, point, pointDetails, wallet } = props
20+ const {
21+ pushRoute, online, wallet, delegatedSending,
22+ point, pointDetails, prefixDetails
23+ } = props
2124
2225 const addr = wallet.matchWith({
2326 Just: (wal) => addressFromSecp256k1Public(wal.value.publicKey),
@@ -61,6 +64,12 @@ const Actions = (props) => {
6164 }
6265 })
6366
67+ const prefixHasDelegatedSending =
68+ prefixDetails.matchWith({
69+ Nothing: () => false,
70+ Just: (details) => eqAddr(details.value.spawnProxy, delegatedSending)
71+ });
72+
6473 const planet = isPlanet(point)
6574 const star = isStar(point)
6675
@@ -114,7 +123,7 @@ const Actions = (props) => {
114123 if (planet) {
115124 invites = (
116125 <Button
117− disabled={(!isActiveOwner || !online)}
126+ disabled={(!isActiveOwner || !online || !prefixHasDelegatedSending)}
118127 prop-size={'sm'}
119128 prop-type={'link'}
120129 onClick={ () => {
src/walletgen/lib/lib.js+65
@@ -1,9 +1,74 @@
11
2+import * as ob from 'urbit-ob'
3+import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
4+import * as more from 'more-entropy'
5+import lodash from 'lodash'
6+import {
7+ MAX_GALAXY, MIN_STAR, MAX_STAR, MIN_PLANET,
8+ GALAXY_ENTROPY_BITS, STAR_ENTROPY_BITS, PLANET_ENTROPY_BITS,
9+ SEED_ENTROPY_BITS,
10+ GEN_STATES
11+} from './constants'
12+
13+const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8
14+
15+// returns a promise for a ticket string
16+const makeTicket = point => {
17+
18+ const bits = point < MIN_STAR
19+ ? GALAXY_ENTROPY_BITS
20+ : point < MIN_PLANET
21+ ? STAR_ENTROPY_BITS
22+ : PLANET_ENTROPY_BITS
23+
24+ const bytes = bits / 8
25+ const some = new Uint8Array(bytes)
26+ window.crypto.getRandomValues(some)
27+
28+ const gen = new more.Generator()
29+
30+ return new Promise((resolve, reject) => {
31+ gen.generate(bits, result => {
32+ const chunked = lodash.chunk(result, 2)
33+ const desired = chunked.slice(0, bytes) // only take required entropy
34+ const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
35+ const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
36+ const buf = Buffer.from(entropy)
37+ const patq = ob.hex2patq(buf.toString('hex'))
38+ resolve(patq)
39+ reject('Entropy generation failed')
40+ })
41+ })
42+}
43+
44+// return a wallet object
45+const generateWallet = async (point, ticket, boot) => {
46+
47+ const config = {
48+ ticket: ticket,
49+ seedSize: SEED_LENGTH_BYTES,
50+ ship: point,
51+ password: '',
52+ revisions: {},
53+ boot: boot
54+ };
55+
56+ const wallet = await kg.generateWallet(config);
57+
58+ // This is here to notify the anyone who opens console because the thread
59+ // hangs, blocking UI updates so this cannot be doen in the UI
60+ console.log('Generating Wallet for point address: ', point);
61+
62+ return wallet;
63+}
64+
265const seq = num => Array.from(Array(num), (_, i) => i)
366
467const compose = (...fs) => fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v)
568
669export {
70+ makeTicket,
71+ generateWallet,
772 compose,
873 seq,
974}
src/walletgen/views/Generate.js+4−63
@@ -4,8 +4,7 @@ import lodash from 'lodash'
44import * as sigil from 'sigil-js'
55import ob from 'urbit-ob'
66import classnames from 'classnames'
7−// temporarily use keygen-js with argon2u that is configured with 520MB
8−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
7+import * as wg from '../lib/lib'
98
109
1110import Button from '../components/Button'
@@ -169,65 +168,6 @@ const planetInfo = (ship, sig) =>
169168 </div>
170169 </div>
171170
172−
173−
174−const makeTicket = ship => {
175−
176− const bits = ship < MIN_STAR
177− ? GALAXY_ENTROPY_BITS
178− : ship < MIN_PLANET
179− ? STAR_ENTROPY_BITS
180− : PLANET_ENTROPY_BITS
181−
182− const bytes = bits / 8
183− const some = new Uint8Array(bytes)
184− window.crypto.getRandomValues(some)
185−
186− const gen = new more.Generator()
187−
188− return new Promise((resolve, reject) => {
189− gen.generate(bits, result => {
190− const chunked = lodash.chunk(result, 2)
191− const desired = chunked.slice(0, bytes) // only take required entropy
192− const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
193− const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
194− const buf = Buffer.from(entropy)
195− const patq = ob.hex2patq(buf.toString('hex'))
196− resolve(patq)
197− reject('Entropy generation failed')
198− })
199− })
200−}
201−
202−
203−
204−
205−const genWallet = async (ship, ticket, cb) => {
206−
207− const boot = ship < MIN_STAR || ship > MAX_STAR;
208−
209− const config = {
210− ticket: ticket,
211− seedSize: SEED_LENGTH_BYTES,
212− ship: ship,
213− password: '',
214− revisions: {},
215− boot: boot
216− };
217−
218− const wallet = await kg.generateWallet(config);
219−
220− // This is here to notify the anyone who opens console because the thread
221− // hangs, blocking UI updates so this cannot be doen in the UI
222− console.log('Generating Wallet for ship address: ', ship);
223−
224− return wallet;
225−}
226−
227−
228−
229−
230−
231171class Generate extends React.Component {
232172 constructor(props) {
233173 super(props)
@@ -249,7 +189,7 @@ class Generate extends React.Component {
249189 const enyPromises = ships.map(async ship => {
250190 return {
251191 ship,
252− ticket: await makeTicket(ship),
192+ ticket: await wg.makeTicket(ship),
253193 };
254194 });
255195
@@ -264,9 +204,10 @@ class Generate extends React.Component {
264204 await this.forciblyExecuteArgon2Once(enyTable);
265205
266206 const walletPromises = enyTable.map(async ({ ship, ticket }) => {
207+ const boot = ship < MIN_STAR || ship > MAX_STAR;
267208 return {
268209 ship,
269− wallet: await genWallet(ship, ticket),
210+ wallet: await wg.generateWallet(ship, ticket, boot),
270211 };
271212 });
272213