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(); 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()); bytes32 latestPublicKey = bytes32(_aggregatedPublicKeys.latest());
uint256 packed = _packedRotationStates[latestPublicKey]; uint256 packed = _packedRotationStates[latestPublicKey];
@ -152,7 +152,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
RequestPacking.RequestPayload memory payload = RequestPacking.unpack(packed); RequestPacking.RequestPayload memory payload = RequestPacking.unpack(packed);
if (payload.chainId != block.chainid) revert WrongChainId(); 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; uint256 receiverAmount = amount - bountyAmount;
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
@ -163,7 +164,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
(address token, uint256 sent) = IStaking(staking).recall(bountyAmount, REGISTRY_INDEX); (address token, uint256 sent) = IStaking(staking).recall(bountyAmount, REGISTRY_INDEX);
IWETH9(token).withdraw(sent); IWETH9(token).withdraw(sent);
(bool sentSuccess,) = payload.receiver.call{ value: sent }(""); (bool sentSuccess,) = payload.receiver.call{ value: sent, gas: 3000 }("");
if (!sentSuccess) revert SendFailed(); if (!sentSuccess) revert SendFailed();
} }
@ -192,7 +193,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
uint256 exodusSession, uint256 exodusSession,
uint256 packed, uint256 packed,
bytes calldata bytes calldata
) external returns (bytes memory) { ) external {
if (msg.sender != address(this)) revert NotGatekeeper(); if (msg.sender != address(this)) revert NotGatekeeper();
GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed); GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed);
@ -200,10 +201,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); 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(); if (!success) revert ExecutionReverted();
return data;
} }
function _extractPublicKey(bytes calldata call) internal view returns (uint256) { function _extractPublicKey(bytes calldata call) internal view returns (uint256) {
@ -217,4 +216,12 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
return uint256(publicKey); 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 deployer() external view returns (address);
function ghostedSupply() external view returns (uint256); function ghostedSupply() external view returns (uint256);
function storageHistory() external view returns (address); 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 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 ghost(bytes32 receiver, uint256 amount) external;
function initialize(address previousAddress) external; function initialize(address previousAddress) external;
} }

View File

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