Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qr scanners and codes for passing around guardian links / verification codes. #435

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
49fb445
chore: api rotation
Kodylow Jul 22, 2024
7893518
fix: bump docker-compose versions for 0.4
Kodylow Jul 22, 2024
ad6dc5f
fix: env vars for 0.4
Kodylow Jul 22, 2024
abdc565
chore: add user shell
Kodylow Jul 22, 2024
24cc84d
chore: default ports
Kodylow Jul 22, 2024
5220dd6
feat: rotation stuff for docker compose
Kodylow Jul 22, 2024
3afb0c0
fix: fix restart script for overwriting docker-compose
Kodylow Jul 22, 2024
2dcd34a
fix: hold
Kodylow Jul 22, 2024
4523ed9
fix: hold
Kodylow Jul 22, 2024
84db528
fix: fix
Kodylow Jul 24, 2024
e71f23c
fix: hold
Kodylow Jul 29, 2024
7f7f368
fix: fix
Kodylow Jul 29, 2024
bef9dd2
fix: initial api announcement modal on open
Kodylow Jul 29, 2024
21f1789
fix: sign new api announcement working
Kodylow Jul 30, 2024
4c26bec
fix: edit for signing
Kodylow Jul 30, 2024
807bf01
fix: working confirmed api rotation
Kodylow Jul 30, 2024
bee4720
docs: add testing instructions for end to end and api rotation
Kodylow Jul 30, 2024
63a5dc0
docs: final clean instructions
Kodylow Jul 30, 2024
26e38e4
fix: coderabbit recs
Kodylow Jul 30, 2024
c8df163
feat: qr for leader ws link
Kodylow Jun 14, 2024
62766bc
fix: merge
Kodylow Jul 12, 2024
807aa4d
fix: fix
Kodylow Jul 12, 2024
bb7034b
feat: verify guardian scanners
Kodylow Jun 15, 2024
37b9e21
fix: rebase
Kodylow Jul 1, 2024
7736e97
fix: fix
Kodylow Jul 9, 2024
b5ef738
feat: working stuff
Kodylow Jul 9, 2024
aa30b71
Update scripts/mprocs-nix.sh
Kodylow Jul 13, 2024
4abf4fd
fix: fix
Kodylow Jul 28, 2024
2594afb
fix: destroy scanner on close;
Kodylow Jul 28, 2024
0869f7d
fix: paste button
Kodylow Jul 28, 2024
ab736cb
fix: dont show scan if already valid verification code
Kodylow Jul 31, 2024
42a2b22
fix: add copy button
Kodylow Jul 31, 2024
15a1044
fix: copied on copy button click
Kodylow Jul 31, 2024
b1c1d4d
fix: correct modal for qr vs scanner
Kodylow Jul 31, 2024
35bb1f8
Update testing.md
Kodylow Jul 31, 2024
8e4af40
Update testing.md
Kodylow Jul 31, 2024
0f2d70a
Update testing.md
Kodylow Jul 31, 2024
d28e2f6
Update testing.md
Kodylow Jul 31, 2024
6de90cd
Update testing.md
Kodylow Jul 31, 2024
5af2e1f
feat: new verify guardians scanner
Kodylow Jul 31, 2024
3c37e22
fix: working sethas
Kodylow Jul 31, 2024
3537965
refactor: simplified VerifyGuardians step
Kodylow Aug 1, 2024
932049f
fix: dont auto open api announcement required before signing at all
Kodylow Aug 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions JUSTFILE
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ default:
@just --list
mprocs:
mprocs -c mprocs.yml
rotate:
mprocs -c mprocs-rotate.yml
restart:
docker compose down && echo 'Removing fm dirs' && sudo rm -rf fm_* && echo 'Done' && mprocs -c mprocs.yml
gateway:
yarn nix-gateway
guardian:
yarn nix-guardian
reset dc:
cp original-docker-compose.yml docker-compose.yml && docker compose down -v && echo 'Removing fm dirs' && sudo rm -rf fm_* && echo 'Done'
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ This project includes the following apps / packages:
- `eslint-config`: Shared `eslint` configurations (includes `eslint-plugin-react` and `eslint-config-prettier`)
- `tsconfig`: Shared `tsconfig.json`s used throughout Fedimint UI apps

## Testing

For detailed testing instructions, please refer to the [testing.md](./testing.md) file in this repository. It contains comprehensive information on how to set up and run local tests for the Fedimint UI projects.

## Version Policy

Fedimint UI releases use semantic versioning (`major.minor.patch`)
Expand Down
1 change: 1 addition & 0 deletions apps/guardian-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"framer-motion": "^6",
"jsonrpc-client-websocket": "^1.5.2",
"node": "^20.1.0",
"qr-scanner": "^1.4.2",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
6 changes: 3 additions & 3 deletions apps/guardian-ui/src/GuardianApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export class GuardianApi {
public signApiAnnouncement = async (
newUrl: string
): Promise<SignedApiAnnouncement> => {
return this.call(AdminRpc.signApiAnnouncement, { new_url: newUrl });
return this.call(AdminRpc.signApiAnnouncement, newUrl);
};

public shutdown = async (session?: number): Promise<void> => {
Expand Down Expand Up @@ -314,14 +314,14 @@ export class GuardianApi {
): Promise<T> => {
try {
const websocket = await this.connect();
console.log('method', method);
// console.log('method', method);
const response = await websocket.call(method, [
{
auth: this.getPassword() || null,
params,
},
]);
console.log('response', response);
// console.log('response', response);

if (response.error) {
throw response.error;
Expand Down
123 changes: 97 additions & 26 deletions apps/guardian-ui/src/admin/FederationAdmin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Flex, Box, Heading, Skeleton } from '@chakra-ui/react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Flex, Box, Heading, Skeleton, useDisclosure } from '@chakra-ui/react';
import {
ClientConfig,
SignedApiAnnouncement,
Expand All @@ -15,6 +15,38 @@ import { InviteCode } from '../components/dashboard/admin/InviteCode';
import { FederationTabsCard } from '../components/dashboard/tabs/FederationTabsCard';
import { BftInfo } from '../components/BftInfo';
import { DangerZone } from '../components/dashboard/danger/DangerZone';
import { SignApiAnnouncement } from '../components/dashboard/danger/SignApiAnnouncement';
import { normalizeUrl } from '../utils';

const checkAnnouncementNeeded = (
currentApiUrl: string,
currentAnnouncement: SignedApiAnnouncement['api_announcement'] | undefined,
setAnnouncementNeeded: React.Dispatch<React.SetStateAction<boolean>>,
onOpen: () => void
) => {
try {
if (currentAnnouncement) {
console.log('currentAnnouncement', currentAnnouncement.api_url);
console.log('currentApiUrl', currentApiUrl);
const announcementMatches =
normalizeUrl(currentAnnouncement.api_url) ===
normalizeUrl(currentApiUrl);
setAnnouncementNeeded(!announcementMatches);
if (!announcementMatches) {
onOpen();
}
}
} catch (error) {
console.error('Error checking announcement:', error);
}
};

const findOurPeerId = (
configPeerIds: number[],
statusPeerIds: number[]
): number | undefined => {
return configPeerIds.find((id) => !statusPeerIds.includes(id));
};

export const FederationAdmin: React.FC = () => {
const { api } = useAdminContext();
Expand All @@ -26,40 +58,65 @@ export const FederationAdmin: React.FC = () => {
>({});
const [ourPeer, setOurPeer] = useState<{ id: number; name: string }>();
const [latestSession, setLatestSession] = useState<number>();
// API announcement modal
const { isOpen, onOpen, onClose } = useDisclosure();
const [announcementNeeded, setAnnouncementNeeded] = useState(false);
const checkAnnouncementRef = useRef(false);

// Extracting our peer ID and name from intersection of config and status
useEffect(() => {
if (config && status?.federation) {
const peerIds = Object.keys(status.federation.status_by_peer).map((id) =>
parseInt(id, 10)
if (announcementNeeded) {
onOpen();
}
}, [announcementNeeded, onOpen]);

const fetchData = useCallback(() => {
api.inviteCode().then(setInviteCode).catch(console.error);
api.config().then(setConfig).catch(console.error);
api.apiAnnouncements().then(setSignedApiAnnouncements).catch(console.error);
api
.status()
.then((statusData) => {
setStatus(statusData);
setLatestSession(statusData?.federation?.session_count);
})
.catch(console.error);
}, [api]);

useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 5000);
return () => clearInterval(interval);
}, [fetchData]);

useEffect(() => {
if (config && status?.federation && !checkAnnouncementRef.current) {
const statusPeerIds = Object.keys(status.federation.status_by_peer).map(
(id) => parseInt(id, 10)
);
const configPeerIds = Object.keys(config.global.api_endpoints).map((id) =>
parseInt(id, 10)
);
// Finding our peer ID as the one present in config but not in status
const ourPeerId = configPeerIds.find((id) => !peerIds.includes(id));

const ourPeerId = findOurPeerId(configPeerIds, statusPeerIds);
if (ourPeerId !== undefined) {
setOurPeer({
id: ourPeerId,
name: config.global.api_endpoints[ourPeerId].name,
});
const currentApiUrl = process.env.REACT_APP_FM_CONFIG_API || '';
const currentAnnouncement =
signedApiAnnouncements[ourPeerId.toString()]?.api_announcement;

checkAnnouncementNeeded(
currentApiUrl,
currentAnnouncement,
setAnnouncementNeeded,
onOpen
);
checkAnnouncementRef.current = true;
}
const latestSession = status?.federation?.session_count;
setLatestSession(latestSession);
}
}, [config, status]);

useEffect(() => {
api.inviteCode().then(setInviteCode).catch(console.error);
api.config().then(setConfig).catch(console.error);
const fetchStatus = () => {
api.status().then(setStatus).catch(console.error);
};
api.apiAnnouncements().then(setSignedApiAnnouncements).catch(console.error);
fetchStatus();
const interval = setInterval(fetchStatus, 5000);
return () => clearInterval(interval);
}, [api]);
}, [config, status, signedApiAnnouncements, onOpen]);

return (
<Flex gap='32px' flexDirection='row'>
Expand Down Expand Up @@ -109,16 +166,30 @@ export const FederationAdmin: React.FC = () => {
signedApiAnnouncements={signedApiAnnouncements}
/>
<GatewaysCard config={config} />
{ourPeer ? (
<FederationTabsCard config={config} ourPeer={ourPeer} />
) : null}
{ourPeer && (
<FederationTabsCard
config={config}
ourPeer={ourPeer}
signedApiAnnouncements={signedApiAnnouncements}
/>
)}
<DangerZone
inviteCode={inviteCode}
ourPeer={ourPeer}
latestSession={latestSession}
signedApiAnnouncements={signedApiAnnouncements}
onApiAnnouncementOpen={onOpen}
/>
</Flex>
{ourPeer && (
<SignApiAnnouncement
isOpen={isOpen}
onClose={onClose}
ourPeer={ourPeer}
signedApiAnnouncements={signedApiAnnouncements}
currentApiUrl={process.env.REACT_APP_FM_CONFIG_API || ''}
/>
)}
</Flex>
);
};
14 changes: 14 additions & 0 deletions apps/guardian-ui/src/assets/svgs/scan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,10 @@ export const BitcoinNodeCard: React.FC<Props> = ({ modulesConfigs }) => {
key: 'network',
label: t('federation-dashboard.bitcoin-node.network-label'),
value: walletConfig ? (
<Text as='span'>
{walletConfig.network && (
<NetworkIndicator
network={walletConfig.network}
bitcoinRpcUrl={walletConfig.default_bitcoin_rpc?.url}
/>
)}
</Text>
<NetworkIndicator
network={walletConfig.network}
bitcoinRpcUrl={walletConfig.default_bitcoin_rpc?.url}
/>
) : (
<Skeleton height='24px' width='100px' />
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import { useTranslation } from '@fedimint/utils';
import { ReactComponent as CopyIcon } from '../../../assets/svgs/copy.svg';
import { ReactComponent as QrIcon } from '../../../assets/svgs/qr.svg';
import QRCode from 'qrcode.react';

const QR_CODE_SIZE = 256;
import { QR_CODE_SIZE } from '../../../utils/constants';

interface InviteCodeProps {
inviteCode: string;
Expand Down
30 changes: 22 additions & 8 deletions apps/guardian-ui/src/components/dashboard/danger/DangerZone.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import React, { useState } from 'react';
import { Box, Text, Flex, theme, Collapse, IconButton } from '@chakra-ui/react';
import {
Box,
Text,
Flex,
theme,
Collapse,
IconButton,
Button,
} from '@chakra-ui/react';
import { GuardianAuthenticationCode } from './GuardianAuthenticationCode';
import { DownloadBackup } from './DownloadBackup';
import { useTranslation } from '@fedimint/utils';
import { ReactComponent as ChevronDownIcon } from '../../../assets/svgs/chevron-down.svg';
import { ReactComponent as ChevronUpIcon } from '../../../assets/svgs/chevron-up.svg';
import { ScheduleShutdown } from './ScheduleShutdown';
import { SignedApiAnnouncement } from '@fedimint/types';
import { SignApiAnnouncement } from './SignApiAnnouncement';

interface DangerZoneProps {
ourPeer: { id: number; name: string } | undefined;
inviteCode: string;
latestSession: number | undefined;
signedApiAnnouncements: Record<string, SignedApiAnnouncement>;
onApiAnnouncementOpen: () => void;
}

export const DangerZone: React.FC<DangerZoneProps> = ({
ourPeer,
inviteCode,
latestSession,
signedApiAnnouncements,
onApiAnnouncementOpen,
}) => {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -59,18 +67,24 @@ export const DangerZone: React.FC<DangerZoneProps> = ({
</Flex>
<Collapse in={isOpen} animateOpacity>
<Flex direction={['column', 'row']} alignItems='center' gap='6px'>
{ourPeer !== undefined && (
{ourPeer && (
<GuardianAuthenticationCode
ourPeer={ourPeer}
inviteCode={inviteCode}
/>
)}
<DownloadBackup />
{ourPeer && (
<SignApiAnnouncement
ourPeer={ourPeer}
signedApiAnnouncements={signedApiAnnouncements}
/>
<Button
size={['sm', 'md']}
bg={theme.colors.red[500]}
_hover={{ bg: theme.colors.red[600] }}
onClick={onApiAnnouncementOpen}
>
{t(
'federation-dashboard.danger-zone.sign-api-announcement.label'
)}
</Button>
)}
{latestSession && <ScheduleShutdown latestSession={latestSession} />}
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from '@fedimint/utils';
import QRCode from 'qrcode.react';
import { QR_CODE_SIZE } from '../../../utils/constants';

const QR_CODE_SIZE = 256;
const FEDIMINT_GUARDIAN_PREFIX = 'fedimint:guardian:';

type GuardianAuth = {
Expand Down Expand Up @@ -89,7 +89,7 @@ export const GuardianAuthenticationCode: React.FC<
>
<Text mb={4}>
{t(
'federation-dashboard.danger-zone.guardian-warning-message',
'federation-dashboard.danger-zone.guardian-warning-message'
)}
</Text>
<Flex
Expand All @@ -113,7 +113,7 @@ export const GuardianAuthenticationCode: React.FC<
}}
>
{t(
'federation-dashboard.danger-zone.acknowledge-and-download',
'federation-dashboard.danger-zone.acknowledge-and-download'
)}
</Button>
</Flex>
Expand All @@ -135,7 +135,7 @@ export const GuardianAuthenticationCode: React.FC<
/>
<Text fontWeight={'bold'} color={'red'} mt={4}>
{t(
'federation-dashboard.danger-zone.guardian-connect-warning',
'federation-dashboard.danger-zone.guardian-connect-warning'
)}
</Text>
</Flex>
Expand Down
Loading
Loading