Skip to content

Commit

Permalink
Merge branch '4.x' into debug-validator
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex authored Oct 5, 2023
2 parents f978d68 + 6d99cd0 commit 3f02119
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 46 deletions.
20 changes: 10 additions & 10 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment.'
days-before-stale: 60
days-before-close: 14
operations-per-run: 100
exempt-pr-labels: 'work-in-progress,4.x'
exempt-issue-labels: 'work-in-progress,4.x'
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions. If you believe this was a mistake, please comment.'
days-before-stale: 60
days-before-close: 14
operations-per-run: 100
exempt-pr-labels: 'work-in-progress,4.x'
exempt-issue-labels: 'work-in-progress,4.x'\
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2098,6 +2098,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f
#### web3-eth

- Ensure provider.supportsSubscriptions exists before watching by subscription (#6440)
- Fixed `withdrawalsSchema.address` property type `bytes32` to `address` (#6470)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion packages/web3-eth/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export const withdrawalsSchema = {
format: 'uint',
},
address: {
format: 'bytes32',
format: 'address',
},
amount: {
format: 'uint',
Expand Down
10 changes: 7 additions & 3 deletions packages/web3-eth/src/utils/wait_for_transaction_receipt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { TransactionPollingTimeoutError } from 'web3-errors';
import { EthExecutionAPI, Bytes, TransactionReceipt, DataFormat } from 'web3-types';

// eslint-disable-next-line import/no-cycle
import { pollTillDefined, rejectIfTimeout } from 'web3-utils';
import { pollTillDefinedAndReturnIntervalId, rejectIfTimeout } from 'web3-utils';
// eslint-disable-next-line import/no-cycle
import { rejectIfBlockTimeout } from './reject_if_block_timeout.js';
// eslint-disable-next-line import/no-cycle
Expand All @@ -31,10 +31,11 @@ export async function waitForTransactionReceipt<ReturnFormat extends DataFormat>
transactionHash: Bytes,
returnFormat: ReturnFormat,
): Promise<TransactionReceipt> {

const pollingInterval =
web3Context.transactionReceiptPollingInterval ?? web3Context.transactionPollingInterval;

const awaitableTransactionReceipt: Promise<TransactionReceipt> = pollTillDefined(async () => {
const [awaitableTransactionReceipt, IntervalId] = pollTillDefinedAndReturnIntervalId(async () => {
try {
return getTransactionReceipt(web3Context, transactionHash, returnFormat);
} catch (error) {
Expand Down Expand Up @@ -64,7 +65,10 @@ export async function waitForTransactionReceipt<ReturnFormat extends DataFormat>
rejectOnBlockTimeout, // this will throw an error on Transaction Block Timeout
]);
} finally {
clearTimeout(timeoutId);
if(timeoutId)
clearTimeout(timeoutId);
if(IntervalId)
clearInterval(IntervalId);
blockTimeoutResourceCleaner.clean();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ describe('defaults', () => {
beforeEach(() => {
clientUrl = getSystemTestProvider();
web3 = new Web3(clientUrl);
// Make the test run faster by casing the polling to start after 2 blocks
web3.eth.transactionBlockTimeout = 2;

// Increase other timeouts so only `transactionBlockTimeout` would be reached
web3.eth.transactionSendTimeout = MAX_32_SIGNED_INTEGER;
Expand All @@ -64,6 +62,7 @@ describe('defaults', () => {
account1 = await createLocalAccount(web3);
account2 = await createLocalAccount(web3);
// Setting a high `nonce` when sending a transaction, to cause the RPC call to stuck at the Node

const sentTx: Web3PromiEvent<
TransactionReceipt,
SendTransactionEvents<typeof DEFAULT_RETURN_FORMAT>
Expand All @@ -81,18 +80,13 @@ describe('defaults', () => {
// So, send 2 transactions, one after another, because in this test `transactionBlockTimeout = 2`.
// eslint-disable-next-line no-void
await sendFewSampleTxs(2);

web3.eth.transactionBlockTimeout = 2;

await expect(sentTx).rejects.toThrow(/was not mined within [0-9]+ blocks/);

await expect(sentTx).rejects.toThrow(TransactionBlockTimeoutError);

try {
await sentTx;
throw new Error(
'The test should fail if there is no exception when sending a transaction that could not be mined within transactionBlockTimeout',
);
} catch (error) {
// eslint-disable-next-line jest/no-conditional-expect
expect(error).toBeInstanceOf(TransactionBlockTimeoutError);
// eslint-disable-next-line jest/no-conditional-expect
expect((error as Error).message).toMatch(/was not mined within [0-9]+ blocks/);
}
await closeOpenConnection(web3.eth);
});

Expand Down Expand Up @@ -128,6 +122,8 @@ describe('defaults', () => {
// eslint-disable-next-line no-void, @typescript-eslint/no-unsafe-call
void sendFewSampleTxs(2);

web3.eth.transactionBlockTimeout = 2;

await expect(sentTx).rejects.toThrow(/was not mined within [0-9]+ blocks/);

await expect(sentTx).rejects.toThrow(TransactionBlockTimeoutError);
Expand Down
126 changes: 126 additions & 0 deletions packages/web3-eth/test/unit/utils/wait_for_transaction_receipt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { Web3Context } from 'web3-core';
import { DEFAULT_RETURN_FORMAT, Web3EthExecutionAPI } from 'web3-types';
import { TransactionBlockTimeoutError } from 'web3-errors';
import { waitForTransactionReceipt } from '../../../src/utils/wait_for_transaction_receipt';

describe('waitForTransactionReceipt unit test', () => {
let web3Context: Web3Context<Web3EthExecutionAPI>;

it(`waitForTransactionReceipt should throw error after block timeout`, async () => {
let blockNum = 1;

web3Context = new Web3Context(
{
request: async (payload: any) => {
let response: { jsonrpc: string; id: any; result: string } | undefined;

switch (payload.method) {
case 'eth_blockNumber':
blockNum += 50;
response = {
jsonrpc: '2.0',
id: payload.id,
result: `0x${blockNum.toString(16)}`,
};
break;

case 'eth_getTransactionReceipt':
response = undefined;
break;

default:
throw new Error(`Unknown payload ${payload}`);
}

return new Promise(resolve => {
resolve(response as any);
});
},
supportsSubscriptions: () => false,
},
);

await expect(async () =>
waitForTransactionReceipt(
web3Context,
'0x0430b701e657e634a9d5480eae0387a473913ef29af8e60c38a3cee24494ed54',
DEFAULT_RETURN_FORMAT
)
).rejects.toThrow(TransactionBlockTimeoutError);

});

it(`waitForTransactionReceipt should resolve immediatly if receipt is avalible`, async () => {
let blockNum = 1;
const txHash = '0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5';
const blockHash = '0xa957d47df264a31badc3ae823e10ac1d444b098d9b73d204c40426e57f47e8c3';

web3Context = new Web3Context(
{
request: async (payload: any) => {
const response = {
jsonrpc: '2.0',
id: payload.id,
result: {},
};

switch (payload.method) {
case 'eth_blockNumber':
blockNum += 10;
response.result = `0x${blockNum.toString(16)}`;
break;

case 'eth_getTransactionReceipt':
response.result = {
blockHash,
blockNumber: `0x1`,
cumulativeGasUsed: '0xa12515',
from: payload.from,
gasUsed: payload.gasLimit,
status: '0x1',
to: payload.to,
transactionHash: txHash,
transactionIndex: '0x66',

};
break;

default:
throw new Error(`Unknown payload ${payload}`);
}

return new Promise(resolve => {
resolve(response as any);
});
},
supportsSubscriptions: () => false,
},
);

const res = await waitForTransactionReceipt(
web3Context,
'0x0430b701e657e634a9d5480eae0387a473913ef29af8e60c38a3cee24494ed54',
DEFAULT_RETURN_FORMAT
);

expect(res).toBeDefined();
expect(res.transactionHash).toStrictEqual(txHash);
expect(res.blockHash).toStrictEqual(blockHash);
});
})
39 changes: 24 additions & 15 deletions packages/web3-utils/src/promise_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,22 @@ export async function waitWithTimeout<T>(
}
return result;
}


/**
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached.
* or until a timeout is reached. It returns promise and intervalId.
* @param func - The function to call.
* @param interval - The interval in milliseconds.
*/
export async function pollTillDefined<T>(
export function pollTillDefinedAndReturnIntervalId<T>(
func: AsyncFunction<T>,
interval: number,
): Promise<Exclude<T, undefined>> {
const awaitableRes = waitWithTimeout(func, interval);
): [Promise<Exclude<T, undefined>>, Timer] {

let intervalId: Timer | undefined;
const polledRes = new Promise<Exclude<T, undefined>>((resolve, reject) => {
intervalId = setInterval(() => {
intervalId = setInterval(function intervalCallbackFunc(){
(async () => {
try {
const res = await waitWithTimeout(func, interval);
Expand All @@ -101,19 +102,26 @@ export async function pollTillDefined<T>(
reject(error);
}
})() as unknown;
}, interval);
return intervalCallbackFunc;}() // this will immediate invoke first call
, interval);
});

// If the first call to awaitableRes succeeded, return the result
const res = await awaitableRes;
if (!isNullish(res)) {
if (intervalId) {
clearInterval(intervalId);
}
return res as unknown as Exclude<T, undefined>;
}
return [polledRes as unknown as Promise<Exclude<T, undefined>>, intervalId!];
}

return polledRes;
/**
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached.
* pollTillDefinedAndReturnIntervalId() function should be used instead of pollTillDefined if you need IntervalId in result.
* This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId().
* @param func - The function to call.
* @param interval - The interval in milliseconds.
*/
export async function pollTillDefined<T>(
func: AsyncFunction<T>,
interval: number,
): Promise<Exclude<T, undefined>> {
return pollTillDefinedAndReturnIntervalId(func, interval)[0];
}
/**
* Enforce a timeout on a promise, so that it can be rejected if it takes too long to complete
Expand Down Expand Up @@ -160,3 +168,4 @@ export function rejectIfConditionAtInterval<T>(
});
return [intervalId!, rejectIfCondition];
}

Loading

0 comments on commit 3f02119

Please sign in to comment.