on df0c310 help sign in
bridge/src/lib/walletgen.ts
117 lines 3.5 kB history raw
1import ob from 'urbit-ob';
2import kg from 'urbit-key-generation';
3// @ts-expect-error no types available for more-entropy
4import * as more from 'more-entropy';
5import { chunk, flatMap, zipWith } from 'lodash';
6import { shas } from 'lib/networkCode';
7import { walletgenWorker } from 'worker/';
9import {
10 MIN_STAR,
11 MIN_PLANET,
12 SEED_ENTROPY_BITS,
13 GALAXY_ENTROPY_BITS,
14 STAR_ENTROPY_BITS,
15 PLANET_ENTROPY_BITS,
16 ZOD,
17} from './constants';
18import { stripHexPrefix } from './utils/address';
20const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8;
22const getTicketBitSize = (point: number) =>
23 point < MIN_STAR
24 ? GALAXY_ENTROPY_BITS
25 : point < MIN_PLANET
26 ? STAR_ENTROPY_BITS
27 : PLANET_ENTROPY_BITS;
29// returns a promise for a ticket string
30export const makeTicket = (point: number): Promise<string> => {
31 const bits = getTicketBitSize(point);
33 const bytes = bits / 8;
34 const some = new Uint8Array(bytes);
35 window.crypto.getRandomValues(some);
37 const gen = new more.Generator();
39 return new Promise<string>((resolve, reject) => {
40 gen.generate(bits, result => {
41 const chunked = chunk(result, 2);
42 const desired = chunked.slice(0, bytes); // only take required entropy
43 const more = flatMap(desired, arr => arr[0] ^ arr[1]);
44 const entropy = zipWith(some, more, (x, y) => x ^ y);
45 const buf = Buffer.from(entropy);
46 const patq = ob.hex2patq(buf.toString('hex'));
47 resolve(patq);
48 reject('Entropy generation failed');
49 });
50 });
51};
53export const makeDeterministicTicket = (point: number, seed: string) => {
54 const bits = getTicketBitSize(point);
56 const bytes = bits / 8;
58 const pointSalt = Buffer.concat([
59 Buffer.from(point.toString()),
60 Buffer.from('invites'),
61 ]);
62 const normalizedSeed = stripHexPrefix(seed);
63 const entropy = shas(Buffer.from(normalizedSeed, 'hex'), pointSalt);
65 const buf = entropy.slice(0, bytes);
66 const patq = ob.hex2patq(buf.toString('hex'));
67 return patq;
68};
70// return a wallet object
71export const generateWallet = async (point: number, ticket: string, boot: boolean, revision = 0) => {
72 const config = {
73 ticket: ticket,
74 seedSize: SEED_LENGTH_BYTES,
75 ship: point,
76 password: '',
77 revision,
78 boot: boot,
79 };
81 // This is here to notify anyone who opens console because the thread
82 // hangs, blocking UI updates so this cannot be done in the UI
83 console.log('Generating Wallet for point address: ', point);
85 return new Promise(async (resolve, reject) => {
86 // Use a web worker to process the data
87 try {
88 const processed = await walletgenWorker.generate(JSON.stringify(config));
89 resolve(processed);
90 } catch (error) {
91 reject(error);
92 }
93 });
94};
96export const generateOwnershipWallet = async (ship: number, ticket: string) => {
97 return kg.generateOwnershipWallet({ ship, ticket });
98};
100// temporary wallets need to be derivable from just the ticket,
101// so we always use ~zod as the wallet point.
102export const generateTemporaryOwnershipWallet = (ticket: string) =>
103 generateOwnershipWallet(ZOD, ticket);
105export const generateTemporaryTicketAndWallet = async (point: number) => {
106 const ticket = await makeTicket(point);
107 const owner = await generateOwnershipWallet(ZOD, ticket);
109 return { ticket, owner };
110};
112export const generateTemporaryDeterministicWallet = async (point: number, seed: string) => {
113 const ticket = makeDeterministicTicket(point, seed);
114 const owner = await generateOwnershipWallet(ZOD, ticket);
116 return { ticket, owner };
117};