1import saveAs from 'file-saver';
2import ob from 'urbit-ob';
3import { Nothing } from 'folktale/maybe';
5import { useCallback, useEffect, useMemo, useState } from 'react';
6import { deriveNetworkKeys } from 'urbit-key-generation';
9 deriveNetworkSeedFromUrbitWallet,
12import { generateCode } from './networkCode';
13import { stripSigPrefix } from 'form/formatters';
14import useRoller from './useRoller';
15import Point from './types/Point';
17interface useMultiKeyfileGeneratorArgs {
19 inviteWallet: UrbitWallet;
20 inviteMasterTicketWallet: UrbitWallet;
25 * - point: the activating point
26 * - inviteWallet: determinstic invite wallet (generated during invite creation)
27 * - inviteMasterTicketWallet: the non-deterministic wallet (generated during invite acceptance);
28 * it is the wallet that receives the point transfer during Activation (and accessed by Master Ticket)
30 * This should only be used during the Activation flow,
31 * because the point will be "in between" key revisions.
32 * (0 --> 1 is when the invite is generated, 1 --> 2 is upon invite acceptance).
34 * For other keyfile use cases, see: useKeyfileGenerator
36export default function useMultiKeyfileGenerator({
39 inviteMasterTicketWallet,
40}: useMultiKeyfileGeneratorArgs) {
41 const [notice, setNotice] = useState('Deriving networking keys...');
42 const [downloaded, setDownloaded] = useState(false);
43 const [generating, setGenerating] = useState(true);
44 const [keyfile, setKeyfile] = useState<boolean | string>(false);
45 const [code, setCode] = useState(false);
46 const [pointDetails, setPointDetails] = useState<Point | null>(null);
47 const { initPoint } = useRoller();
49 const fetchPoint = useCallback(async () => {
50 setPointDetails(await initPoint(point));
51 }, [initPoint, point]);
57 const currentRevision = useMemo(
58 () => (pointDetails ? pointDetails.keyRevisionNumber : 0),
61 const nextRevision = useMemo(() => currentRevision + 1, [currentRevision]);
63 const pairFromRevision = useCallback(
64 async (revision: number, derivationWallet) => {
69 const networkSeed = await deriveNetworkSeedFromUrbitWallet({
70 urbitWallet: derivationWallet,
74 if (Nothing.hasInstance(networkSeed)) {
77 'Custom or nondeterministic networking keys cannot be re-downloaded.'
79 console.log(`seed is nondeterminable for revision ${revision}`);
83 const _networkSeed = networkSeed.value;
85 return deriveNetworkKeys(_networkSeed);
90 const hasNetworkKeys = currentRevision > 0;
92 const generate = useCallback(async () => {
93 if (!hasNetworkKeys) {
95 setNotice('Network keys not yet set.');
97 `no networking keys available for revision ${currentRevision}`
102 const currentPair = await pairFromRevision(currentRevision, inviteWallet);
104 if (!currentPair || !keysMatchChain(currentPair, pointDetails || {})) {
105 setGenerating(false);
106 setNotice('Derived networking keys do not match on-chain details.');
107 console.log(`keys do not match details for revision ${currentRevision}`);
111 // During the activation flow, we pass in the
112 // inviteMasterTicketWallet to derive the multikey
113 // During the activation flow, we want to use the invite wallet for the next pair
114 const nextPair = await pairFromRevision(
116 inviteMasterTicketWallet
120 setCode(generateCode(currentPair));
122 compileMultiKey(point, [
124 revision: currentRevision,
128 revision: nextRevision,
133 setGenerating(false);
141 inviteMasterTicketWallet,
145 const filename = useMemo(() => {
146 return `${stripSigPrefix(
148 )}-${currentRevision}-${nextRevision}.key`;
149 }, [point, currentRevision, nextRevision]);
151 const download = useCallback(() => {
152 if (typeof keyfile !== 'string') {
157 new Blob([keyfile], {
158 type: 'text/plain;charset=utf-8',
163 }, [filename, keyfile]);
179 return { ...output, bind: output };
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list