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

feat: optimize and delay even more fallback rpcs for blockhash #744

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 17 additions & 5 deletions packages/core/src/lib/lit-core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,21 @@ describe('LitCore', () => {
timestamp: currentTime,
}),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(core as any, '_getProviderWithFallback').mockResolvedValue({
...mockProvider,
provider: mockProvider,
testResult: {
hash: mockBlockhash,
number: 12345,
timestamp: currentTime,
},
});

// Execute
const result = await core.getLatestBlockhash();

// Assert
expect(fetch).toHaveBeenCalledWith(mockBlockhashUrl);
expect(mockProvider.getBlock).toHaveBeenCalledWith(-1); // safety margin
expect(result).toBe(mockBlockhash);
});

Expand All @@ -132,16 +137,21 @@ describe('LitCore', () => {
timestamp: currentTime,
}),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(core as any, '_getProviderWithFallback').mockResolvedValue({
...mockProvider,
provider: mockProvider,
testResult: {
hash: mockBlockhash,
number: 12345,
timestamp: currentTime,
},
});

// Execute
const result = await core.getLatestBlockhash();

// Assert
expect(fetch).toHaveBeenCalledWith(mockBlockhashUrl);
expect(mockProvider.getBlock).toHaveBeenCalledWith(-1); // safety margin
expect(result).toBe(mockBlockhash);
});

Expand All @@ -164,8 +174,10 @@ describe('LitCore', () => {
getBlock: jest.fn().mockResolvedValue(null), // Provider also fails
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(core as any, '_getProviderWithFallback').mockResolvedValue({
...mockProvider,
provider: mockProvider,
testResult: null,
});

// Execute & Assert
Expand Down
65 changes: 37 additions & 28 deletions packages/core/src/lib/lit-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
LIT_CURVE,
LIT_CURVE_VALUES,
LIT_ENDPOINT,
LIT_ERROR,
LIT_ERROR_CODE,
LIT_NETWORK,
LIT_NETWORKS,
Expand Down Expand Up @@ -69,7 +68,6 @@ import {
NodeClientErrorV0,
NodeClientErrorV1,
NodeCommandServerKeysResponse,
NodeErrorV3,
RejectedNodePromises,
SendNodeCommand,
SessionSigsMap,
Expand All @@ -82,6 +80,10 @@ import { composeLitUrl } from './endpoint-version';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Listener = (...args: any[]) => void;

type providerTest<T> = (
provider: ethers.providers.JsonRpcProvider
) => Promise<T>;

interface CoreNodeConfig {
subnetPubKey: string;
networkPubKey: string;
Expand Down Expand Up @@ -119,8 +121,8 @@ export type LitNodeClientConfigWithDefaults = Required<
const EPOCH_PROPAGATION_DELAY = 45_000;
// This interval is responsible for keeping latest block hash up to date
const BLOCKHASH_SYNC_INTERVAL = 30_000;
// When fetching the blockhash from a provider (not lit), we use a previous block to avoid a nodes not knowing about the new block yet
const BLOCKHASH_COUNT_PROVIDER_DELAY = -1;
// When fetching the blockhash from a provider (not lit), we use a 5 minutes old block to ensure the nodes centralized indexer has it
const BLOCKHASH_COUNT_PROVIDER_DELAY = -30; // 30 blocks ago. Eth block are mined every 12s. 30 blocks is 6 minutes, indexer/nodes must have it by now

// Intentionally not including datil-dev here per discussion with Howard
const NETWORKS_REQUIRING_SEV: string[] = [
Expand Down Expand Up @@ -766,24 +768,31 @@ export class LitCore {
};
}

private _getProviderWithFallback =
async (): Promise<ethers.providers.JsonRpcProvider | null> => {
for (const url of FALLBACK_RPC_URLS) {
try {
const provider = new ethers.providers.JsonRpcProvider({
url: url,
private _getProviderWithFallback = async <T>(
providerTest: providerTest<T>
): Promise<{
provider: ethers.providers.JsonRpcProvider;
testResult: T;
} | null> => {
for (const url of FALLBACK_RPC_URLS) {
try {
const provider = new ethers.providers.JsonRpcProvider({
url: url,

// https://docs.ethers.org/v5/api/utils/web/#ConnectionInfo
timeout: 60000,
});
await provider.getBlockNumber(); // Simple check to see if the provider is working
return provider;
} catch (error) {
logError(`RPC URL failed: ${url}`);
}
// https://docs.ethers.org/v5/api/utils/web/#ConnectionInfo
timeout: 60000,
});
const testResult = await providerTest(provider); // Check to see if the provider is working
return {
provider,
testResult,
};
} catch (error) {
logError(`RPC URL failed: ${url}`);
}
return null;
};
}
return null;
};

/**
* Fetches the latest block hash and log any errors that are returned
Expand Down Expand Up @@ -854,20 +863,20 @@ export class LitCore {
log(
'Attempting to fetch blockhash manually using ethers with fallback RPC URLs...'
);
const provider = await this._getProviderWithFallback();
const { testResult } =
(await this._getProviderWithFallback<ethers.providers.Block>(
// We use a previous block to avoid nodes not having received the latest block yet
(provider) => provider.getBlock(BLOCKHASH_COUNT_PROVIDER_DELAY)
)) || {};

if (!provider) {
if (!testResult || !testResult.hash) {
logError('All fallback RPC URLs failed. Unable to retrieve blockhash.');
return;
}

try {
// We use a previous block to avoid nodes not having received the latest block yet
const priorBlock = await provider.getBlock(
BLOCKHASH_COUNT_PROVIDER_DELAY
);
this.latestBlockhash = priorBlock.hash;
this.lastBlockHashRetrieved = priorBlock.timestamp;
this.latestBlockhash = testResult.hash;
this.lastBlockHashRetrieved = testResult.timestamp;
log(
'Successfully retrieved blockhash manually: ',
this.latestBlockhash
Expand Down
Loading