Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 26c283e

Browse files
committed
feat: limit permissionless generic call gas usage
1 parent 0cc78cf commit 26c283e

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

contracts/handlers/PermissionlessGenericHandler.sol

+3-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ contract PermissionlessGenericHandler is IHandler {
143143
executeFuncSignature(address executionDataDepositor, uint[] uintArray, address addr)
144144
*/
145145
function executeProposal(bytes32 resourceID, bytes calldata data) external onlyBridge returns (bytes memory) {
146+
uint256 maxFee;
146147
uint16 lenExecuteFuncSignature;
147148
bytes4 executeFuncSignature;
148149
uint8 lenExecuteContractAddress;
@@ -151,6 +152,7 @@ contract PermissionlessGenericHandler is IHandler {
151152
address executionDataDepositor;
152153
bytes memory executionData;
153154

155+
maxFee = uint256(bytes32(data[0:32]));
154156
lenExecuteFuncSignature = uint16(bytes2(data[32:34]));
155157
executeFuncSignature = bytes4(data[34:34 + lenExecuteFuncSignature]);
156158
lenExecuteContractAddress = uint8(bytes1(data[34 + lenExecuteFuncSignature:35 + lenExecuteFuncSignature]));
@@ -160,7 +162,7 @@ contract PermissionlessGenericHandler is IHandler {
160162
executionData = bytes(data[36 + lenExecuteFuncSignature + lenExecuteContractAddress + lenExecutionDataDepositor:]);
161163

162164
bytes memory callData = abi.encodePacked(executeFuncSignature, abi.encode(executionDataDepositor), executionData);
163-
(bool success, bytes memory returndata) = executeContractAddress.call(callData);
165+
(bool success, bytes memory returndata) = executeContractAddress.call{gas: maxFee}(callData);
164166
return abi.encode(success, returndata);
165167
}
166168
}

test/handlers/generic/permissionlessExecuteProposal.js

+68
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,74 @@ contract(
226226
);
227227
});
228228

229+
it("ProposalExecution should be emitted even if gas specified too small", async () => {
230+
const num = 6;
231+
const addresses = [BridgeInstance.address, TestStoreInstance.address];
232+
const message = Ethers.utils.hexlify(Ethers.utils.toUtf8Bytes("message"));
233+
const executionData = Helpers.abiEncode(["uint", "address[]", "bytes"], [num, addresses, message]);
234+
235+
// If the target function accepts (address depositor, bytes executionData)
236+
// then this helper can be used
237+
const preparedExecutionData = await TestDepositInstance.prepareDepositData(executionData);
238+
const depositFunctionSignature = Helpers.getFunctionSignature(
239+
TestDepositInstance,
240+
"executePacked"
241+
);
242+
const tooSmallGas = 500;
243+
const depositData = Helpers.createPermissionlessGenericDepositData(
244+
depositFunctionSignature,
245+
TestDepositInstance.address,
246+
tooSmallGas,
247+
depositorAddress,
248+
preparedExecutionData
249+
);
250+
const proposal = {
251+
originDomainID: originDomainID,
252+
depositNonce: expectedDepositNonce,
253+
data: depositData,
254+
resourceID: resourceID,
255+
};
256+
const proposalSignedData = await Helpers.signTypedProposal(
257+
BridgeInstance.address,
258+
[proposal]
259+
);
260+
261+
// relayer1 executes the proposal
262+
const executeTx = await BridgeInstance.executeProposal(
263+
proposal,
264+
proposalSignedData,
265+
{from: relayer1Address}
266+
);
267+
// check that ProposalExecution event is emitted
268+
TruffleAssert.eventEmitted(executeTx, "ProposalExecution", (event) => {
269+
return (
270+
event.originDomainID.toNumber() === originDomainID &&
271+
event.depositNonce.toNumber() === expectedDepositNonce
272+
);
273+
});
274+
275+
// check that deposit nonce isn't unmarked as used in bitmap
276+
assert.isTrue(
277+
await BridgeInstance.isProposalExecuted(
278+
originDomainID,
279+
expectedDepositNonce
280+
)
281+
);
282+
283+
const internalTx = await TruffleAssert.createTransactionResult(
284+
TestDepositInstance,
285+
executeTx.tx
286+
);
287+
TruffleAssert.eventNotEmitted(internalTx, "TestExecute", (event) => {
288+
return (
289+
event.depositor === depositorAddress &&
290+
event.num.toNumber() === num &&
291+
event.addr === TestStoreInstance.address &&
292+
event.message === message
293+
);
294+
});
295+
});
296+
229297
it("call with packed depositData should be successful", async () => {
230298
const num = 5;
231299
const addresses = [BridgeInstance.address, TestStoreInstance.address];

0 commit comments

Comments
 (0)