-
Notifications
You must be signed in to change notification settings - Fork 0
/
DisputeGameFactory.sol
175 lines (147 loc) · 6.94 KB
/
DisputeGameFactory.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// IMPORTANT!! This is forked from the Optimism implmentation of DisputeGameFactory.sol and should
// not be considered as part of the hackathon submission.
import "@eth-optimism/contracts-bedrock/src/libraries/DisputeTypes.sol";
import "@eth-optimism/contracts-bedrock/src/libraries/DisputeErrors.sol";
import { ClonesWithImmutableArgs } from "clones-with-immutable-args/ClonesWithImmutableArgs.sol";
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Semver } from "@eth-optimism/contracts-bedrock/src/universal/Semver.sol";
import { IDisputeGame } from "@eth-optimism/contracts-bedrock/src/dispute/interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "@eth-optimism/contracts-bedrock/src/dispute/interfaces/IDisputeGameFactory.sol";
/// @title DisputeGameFactory
/// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games
/// are stored in both a mapping and an append only array. The timestamp of the creation
/// time of the dispute game is packed tightly into the storage slot with the address of
/// the dispute game. This is to make offchain discoverability of playable dispute games
/// easier.
contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
/// @dev Allows for the creation of clone proxies with immutable arguments.
using ClonesWithImmutableArgs for address;
/// @inheritdoc IDisputeGameFactory
mapping(GameType => IDisputeGame) public gameImpls;
/// @notice Mapping of a hash of `gameType || rootClaim || extraData` to
/// the deployed `IDisputeGame` clone.
/// @dev Note: `||` denotes concatenation.
mapping(Hash => GameId) internal _disputeGames;
/// @notice an append-only array of disputeGames that have been created.
/// @dev this accessor is used by offchain game solvers to efficiently
/// track dispute games
GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() Semver(0, 0, 2) {
initialize(msg.sender);
}
/// @notice Initializes the contract.
/// @param _owner The owner of the contract.
function initialize(address _owner) public initializer {
__Ownable_init();
_transferOwnership(_owner);
}
/// @inheritdoc IDisputeGameFactory
function gameCount() external view returns (uint256 gameCount_) {
gameCount_ = _disputeGameList.length;
}
/// @inheritdoc IDisputeGameFactory
function games(
GameType _gameType,
Claim _rootClaim,
bytes calldata _extraData
) external view returns (IDisputeGame proxy_, uint256 timestamp_) {
Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
GameId slot = _disputeGames[uuid];
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
}
/// @inheritdoc IDisputeGameFactory
function gameAtIndex(uint256 _index)
external
view
returns (IDisputeGame proxy_, uint256 timestamp_)
{
GameId slot = _disputeGameList[_index];
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
}
/// @inheritdoc IDisputeGameFactory
function create(
GameType gameType,
Claim rootClaim,
bytes calldata extraData
) external returns (IDisputeGame proxy) {
// Grab the implementation contract for the given `GameType`.
IDisputeGame impl = gameImpls[gameType];
// If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) {
revert NoImplementation(gameType);
}
// Clone the implementation contract and initialize it with the given parameters.
proxy = IDisputeGame(address(impl).clone(abi.encodePacked(rootClaim, extraData)));
proxy.initialize();
// Compute the unique identifier for the dispute game.
Hash uuid = getGameUUID(gameType, rootClaim, extraData);
// If a dispute game with the same UUID already exists, revert.
if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) {
revert GameAlreadyExists(uuid);
}
GameId slot = _packSlot(address(proxy), block.timestamp);
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event.
_disputeGames[uuid] = slot;
_disputeGameList.push(slot);
emit DisputeGameCreated(address(proxy), gameType, rootClaim);
}
/// @inheritdoc IDisputeGameFactory
function getGameUUID(
GameType gameType,
Claim rootClaim,
bytes memory extraData
) public pure returns (Hash _uuid) {
assembly {
// Grab the offsets of the other memory locations we will need to temporarily overwrite.
let gameTypeOffset := sub(extraData, 0x60)
let rootClaimOffset := add(gameTypeOffset, 0x20)
let pointerOffset := add(rootClaimOffset, 0x20)
// Copy the memory that we will temporarily overwrite onto the stack
// so we can restore it later
let tempA := mload(gameTypeOffset)
let tempB := mload(rootClaimOffset)
let tempC := mload(pointerOffset)
// Overwrite the memory with the data we want to hash
mstore(gameTypeOffset, gameType)
mstore(rootClaimOffset, rootClaim)
mstore(pointerOffset, 0x60)
// Compute the length of the memory to hash
// `0x60 + 0x20 + extraData.length` rounded to the *next* multiple of 32.
let hashLen := and(add(mload(extraData), 0x9F), not(0x1F))
// Hash the memory to produce the UUID digest
_uuid := keccak256(gameTypeOffset, hashLen)
// Restore the memory prior to `extraData`
mstore(gameTypeOffset, tempA)
mstore(rootClaimOffset, tempB)
mstore(pointerOffset, tempC)
}
}
/// @inheritdoc IDisputeGameFactory
function setImplementation(GameType gameType, IDisputeGame impl) external onlyOwner {
gameImpls[gameType] = impl;
emit ImplementationSet(address(impl), gameType);
}
/// @dev Packs an address and a uint256 into a single bytes32 slot. This
/// is only safe for up to uint96 values.
function _packSlot(address _addr, uint256 _num) internal pure returns (GameId slot_) {
assembly {
slot_ := or(shl(0xa0, _num), _addr)
}
}
/// @dev Unpacks an address and a uint256 from a single bytes32 slot.
function _unpackSlot(GameId _slot) internal pure returns (address addr_, uint256 num_) {
assembly {
addr_ := and(_slot, 0xffffffffffffffffffffffffffffffffffffffff)
num_ := shr(0xa0, _slot)
}
}
}