Commit 9d9a79a3 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6900 from ethereum-optimism/clabby/ctb/dispute-game-type-storage

feat(ctb): Pack `GameType` in DGF's `GameId` type
parents 4b700774 f077424a
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -32,10 +32,30 @@ type TraceType string ...@@ -32,10 +32,30 @@ type TraceType string
const ( const (
TraceTypeAlphabet TraceType = "alphabet" TraceTypeAlphabet TraceType = "alphabet"
TraceTypeCannon TraceType = "cannon" TraceTypeCannon TraceType = "cannon"
// Devnet game IDs
DevnetGameIDAlphabet = uint8(0)
DevnetGameIDCannon = uint8(1)
// Mainnet game IDs
MainnetGameIDFault = uint8(0)
) )
var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon} var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon}
// GameIdToString maps game IDs to their string representation on a per-network basis.
var GameIdToString = map[uint64]map[uint8]string{
// Mainnet
1: {
MainnetGameIDFault: "fault-cannon",
},
// Devnet
900: {
DevnetGameIDAlphabet: "fault-alphabet",
DevnetGameIDCannon: "fault-cannon",
},
}
func (t TraceType) String() string { func (t TraceType) String() string {
return string(t) return string(t)
} }
......
...@@ -19,14 +19,16 @@ var ( ...@@ -19,14 +19,16 @@ var (
type MinimalDisputeGameFactoryCaller interface { type MinimalDisputeGameFactoryCaller interface {
GameCount(opts *bind.CallOpts) (*big.Int, error) GameCount(opts *bind.CallOpts) (*big.Int, error)
GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct { GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) }, error)
} }
type FaultDisputeGame struct { type FaultDisputeGame struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
} }
// GameLoader is a minimal interface for fetching on chain dispute games. // GameLoader is a minimal interface for fetching on chain dispute games.
......
...@@ -90,7 +90,7 @@ func generateMockGames(count uint64) []FaultDisputeGame { ...@@ -90,7 +90,7 @@ func generateMockGames(count uint64) []FaultDisputeGame {
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
games[i] = FaultDisputeGame{ games[i] = FaultDisputeGame{
Proxy: common.BigToAddress(big.NewInt(int64(i))), Proxy: common.BigToAddress(big.NewInt(int64(i))),
Timestamp: big.NewInt(int64(i)), Timestamp: i,
} }
} }
...@@ -151,22 +151,26 @@ func (m *mockMinimalDisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*b ...@@ -151,22 +151,26 @@ func (m *mockMinimalDisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*b
} }
func (m *mockMinimalDisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct { func (m *mockMinimalDisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) { }, error) {
index := _index.Uint64() index := _index.Uint64()
if m.indexErrors[index] { if m.indexErrors[index] {
return struct { return struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}{}, gameIndexErr }{}, gameIndexErr
} }
return struct { return struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}{ }{
Proxy: m.games[index].Proxy, GameType: m.games[index].GameType,
Timestamp: m.games[index].Timestamp, Timestamp: m.games[index].Timestamp,
Proxy: m.games[index].Proxy,
}, nil }, nil
} }
...@@ -8,6 +8,8 @@ import { Semver } from "src/universal/Semver.sol"; ...@@ -8,6 +8,8 @@ import { Semver } from "src/universal/Semver.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol"; import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { LibGameId } from "src/dispute/lib/LibGameId.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol"; import "src/libraries/DisputeErrors.sol";
...@@ -35,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -35,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
GameId[] internal _disputeGameList; GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract. /// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() Semver(0, 0, 4) { constructor() OwnableUpgradeable() Semver(0, 0, 5) {
initialize(address(0)); initialize(address(0));
} }
...@@ -59,61 +61,67 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -59,61 +61,67 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
) )
external external
view view
returns (IDisputeGame proxy_, uint256 timestamp_) returns (IDisputeGame proxy_, Timestamp timestamp_)
{ {
Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData); Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
GameId slot = _disputeGames[uuid]; (, timestamp_, proxy_) = _disputeGames[uuid].unpack();
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function gameAtIndex(uint256 _index) external view returns (IDisputeGame proxy_, uint256 timestamp_) { function gameAtIndex(uint256 _index)
GameId slot = _disputeGameList[_index]; external
(address addr, uint256 timestamp) = _unpackSlot(slot); view
proxy_ = IDisputeGame(addr); returns (GameType gameType_, Timestamp timestamp_, IDisputeGame proxy_)
timestamp_ = timestamp; {
(gameType_, timestamp_, proxy_) = _disputeGameList[_index].unpack();
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function create( function create(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
returns (IDisputeGame proxy) returns (IDisputeGame proxy)
{ {
// Grab the implementation contract for the given `GameType`. // Grab the implementation contract for the given `GameType`.
IDisputeGame impl = gameImpls[gameType]; IDisputeGame impl = gameImpls[_gameType];
// If there is no implementation to clone for the given `GameType`, revert. // If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) revert NoImplementation(gameType); if (address(impl) == address(0)) revert NoImplementation(_gameType);
// Clone the implementation contract and initialize it with the given parameters. // Clone the implementation contract and initialize it with the given parameters.
proxy = IDisputeGame(address(impl).clone(abi.encodePacked(rootClaim, extraData))); proxy = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData)));
proxy.initialize(); proxy.initialize();
// Compute the unique identifier for the dispute game. // Compute the unique identifier for the dispute game.
Hash uuid = getGameUUID(gameType, rootClaim, extraData); Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
// If a dispute game with the same UUID already exists, revert. // If a dispute game with the same UUID already exists, revert.
if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid); if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid);
GameId slot = _packSlot(address(proxy), block.timestamp); GameId id = LibGameId.pack(_gameType, Timestamp.wrap(uint64(block.timestamp)), proxy);
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event. // Store the dispute game id in the mapping & emit the `DisputeGameCreated` event.
_disputeGames[uuid] = slot; _disputeGames[uuid] = id;
_disputeGameList.push(slot); _disputeGameList.push(id);
emit DisputeGameCreated(address(proxy), gameType, rootClaim); emit DisputeGameCreated(address(proxy), _gameType, _rootClaim);
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function getGameUUID(GameType gameType, Claim rootClaim, bytes memory extraData) public pure returns (Hash _uuid) { function getGameUUID(
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
public
pure
returns (Hash _uuid)
{
assembly { assembly {
// Grab the offsets of the other memory locations we will need to temporarily overwrite. // Grab the offsets of the other memory locations we will need to temporarily overwrite.
let gameTypeOffset := sub(extraData, 0x60) let gameTypeOffset := sub(_extraData, 0x60)
let rootClaimOffset := add(gameTypeOffset, 0x20) let rootClaimOffset := add(gameTypeOffset, 0x20)
let pointerOffset := add(rootClaimOffset, 0x20) let pointerOffset := add(rootClaimOffset, 0x20)
...@@ -124,13 +132,13 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -124,13 +132,13 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
let tempC := mload(pointerOffset) let tempC := mload(pointerOffset)
// Overwrite the memory with the data we want to hash // Overwrite the memory with the data we want to hash
mstore(gameTypeOffset, gameType) mstore(gameTypeOffset, _gameType)
mstore(rootClaimOffset, rootClaim) mstore(rootClaimOffset, _rootClaim)
mstore(pointerOffset, 0x60) mstore(pointerOffset, 0x60)
// Compute the length of the memory to hash // Compute the length of the memory to hash
// `0x60 + 0x20 + extraData.length` rounded to the *next* multiple of 32. // `0x60 + 0x20 + extraData.length` rounded to the *next* multiple of 32.
let hashLen := and(add(mload(extraData), 0x9F), not(0x1F)) let hashLen := and(add(mload(_extraData), 0x9F), not(0x1F))
// Hash the memory to produce the UUID digest // Hash the memory to produce the UUID digest
_uuid := keccak256(gameTypeOffset, hashLen) _uuid := keccak256(gameTypeOffset, hashLen)
...@@ -143,24 +151,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -143,24 +151,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function setImplementation(GameType gameType, IDisputeGame impl) external onlyOwner { function setImplementation(GameType _gameType, IDisputeGame _impl) external onlyOwner {
gameImpls[gameType] = impl; gameImpls[_gameType] = _impl;
emit ImplementationSet(address(impl), gameType); 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)
}
} }
} }
...@@ -20,8 +20,8 @@ interface IDisputeGameFactory { ...@@ -20,8 +20,8 @@ interface IDisputeGameFactory {
event ImplementationSet(address indexed impl, GameType indexed gameType); event ImplementationSet(address indexed impl, GameType indexed gameType);
/// @notice The total number of dispute games created by this factory. /// @notice The total number of dispute games created by this factory.
/// @return _gameCount 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); function gameCount() external view returns (uint256 gameCount_);
/// @notice `games` queries an internal mapping that maps the hash of /// @notice `games` queries an internal mapping that maps the hash of
/// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone. /// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone.
...@@ -29,9 +29,9 @@ interface IDisputeGameFactory { ...@@ -29,9 +29,9 @@ interface IDisputeGameFactory {
/// @param gameType The type of the DisputeGame - used to decide the proxy implementation /// @param gameType The type of the DisputeGame - used to decide the proxy implementation
/// @param rootClaim The root claim of the DisputeGame. /// @param rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param extraData Any extra data that should be provided to the created dispute game.
/// @return _proxy The clone of the `DisputeGame` created with the given parameters. /// @return proxy_ The clone of the `DisputeGame` created with the given parameters.
/// Returns `address(0)` if nonexistent. /// Returns `address(0)` if nonexistent.
/// @return _timestamp The timestamp of the creation of the dispute game. /// @return timestamp_ The timestamp of the creation of the dispute game.
function games( function games(
GameType gameType, GameType gameType,
Claim rootClaim, Claim rootClaim,
...@@ -39,53 +39,57 @@ interface IDisputeGameFactory { ...@@ -39,53 +39,57 @@ interface IDisputeGameFactory {
) )
external external
view view
returns (IDisputeGame _proxy, uint256 _timestamp); returns (IDisputeGame proxy_, Timestamp timestamp_);
/// @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp /// @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp
/// at the given index. Each created dispute game increments the underlying index. /// at the given index. Each created dispute game increments the underlying index.
/// @param _index The index of the dispute game. /// @param _index The index of the dispute game.
/// @return _proxy The clone of the `DisputeGame` created with the given parameters. /// @return gameType_ The type of the DisputeGame - used to decide the proxy implementation.
/// @return timestamp_ The timestamp of the creation of the dispute game.
/// @return proxy_ The clone of the `DisputeGame` created with the given parameters.
/// Returns `address(0)` if nonexistent. /// Returns `address(0)` if nonexistent.
/// @return _timestamp The timestamp of the creation of the dispute game. function gameAtIndex(uint256 _index)
function gameAtIndex(uint256 _index) external view returns (IDisputeGame _proxy, uint256 _timestamp); external
view
returns (GameType gameType_, Timestamp timestamp_, IDisputeGame proxy_);
/// @notice `gameImpls` is a mapping that maps `GameType`s to their respective /// @notice `gameImpls` is a mapping that maps `GameType`s to their respective
/// `IDisputeGame` implementations. /// `IDisputeGame` implementations.
/// @param gameType The type of the dispute game. /// @param _gameType The type of the dispute game.
/// @return _impl The address of the implementation of the game type. /// @return _impl The address of the implementation of the game type.
/// Will be cloned on creation of a new dispute game with the given `gameType`. /// Will be cloned on creation of a new dispute game with the given `gameType`.
function gameImpls(GameType gameType) external view returns (IDisputeGame _impl); function gameImpls(GameType _gameType) external view returns (IDisputeGame _impl);
/// @notice Creates a new DisputeGame proxy contract. /// @notice Creates a new DisputeGame proxy contract.
/// @param gameType The type of the DisputeGame - used to decide the proxy implementation. /// @param _gameType The type of the DisputeGame - used to decide the proxy implementation.
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return proxy The address of the created DisputeGame proxy. /// @return proxy_ The address of the created DisputeGame proxy.
function create( function create(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
returns (IDisputeGame proxy); returns (IDisputeGame proxy_);
/// @notice Sets the implementation contract for a specific `GameType`. /// @notice Sets the implementation contract for a specific `GameType`.
/// @dev May only be called by the `owner`. /// @dev May only be called by the `owner`.
/// @param gameType The type of the DisputeGame. /// @param _gameType The type of the DisputeGame.
/// @param impl The implementation contract for the given `GameType`. /// @param _impl The implementation contract for the given `GameType`.
function setImplementation(GameType gameType, IDisputeGame impl) external; function setImplementation(GameType _gameType, IDisputeGame _impl) external;
/// @notice Returns a unique identifier for the given dispute game parameters. /// @notice Returns a unique identifier for the given dispute game parameters.
/// @dev Hashes the concatenation of `gameType . rootClaim . extraData` /// @dev Hashes the concatenation of `gameType . rootClaim . extraData`
/// without expanding memory. /// without expanding memory.
/// @param gameType The type of the DisputeGame. /// @param _gameType The type of the DisputeGame.
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return _uuid The unique identifier for the given dispute game parameters. /// @return _uuid The unique identifier for the given dispute game parameters.
function getGameUUID( function getGameUUID(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes memory extraData bytes memory _extraData
) )
external external
pure pure
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "src/libraries/DisputeTypes.sol";
import "src/dispute/interfaces/IDisputeGame.sol";
/// @title LibGameId
/// @notice Utility functions for packing and unpacking GameIds.
library LibGameId {
/// @notice Packs values into a 32 byte GameId type.
/// @param _gameType The game type.
/// @param _timestamp The timestamp of the game's creation.
/// @param _gameProxy The game proxy address.
/// @return gameId_ The packed GameId.
function pack(
GameType _gameType,
Timestamp _timestamp,
IDisputeGame _gameProxy
)
internal
pure
returns (GameId gameId_)
{
assembly {
gameId_ := or(or(shl(248, _gameType), shl(184, _timestamp)), _gameProxy)
}
}
/// @notice Unpacks values from a 32 byte GameId type.
/// @param _gameId The packed GameId.
/// @return gameType_ The game type.
/// @return timestamp_ The timestamp of the game's creation.
/// @return gameProxy_ The game proxy address.
function unpack(GameId _gameId)
internal
pure
returns (GameType gameType_, Timestamp timestamp_, IDisputeGame gameProxy_)
{
assembly {
gameType_ := shr(248, _gameId)
timestamp_ := shr(184, and(_gameId, not(shl(248, 0xff))))
gameProxy_ := and(_gameId, 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}
...@@ -4,10 +4,12 @@ pragma solidity ^0.8.15; ...@@ -4,10 +4,12 @@ pragma solidity ^0.8.15;
import { LibHashing } from "../dispute/lib/LibHashing.sol"; import { LibHashing } from "../dispute/lib/LibHashing.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol"; import { LibPosition } from "../dispute/lib/LibPosition.sol";
import { LibClock } from "../dispute/lib/LibClock.sol"; import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibGameId } from "../dispute/lib/LibGameId.sol";
using LibHashing for Claim global; using LibHashing for Claim global;
using LibPosition for Position global; using LibPosition for Position global;
using LibClock for Clock global; using LibClock for Clock global;
using LibGameId for GameId global;
/// @notice A custom type for a generic hash. /// @notice A custom type for a generic hash.
type Hash is bytes32; type Hash is bytes32;
...@@ -29,14 +31,15 @@ type Timestamp is uint64; ...@@ -29,14 +31,15 @@ type Timestamp is uint64;
/// @dev Unit: seconds /// @dev Unit: seconds
type Duration is uint64; type Duration is uint64;
/// @notice A `GameId` represents a packed 12 byte timestamp and a 20 byte address. /// @notice A `GameId` represents a packed 1 byte game ID, an 11 byte timestamp, and a 20 byte address.
/// @dev The packed layout of this type is as follows: /// @dev The packed layout of this type is as follows:
/// ┌────────────┬────────────────┐ /// ┌───────────┬───────────┐
/// │ Bits │ Value │ /// │ Bits │ Value │
/// ├────────────┼────────────────┤ /// ├───────────┼───────────┤
/// │ [0, 96) │ Timestamp │ /// │ [0, 8) │ Game Type │
/// │ [96, 256) │ Address │ /// │ [8, 96) │ Timestamp │
/// └────────────┴────────────────┘ /// │ [96, 256) │ Address │
/// └───────────┴───────────┘
type GameId is bytes32; type GameId is bytes32;
/// @notice A `Clock` represents a packed `Duration` and `Timestamp` /// @notice A `Clock` represents a packed `Duration` and `Timestamp`
......
...@@ -51,16 +51,16 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -51,16 +51,16 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData); (IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp); assertEq(Timestamp.unwrap(timestamp), block.timestamp);
assertEq(factory.gameCount(), 1); assertEq(factory.gameCount(), 1);
(IDisputeGame game2, uint256 timestamp2) = factory.gameAtIndex(0); (, Timestamp timestamp2, IDisputeGame game2) = factory.gameAtIndex(0);
assertEq(address(game2), address(proxy)); assertEq(address(game2), address(proxy));
assertEq(timestamp2, block.timestamp); assertEq(Timestamp.unwrap(timestamp2), block.timestamp);
} }
/// @dev Tests that the `create` function reverts when there is no implementation /// @dev Tests that the `create` function reverts when there is no implementation
...@@ -88,10 +88,10 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -88,10 +88,10 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData); (IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp); assertEq(Timestamp.unwrap(timestamp), block.timestamp);
// Ensure that the `create` function reverts when called with parameters that would result in the same UUID. // Ensure that the `create` function reverts when called with parameters that would result in the same UUID.
vm.expectRevert( vm.expectRevert(
...@@ -161,37 +161,6 @@ contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init { ...@@ -161,37 +161,6 @@ contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init {
} }
} }
/// @title PackingTester
/// @notice Exposes the internal packing functions so that they can be fuzzed
/// in a roundtrip manner.
contract PackingTester is DisputeGameFactory {
function packSlot(address _addr, uint256 _num) external pure returns (GameId) {
return _packSlot(_addr, _num);
}
function unpackSlot(GameId _slot) external pure returns (address, uint256) {
return _unpackSlot(_slot);
}
}
/// @title DisputeGameFactory_PackSlot_Test
/// @notice Fuzzes the PackingTester contract
contract DisputeGameFactory_PackSlot_Test is Test {
PackingTester tester;
function setUp() public {
tester = new PackingTester();
}
/// @dev Tests that the `packSlot` and `unpackSlot` functions roundtrip correctly.
function testFuzz_packSlot_succeeds(address _addr, uint96 _num) public {
GameId slot = tester.packSlot(_addr, uint256(_num));
(address addr, uint256 num) = tester.unpackSlot(slot);
assertEq(addr, _addr);
assertEq(num, _num);
}
}
/// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function. /// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function.
contract FakeClone { contract FakeClone {
function initialize() external { function initialize() external {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibGameId } from "src/dispute/lib/LibGameId.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import "src/libraries/DisputeTypes.sol";
contract LibGameId_Test is Test {
/// @dev Tests that a round trip of packing and unpacking a GameId maintains the same values.
function testFuzz_gameId_roundTrip_succeeds(
GameType _gameType,
Timestamp _timestamp,
IDisputeGame _gameProxy
)
public
{
GameId gameId = LibGameId.pack(_gameType, _timestamp, _gameProxy);
(GameType gameType_, Timestamp timestamp_, IDisputeGame gameProxy_) = LibGameId.unpack(gameId);
assertEq(GameType.unwrap(gameType_), GameType.unwrap(_gameType));
assertEq(Timestamp.unwrap(timestamp_), Timestamp.unwrap(_timestamp));
assertEq(address(gameProxy_), address(_gameProxy));
}
}
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