1import { useCallback, useMemo, useEffect, useState } from 'react';
2import { Just } from 'folktale/maybe';
3import saveAs from 'file-saver';
4import { useWallet } from 'store/wallet';
5import { EMPTY_POINT, useRollerStore } from 'store/rollerStore';
6import { generateCode } from 'lib/networkCode';
8 attemptNetworkSeedDerivation,
9 deriveNetworkSeedFromUrbitWallet,
14import { stripSigPrefix } from 'form/formatters';
15import useRoller from './useRoller';
16import Point from './types/Point';
17import { toL1Details } from './utils/point';
18import { ETH_ZERO_ADDR } from './constants';
20interface useKeyfileGeneratorArgs {
26 * The default Keyfile Generator. Can be used by itself, or composed with
27 * other hooks (see `useActivationKeyfileGenerator` and
28 * `useSingleKeyfileGenerator`).
30 * @param point Point - (optional) the Point for which to generate keys
31 * @param seeds string[] - (required) one or more seeds consumed by deriveNetworkKeys
34function useKeyfileGenerator({ point, seeds }: useKeyfileGeneratorArgs) {
35 // exported keyfile generator properties
36 const [notice, setNotice] = useState('Deriving networking keys...');
37 const [downloaded, setDownloaded] = useState(false);
38 const [generating, setGenerating] = useState(true);
39 const [keyfile, setKeyfile] = useState<boolean | string>(false);
40 const [code, setCode] = useState(false);
43 const { point: storedPoint } = useRollerStore();
44 const resolvedPoint = useMemo(() => {
45 return point || storedPoint;
46 }, [point, storedPoint]);
48 const networkRevision = useMemo(() => {
49 return Number(resolvedPoint.keyRevisionNumber);
51 const canManage = useMemo(() => {
52 return resolvedPoint.isOwner || resolvedPoint.isManagementProxy;
55 const hasNetworkKeys = networkRevision > 0;
56 const available = canManage && hasNetworkKeys && !!keyfile;
58 const generate = useCallback(async () => {
59 // Point still loading ...
60 if (resolvedPoint.value === EMPTY_POINT.value) {
64 if (!hasNetworkKeys) {
66 setNotice('Network keys not yet set.');
68 `no networking keys available for revision ${networkRevision}`
73 // seeds were either generated non-deterministically,
74 // or still being derived ...
75 if (seeds.length === 0) {
78 'Custom or nondeterministic networking keys cannot be re-downloaded.'
80 console.log(`seed is nondeterminable for revision ${networkRevision}`);
84 const pairs = seeds.map(seed => deriveNetworkKeys(seed));
85 const firstPair = pairs[0];
87 if (!keysMatchChain(firstPair, resolvedPoint)) {
89 setNotice('Derived networking keys do not match on-chain details.');
90 console.log(`keys do not match details for revision ${networkRevision}`);
95 setCode(generateCode(firstPair));
99 pairs.map((pair, k) => {
100 return { revision: networkRevision + k, pair };
104 setGenerating(false);
105 }, [hasNetworkKeys, seeds, resolvedPoint, networkRevision]);
107 const filename = useMemo(() => {
108 return `${stripSigPrefix(resolvedPoint.patp)}-${networkRevision}.key`;
109 }, [resolvedPoint, networkRevision]);
111 const download = useCallback(() => {
112 if (typeof keyfile !== 'string') {
117 new Blob([keyfile], {
118 type: 'text/plain;charset=utf-8',
123 }, [filename, keyfile]);
140 return { ...values, bind: values };
143interface ActivationKeyfileGeneratorArgs {
145 wallets: UrbitWallet[];
149 * This Activation Keyfile Generator is used to derive a multikey during the
150 * Activation Flow. At the time of generation, there is no Point yet set in
151 * the Roller Store, so we need to look it up by Point ID (to retreive the
152 * current key revision via API).
154 * @param pointId number - (required) the point ID for which to generate keys
155 * @param wallets UrbitWallet[] - (required) for each wallet,
156 * a seed + revision pair is generated and consumed by `compileMultiKey`.
157 * It should be an array of [deterministic invite wallet, non-deterministic
158 * master ticket wallet] (the sending and receiving wallets)
160export const useActivationKeyfileGenerator = ({
163}: ActivationKeyfileGeneratorArgs) => {
165 const { api } = useRoller();
166 const [point, setPoint] = useState<Point>(EMPTY_POINT);
168 const resolvePoint = useCallback(async () => {
169 const resolved = new Point({
171 details: toL1Details(await api.getPoint(pointId)),
172 address: ETH_ZERO_ADDR,
182 // derive seeds from wallets
183 const [seeds, setSeeds] = useState<string[]>([]);
184 const deriveSeeds = useCallback(async () => {
185 if (!('keyRevisionNumber' in point)) {
189 const derivedSeeds = await Promise.all(
190 wallets.map(async (w, i) => {
191 return deriveNetworkSeedFromUrbitWallet(w, point.keyRevisionNumber + i);
197 .map(maybeSeed => maybeSeed.getOrElse(null))
198 .filter(s => s !== null)
200 }, [point, wallets]);
204 // eslint-disable-next-line react-hooks/exhaustive-deps
207 return useKeyfileGenerator({ point, seeds });
210interface SingleKeyfileGeneratorArgs {
215 * This Single Keyfile Generator is used to derive network keys in multiple
216 * views in the Bridge dashboard (Set Network Keys, Home > ID, Home > OS).
217 * By default, it uses the logged in wallet's seed phrase.
219 * * @param seed string - (optional) default: provided by the logged in wallet
221export const useSingleKeyfileGenerator = ({
223}: SingleKeyfileGeneratorArgs) => {
224 // use provided seed, or fallback to deriving from logged in wallet
225 const { point } = useRollerStore();
226 const { authMnemonic, authToken, urbitWallet, wallet }: any = useWallet();
227 const [seeds, setSeeds] = useState<string[]>([]);
229 const deriveSeeds = useCallback(async () => {
235 const derivedSeed = await attemptNetworkSeedDerivation({
242 revision: point.keyRevisionNumber,
245 if (Just.hasInstance(derivedSeed)) {
246 setSeeds([derivedSeed.value]);
248 }, [authMnemonic, authToken, point, seed, urbitWallet, wallet]);
254 return useKeyfileGenerator({ seeds });
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