Commit 0a9d6b10 authored by clabby's avatar clabby

init

parent 076abc55
......@@ -22,6 +22,8 @@ abstract contract GameSolver is CommonBase {
/// @notice The execution trace that the `GameSolver` will be representing.
bytes public trace;
/// @notice The raw absolute prestate data.
bytes public absolutePrestateData;
/// @notice The offset of previously processed claims in the `GAME` contract's `claimData` array.
/// Starts at 0 and increments by 1 for each claim processed.
uint256 public processedBuf;
......@@ -44,14 +46,14 @@ abstract contract GameSolver is CommonBase {
/// to the `OutputBisectionGame` contract by a consumer of this contract.
struct Move {
MoveKind kind;
address sender;
bytes data;
}
constructor(OutputBisectionGame _gameProxy, bytes memory _trace) {
constructor(OutputBisectionGame _gameProxy, bytes memory _trace, bytes memory _preStateData) {
GAME = _gameProxy;
MAX_TRACE_LENGTH = 2 ** (_gameProxy.MAX_GAME_DEPTH() - _gameProxy.SPLIT_DEPTH());
trace = _trace;
absolutePrestateData = _preStateData;
}
/// @notice Returns an array of `Move`s that can be taken from the perspective of an honest
......@@ -72,7 +74,13 @@ contract HonestGameSolver is GameSolver {
Noop
}
constructor(OutputBisectionGame _gameProxy, bytes memory _trace) GameSolver(_gameProxy, _trace) {
constructor(
OutputBisectionGame _gameProxy,
bytes memory _trace,
bytes memory _preStateData
)
GameSolver(_gameProxy, _trace, _preStateData)
{
// Mark agreement with the root claim if the local opinion of the root claim is the same as the
// observed root claim.
if (Claim.unwrap(outputAt(MAX_TRACE_LENGTH)) == Claim.unwrap(_gameProxy.rootClaim())) {
......@@ -193,7 +201,6 @@ contract HonestGameSolver is GameSolver {
bool isAttack = _direction == Direction.Attack;
move_ = Move({
kind: isAttack ? MoveKind.Attack : MoveKind.Defend,
sender: address(this), // TODO: Change to allow for alt actors?
data: abi.encodeCall(OutputBisectionGame.move, (_challengeIndex, outputAt(_movePos), isAttack))
});
}
......@@ -214,7 +221,6 @@ contract HonestGameSolver is GameSolver {
bool isAttack = _direction == Direction.Attack;
move_ = Move({
kind: isAttack ? MoveKind.Attack : MoveKind.Defend,
sender: address(this), // TODO: Change to allow for alt actors?
data: abi.encodeCall(OutputBisectionGame.move, (_challengeIndex, claimAt(_movePos), isAttack))
});
}
......@@ -241,7 +247,7 @@ contract HonestGameSolver is GameSolver {
// are making an attack step or a defense step. If the index at depth of the
// move position is 0, the prestate is the absolute prestate and we need to
// do nothing.
if (_movePos.indexAtDepth() > 0) {
if ((_movePos.indexAtDepth() % (2 ** (GAME.MAX_GAME_DEPTH() - GAME.SPLIT_DEPTH()))) > 0) {
Position leafPos =
isAttack ? Position.wrap(Position.unwrap(parentPos) - 1) : Position.wrap(Position.unwrap(parentPos) + 1);
Position statePos = leafPos.traceAncestor();
......@@ -253,13 +259,11 @@ contract HonestGameSolver is GameSolver {
preStateTrace = abi.encode(parentPos.traceIndex(GAME.MAX_GAME_DEPTH()), traceAt(parentPos));
}
} else {
// TODO: Prestate trace value.
preStateTrace = abi.encode(0xdead);
preStateTrace = absolutePrestateData;
}
move_ = Move({
kind: MoveKind.Step,
sender: address(this), // TODO: Change to allow for alt actors?
data: abi.encodeCall(OutputBisectionGame.step, (_challengeIndex, isAttack, preStateTrace, hex""))
});
}
......@@ -287,30 +291,26 @@ contract HonestGameSolver is GameSolver {
}
/// @notice Returns the mock output at the given position.
/// @dev TODO: Variability for malicious actors?
function outputAt(Position _position) public view returns (Claim claim_) {
// Don't allow for positions that are deeper than the split depth.
if (_position.depth() > GAME.SPLIT_DEPTH()) {
revert("GameSolver: invalid position depth");
}
return outputAt(_position.traceIndex(GAME.SPLIT_DEPTH()));
return outputAt(_position.traceIndex(GAME.SPLIT_DEPTH()) + 1);
}
/// @notice Returns the mock output at the given L2 block number.
/// @dev TODO: Variability for malicious actors?
function outputAt(uint256 _l2BlockNumber) public pure returns (Claim claim_) {
return Claim.wrap(bytes32(_l2BlockNumber));
}
/// @notice Returns the state at the trace index within the player's trace.
/// @dev TODO: Separate traces per execution trace game.
function traceAt(Position _position) public view returns (uint256 state_) {
return traceAt(_position.traceIndex(GAME.MAX_GAME_DEPTH()));
}
/// @notice Returns the state at the trace index within the player's trace.
/// @dev TODO: Separate traces per execution trace game.
function traceAt(uint256 _traceIndex) public view returns (uint256 state_) {
return uint256(uint8(_traceIndex >= trace.length ? trace[trace.length - 1] : trace[_traceIndex]));
}
......@@ -336,6 +336,35 @@ contract HonestGameSolver is GameSolver {
}
}
// TODO: `DishonestGameSolver`. Can remove a lot of the cruft and just throw bad claims
// at the wall.
// TODO: Actors that utilize the `HonestGameSolver`
/// @title DisputeActor
/// @notice The `DisputeActor` contract is an abstract contract that represents an actor
/// that consumes the suggested moves from a `GameSolver` contract.
abstract contract DisputeActor {
/// @notice The `GameSolver` contract used to determine the moves to be taken.
GameSolver public solver;
/// @notice Performs all available moves deemed by the attached solver.
/// @return success_ True if all moves were successful, false otherwise.
function move() external virtual returns (bool success_);
}
/// @title HonestDisputeActor
/// @notice An actor that consumes the suggested moves from an `HonestGameSolver` contract. Note
/// that this actor *can* be dishonest if the trace is faulty, but it will always follow
/// the rules of the honest actor.
contract HonestDisputeActor is DisputeActor {
OutputBisectionGame public immutable GAME;
constructor(OutputBisectionGame _gameProxy, bytes memory _trace, bytes memory _preStateData) {
GAME = _gameProxy;
solver = GameSolver(new HonestGameSolver(_gameProxy, _trace, _preStateData));
}
/// @inheritdoc DisputeActor
function move() external override returns (bool success_) {
GameSolver.Move[] memory moves = solver.solveGame();
for (uint256 i = 0; i < moves.length; i++) {
(success_,) = address(GAME).call(moves[i].data);
}
}
}
......@@ -19,16 +19,11 @@ import { LibPosition } from "src/dispute/lib/LibPosition.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
import { AlphabetVM2 } from "test/mocks/AlphabetVM2.sol";
import { DisputeActor, HonestDisputeActor } from "test/actors/OutputBisectionActors.sol";
contract OutputBisectionGame_Init is DisputeGameFactory_Init {
/// @dev The type of the game being tested.
GameType internal constant GAME_TYPE = GameType.wrap(0);
/// @dev The L2 Block Number for the game's proposed output (the root claim)
uint256 internal constant L2_BLOCK_NUMBER = 0x0a;
/// @dev The genesis block number configured for the output bisection portion of the game.
uint256 internal constant GENESIS_BLOCK_NUMBER = 0;
/// @dev The genesis output root commitment
Hash internal constant GENESIS_OUTPUT_ROOT = Hash.wrap(bytes32(0));
/// @dev The implementation of the game.
OutputBisectionGame internal gameImpl;
......@@ -40,12 +35,20 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
function init(Claim rootClaim, Claim absolutePrestate) public {
function init(
Claim rootClaim,
Claim absolutePrestate,
uint256 l2BlockNumber,
uint256 genesisBlockNumber,
Hash genesisOutputRoot
)
public
{
// Set the time to a realistic date.
vm.warp(1690906994);
// Set the extra data for the game creation
extraData = abi.encode(L2_BLOCK_NUMBER);
extraData = abi.encode(l2BlockNumber);
AlphabetVM2 _vm = new AlphabetVM2(absolutePrestate);
......@@ -53,8 +56,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
gameImpl = new OutputBisectionGame({
_gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate,
_genesisBlockNumber: GENESIS_BLOCK_NUMBER,
_genesisOutputRoot: GENESIS_OUTPUT_ROOT,
_genesisBlockNumber: genesisBlockNumber,
_genesisOutputRoot: genesisOutputRoot,
_maxGameDepth: 2 ** 3,
_splitDepth: 2 ** 2,
_gameDuration: Duration.wrap(7 days),
......@@ -68,8 +71,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
// Check immutables
assertEq(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(GAME_TYPE));
assertEq(Claim.unwrap(gameProxy.ABSOLUTE_PRESTATE()), Claim.unwrap(absolutePrestate));
assertEq(gameProxy.GENESIS_BLOCK_NUMBER(), GENESIS_BLOCK_NUMBER);
assertEq(Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT()), Hash.unwrap(GENESIS_OUTPUT_ROOT));
assertEq(gameProxy.GENESIS_BLOCK_NUMBER(), genesisBlockNumber);
assertEq(Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT()), Hash.unwrap(genesisOutputRoot));
assertEq(gameProxy.MAX_GAME_DEPTH(), 2 ** 3);
assertEq(gameProxy.SPLIT_DEPTH(), 2 ** 2);
assertEq(Duration.unwrap(gameProxy.GAME_DURATION()), 7 days);
......@@ -88,7 +91,13 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
function setUp() public override {
super.setUp();
super.init(ROOT_CLAIM, ABSOLUTE_PRESTATE);
super.init({
rootClaim: ROOT_CLAIM,
absolutePrestate: ABSOLUTE_PRESTATE,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
}
////////////////////////////////////////////////////////////////
......@@ -109,8 +118,8 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
new OutputBisectionGame({
_gameType: GAME_TYPE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_genesisBlockNumber: GENESIS_BLOCK_NUMBER,
_genesisOutputRoot: GENESIS_OUTPUT_ROOT,
_genesisBlockNumber: 0,
_genesisOutputRoot: Hash.wrap(bytes32(0)),
_maxGameDepth: 2 ** 3,
_splitDepth: _splitDepth,
_gameDuration: Duration.wrap(7 days),
......@@ -125,9 +134,9 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
new OutputBisectionGame({
_gameType: GAME_TYPE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_genesisBlockNumber: GENESIS_BLOCK_NUMBER,
_genesisOutputRoot: GENESIS_OUTPUT_ROOT,
_maxGameDepth: 2 ** 3,
_genesisBlockNumber: 0,
_genesisOutputRoot: Hash.wrap(bytes32(0)),
_splitDepth: _splitDepth,
_gameDuration: Duration.wrap(7 days),
_vm: alphabetVM
......@@ -547,7 +556,7 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
gameProxy.attack(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC));
// Expected start/disputed claims
bytes32 startingClaim = Hash.unwrap(GENESIS_OUTPUT_ROOT);
bytes32 startingClaim = Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT());
bytes32 disputedClaim = bytes32(uint256(3));
Position disputedPos = LibPosition.wrap(4, 0);
......@@ -640,3 +649,58 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
}
}
}
contract ThunderDome_1v1_Test is OutputBisectionGame_Init {
/// @dev The root claim of the game. This is the output root at block # 2 ** SPLIT_DEPTH
/// in the alphabet game.
Claim internal immutable ROOT_CLAIM;
/// @dev The absolute prestate of the trace.
Claim internal immutable ABSOLUTE_PRESTATE;
/// @dev The state data (preimage) of the absolute prestate hash.
bytes internal absolutePrestateData;
constructor() {
ROOT_CLAIM = Claim.wrap(bytes32((uint256(1) << 248) | uint256(10)));
ABSOLUTE_PRESTATE = Claim.wrap(bytes32((uint256(3) << 248) | uint256(0)));
}
function setUp() public override {
// Setup the `OutputBisectionGame`
super.setUp();
}
/// @notice Static unit test for a 1v1 output bisection dispute.
function test_static_1v1_succeeds() public {
// Create the dispute game.
super.init({
rootClaim: ROOT_CLAIM,
absolutePrestate: ABSOLUTE_PRESTATE,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
bytes memory honestPreStateData = abi.encode(0);
bytes memory honestTrace = hex"";
}
/// @dev Helper to create actors for the 1v1 dispute.
function _createActors(
bytes memory _honestTrace,
bytes memory _honestPreStateData,
bytes memory _dishonestTrace,
bytes memory _dishonestPreStateData
)
internal
returns (DisputeActor honest_, DisputeActor dishonest_)
{
// Setup the honest and dishonest actors (todo)
honest_ =
new HonestDisputeActor({ _gameProxy: gameProxy, _trace: _honestTrace, _preStateData: _honestPreStateData });
dishonest_ = new HonestDisputeActor({
_gameProxy: gameProxy,
_trace: _dishonestTrace,
_preStateData: _dishonestPreStateData
});
}
}
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