Improve Federation start order

This commit is contained in:
KoalaSat 2024-03-28 23:21:15 +01:00 committed by Reckless_Satoshi
parent a1c63ca622
commit d8490d7530
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
9 changed files with 33 additions and 113 deletions

View File

@ -226,7 +226,6 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
value={settings.network} value={settings.network}
onChange={(e, network) => { onChange={(e, network) => {
setSettings({ ...settings, network }); setSettings({ ...settings, network });
void federation.updateUrls(origin, { ...settings, network }, hostUrl);
systemClient.setItem('settings_network', network); systemClient.setItem('settings_network', network);
}} }}
> >

View File

@ -9,7 +9,7 @@ import React, {
type ReactNode, type ReactNode,
} from 'react'; } from 'react';
import { type Order, Federation } from '../models'; import { type Order, Federation, Settings } from '../models';
import { federationLottery } from '../utils'; import { federationLottery } from '../utils';
@ -62,7 +62,7 @@ export interface UseFederationStoreType {
} }
export const initialFederationContext: UseFederationStoreType = { export const initialFederationContext: UseFederationStoreType = {
federation: new Federation(), federation: new Federation('onion', new Settings(), ''),
sortedCoordinators: [], sortedCoordinators: [],
setDelay: () => {}, setDelay: () => {},
currentOrderId: { id: null, shortAlias: null }, currentOrderId: { id: null, shortAlias: null },
@ -80,7 +80,7 @@ export const FederationContextProvider = ({
const { settings, page, origin, hostUrl, open, torStatus } = const { settings, page, origin, hostUrl, open, torStatus } =
useContext<UseAppStoreType>(AppContext); useContext<UseAppStoreType>(AppContext);
const { setMaker, garage, setBadOrder } = useContext<UseGarageStoreType>(GarageContext); const { setMaker, garage, setBadOrder } = useContext<UseGarageStoreType>(GarageContext);
const [federation, setFederation] = useState(initialFederationContext.federation); const [federation] = useState(new Federation(origin, settings, hostUrl));
const sortedCoordinators = useMemo(() => federationLottery(federation), []); const sortedCoordinators = useMemo(() => federationLottery(federation), []);
const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState<string>( const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState<string>(
new Date().toISOString(), new Date().toISOString(),
@ -102,20 +102,19 @@ export const FederationContextProvider = ({
setMaker((maker) => { setMaker((maker) => {
return { ...maker, coordinator: sortedCoordinators[0] }; return { ...maker, coordinator: sortedCoordinators[0] };
}); // default MakerForm coordinator is decided via sorted lottery }); // default MakerForm coordinator is decided via sorted lottery
federation.registerHook('onFederationUpdate', () => {
setFederationUpdatedAt(new Date().toISOString());
});
federation.registerHook('onCoordinatorUpdate', () => {
setCoordinatorUpdatedAt(new Date().toISOString());
});
}, []); }, []);
useEffect(() => { useEffect(() => {
// On bitcoin network change we reset book, limits and federation info and fetch everything again // On bitcoin network change we reset book, limits and federation info and fetch everything again
if (window.NativeRobosats === undefined || torStatus === 'ON') { if (window.NativeRobosats === undefined || torStatus === 'ON') {
const newFed = initialFederationContext.federation; void federation.updateUrl(origin, settings, hostUrl);
newFed.registerHook('onFederationUpdate', () => { void federation.update();
setFederationUpdatedAt(new Date().toISOString());
});
newFed.registerHook('onCoordinatorUpdate', () => {
setCoordinatorUpdatedAt(new Date().toISOString());
});
void newFed.start(origin, settings, hostUrl);
setFederation(newFed);
} }
}, [settings.network, torStatus]); }, [settings.network, torStatus]);

View File

@ -97,7 +97,7 @@ function calculateSizeLimit(inputDate: Date): number {
} }
export class Coordinator { export class Coordinator {
constructor(value: any) { constructor(value: any, origin: Origin, settings: Settings, hostUrl: string) {
const established = new Date(value.established); const established = new Date(value.established);
this.longAlias = value.longAlias; this.longAlias = value.longAlias;
this.shortAlias = value.shortAlias; this.shortAlias = value.shortAlias;
@ -115,6 +115,8 @@ export class Coordinator {
this.testnetNodesPubkeys = value.testnetNodesPubkeys; this.testnetNodesPubkeys = value.testnetNodesPubkeys;
this.url = ''; this.url = '';
this.basePath = ''; this.basePath = '';
this.updateUrl(origin, settings, hostUrl);
} }
// These properties are loaded from federation.json // These properties are loaded from federation.json
@ -145,22 +147,7 @@ export class Coordinator {
public loadingLimits: boolean = false; public loadingLimits: boolean = false;
public loadingRobot: boolean = true; public loadingRobot: boolean = true;
start = async ( updateUrl = (origin: Origin, settings: Settings, hostUrl: string): void => {
origin: Origin,
settings: Settings,
hostUrl: string,
onUpdate: (shortAlias: string) => void = () => {},
): Promise<void> => {
if (this.enabled !== true) return;
void this.updateUrl(settings, origin, hostUrl, onUpdate);
};
updateUrl = async (
settings: Settings,
origin: Origin,
hostUrl: string,
onUpdate: (shortAlias: string) => void = () => {},
): Promise<void> => {
if (settings.selfhostedClient && this.shortAlias !== 'local') { if (settings.selfhostedClient && this.shortAlias !== 'local') {
this.url = hostUrl; this.url = hostUrl;
this.basePath = `/${settings.network}/${this.shortAlias}`; this.basePath = `/${settings.network}/${this.shortAlias}`;
@ -168,9 +155,6 @@ export class Coordinator {
this.url = String(this[settings.network][origin]); this.url = String(this[settings.network][origin]);
this.basePath = ''; this.basePath = '';
} }
void this.update(() => {
onUpdate(this.shortAlias);
});
}; };
update = async (onUpdate: (shortAlias: string) => void = () => {}): Promise<void> => { update = async (onUpdate: (shortAlias: string) => void = () => {}): Promise<void> => {
@ -205,7 +189,6 @@ export class Coordinator {
apiClient apiClient
.get(this.url, `${this.basePath}/api/book/`) .get(this.url, `${this.basePath}/api/book/`)
.then((data) => { .then((data) => {
console.log('BOOK', data);
if (!data?.not_found) { if (!data?.not_found) {
this.book = (data as PublicOrder[]).map((order) => { this.book = (data as PublicOrder[]).map((order) => {
order.coordinatorShortAlias = this.shortAlias; order.coordinatorShortAlias = this.shortAlias;

View File

@ -14,14 +14,14 @@ import { updateExchangeInfo } from './Exchange.model';
type FederationHooks = 'onCoordinatorUpdate' | 'onFederationUpdate'; type FederationHooks = 'onCoordinatorUpdate' | 'onFederationUpdate';
export class Federation { export class Federation {
constructor() { constructor(origin: Origin, settings: Settings, hostUrl: string) {
this.coordinators = Object.entries(defaultFederation).reduce( this.coordinators = Object.entries(defaultFederation).reduce(
(acc: Record<string, Coordinator>, [key, value]: [string, any]) => { (acc: Record<string, Coordinator>, [key, value]: [string, any]) => {
if (getHost() !== '127.0.0.1:8000' && key === 'local') { if (getHost() !== '127.0.0.1:8000' && key === 'local') {
// Do not add `Local Dev` unless it is running on localhost // Do not add `Local Dev` unless it is running on localhost
return acc; return acc;
} else { } else {
acc[key] = new Coordinator(value); acc[key] = new Coordinator(value, origin, settings, hostUrl);
return acc; return acc;
} }
}, },
@ -36,7 +36,16 @@ export class Federation {
onCoordinatorUpdate: [], onCoordinatorUpdate: [],
onFederationUpdate: [], onFederationUpdate: [],
}; };
this.loading = true; this.loading = true;
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
const host = getHost();
const url = `${window.location.protocol}//${host}`;
const tesnetHost = Object.values(this.coordinators).find((coor) => {
return Object.values(coor.testnet).includes(url);
});
if (tesnetHost) settings.network = 'testnet';
} }
public coordinators: Record<string, Coordinator>; public coordinators: Record<string, Coordinator>;
@ -69,38 +78,10 @@ export class Federation {
this.triggerHook('onFederationUpdate'); this.triggerHook('onFederationUpdate');
}; };
// Setup updateUrl = async (origin: Origin, settings: Settings, hostUrl: string): Promise<void> => {
start = async (origin: Origin, settings: Settings, hostUrl: string): Promise<void> => {
const onCoordinatorStarted = (): void => {
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
this.onCoordinatorSaved();
};
this.loading = true;
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
const host = getHost();
const url = `${window.location.protocol}//${host}`;
const tesnetHost = Object.values(this.coordinators).find((coor) => {
return Object.values(coor.testnet).includes(url);
});
if (tesnetHost) settings.network = 'testnet';
for (const coor of Object.values(this.coordinators)) { for (const coor of Object.values(this.coordinators)) {
if (coor.enabled) { coor.updateUrl(origin, settings, hostUrl);
await coor.start(origin, settings, hostUrl, onCoordinatorStarted);
}
} }
this.updateEnabledCoordinators();
};
// On Testnet/Mainnet change
updateUrls = async (origin: Origin, settings: Settings, hostUrl: string): Promise<void> => {
this.loading = true;
for (const coor of Object.values(this.coordinators)) {
await coor.updateUrl(settings, origin, hostUrl);
}
this.loading = false;
}; };
update = async (): Promise<void> => { update = async (): Promise<void> => {
@ -115,9 +96,12 @@ export class Federation {
lifetime_volume: 0, lifetime_volume: 0,
version: { major: 0, minor: 0, patch: 0 }, version: { major: 0, minor: 0, patch: 0 },
}; };
this.exchange.onlineCoordinators = 0;
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length; this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
this.updateEnabledCoordinators();
for (const coor of Object.values(this.coordinators)) { for (const coor of Object.values(this.coordinators)) {
await coor.update(() => { await coor.update(() => {
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
this.onCoordinatorSaved(); this.onCoordinatorSaved();
}); });
} }

View File

@ -15,7 +15,7 @@ export interface ReactNativeWebView {
export interface NativeWebViewMessageHttp { export interface NativeWebViewMessageHttp {
id?: number; id?: number;
category: 'http'; category: 'http';
type: 'post' | 'get' | 'put' | 'delete' | 'xhr'; type: 'post' | 'get' | 'put' | 'delete';
path: string; path: string;
baseUrl: string; baseUrl: string;
headers?: object; headers?: object;

View File

@ -90,41 +90,6 @@ class ApiNativeClient implements ApiClient {
headers: this.getHeaders(auth), headers: this.getHeaders(auth),
}).then(this.parseResponse); }).then(this.parseResponse);
}; };
public fileImageUrl: (baseUrl: string, path: string) => Promise<string | undefined> = async (
baseUrl,
path,
) => {
if (path === '') {
return await Promise.resolve('');
}
if (this.assetsCache[path] != null) {
return await Promise.resolve(this.assetsCache[path]);
} else if (this.assetsPromises.has(path)) {
return await this.assetsPromises.get(path);
}
this.assetsPromises.set(
path,
new Promise<string>((resolve, reject) => {
window.NativeRobosats?.postMessage({
category: 'http',
type: 'xhr',
baseUrl,
path,
})
.then((fileB64: { b64Data: string }) => {
this.assetsCache[path] = `data:image/png;base64,${fileB64.b64Data}`;
this.assetsPromises.delete(path);
resolve(this.assetsCache[path]);
})
.catch(reject);
}),
);
return await this.assetsPromises.get(path);
};
} }
export default ApiNativeClient; export default ApiNativeClient;

View File

@ -11,7 +11,6 @@ export interface ApiClient {
put: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>; put: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>;
get: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>; get: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>;
delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>; delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>;
fileImageUrl?: (baseUrl: string, path: string) => Promise<string | undefined>;
} }
export const apiClient: ApiClient = export const apiClient: ApiClient =

View File

@ -23,7 +23,6 @@ const App = () => {
useEffect(() => { useEffect(() => {
TorModule.start(); TorModule.start();
DeviceEventEmitter.addListener('TorStatus', (payload) => { DeviceEventEmitter.addListener('TorStatus', (payload) => {
console.log(payload.torStatus);
if (payload.torStatus === 'OFF') TorModule.restart(); if (payload.torStatus === 'OFF') TorModule.restart();
injectMessage({ injectMessage({
category: 'system', category: 'system',
@ -119,14 +118,6 @@ const App = () => {
}) })
.catch((e) => onCatch(data.id, e)) .catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus); .finally(TorModule.getTorStatus);
} else if (data.type === 'xhr') {
torClient
.request(data.baseUrl, data.path)
.then((response: object) => {
injectMessageResolve(data.id, response);
})
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
} }
} else if (data.category === 'system') { } else if (data.category === 'system') {
if (data.type === 'init') { if (data.type === 'init') {

View File

@ -85,7 +85,7 @@ public class TorModule extends ReactContextBaseJavaModule {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
if (response.code() != 200) { if (response.code() != 200 && response.code() != 201) {
Log.d("RobosatsError", "Request error code: " + response.code()); Log.d("RobosatsError", "Request error code: " + response.code());
} else if (response.isSuccessful()) { } else if (response.isSuccessful()) {
promise.resolve("{\"json\":" + body + ", \"headers\": " + headersJson +"}"); promise.resolve("{\"json\":" + body + ", \"headers\": " + headersJson +"}");