cap bounty payout gas to prevent receiver gas units abuse

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-09-24 15:23:05 +03:00
parent 40c203a6b0
commit de2e49c877
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
3 changed files with 15 additions and 12 deletions

View File

@ -89,7 +89,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
return IStorageHistory(storageHistory).bridgeImbalance();
}
function latestPublicKeyInfo() external override view returns (bytes32, uint8, uint64) {
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64) {
bytes32 latestPublicKey = bytes32(_aggregatedPublicKeys.latest());
uint256 packed = _packedRotationStates[latestPublicKey];
@ -152,7 +152,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
RequestPacking.RequestPayload memory payload = RequestPacking.unpack(packed);
if (payload.chainId != block.chainid) revert WrongChainId();
uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
uint256 requestedBountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
uint256 bountyAmount = _isContract(payload.receiver) ? 0 : requestedBountyAmount;
uint256 receiverAmount = amount - bountyAmount;
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
@ -163,7 +164,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
(address token, uint256 sent) = IStaking(staking).recall(bountyAmount, REGISTRY_INDEX);
IWETH9(token).withdraw(sent);
(bool sentSuccess,) = payload.receiver.call{ value: sent }("");
(bool sentSuccess,) = payload.receiver.call{ value: sent, gas: 3000 }("");
if (!sentSuccess) revert SendFailed();
}
@ -192,7 +193,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
uint256 exodusSession,
uint256 packed,
bytes calldata
) external returns (bytes memory) {
) external {
if (msg.sender != address(this)) revert NotGatekeeper();
GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed);
@ -200,10 +201,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
(bool success, bytes memory data) = payload.target.call(msg.data[132:]);
(bool success,) = payload.target.call(msg.data[132:]);
if (!success) revert ExecutionReverted();
return data;
}
function _extractPublicKey(bytes calldata call) internal view returns (uint256) {
@ -217,4 +216,12 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
return uint256(publicKey);
}
function _isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
}

View File

@ -22,12 +22,8 @@ interface IGatekeeper {
function deployer() external view returns (address);
function ghostedSupply() external view returns (uint256);
function storageHistory() external view returns (address);
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64);
function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64);
function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata);
function recall(uint256 session, uint256 amount, uint256 payload) external;
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
function ghost(bytes32 receiver, uint256 amount) external;
function initialize(address previousAddress) external;
}

View File

@ -195,7 +195,7 @@ contract GhostBondDepositoryTest is Test {
[CAPACITY, initialPrice, minPrice, BUFFER],
[VESTING, TIME_TO_CONCLUSION],
address(reserve),
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)],
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
[false, true]
);