Commit 90552788 authored by refcell's avatar refcell Committed by GitHub

fix(ctb): Exact Bond Amounts (#9924)

* fix(ctb): require exact bond amounts

* fix(ctb): precise bond amount tests

* fix(ctb): ci checks

* Semver / DGF strict bonds

* Rename `InsufficientBond` error -> `IncorrectBondAmount`

---------
Co-authored-by: default avatarclabby <ben@clab.by>
parent 786cca3d
This diff is collapsed.
This diff is collapsed.
...@@ -108,12 +108,12 @@ ...@@ -108,12 +108,12 @@
"sourceCodeHash": "0xba941ad1f941f5a4a066182d50634fa9b190085ed82779decef71c019ba963c5" "sourceCodeHash": "0xba941ad1f941f5a4a066182d50634fa9b190085ed82779decef71c019ba963c5"
}, },
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0x80d749a56c1776930fe0deb5c3c646217716e5875ace99c4d036af0452236476", "initCodeHash": "0xdcdf98426bbe8ad7c9112dff7d7560c6cc39970aff9cc18b1a45753528b7b6cb",
"sourceCodeHash": "0xf897c1a845a16cb8b217135a1c7819cdb20b315567066282f5860251e48d3611" "sourceCodeHash": "0x3ac8675f5dbc23ea992b23aa55504c89cf495199570ba420d46fef6c19349322"
}, },
"src/dispute/FaultDisputeGame.sol": { "src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0x9dcd4df1dd3e7a09dab46bfe1ebd9376f533cc533f9edce2f01aa754301e25aa", "initCodeHash": "0x047f99f09b4f6fb5b003e47e26af9134448aa026659716231a222a30109e549f",
"sourceCodeHash": "0xbe89df391f9cd4165389a7f6f65af752db13d0e508a1ec8430737aba5b7174dc" "sourceCodeHash": "0x1c29d6edfa476ebb2c5cd2981aa8ac6eabcb7ded842dd6829cbaf833e575edad"
}, },
"src/dispute/weth/DelayedWETH.sol": { "src/dispute/weth/DelayedWETH.sol": {
"initCodeHash": "0xf179e4249be6eda22b24ae2b32717f154f35edeb9dee0332aefa6fad3ace4dbe", "initCodeHash": "0xf179e4249be6eda22b24ae2b32717f154f35edeb9dee0332aefa6fad3ace4dbe",
......
...@@ -435,7 +435,7 @@ ...@@ -435,7 +435,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InsufficientBond", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -187,19 +187,6 @@ ...@@ -187,19 +187,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "claimedBondFlag",
"outputs": [
{
"internalType": "uint128",
"name": "claimedBondFlag_",
"type": "uint128"
}
],
"stateMutability": "pure",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "createdAt", "name": "createdAt",
...@@ -702,7 +689,7 @@ ...@@ -702,7 +689,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InsufficientBond", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -197,19 +197,6 @@ ...@@ -197,19 +197,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "claimedBondFlag",
"outputs": [
{
"internalType": "uint128",
"name": "claimedBondFlag_",
"type": "uint128"
}
],
"stateMutability": "pure",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "createdAt", "name": "createdAt",
...@@ -717,7 +704,7 @@ ...@@ -717,7 +704,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InsufficientBond", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -23,8 +23,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver ...@@ -23,8 +23,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
using ClonesWithImmutableArgs for address; using ClonesWithImmutableArgs for address;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 0.2.0 /// @custom:semver 0.3.0
string public constant version = "0.2.0"; string public constant version = "0.3.0";
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
mapping(GameType => IDisputeGame) public gameImpls; mapping(GameType => IDisputeGame) public gameImpls;
...@@ -97,7 +97,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver ...@@ -97,7 +97,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
if (address(impl) == address(0)) revert NoImplementation(_gameType); if (address(impl) == address(0)) revert NoImplementation(_gameType);
// If the required initialization bond is not met, revert. // If the required initialization bond is not met, revert.
if (msg.value < initBonds[_gameType]) revert InsufficientBond(); if (msg.value != initBonds[_gameType]) revert IncorrectBondAmount();
// Get the hash of the parent block. // Get the hash of the parent block.
bytes32 parentHash = blockhash(block.number - 1); bytes32 parentHash = blockhash(block.number - 1);
......
...@@ -91,8 +91,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -91,8 +91,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
OutputRoot public startingOutputRoot; OutputRoot public startingOutputRoot;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 0.8.1 /// @custom:semver 0.9.0
string public constant version = "0.8.1"; string public constant version = "0.9.0";
/// @param _gameType The type ID of the game. /// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace. /// @param _absolutePrestate The absolute prestate of the instruction trace.
...@@ -256,8 +256,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -256,8 +256,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
_verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack); _verifyExecBisectionRoot(_claim, _challengeIndex, parentPos, _isAttack);
} }
// INVARIANT: The `msg.value` must be sufficient to cover the required bond. // INVARIANT: The `msg.value` must exactly equal the required bond.
if (getRequiredBond(nextPosition) > msg.value) revert InsufficientBond(); if (getRequiredBond(nextPosition) != msg.value) revert IncorrectBondAmount();
// Fetch the grandparent clock, if it exists. // Fetch the grandparent clock, if it exists.
// The grandparent clock should always exist unless the parent is the root claim. // The grandparent clock should always exist unless the parent is the root claim.
...@@ -645,12 +645,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -645,12 +645,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
if (!success) revert BondTransferFailed(); if (!success) revert BondTransferFailed();
} }
/// @notice Returns the flag set in the `bond` field of a `ClaimData` struct to indicate that the bond has been
/// claimed.
function claimedBondFlag() external pure returns (uint128 claimedBondFlag_) {
claimedBondFlag_ = CLAIMED_BOND_FLAG;
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// IMMUTABLE GETTERS // // IMMUTABLE GETTERS //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -27,8 +27,8 @@ error UnexpectedRootClaim(Claim rootClaim); ...@@ -27,8 +27,8 @@ error UnexpectedRootClaim(Claim rootClaim);
/// @notice Thrown when a dispute game has already been initialized. /// @notice Thrown when a dispute game has already been initialized.
error AlreadyInitialized(); error AlreadyInitialized();
/// @notice Thrown when a supplied bond is too low to cover the cost of the interaction. /// @notice Thrown when a supplied bond is not equal to the required bond amount to cover the cost of the interaction.
error InsufficientBond(); error IncorrectBondAmount();
/// @notice Thrown when a credit claim is attempted for a value of 0. /// @notice Thrown when a credit claim is attempted for a value of 0.
error NoCreditToClaim(); error NoCreditToClaim();
......
...@@ -72,8 +72,8 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -72,8 +72,8 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
assertEq(address(proxy).balance, _value); assertEq(address(proxy).balance, _value);
} }
/// @dev Tests that the `create` function reverts when creating a new dispute game with an insufficient bond. /// @dev Tests that the `create` function reverts when creating a new dispute game with an incorrect bond amount.
function testFuzz_create_insufficientBond_reverts( function testFuzz_create_incorrectBondAmount_reverts(
uint8 gameType, uint8 gameType,
Claim rootClaim, Claim rootClaim,
bytes calldata extraData bytes calldata extraData
...@@ -92,7 +92,7 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -92,7 +92,7 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
disputeGameFactory.setInitBond(lgt, 1 ether); disputeGameFactory.setInitBond(lgt, 1 ether);
} }
vm.expectRevert(InsufficientBond.selector); vm.expectRevert(IncorrectBondAmount.selector);
disputeGameFactory.create(gt, rootClaim, extraData); disputeGameFactory.create(gt, rootClaim, extraData);
} }
......
...@@ -126,20 +126,30 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init { ...@@ -126,20 +126,30 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
/// @dev Tests that the challenger can participate in a permissioned dispute game. /// @dev Tests that the challenger can participate in a permissioned dispute game.
function test_participateInGame_challenger_succeeds() public { function test_participateInGame_challenger_succeeds() public {
vm.startPrank(CHALLENGER, CHALLENGER); vm.startPrank(CHALLENGER, CHALLENGER);
vm.deal(CHALLENGER, MIN_BOND * 3); uint256 firstBond = _getRequiredBond(0);
gameProxy.attack{ value: MIN_BOND }(0, Claim.wrap(0)); vm.deal(CHALLENGER, firstBond);
gameProxy.defend{ value: MIN_BOND }(1, Claim.wrap(0)); gameProxy.attack{ value: firstBond }(0, Claim.wrap(0));
gameProxy.move{ value: MIN_BOND }(2, Claim.wrap(0), true); uint256 secondBond = _getRequiredBond(1);
vm.deal(CHALLENGER, secondBond);
gameProxy.defend{ value: secondBond }(1, Claim.wrap(0));
uint256 thirdBond = _getRequiredBond(2);
vm.deal(CHALLENGER, thirdBond);
gameProxy.move{ value: thirdBond }(2, Claim.wrap(0), true);
vm.stopPrank(); vm.stopPrank();
} }
/// @dev Tests that the proposer can participate in a permissioned dispute game. /// @dev Tests that the proposer can participate in a permissioned dispute game.
function test_participateInGame_proposer_succeeds() public { function test_participateInGame_proposer_succeeds() public {
vm.startPrank(PROPOSER, PROPOSER); vm.startPrank(PROPOSER, PROPOSER);
vm.deal(PROPOSER, MIN_BOND * 3); uint256 firstBond = _getRequiredBond(0);
gameProxy.attack{ value: MIN_BOND }(0, Claim.wrap(0)); vm.deal(PROPOSER, firstBond);
gameProxy.defend{ value: MIN_BOND }(1, Claim.wrap(0)); gameProxy.attack{ value: firstBond }(0, Claim.wrap(0));
gameProxy.move{ value: MIN_BOND }(2, Claim.wrap(0), true); uint256 secondBond = _getRequiredBond(1);
vm.deal(PROPOSER, secondBond);
gameProxy.defend{ value: secondBond }(1, Claim.wrap(0));
uint256 thirdBond = _getRequiredBond(2);
vm.deal(PROPOSER, thirdBond);
gameProxy.move{ value: thirdBond }(2, Claim.wrap(0), true);
vm.stopPrank(); vm.stopPrank();
} }
...@@ -157,6 +167,13 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init { ...@@ -157,6 +167,13 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
gameProxy.move(2, Claim.wrap(0), true); gameProxy.move(2, Claim.wrap(0), true);
vm.stopPrank(); vm.stopPrank();
} }
/// @dev Helper to get the required bond for the given claim index.
function _getRequiredBond(uint256 _claimIndex) internal view returns (uint256 bond_) {
(,,,,, Position parent,) = gameProxy.claimData(_claimIndex);
Position pos = parent.move(true);
bond_ = gameProxy.getRequiredBond(pos);
}
} }
/// @dev Helper to change the VM status byte of a claim. /// @dev Helper to change the VM status byte of a claim.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment