Skip to content

Commit 3524f2c

Browse files
authored
Convert block height exceeded exception into a coded exception (#2160)
1 parent 2d1d49c commit 3524f2c

File tree

5 files changed

+44
-15
lines changed

5 files changed

+44
-15
lines changed

packages/errors/src/codes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES = 1 as const;
1010
export const SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE = 2 as const;
1111
export const SOLANA_ERROR__RPC_INTEGER_OVERFLOW = 3 as const;
1212
export const SOLANA_ERROR__INVALID_KEYPAIR_BYTES = 4 as const;
13+
export const SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 5 as const;
1314

1415
/**
1516
* A union of every Solana error code
@@ -30,4 +31,5 @@ export type SolanaErrorCode =
3031
| typeof SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES
3132
| typeof SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE
3233
| typeof SOLANA_ERROR__RPC_INTEGER_OVERFLOW
33-
| typeof SOLANA_ERROR__INVALID_KEYPAIR_BYTES;
34+
| typeof SOLANA_ERROR__INVALID_KEYPAIR_BYTES
35+
| typeof SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED;

packages/errors/src/context.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
23
SOLANA_ERROR__INVALID_KEYPAIR_BYTES,
34
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
45
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
@@ -17,6 +18,10 @@ export type DefaultUnspecifiedErrorContextToUndefined<T> = {
1718
* - Don't change or remove members of an error's context.
1819
*/
1920
export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
21+
[SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]: {
22+
currentBlockHeight: bigint;
23+
lastValidBlockHeight: bigint;
24+
};
2025
[SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES]: {
2126
addresses: string[];
2227
};

packages/errors/src/messages.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
23
SOLANA_ERROR__INVALID_KEYPAIR_BYTES,
34
SOLANA_ERROR__RPC_INTEGER_OVERFLOW,
45
SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES,
@@ -18,6 +19,8 @@ export const SolanaErrorMessages: Readonly<{
1819
// TypeScript will fail to build this project if add an error code without a message.
1920
[P in SolanaErrorCode]: string;
2021
}> = {
22+
[SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]:
23+
'The network has progressed past the last block for which this transaction could have been committed.',
2124
[SOLANA_ERROR__INVALID_KEYPAIR_BYTES]: 'Key pair bytes must be of length 64, got $byteLength.',
2225
[SOLANA_ERROR__RPC_INTEGER_OVERFLOW]:
2326
'The $argumentLabel argument to the `$methodName` RPC method$optionalPathLabel was ' +

packages/library/src/__tests__/transaction-confirmation-strategy-blockheight-test.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';
12
import { Commitment } from '@solana/rpc-types';
23

34
import { createBlockHeightExceedencePromiseFactory } from '../transaction-confirmation-strategy-blockheight';
@@ -44,7 +45,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
4445
abortSignal: new AbortController().signal,
4546
lastValidBlockHeight: 100n,
4647
});
47-
await expect(exceedencePromise).rejects.toThrow();
48+
await expect(exceedencePromise).rejects.toThrow(
49+
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
50+
currentBlockHeight: 101n,
51+
lastValidBlockHeight: 100n,
52+
}),
53+
);
4854
});
4955
it('continues to pend when the block height in the initial fetch is lower than the last valid block height', async () => {
5056
expect.assertions(1);
@@ -72,7 +78,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
7278
abortSignal: new AbortController().signal,
7379
lastValidBlockHeight: 100n,
7480
});
75-
await expect(exceedencePromise).rejects.toThrow();
81+
await expect(exceedencePromise).rejects.toThrow(
82+
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
83+
currentBlockHeight: 101n,
84+
lastValidBlockHeight: 100n,
85+
}),
86+
);
7687
});
7788
it('continues to pend when slot at which the block height is expected to be exceeded has not been reached', async () => {
7889
expect.assertions(1);
@@ -111,7 +122,12 @@ describe('createBlockHeightExceedencePromiseFactory', () => {
111122
abortSignal: new AbortController().signal,
112123
lastValidBlockHeight: 100n,
113124
});
114-
await expect(exceedencePromise).rejects.toThrow();
125+
await expect(exceedencePromise).rejects.toThrow(
126+
new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
127+
currentBlockHeight: 101n,
128+
lastValidBlockHeight: 100n,
129+
}),
130+
);
115131
});
116132
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 () => {
117133
expect.assertions(1);

packages/library/src/transaction-confirmation-strategy-blockheight.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';
12
import type { GetEpochInfoApi, SlotNotificationsApi } from '@solana/rpc-core';
23
import type { Rpc, RpcSubscriptions } from '@solana/rpc-types';
34
import { Commitment } from '@solana/rpc-types';
@@ -35,20 +36,23 @@ export function createBlockHeightExceedencePromiseFactory({
3536
};
3637
}
3738
try {
38-
const [slotNotifications, { blockHeight, differenceBetweenSlotHeightAndBlockHeight }] = await Promise.all([
39-
rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
40-
getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight(),
41-
]);
42-
if (blockHeight <= lastValidBlockHeight) {
39+
const [slotNotifications, { blockHeight: initialBlockHeight, differenceBetweenSlotHeightAndBlockHeight }] =
40+
await Promise.all([
41+
rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
42+
getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight(),
43+
]);
44+
let currentBlockHeight = initialBlockHeight;
45+
if (currentBlockHeight <= lastValidBlockHeight) {
4346
let lastKnownDifferenceBetweenSlotHeightAndBlockHeight = differenceBetweenSlotHeightAndBlockHeight;
4447
for await (const slotNotification of slotNotifications) {
4548
const { slot } = slotNotification;
4649
if (slot - lastKnownDifferenceBetweenSlotHeightAndBlockHeight > lastValidBlockHeight) {
4750
// Before making a final decision, recheck the actual block height.
4851
const {
49-
blockHeight: currentBlockHeight,
52+
blockHeight: recheckedBlockHeight,
5053
differenceBetweenSlotHeightAndBlockHeight: currentDifferenceBetweenSlotHeightAndBlockHeight,
5154
} = await getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight();
55+
currentBlockHeight = recheckedBlockHeight;
5256
if (currentBlockHeight > lastValidBlockHeight) {
5357
// Verfied; the block height has been exceeded.
5458
break;
@@ -63,11 +67,10 @@ export function createBlockHeightExceedencePromiseFactory({
6367
}
6468
}
6569
}
66-
// TODO: Coded error.
67-
throw new Error(
68-
'The network has progressed past the last block for which this transaction could ' +
69-
'have been committed.',
70-
);
70+
throw new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
71+
currentBlockHeight,
72+
lastValidBlockHeight,
73+
});
7174
} finally {
7275
abortController.abort();
7376
}

0 commit comments

Comments
 (0)