REF: blue electrum
This commit is contained in:
parent
220cd7e61d
commit
8e62aee2fc
@ -166,10 +166,6 @@ const RECONNECT_TCP_DELAY_MS = 500;
|
||||
export const ENSURE_CONNECTED_MAX_WALL_MS =
|
||||
CONNECT_MAX_ATTEMPTS * CONNECT_ATTEMPT_TIMEOUT_MS + (CONNECT_MAX_ATTEMPTS - 1) * CONNECT_BACKOFF_MS;
|
||||
|
||||
/** Backward-compat exports for callers (e.g. StorageProvider fetch budget). */
|
||||
export const WAIT_TILL_CONNECTED_MAX_WALL_MS_AFTER_FIRST = ENSURE_CONNECTED_MAX_WALL_MS;
|
||||
export const WAIT_TILL_CONNECTED_MAX_WALL_MS_NEVER = ENSURE_CONNECTED_MAX_WALL_MS;
|
||||
|
||||
/** Coalesces concurrent `ensureConnected()` callers — at most one connect attempt at a time. */
|
||||
let ensureInFlight: Promise<boolean> | null = null;
|
||||
|
||||
@ -448,8 +444,9 @@ async function pingWithTimeout(timeoutMs: number = PING_TIMEOUT_MS): Promise<boo
|
||||
export type EnsureConnectedOptions = {
|
||||
/**
|
||||
* Show the legacy "couldn't connect" alert (Try again / Reset / Cancel) on failure.
|
||||
* Used by initial bootstrap (`connectMain`) and the manual help alert. Off-hot-path
|
||||
* callers (refresh, broadcast, etc.) should leave this false and surface their own UI.
|
||||
* Used by initial bootstrap (`SettingsProvider` re-enabling Electrum) and the manual
|
||||
* help alert. Off-hot-path callers (refresh, broadcast, etc.) should leave this false
|
||||
* and surface their own UI.
|
||||
*/
|
||||
showAlertOnFailure?: boolean;
|
||||
};
|
||||
@ -480,12 +477,18 @@ export async function ensureConnected(opts: EnsureConnectedOptions = {}): Promis
|
||||
|
||||
ensureInFlight = (async (): Promise<boolean> => {
|
||||
const myGeneration = disconnectGeneration;
|
||||
/** True iff the current generation no longer matches ours (i.e. `forceDisconnect()` ran). */
|
||||
const aborted = (where: string): boolean => {
|
||||
if (myGeneration === disconnectGeneration) return false;
|
||||
console.log(`[BlueElectrum] ensureConnected aborted by forceDisconnect at ${where} (gen ${myGeneration} → ${disconnectGeneration})`);
|
||||
return true;
|
||||
};
|
||||
let lastPeer: Peer | undefined;
|
||||
try {
|
||||
// Fast path: live ping on the existing client.
|
||||
if (mainClient && connState === 'connected') {
|
||||
if (await pingWithTimeout()) {
|
||||
if (myGeneration !== disconnectGeneration) {
|
||||
if (aborted('post-ping')) {
|
||||
setConnectionState('disconnected');
|
||||
return false;
|
||||
}
|
||||
@ -499,7 +502,7 @@ export async function ensureConnected(opts: EnsureConnectedOptions = {}): Promis
|
||||
setConnectionState('disconnected');
|
||||
}
|
||||
|
||||
if (myGeneration !== disconnectGeneration) return false;
|
||||
if (aborted('pre-loop')) return false;
|
||||
setConnectionState('connecting');
|
||||
|
||||
for (let i = 0; i < CONNECT_MAX_ATTEMPTS; i++) {
|
||||
@ -507,7 +510,7 @@ export async function ensureConnected(opts: EnsureConnectedOptions = {}): Promis
|
||||
setConnectionState('disabled');
|
||||
return false;
|
||||
}
|
||||
if (myGeneration !== disconnectGeneration) {
|
||||
if (aborted(`attempt ${i} start`)) {
|
||||
setConnectionState('disconnected');
|
||||
return false;
|
||||
}
|
||||
@ -515,7 +518,7 @@ export async function ensureConnected(opts: EnsureConnectedOptions = {}): Promis
|
||||
const { ok, peer } = await attemptConnectOnce();
|
||||
lastPeer = peer;
|
||||
|
||||
if (myGeneration !== disconnectGeneration) {
|
||||
if (aborted(`attempt ${i} end`)) {
|
||||
if (mainClient) {
|
||||
try {
|
||||
mainClient.close();
|
||||
@ -548,20 +551,6 @@ export async function ensureConnected(opts: EnsureConnectedOptions = {}): Promis
|
||||
return ensureInFlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public bootstrap: connect on app start (or when user re-enables Electrum). On
|
||||
* failure presents the legacy Try-again/Reset/Cancel alert. Always resolves; never
|
||||
* throws. Internally delegates to `ensureConnected()` so concurrent callers share
|
||||
* a single attempt.
|
||||
*/
|
||||
export async function connectMain(): Promise<void> {
|
||||
if (await isDisabled()) {
|
||||
console.log('Electrum connection disabled by user. Skipping connectMain call');
|
||||
return;
|
||||
}
|
||||
await ensureConnected({ showAlertOnFailure: true });
|
||||
}
|
||||
|
||||
export async function presentResetToDefaultsAlert(): Promise<boolean> {
|
||||
const hasPreferredServer = await getPreferredServer();
|
||||
const serverHistoryStr = await DefaultPreference.get(ELECTRUM_SERVER_HISTORY);
|
||||
@ -1261,20 +1250,6 @@ export async function multiGetTransactionByTxid<T extends boolean>(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward-compatible wrapper around `ensureConnected()`. Throws on failure to
|
||||
* match the old throwing contract that existing callers' `try/catch` blocks expect.
|
||||
*
|
||||
* New code should call `ensureConnected()` directly and inspect its boolean result.
|
||||
*
|
||||
* @deprecated Use `ensureConnected()`.
|
||||
*/
|
||||
export const waitTillConnected = async function (): Promise<boolean> {
|
||||
const ok = await ensureConnected();
|
||||
if (!ok) throw new Error('Waiting for Electrum connection timeout');
|
||||
return true;
|
||||
};
|
||||
|
||||
// Returns the value at a given percentile in a sorted numeric array.
|
||||
// "Linear interpolation between closest ranks" method
|
||||
function percentile(arr: number[], p: number) {
|
||||
|
||||
@ -220,7 +220,11 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = React.m
|
||||
|
||||
useEffect(() => {
|
||||
if (walletsInitialized) {
|
||||
isElectrumDisabled ? BlueElectrum.forceDisconnect() : BlueElectrum.connectMain();
|
||||
if (isElectrumDisabled) {
|
||||
BlueElectrum.forceDisconnect();
|
||||
} else {
|
||||
BlueElectrum.ensureConnected({ showAlertOnFailure: true });
|
||||
}
|
||||
}
|
||||
}, [isElectrumDisabled, walletsInitialized]);
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import { BlueDefaultTheme } from '../../components/themes';
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
connectMain: jest.fn(),
|
||||
ensureConnected: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -12,11 +12,8 @@ afterAll(() => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
try {
|
||||
await BlueElectrum.connectMain();
|
||||
} catch (err) {
|
||||
console.log('failed to connect to Electrum:', err);
|
||||
process.exit(1);
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -21,7 +21,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
||||
|
||||
@ -17,7 +17,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
it('Legacy HD Breadwallet can fetch utxo, balance, and create transaction', async () => {
|
||||
|
||||
@ -18,7 +18,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
let _cachedHdWallet = false;
|
||||
|
||||
@ -13,7 +13,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
describe('Bech32 Segwit HD (BIP84)', () => {
|
||||
|
||||
@ -14,11 +14,8 @@ afterAll(() => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
try {
|
||||
await BlueElectrum.connectMain();
|
||||
} catch (Err) {
|
||||
console.log('failed to connect to Electrum:', Err);
|
||||
process.exit(2);
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -30,7 +30,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
type THistoryItem = { action: 'progress'; data: string } | { action: 'wallet'; data: TWallet } | { action: 'password'; data: string };
|
||||
|
||||
@ -15,7 +15,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
describe('LegacyWallet', function () {
|
||||
|
||||
@ -13,11 +13,8 @@ afterAll(() => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
try {
|
||||
await BlueElectrum.connectMain();
|
||||
} catch (Err) {
|
||||
console.log('failed to connect to Electrum:', Err);
|
||||
process.exit(2);
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -13,7 +13,9 @@ afterAll(async () => {
|
||||
beforeAll(async () => {
|
||||
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
|
||||
// while app starts up, but for tests we need to wait for it
|
||||
await BlueElectrum.connectMain();
|
||||
if (!(await BlueElectrum.ensureConnected())) {
|
||||
throw new Error('failed to connect to Electrum');
|
||||
}
|
||||
});
|
||||
|
||||
describe('Watch only wallet', () => {
|
||||
|
||||
@ -9,7 +9,7 @@ jest.mock('../../blue_modules/currency', () => {
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
connectMain: jest.fn(),
|
||||
ensureConnected: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { LightningCustodianWallet } from '../../class/wallets/lightning-custodia
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
connectMain: jest.fn(),
|
||||
ensureConnected: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { WatchOnlyWallet } from '../../class/wallets/watch-only-wallet';
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
connectMain: jest.fn(),
|
||||
ensureConnected: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user