Commit 19c79fe6 authored by clabby's avatar clabby Committed by GitHub

feat(ctb + proposer): Send initial bond when submitting outputs to DGF (#8821)

* Add initial bond to `DisputeGameFactory`

* Update proposer

* fdg semver

* fmt

* bindings + semver + slither

* @refcell nit
parent e496caa5
This diff is collapsed.
This diff is collapsed.
......@@ -310,8 +310,16 @@ func proposeL2OutputTxData(abi *abi.ABI, output *eth.OutputResponse) ([]byte, er
new(big.Int).SetUint64(output.Status.CurrentL1.Number))
}
func (l *L2OutputSubmitter) ProposeL2OutputDGFTxData(output *eth.OutputResponse) ([]byte, error) {
return proposeL2OutputDGFTxData(l.dgfABI, l.Cfg.DisputeGameType, output)
func (l *L2OutputSubmitter) ProposeL2OutputDGFTxData(output *eth.OutputResponse) ([]byte, *big.Int, error) {
bond, err := l.dgfContract.InitBonds(&bind.CallOpts{}, l.Cfg.DisputeGameType)
if err != nil {
return nil, nil, err
}
data, err := proposeL2OutputDGFTxData(l.dgfABI, l.Cfg.DisputeGameType, output)
if err != nil {
return nil, nil, err
}
return data, bond, err
}
// proposeL2OutputDGFTxData creates the transaction data for the DisputeGameFactory's `create` function
......@@ -356,7 +364,7 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out
var receipt *types.Receipt
if l.Cfg.DisputeGameFactoryAddr != nil {
data, err := l.ProposeL2OutputDGFTxData(output)
data, bond, err := l.ProposeL2OutputDGFTxData(output)
if err != nil {
return err
}
......@@ -364,6 +372,7 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out
TxData: data,
To: l.Cfg.DisputeGameFactoryAddr,
GasLimit: 0,
Value: bond,
})
if err != nil {
return err
......
......@@ -88,12 +88,12 @@
"sourceCodeHash": "0x1afb1d392e8f6a58ff86ea7f648e0d1756d4ba8d0d964279d58a390deaa53b7e"
},
"src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0x79ede9274590494d776c9e2ecdf006cb7106f97490841ea85b52fc8eb18cd7b9",
"sourceCodeHash": "0x6e3a697764840cd30ca4b3e514528b832ce80bb412ac958b5ec6b751a07d735f"
"initCodeHash": "0xf6ab546550f8d3d85e9bde748eeea21f7fb16db3d63b8312c04598ca64c1be16",
"sourceCodeHash": "0x01c757935c87dcf2faa7f16bcf29cfb29b90c671a874f983067614670147b11d"
},
"src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0xe3c50aa8ab4ba7a4b4b6d5752a3a3acc2ca0efccbc086df8e00f85d4d696ab15",
"sourceCodeHash": "0xbc5e4e6ec9d4438494cacd6ed284456b41a9b6e090fa4b39a59cf122c8c3667d"
"initCodeHash": "0x2f30aa96d5c4d126d93dfe15bbfc90e1bbd6bc6208bcc4a49e250790c2a0918d",
"sourceCodeHash": "0x315508f5b100dcb6b81077fb8d6999de425b47f0dfd0330ac1973ee4df166f4a"
},
"src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d",
......
......@@ -952,7 +952,7 @@
"impact": "Medium",
"confidence": "Medium",
"check": "reentrancy-no-eth",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#664-723)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#637-655)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#512-514)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-509)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#662-721)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#635-653)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#510-512)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-507)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"type": "function",
"name": "step",
"start": 4981,
......@@ -964,7 +964,7 @@
"impact": "Medium",
"confidence": "Medium",
"check": "reentrancy-no-eth",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#664-723)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#637-655)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#512-514)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-509)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#662-721)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#635-653)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#510-512)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-507)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"type": "node",
"name": "validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw()",
"start": 8761,
......@@ -976,7 +976,7 @@
"impact": "Medium",
"confidence": "Medium",
"check": "reentrancy-no-eth",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#664-723)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#637-655)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#512-514)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-509)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"description": "Reentrancy in FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191):\n\tExternal calls:\n\t- validStep = VM.step(_stateData,_proof,uuid.raw()) == postState.claim.raw() (src/dispute/FaultDisputeGame.sol#184)\n\tState variables written after the call(s):\n\t- parent.counteredBy = msg.sender (src/dispute/FaultDisputeGame.sol#190)\n\tFaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66) can be used in cross function reentrancies:\n\t- FaultDisputeGame._findStartingAndDisputedOutputs(uint256) (src/dispute/FaultDisputeGame.sol#662-721)\n\t- FaultDisputeGame._findTraceAncestor(Position,uint256,bool) (src/dispute/FaultDisputeGame.sol#635-653)\n\t- FaultDisputeGame.claimData (src/dispute/FaultDisputeGame.sol#66)\n\t- FaultDisputeGame.claimDataLen() (src/dispute/FaultDisputeGame.sol#510-512)\n\t- FaultDisputeGame.initialize() (src/dispute/FaultDisputeGame.sol#454-507)\n\t- FaultDisputeGame.move(uint256,Claim,bool) (src/dispute/FaultDisputeGame.sol#197-288)\n\t- FaultDisputeGame.resolve() (src/dispute/FaultDisputeGame.sol#353-365)\n\t- FaultDisputeGame.resolveClaim(uint256) (src/dispute/FaultDisputeGame.sol#368-428)\n\t- FaultDisputeGame.step(uint256,bool,bytes,bytes) (src/dispute/FaultDisputeGame.sol#124-191)\n",
"type": "node",
"name": "parent.counteredBy = msg.sender",
"start": 9171,
......@@ -1072,10 +1072,10 @@
"impact": "Medium",
"confidence": "Medium",
"check": "uninitialized-local",
"description": "FaultDisputeGame._findStartingAndDisputedOutputs(uint256).currentDepth (src/dispute/FaultDisputeGame.sol#682) is a local variable never initialized\n",
"description": "FaultDisputeGame._findStartingAndDisputedOutputs(uint256).currentDepth (src/dispute/FaultDisputeGame.sol#680) is a local variable never initialized\n",
"type": "variable",
"name": "currentDepth",
"start": 32191,
"start": 32115,
"length": 20,
"filename_relative": "src/dispute/FaultDisputeGame.sol"
},
......
......@@ -157,6 +157,25 @@
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "GameType",
"name": "",
"type": "uint8"
}
],
"name": "initBonds",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
......@@ -208,6 +227,24 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "GameType",
"name": "_gameType",
"type": "uint8"
},
{
"internalType": "uint256",
"name": "_initBond",
"type": "uint256"
}
],
"name": "setInitBond",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
......@@ -278,6 +315,25 @@
"name": "ImplementationSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "GameType",
"name": "gameType",
"type": "uint8"
},
{
"indexed": true,
"internalType": "uint256",
"name": "newBond",
"type": "uint256"
}
],
"name": "InitBondUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
......@@ -321,6 +377,11 @@
"name": "GameAlreadyExists",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientBond",
"type": "error"
},
{
"inputs": [
{
......
......@@ -43,16 +43,23 @@
},
{
"bytes": "32",
"label": "_disputeGames",
"label": "initBonds",
"offset": 0,
"slot": "102",
"type": "mapping(GameType => uint256)"
},
{
"bytes": "32",
"label": "_disputeGames",
"offset": 0,
"slot": "103",
"type": "mapping(Hash => GameId)"
},
{
"bytes": "32",
"label": "_disputeGameList",
"offset": 0,
"slot": "103",
"slot": "104",
"type": "GameId[]"
}
]
\ No newline at end of file
......@@ -23,9 +23,16 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
/// @dev Allows for the creation of clone proxies with immutable arguments.
using ClonesWithImmutableArgs for address;
/// @notice Semantic version.
/// @custom:semver 0.0.8
string public constant version = "0.0.8";
/// @inheritdoc IDisputeGameFactory
mapping(GameType => IDisputeGame) public gameImpls;
/// @inheritdoc IDisputeGameFactory
mapping(GameType => uint256) public initBonds;
/// @notice Mapping of a hash of `gameType || rootClaim || extraData` to
/// the deployed `IDisputeGame` clone.
/// @dev Note: `||` denotes concatenation.
......@@ -36,10 +43,6 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
/// track dispute games
GameId[] internal _disputeGameList;
/// @notice Semantic version.
/// @custom:semver 0.0.7
string public constant version = "0.0.7";
/// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() {
initialize(address(0));
......@@ -96,6 +99,9 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
// If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) revert NoImplementation(_gameType);
// If the required initialization bond is not met, revert.
if (msg.value < initBonds[_gameType]) revert InsufficientBond();
// Clone the implementation contract and initialize it with the given parameters.
proxy_ = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData)));
proxy_.initialize{ value: msg.value }();
......@@ -132,4 +138,10 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, ISemver
gameImpls[_gameType] = _impl;
emit ImplementationSet(address(_impl), _gameType);
}
/// @inheritdoc IDisputeGameFactory
function setInitBond(GameType _gameType, uint256 _initBond) external onlyOwner {
initBonds[_gameType] = _initBond;
emit InitBondUpdated(_gameType, _initBond);
}
}
......@@ -81,8 +81,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
bool internal initialized;
/// @notice Semantic version.
/// @custom:semver 0.0.23
string public constant version = "0.0.23";
/// @custom:semver 0.0.24
string public constant version = "0.0.24";
/// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace.
......@@ -457,6 +457,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
//
// Implicit assumptions:
// - The `gameStatus` state variable defaults to 0, which is `GameStatus.IN_PROGRESS`
// - The dispute game factory will enforce the required bond to initialize the game.
//
// Explicit checks:
// - The game must not have already been initialized.
......@@ -482,9 +483,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
}
}
// INVARIANT: The `msg.value` must be sufficient to cover the required bond.
if (getRequiredBond(ROOT_POSITION) > msg.value) revert InsufficientBond();
// Set the root claim
claimData.push(
ClaimData({
......
......@@ -19,6 +19,11 @@ interface IDisputeGameFactory {
/// @param gameType The type of the DisputeGame.
event ImplementationSet(address indexed impl, GameType indexed gameType);
/// @notice Emitted when a game type's initialization bond is updated
/// @param gameType The type of the DisputeGame.
/// @param newBond The new bond (in wei) for initializing the game type.
event InitBondUpdated(GameType indexed gameType, uint256 indexed newBond);
/// @notice The total number of dispute games created by this factory.
/// @return gameCount_ The total number of dispute games created by this factory.
function gameCount() external view returns (uint256 gameCount_);
......@@ -60,6 +65,11 @@ interface IDisputeGameFactory {
/// Will be cloned on creation of a new dispute game with the given `gameType`.
function gameImpls(GameType _gameType) external view returns (IDisputeGame impl_);
/// @notice Returns the required bonds for initializing a dispute game of the given type.
/// @param _gameType The type of the dispute game.
/// @return bond_ The required bond for initializing a dispute game of the given type.
function initBonds(GameType _gameType) external view returns (uint256 bond_);
/// @notice Creates a new DisputeGame proxy contract.
/// @param _gameType The type of the DisputeGame - used to decide the proxy implementation.
/// @param _rootClaim The root claim of the DisputeGame.
......@@ -80,6 +90,12 @@ interface IDisputeGameFactory {
/// @param _impl The implementation contract for the given `GameType`.
function setImplementation(GameType _gameType, IDisputeGame _impl) external;
/// @notice Sets the bond (in wei) for initializing a game type.
/// @dev May only be called by the `owner`.
/// @param _gameType The type of the DisputeGame.
/// @param _initBond The bond (in wei) for initializing a game type.
function setInitBond(GameType _gameType, uint256 _initBond) external;
/// @notice Returns a unique identifier for the given dispute game parameters.
/// @dev Hashes the concatenation of `gameType . rootClaim . extraData`
/// without expanding memory.
......
......@@ -15,8 +15,8 @@ contract DisputeGameFactory_Init is CommonTest {
FakeClone fakeClone;
event DisputeGameCreated(address indexed disputeProxy, GameType indexed gameType, Claim indexed rootClaim);
event ImplementationSet(address indexed impl, GameType indexed gameType);
event InitBondUpdated(GameType indexed gameType, uint256 indexed newBond);
function setUp() public virtual override {
super.setUp();
......@@ -38,7 +38,14 @@ contract DisputeGameFactory_Init is CommonTest {
contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
/// @dev Tests that the `create` function succeeds when creating a new dispute game
/// with a `GameType` that has an implementation set.
function testFuzz_create_succeeds(uint8 gameType, Claim rootClaim, bytes calldata extraData) public {
function testFuzz_create_succeeds(
uint8 gameType,
Claim rootClaim,
bytes calldata extraData,
uint256 _value
)
public
{
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the rootClaim has a VMStatus that disagrees with the validity.
......@@ -46,12 +53,16 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
GameType lgt = GameType.wrap(i);
factory.setImplementation(lgt, IDisputeGame(address(fakeClone)));
factory.setInitBond(lgt, _value);
}
vm.deal(address(this), _value);
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
IDisputeGame proxy = factory.create{ value: _value }(gt, rootClaim, extraData);
(IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
......@@ -63,6 +74,33 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
(, Timestamp timestamp2, IDisputeGame game2) = factory.gameAtIndex(0);
assertEq(address(game2), address(proxy));
assertEq(Timestamp.unwrap(timestamp2), block.timestamp);
// Ensure that the game proxy received the bonded ETH.
assertEq(address(proxy).balance, _value);
}
/// @dev Tests that the `create` function reverts when creating a new dispute game with an insufficient bond.
function testFuzz_create_insufficientBond_reverts(
uint8 gameType,
Claim rootClaim,
bytes calldata extraData
)
public
{
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the rootClaim has a VMStatus that disagrees with the validity.
rootClaim = changeClaimStatus(rootClaim, VMStatuses.INVALID);
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
GameType lgt = GameType.wrap(i);
factory.setImplementation(lgt, IDisputeGame(address(fakeClone)));
factory.setInitBond(lgt, 1 ether);
}
vm.expectRevert(InsufficientBond.selector);
factory.create(gt, rootClaim, extraData);
}
/// @dev Tests that the `create` function reverts when there is no implementation
......@@ -138,6 +176,31 @@ contract DisputeGameFactory_SetImplementation_Test is DisputeGameFactory_Init {
}
}
contract DisputeGameFactory_SetInitBond_Test is DisputeGameFactory_Init {
/// @dev Tests that the `setInitBond` function properly sets the init bond for a given `GameType`.
function test_setInitBond_succeeds() public {
// There should be no init bond for the `GameTypes.CANNON` enum value, it has not been set.
assertEq(factory.initBonds(GameTypes.CANNON), 0);
vm.expectEmit(true, true, true, true, address(factory));
emit InitBondUpdated(GameTypes.CANNON, 1 ether);
// Set the init bond for the `GameTypes.CANNON` enum value.
factory.setInitBond(GameTypes.CANNON, 1 ether);
// Ensure that the init bond for the `GameTypes.CANNON` enum value is set.
assertEq(factory.initBonds(GameTypes.CANNON), 1 ether);
}
/// @dev Tests that the `setInitBond` function reverts when called by a non-owner.
function test_setInitBond_notOwner_reverts() public {
// Ensure that the `setInitBond` function reverts when called by a non-owner.
vm.prank(address(0));
vm.expectRevert("Ownable: caller is not the owner");
factory.setInitBond(GameTypes.CANNON, 1 ether);
}
}
contract DisputeGameFactory_GetGameUUID_Test is DisputeGameFactory_Init {
/// @dev Tests that the `getGameUUID` function returns the correct hash when comparing
/// against the keccak256 hash of the abi-encoded parameters.
......@@ -175,7 +238,7 @@ contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init {
/// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function.
contract FakeClone {
function initialize() external {
function initialize() external payable {
// noop
}
}
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