mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-17 09:34:16 -05:00
* mvp * only redial if DisconnectedAndNotDialing * progress * progress * progress * some progress * progress * extract common logic into behaviour_util::ConnectionTracker * extract common logic into BackoffTracker helper struct * add comment in quote::background behaviour * BackoffTracker rename get_backoff to get(...) * cleanup, fix some things that got lost during rebase * properly propagate quote::background ToSwarm events * actually persist event loop, add quotes and rendezvous::discovery to cli behaviour * some progress, cleanup, comments * progress * redial all peers that we dont know dont support quote, use quotes_cached behaviour in example, add remove_peer(...) to redial behaviour, don't redial discovered rendezvous peers * remove old todo * quotes_cached.rs: cache last connected endpoint * rename: add_peer_address -> queue_peer_address * extract p2p defaults into swap-p2p/defaults.rs * split rendezvous.rs into two sub-modules * remove unused bob::BackgroundQuoteReceived * replace usage of list_sellers with event loop * prune: remove list_sellers command * use backoff helper in swap-p2p/src/protocols/quotes.rs * refactor rendezvous::register behaviour, getting some unit tests working again * add all peer addresses to the swarm on init * less agressive redials * extract magic backoff numbers * proof of concept: drill tracing span into event loop through channels * add BackoffTracker::increment, re-schedule register when we lose connection to rendezvous point * fetch identify version and propagate into UI * forbid private/local/loopback ip addresses to be shared/accepted through Identify * remove legacy list_sellers code * ensure uuids are unique for alice during swap_setup * formatting and nitpicks * fix: allow multiple swap setup requests over the same connection handler * small cleanups * fix: protocols/quotes.rs unit tests * revert: listen on 0.0.0.0 for asb p2p * propagate handle_pending_inbound_connection and handle_pending_outbound_connection to identify patch source * replace loop with repeated return Poll::Ready in discovery.rs * format * MultiAddrVecExt trait, emit rendezvous addresses to rendezvous-node swarm * fix: strictly disallow concurrent swap setup requests for the same swap on the same connection * fix tests etc * remove slop from futures_util.rs * address some comments * behaviour_util.rs: track inflight dials, add tests, return Some(peer_id) if internal state was changed * replace boring-avatars with jidenticon * feat: add peer discovery status dialog * remove buy-xmr cli command, remove "sellers" arg for BuyXmrArgs, add changelog * disable body overscroll * add changelog for jidenticon * increase quote fetch interval to 45s * fix rendezvous::register_and_discover_together test
202 lines
6.4 KiB
TypeScript
202 lines
6.4 KiB
TypeScript
import { listen } from "@tauri-apps/api/event";
|
|
import { TauriEvent } from "models/tauriModel";
|
|
import {
|
|
contextStatusEventReceived,
|
|
contextInitializationFailed,
|
|
timelockChangeEventReceived,
|
|
approvalEventReceived,
|
|
backgroundProgressEventReceived,
|
|
} from "store/features/rpcSlice";
|
|
import { setBitcoinBalance } from "store/features/bitcoinWalletSlice";
|
|
import { receivedCliLog } from "store/features/logsSlice";
|
|
import { poolStatusReceived } from "store/features/poolSlice";
|
|
import { swapProgressEventReceived } from "store/features/swapSlice";
|
|
import {
|
|
quotesProgressReceived,
|
|
connectionChangeReceived,
|
|
} from "store/features/p2pSlice";
|
|
import logger from "utils/logger";
|
|
import { fetchAllConversations, updateAlerts, updateRates } from "./api";
|
|
import {
|
|
checkContextStatus,
|
|
getSwapInfo,
|
|
getSwapTimelock,
|
|
initializeContext,
|
|
refreshApprovals,
|
|
updateAllNodeStatuses,
|
|
} from "./rpc";
|
|
import { store } from "./store/storeRenderer";
|
|
import { exhaustiveGuard } from "utils/typescriptUtils";
|
|
import {
|
|
setBalance,
|
|
setHistory,
|
|
setSyncProgress,
|
|
} from "store/features/walletSlice";
|
|
import { applyDefaultNodes } from "store/features/settingsSlice";
|
|
import {
|
|
DEFAULT_NODES,
|
|
NEGATIVE_NODES_MAINNET,
|
|
NEGATIVE_NODES_TESTNET,
|
|
} from "store/defaults";
|
|
|
|
const TAURI_UNIFIED_EVENT_CHANNEL_NAME = "tauri-unified-event";
|
|
|
|
// Update the public registry every 5 minutes
|
|
const PROVIDER_UPDATE_INTERVAL = 5 * 60 * 1_000;
|
|
|
|
// Discover peers every 5 minutes
|
|
const DISCOVER_PEERS_INTERVAL = 5 * 60 * 1_000;
|
|
|
|
// Update node statuses every 2 minutes
|
|
const STATUS_UPDATE_INTERVAL = 2 * 60 * 1_000;
|
|
|
|
// Update the exchange rate every 5 minutes
|
|
const UPDATE_RATE_INTERVAL = 5 * 60 * 1_000;
|
|
|
|
// Fetch all conversations every 10 minutes
|
|
const FETCH_CONVERSATIONS_INTERVAL = 10 * 60 * 1_000;
|
|
|
|
// Fetch pending approvals every 2 seconds
|
|
const FETCH_PENDING_APPROVALS_INTERVAL = 2 * 1_000;
|
|
|
|
// Check context status every 2 seconds
|
|
const CHECK_CONTEXT_STATUS_INTERVAL = 2 * 1_000;
|
|
|
|
function setIntervalImmediate(callback: () => void, interval: number): void {
|
|
callback();
|
|
setInterval(callback, interval);
|
|
}
|
|
|
|
export async function setupBackgroundTasks(): Promise<void> {
|
|
// Apply default nodes on startup (removes broken nodes, adds new ones)
|
|
store.dispatch(
|
|
applyDefaultNodes({
|
|
defaultNodes: DEFAULT_NODES,
|
|
negativeNodesMainnet: NEGATIVE_NODES_MAINNET,
|
|
negativeNodesTestnet: NEGATIVE_NODES_TESTNET,
|
|
}),
|
|
);
|
|
|
|
// Setup periodic fetch tasks
|
|
setIntervalImmediate(updateAllNodeStatuses, STATUS_UPDATE_INTERVAL);
|
|
setIntervalImmediate(updateRates, UPDATE_RATE_INTERVAL);
|
|
setIntervalImmediate(fetchAllConversations, FETCH_CONVERSATIONS_INTERVAL);
|
|
setIntervalImmediate(refreshApprovals, FETCH_PENDING_APPROVALS_INTERVAL);
|
|
|
|
// Fetch all alerts
|
|
updateAlerts();
|
|
|
|
// Setup Tauri event listeners
|
|
// Check if the context is already available. This is to prevent unnecessary re-initialization
|
|
setIntervalImmediate(async () => {
|
|
const contextStatus = await checkContextStatus();
|
|
store.dispatch(contextStatusEventReceived(contextStatus));
|
|
}, CHECK_CONTEXT_STATUS_INTERVAL);
|
|
|
|
const contextStatus = await checkContextStatus();
|
|
|
|
// If all components are unavailable, we need to initialize the context
|
|
if (
|
|
!contextStatus.bitcoin_wallet_available &&
|
|
!contextStatus.monero_wallet_available &&
|
|
!contextStatus.database_available &&
|
|
!contextStatus.tor_available
|
|
)
|
|
// Warning: If we reload the page while the Context is being initialized, this function will throw an error
|
|
initializeContext().catch((e) => {
|
|
logger.error(
|
|
e,
|
|
"Failed to initialize context on page load. This might be because we reloaded the page while the context was being initialized",
|
|
);
|
|
store.dispatch(contextInitializationFailed(String(e)));
|
|
});
|
|
}
|
|
|
|
// Listen for the unified event
|
|
listen<TauriEvent>(TAURI_UNIFIED_EVENT_CHANNEL_NAME, (event) => {
|
|
const { channelName, event: eventData } = event.payload;
|
|
|
|
switch (channelName) {
|
|
case "SwapProgress":
|
|
store.dispatch(swapProgressEventReceived(eventData));
|
|
break;
|
|
|
|
case "CliLog":
|
|
store.dispatch(receivedCliLog(eventData));
|
|
break;
|
|
|
|
case "BalanceChange":
|
|
store.dispatch(setBitcoinBalance(eventData.balance));
|
|
break;
|
|
|
|
case "SwapDatabaseStateUpdate":
|
|
getSwapInfo(eventData.swap_id).catch((error) => {
|
|
logger.debug(
|
|
`Failed to fetch swap info for swap ${eventData.swap_id}: ${error}`,
|
|
);
|
|
});
|
|
getSwapTimelock(eventData.swap_id).catch((error) => {
|
|
logger.debug(
|
|
`Failed to fetch timelock for swap ${eventData.swap_id}: ${error}`,
|
|
);
|
|
});
|
|
|
|
// This is ugly but it's the best we can do for now
|
|
// Sometimes we are too quick to fetch the swap info and the new state is not yet reflected
|
|
// in the database. So we wait a bit before fetching the new state
|
|
setTimeout(() => {
|
|
getSwapInfo(eventData.swap_id).catch((error) => {
|
|
logger.debug(
|
|
`Failed to fetch swap info for swap ${eventData.swap_id}: ${error}`,
|
|
);
|
|
});
|
|
getSwapTimelock(eventData.swap_id).catch((error) => {
|
|
logger.debug(
|
|
`Failed to fetch timelock for swap ${eventData.swap_id}: ${error}`,
|
|
);
|
|
});
|
|
}, 3000);
|
|
break;
|
|
|
|
case "TimelockChange":
|
|
store.dispatch(timelockChangeEventReceived(eventData));
|
|
break;
|
|
|
|
case "Approval":
|
|
store.dispatch(approvalEventReceived(eventData));
|
|
break;
|
|
|
|
case "BackgroundProgress":
|
|
store.dispatch(backgroundProgressEventReceived(eventData));
|
|
break;
|
|
|
|
case "PoolStatusUpdate":
|
|
store.dispatch(poolStatusReceived(eventData));
|
|
break;
|
|
|
|
case "MoneroWalletUpdate":
|
|
console.log("MoneroWalletUpdate", eventData);
|
|
if (eventData.type === "BalanceChange") {
|
|
store.dispatch(setBalance(eventData.content));
|
|
}
|
|
if (eventData.type === "HistoryUpdate") {
|
|
store.dispatch(setHistory(eventData.content));
|
|
}
|
|
if (eventData.type === "SyncProgress") {
|
|
store.dispatch(setSyncProgress(eventData.content));
|
|
}
|
|
break;
|
|
|
|
case "P2P":
|
|
if (eventData.type === "ConnectionChange") {
|
|
store.dispatch(connectionChangeReceived(eventData.content));
|
|
}
|
|
if (eventData.type === "QuotesProgress") {
|
|
store.dispatch(quotesProgressReceived(eventData.content.peers));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
exhaustiveGuard(channelName);
|
|
}
|
|
});
|