forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDisputeGameFactory.t.sol
181 lines (147 loc) · 8.22 KB
/
DisputeGameFactory.t.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../src/libraries/DisputeTypes.sol";
import "../src/libraries/DisputeErrors.sol";
import { Test } from "forge-std/Test.sol";
import { DisputeGameFactory } from "../src/dispute/DisputeGameFactory.sol";
import { IDisputeGame } from "../src/dispute/interfaces/IDisputeGame.sol";
import { Proxy } from "../src/universal/Proxy.sol";
import { L2OutputOracle_Initializer } from "./CommonTest.t.sol";
contract DisputeGameFactory_Init is L2OutputOracle_Initializer {
DisputeGameFactory factory;
FakeClone fakeClone;
event DisputeGameCreated(address indexed disputeProxy, GameType indexed gameType, Claim indexed rootClaim);
event ImplementationSet(address indexed impl, GameType indexed gameType);
function setUp() public virtual override {
super.setUp();
Proxy proxy = new Proxy(address(this));
DisputeGameFactory impl = new DisputeGameFactory();
proxy.upgradeToAndCall({
_implementation: address(impl),
_data: abi.encodeCall(impl.initialize, (address(this)))
});
factory = DisputeGameFactory(address(proxy));
vm.label(address(factory), "DisputeGameFactoryProxy");
fakeClone = new FakeClone();
}
}
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 {
// 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++) {
factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
}
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy));
assertEq(Timestamp.unwrap(timestamp), block.timestamp);
assertEq(factory.gameCount(), 1);
(, Timestamp timestamp2, IDisputeGame game2) = factory.gameAtIndex(0);
assertEq(address(game2), address(proxy));
assertEq(Timestamp.unwrap(timestamp2), block.timestamp);
}
/// @dev Tests that the `create` function reverts when there is no implementation
/// set for the given `GameType`.
function testFuzz_create_noImpl_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);
vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, gt));
factory.create(gt, rootClaim, extraData);
}
/// @dev Tests that the `create` function reverts when there exists a dispute game with the same UUID.
function testFuzz_create_sameUUID_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++) {
factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
}
// Create our first dispute game - this should succeed.
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy));
assertEq(Timestamp.unwrap(timestamp), block.timestamp);
// Ensure that the `create` function reverts when called with parameters that would result in the same UUID.
vm.expectRevert(
abi.encodeWithSelector(GameAlreadyExists.selector, factory.getGameUUID(gt, rootClaim, extraData))
);
factory.create(gt, rootClaim, extraData);
}
function changeClaimStatus(Claim _claim, VMStatus _status) public pure returns (Claim out_) {
assembly {
out_ := or(and(not(shl(248, 0xFF)), _claim), shl(248, _status))
}
}
}
contract DisputeGameFactory_SetImplementation_Test is DisputeGameFactory_Init {
/// @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`.
function test_setImplementation_succeeds() public {
// There should be no implementation for the `GameTypes.FAULT` enum value, it has not been set.
assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(0));
vm.expectEmit(true, true, true, true, address(factory));
emit ImplementationSet(address(1), GameTypes.FAULT);
// Set the implementation for the `GameTypes.FAULT` enum value.
factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
// Ensure that the implementation for the `GameTypes.FAULT` enum value is set.
assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(1));
}
/// @dev Tests that the `setImplementation` function reverts when called by a non-owner.
function test_setImplementation_notOwner_reverts() public {
// Ensure that the `setImplementation` function reverts when called by a non-owner.
vm.prank(address(0));
vm.expectRevert("Ownable: caller is not the owner");
factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
}
}
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.
function testDiff_getGameUUID_succeeds(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)));
assertEq(
Hash.unwrap(factory.getGameUUID(gt, rootClaim, extraData)), keccak256(abi.encode(gt, rootClaim, extraData))
);
}
}
contract DisputeGameFactory_Owner_Test is DisputeGameFactory_Init {
/// @dev Tests that the `owner` function returns the correct address after deployment.
function test_owner_succeeds() public {
assertEq(factory.owner(), address(this));
}
}
contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init {
/// @dev Tests that the `transferOwnership` function succeeds when called by the owner.
function test_transferOwnership_succeeds() public {
factory.transferOwnership(address(1));
assertEq(factory.owner(), address(1));
}
/// @dev Tests that the `transferOwnership` function reverts when called by a non-owner.
function test_transferOwnership_notOwner_reverts() public {
vm.prank(address(0));
vm.expectRevert("Ownable: caller is not the owner");
factory.transferOwnership(address(1));
}
}
/// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function.
contract FakeClone {
function initialize() external {
// noop
}
}