Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Convert block height exceeded exception into a coded exception
Browse files Browse the repository at this point in the history
  • Loading branch information
steveluscher committed Feb 20, 2024
1 parent ee2cd06 commit 13a62ea
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 15 deletions.
4 changes: 3 additions & 1 deletion packages/errors/src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES = 1 as const;
export const SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE = 2 as const;
export const SOLANA_ERROR__RPC_INTEGER_OVERFLOW = 3 as const;
export const SOLANA_ERROR__INVALID_KEYPAIR_BYTES = 4 as const;
export const SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 5 as const;

/**
* A union of every Solana error code
Expand All @@ -30,4 +31,5 @@ export type SolanaErrorCode =
| typeof SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES
| typeof SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE
| typeof SOLANA_ERROR__RPC_INTEGER_OVERFLOW
| typeof SOLANA_ERROR__INVALID_KEYPAIR_BYTES;
| typeof SOLANA_ERROR__INVALID_KEYPAIR_BYTES
| typeof SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED;
5 changes: 5 additions & 0 deletions packages/errors/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
SOLANA_ERROR__INVALID_KEYPAIR_BYTES,
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
Expand All @@ -17,6 +18,10 @@ export type DefaultUnspecifiedErrorContextToUndefined<T> = {
* - Don't change or remove members of an error's context.
*/
export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
[SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]: {
currentBlockHeight: bigint;
lastValidBlockHeight: bigint;
};
[SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES]: {
addresses: string[];
};
Expand Down
3 changes: 3 additions & 0 deletions packages/errors/src/messages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
SOLANA_ERROR__INVALID_KEYPAIR_BYTES,
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
Expand All @@ -18,6 +19,8 @@ export const SolanaErrorMessages: Readonly<{
// TypeScript will fail to build this project if add an error code without a message.
[P in SolanaErrorCode]: string;
}> = {
[SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]:
'The network has progressed past the last block for which this transaction could have been committed.',
[SOLANA_ERROR__INVALID_KEYPAIR_BYTES]: 'Key pair bytes must be of length 64, got $byteLength.',
[SOLANA_ERROR__RPC_INTEGER_OVERFLOW]:
'The $argumentLabel argument to the `$methodName` RPC method$optionalPathLabel was ' +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';
import { Commitment } from '@solana/rpc-types';

import { createBlockHeightExceedencePromiseFactory } from '../transaction-confirmation-strategy-blockheight';
Expand Down Expand Up @@ -44,7 +45,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
abortSignal: new AbortController().signal,
lastValidBlockHeight: 100n,
});
await expect(exceedencePromise).rejects.toThrow();
await expect(exceedencePromise).rejects.toThrow(
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
currentBlockHeight: 101n,
lastValidBlockHeight: 100n,
}),
);
});
it('continues to pend when the block height in the initial fetch is lower than the last valid block height', async () => {
expect.assertions(1);
Expand Down Expand Up @@ -72,7 +78,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
abortSignal: new AbortController().signal,
lastValidBlockHeight: 100n,
});
await expect(exceedencePromise).rejects.toThrow();
await expect(exceedencePromise).rejects.toThrow(
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
currentBlockHeight: 101n,
lastValidBlockHeight: 100n,
}),
);
});
it('continues to pend when slot at which the block height is expected to be exceeded has not been reached', async () => {
expect.assertions(1);
Expand Down Expand Up @@ -111,7 +122,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
abortSignal: new AbortController().signal,
lastValidBlockHeight: 100n,
});
await expect(exceedencePromise).rejects.toThrow();
await expect(exceedencePromise).rejects.toThrow(
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
currentBlockHeight: 101n,
lastValidBlockHeight: 100n,
}),
);
});
it('continues to pend when the slot height / block height delta grows by the time the slot at which the block height is expected to be exceeded is reached', async () => {
expect.assertions(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';
import type { GetEpochInfoApi, SlotNotificationsApi } from '@solana/rpc-core';
import type { Rpc, RpcSubscriptions } from '@solana/rpc-types';
import { Commitment } from '@solana/rpc-types';
Expand Down Expand Up @@ -35,20 +36,23 @@ export function createBlockHeightExceedencePromiseFactory({
};
}
try {
const [slotNotifications, { blockHeight, differenceBetweenSlotHeightAndBlockHeight }] = await Promise.all([
rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight(),
]);
if (blockHeight <= lastValidBlockHeight) {
const [slotNotifications, { blockHeight: initialBlockHeight, differenceBetweenSlotHeightAndBlockHeight }] =
await Promise.all([
rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight(),
]);
let currentBlockHeight = initialBlockHeight;
if (currentBlockHeight <= lastValidBlockHeight) {
let lastKnownDifferenceBetweenSlotHeightAndBlockHeight = differenceBetweenSlotHeightAndBlockHeight;
for await (const slotNotification of slotNotifications) {
const { slot } = slotNotification;
if (slot - lastKnownDifferenceBetweenSlotHeightAndBlockHeight > lastValidBlockHeight) {
// Before making a final decision, recheck the actual block height.
const {
blockHeight: currentBlockHeight,
blockHeight: recheckedBlockHeight,
differenceBetweenSlotHeightAndBlockHeight: currentDifferenceBetweenSlotHeightAndBlockHeight,
} = await getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight();
currentBlockHeight = recheckedBlockHeight;
if (currentBlockHeight > lastValidBlockHeight) {
// Verfied; the block height has been exceeded.
break;
Expand All @@ -63,11 +67,10 @@ export function createBlockHeightExceedencePromiseFactory({
}
}
}
// TODO: Coded error.
throw new Error(
'The network has progressed past the last block for which this transaction could ' +
'have been committed.',
);
throw new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
currentBlockHeight,
lastValidBlockHeight,
});
} finally {
abortController.abort();
}
Expand Down

0 comments on commit 13a62ea

Please sign in to comment.