on master help sign in
activate: generate invite wallet deterministically

Multikey implementation was fine; the issue was that the invite wallet (which the key derivation seed is sourced from) was being generated non-deterministically. This change also adds more TS typing.

commita2920a4bd09b983846a2a6ba633e3a781c3ec98b authortomholford <tomholford@users.noreply.github.com> dateThu 16 Dec 2021, 15:48 (5 years ago) parentbf45baa
10 files changed+107−74
src/lib/keys.js−27 src/lib/types/BridgeWallet.ts+1 src/lib/types/L2Transaction.ts+1−1 src/lib/useMultikeyFileGenerator.ts+28−26 src/lib/useRoller.ts+7−7 src/lib/wallet.ts+5−1 src/urbit-key-generation.d.ts+54 src/views/Activate/ActivateCodeForm.tsx+8−9 src/views/Activate/MasterKeyDownload.tsx+2−2 src/worker/worker.ts+1−1
src/lib/keys.js−27
@@ -246,30 +246,3 @@ export const segmentNetworkKey = hex => {
246246
247247 return [rowFrom(2), rowFrom(18), rowFrom(34), rowFrom(50)];
248248};
249−
250−// ctsy @yosoyubik
251−/**
252− * @param {object} pair
253− * @param {number} point
254− * @param {number} revision
255− * @return {string}
256− */
257−export const compileMultikey = (point, pair1, pair2, revision1, revision2) => {
258− const bnsec1 = new BN(createRing(pair1), 'hex');
259− const bnsec2 = new BN(createRing(pair2), 'hex');
260−
261− const tag = noun.dwim(noun.Atom.fromInt(1), noun.Atom.yes);
262− const ship = noun.Atom.fromInt(point);
263− const key1 = noun.dwim(
264− noun.Atom.fromInt(revision1),
265− noun.Atom.fromString(bnsec1.toString())
266− );
267− const key2 = noun.dwim(
268− noun.Atom.fromInt(revision2),
269− noun.Atom.fromString(bnsec2.toString())
270− );
271−
272− const sed = noun.dwim(tag, ship, key1, key2, noun.Atom.yes);
273−
274− return b64(jam(sed));
275−};
src/lib/types/BridgeWallet.ts+1
@@ -2,4 +2,5 @@ import { BIP32Interface } from 'bitcoinjs-lib';
22
33export default interface BridgeWallet extends BIP32Interface {
44 address: string;
5+ passphrase?: string;
56}
src/lib/types/L2Transaction.ts+1−1
@@ -43,6 +43,6 @@ export interface ReticketParams {
4343 point: Ship;
4444 to: EthAddress;
4545 manager: EthAddress;
46− fromWallet: any; // TODO: wallet type
46+ fromWallet: UrbitWallet;
4747 toWallet: any; // TODO: wallet type
4848}
src/lib/useMultikeyFileGenerator.ts+28−26
@@ -11,7 +11,7 @@ import { deriveNetworkKeys } from 'urbit-key-generation';
1111import { convertToInt } from './convertToInt';
1212import {
1313 attemptNetworkSeedDerivation,
14− compileMultikey,
14+ compileMultiKey,
1515 keysMatchChain,
1616} from './keys';
1717import { generateCode } from './networkCode';
@@ -80,7 +80,7 @@ export default function useMultikeyFileGenerator({
8080 ? Just(seed)
8181 : await attemptNetworkSeedDerivation({
8282 urbitWallet: seedWallet || urbitWallet,
83− wallet,
83+ wallet: wallet,
8484 authMnemonic,
8585 details: _details,
8686 authToken,
@@ -127,38 +127,40 @@ export default function useMultikeyFileGenerator({
127127 return;
128128 }
129129
130− // TODO: Do we still need this? The legacy implementation in useKeyfileGenerator
131− // performed this guard statement
130+ const currentPair = await pairFromRevision(currentRevision);
132131
133− // const currentPair = await pairFromRevision(currentRevision);
134−
135− // if (!currentPair || !keysMatchChain(currentPair, _details)) {
136− // setGenerating(false);
137− // setNotice('Derived networking keys do not match on-chain details.');
138− // console.log(`keys do not match details for revision ${currentRevision}`);
139− // return;
140− // }
141−
142− // const nextPair = await pairFromRevision(nextRevision);
132+ if (!currentPair || !keysMatchChain(currentPair, _details || {})) {
133+ setGenerating(false);
134+ setNotice('Derived networking keys do not match on-chain details.');
135+ console.log(`keys do not match details for revision ${currentRevision}`);
136+ return;
137+ }
143138
144− const [currentPair, nextPair] = await Promise.all([
145− pairFromRevision(currentRevision),
146− pairFromRevision(nextRevision),
147− ]);
139+ const nextPair = await pairFromRevision(nextRevision);
148140
149141 setNotice('');
150142 setCode(generateCode(currentPair));
151143 setKeyfile(
152− compileMultikey(
153− _point,
154− currentPair,
155− nextPair,
156− currentRevision,
157− nextRevision
158− )
144+ compileMultiKey(_point, [
145+ {
146+ revision: currentRevision,
147+ pair: currentPair,
148+ },
149+ {
150+ revision: nextRevision,
151+ pair: nextPair,
152+ },
153+ ])
159154 );
160155 setGenerating(false);
161− }, [_point, currentRevision, hasNetworkKeys, nextRevision, pairFromRevision]);
156+ }, [
157+ _details,
158+ _point,
159+ currentRevision,
160+ hasNetworkKeys,
161+ nextRevision,
162+ pairFromRevision,
163+ ]);
162164
163165 const filename = useMemo(() => {
164166 return `${stripSigPrefix(ob.patp(_point))}-${currentRevision}.key`;
src/lib/useRoller.ts+7−7
@@ -22,7 +22,7 @@ import { usePointCache } from 'store/pointCache';
2222
2323import { getUpdatedPointMessage, isPlanet, toL1Details } from './utils/point';
2424import { convertToInt } from './convertToInt';
25−import { isDevelopment, isRopsten } from './flags';
25+import { isDevelopment, isMainnet, isRopsten } from './flags';
2626import {
2727 generateInviteWallet,
2828 getPendingSpawns,
@@ -113,13 +113,13 @@ export default function useRoller() {
113113 const [config, setConfig] = useState<Config | null>(null);
114114
115115 const options: Options = useMemo(() => {
116− const type = isRopsten || !isDevelopment ? 'https' : 'http';
117− const host = isRopsten
116+ const type = isMainnet || isRopsten ? 'https' : 'http';
117+ const host = isMainnet
118+ ? ROLLER_HOSTS.MAINNET
119+ : isRopsten
118120 ? ROLLER_HOSTS.ROPSTEN
119− : isDevelopment
120− ? ROLLER_HOSTS.LOCAL
121− : ROLLER_HOSTS.MAINNET;
122− const port = isDevelopment && !isRopsten ? 8080 : 443;
121+ : ROLLER_HOSTS.LOCAL;
122+ const port = isMainnet || isRopsten ? 443 : 8080;
123123 const path = '/v1/roller';
124124
125125 return {
src/lib/wallet.ts+5−1
@@ -12,7 +12,11 @@ export function EthereumWallet(privateKey) {
1212 this.address = publicToAddress(this.publicKey);
1313}
1414
15−export const urbitWalletFromTicket = async (ticket, point, passphrase) => {
15+export const urbitWalletFromTicket = async (
16+ ticket: string,
17+ point: number,
18+ passphrase?: string
19+) => {
1620 return await kg.generateWallet({
1721 ticket: ticket,
1822 ship: point,
src/urbit-key-generation.d.ts+54
@@ -9,6 +9,60 @@ interface NetworkKeys {
99 };
1010}
1111
12+interface WalletNodeKeys {
13+ public: string;
14+ private: string;
15+ chain: string;
16+ address: string;
17+}
18+
19+interface WalletNode {
20+ type: string;
21+ seed: string;
22+ keys: WalletNodeKeys;
23+ derivationPath: string;
24+}
25+
26+interface WalletConfig {
27+ ticket: string;
28+ ship: number;
29+ passphrase?: string;
30+ boot?: boolean;
31+}
32+
33+interface UrbitWallet {
34+ meta: {
35+ generator: {
36+ name: string;
37+ version: string;
38+ };
39+ spec: string;
40+ ship: string;
41+ patp: string;
42+ tier: string;
43+ passphrase: string;
44+ };
45+ masterSeed: string;
46+ ownership: WalletNode;
47+ management: WalletNode;
48+ transfer: WalletNode;
49+ network:
50+ | {
51+ type: string;
52+ seed: string;
53+ keys: string;
54+ }
55+ | {};
56+ voting?: WalletNode;
57+ spawn?: WalletNode;
58+}
59+
1260declare module 'urbit-key-generation' {
1361 function deriveNetworkKeys(hex: string): NetworkKeys;
62+ function generateWallet({
63+ ticket,
64+ ship,
65+ passphrase,
66+ boot,
67+ }: WalletConfig): UrbitWallet;
1468}
src/views/Activate/ActivateCodeForm.tsx+8−9
@@ -18,10 +18,9 @@ import { useActivateFlow } from './ActivateFlow';
1818import WarningBox from 'components/WarningBox';
1919import useRoller from 'lib/useRoller';
2020import useImpliedTicket from 'lib/useImpliedTicket';
21−import { generateOwnershipWallet } from 'lib/walletgen';
22−import { generateWallet } from 'lib/invite';
23−import { walletFromMnemonic } from 'lib/wallet';
24−import { DEFAULT_HD_PATH, POINT_PROXIES } from 'lib/constants';
21+import { generateWallet } from 'lib/walletgen';
22+import { urbitWalletFromTicket } from 'lib/wallet';
23+import { POINT_PROXIES } from 'lib/constants';
2524import useBreakpoints from 'lib/useBreakpoints';
2625import { Ship } from '@urbit/roller-api';
2726import { convertToInt } from 'lib/convertToInt';
@@ -109,11 +108,11 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
109108 const { ticket, point } = getTicketAndPoint(
110109 impliedTicket || values.ticket
111110 );
112− const { seed } = await generateOwnershipWallet(point, ticket);
113− const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
111+
112+ const inviteWallet = Just(await urbitWalletFromTicket(ticket, point));
114113 setInviteWallet(inviteWallet);
115− const _inviteWallet = need.wallet(inviteWallet);
116− const inviteAddress = _inviteWallet.address;
114+ const _inviteWallet: UrbitWallet = need.wallet(inviteWallet);
115+ const inviteAddress = _inviteWallet.ownership.keys.address;
117116
118117 let incoming = await loadPoints(inviteAddress);
119118
@@ -136,7 +135,7 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
136135 setDerivedPatp(Just(ob.patp(point)));
137136 setDerivedPoint(Just(point));
138137 setDerivedPointDominion(Just(rollerPoint.dominion));
139− setDerivedWallet(Just(await generateWallet(point, true)));
138+ setDerivedWallet(Just(await generateWallet(point, ticket, true)));
140139 setIsIn(false);
141140 await timeout(100);
142141 } else {
src/views/Activate/MasterKeyDownload.tsx+2−2
@@ -1,7 +1,6 @@
11import React, { useCallback, useEffect, useMemo, useState } from 'react';
22import { Just, Nothing } from 'folktale/maybe';
33import * as need from 'lib/need';
4−import * as ob from 'urbit-ob';
54
65import { ActivateSteps } from './ActivateSteps';
76import { Box } from '@tlon/indigo-react';
@@ -24,6 +23,7 @@ const MasterKeyDownload = () => {
2423 const {
2524 derivedPoint,
2625 derivedWallet,
26+ inviteWallet,
2727 isIn,
2828 setGenerated,
2929 setIsIn,
@@ -47,7 +47,7 @@ const MasterKeyDownload = () => {
4747
4848 const { download } = useMultikeyFileGenerator({
4949 point,
50− seedWallet: derivedWallet,
50+ seedWallet: inviteWallet,
5151 });
5252
5353 // sync paper value to activation state
src/worker/worker.ts+1−1
@@ -1,6 +1,6 @@
11import kg from 'urbit-key-generation';
22
3−export async function generateWallet(data: string): Promise<string> {
3+export async function generateWallet(data: string): Promise<UrbitWallet> {
44 // Process the data without stalling the UI
55 const config = JSON.parse(data);
66 const wallet = await kg.generateWallet(config);