Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
0a9d6b10
Commit
0a9d6b10
authored
Dec 07, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
init
parent
076abc55
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
129 additions
and
36 deletions
+129
-36
OutputBisectionActors.sol
...s/contracts-bedrock/test/actors/OutputBisectionActors.sol
+46
-17
OutputBisectionGame.t.sol
.../contracts-bedrock/test/dispute/OutputBisectionGame.t.sol
+83
-19
No files found.
packages/contracts-bedrock/test/actors/OutputBisectionActors.sol
View file @
0a9d6b10
...
@@ -22,6 +22,8 @@ abstract contract GameSolver is CommonBase {
...
@@ -22,6 +22,8 @@ abstract contract GameSolver is CommonBase {
/// @notice The execution trace that the `GameSolver` will be representing.
/// @notice The execution trace that the `GameSolver` will be representing.
bytes public trace;
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.
/// @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.
/// Starts at 0 and increments by 1 for each claim processed.
uint256 public processedBuf;
uint256 public processedBuf;
...
@@ -44,14 +46,14 @@ abstract contract GameSolver is CommonBase {
...
@@ -44,14 +46,14 @@ abstract contract GameSolver is CommonBase {
/// to the `OutputBisectionGame` contract by a consumer of this contract.
/// to the `OutputBisectionGame` contract by a consumer of this contract.
struct Move {
struct Move {
MoveKind kind;
MoveKind kind;
address sender;
bytes data;
bytes data;
}
}
constructor(OutputBisectionGame _gameProxy, bytes memory _trace) {
constructor(OutputBisectionGame _gameProxy, bytes memory _trace
, bytes memory _preStateData
) {
GAME = _gameProxy;
GAME = _gameProxy;
MAX_TRACE_LENGTH = 2 ** (_gameProxy.MAX_GAME_DEPTH() - _gameProxy.SPLIT_DEPTH());
MAX_TRACE_LENGTH = 2 ** (_gameProxy.MAX_GAME_DEPTH() - _gameProxy.SPLIT_DEPTH());
trace = _trace;
trace = _trace;
absolutePrestateData = _preStateData;
}
}
/// @notice Returns an array of `Move`s that can be taken from the perspective of an honest
/// @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 {
...
@@ -72,7 +74,13 @@ contract HonestGameSolver is GameSolver {
Noop
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
// Mark agreement with the root claim if the local opinion of the root claim is the same as the
// observed root claim.
// observed root claim.
if (Claim.unwrap(outputAt(MAX_TRACE_LENGTH)) == Claim.unwrap(_gameProxy.rootClaim())) {
if (Claim.unwrap(outputAt(MAX_TRACE_LENGTH)) == Claim.unwrap(_gameProxy.rootClaim())) {
...
@@ -193,7 +201,6 @@ contract HonestGameSolver is GameSolver {
...
@@ -193,7 +201,6 @@ contract HonestGameSolver is GameSolver {
bool isAttack = _direction == Direction.Attack;
bool isAttack = _direction == Direction.Attack;
move_ = Move({
move_ = Move({
kind: isAttack ? MoveKind.Attack : MoveKind.Defend,
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))
data: abi.encodeCall(OutputBisectionGame.move, (_challengeIndex, outputAt(_movePos), isAttack))
});
});
}
}
...
@@ -214,7 +221,6 @@ contract HonestGameSolver is GameSolver {
...
@@ -214,7 +221,6 @@ contract HonestGameSolver is GameSolver {
bool isAttack = _direction == Direction.Attack;
bool isAttack = _direction == Direction.Attack;
move_ = Move({
move_ = Move({
kind: isAttack ? MoveKind.Attack : MoveKind.Defend,
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))
data: abi.encodeCall(OutputBisectionGame.move, (_challengeIndex, claimAt(_movePos), isAttack))
});
});
}
}
...
@@ -241,7 +247,7 @@ contract HonestGameSolver is GameSolver {
...
@@ -241,7 +247,7 @@ contract HonestGameSolver is GameSolver {
// are making an attack step or a defense step. If the index at depth of the
// 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
// move position is 0, the prestate is the absolute prestate and we need to
// do nothing.
// do nothing.
if (
_movePos.indexAtDepth(
) > 0) {
if (
(_movePos.indexAtDepth() % (2 ** (GAME.MAX_GAME_DEPTH() - GAME.SPLIT_DEPTH()))
) > 0) {
Position leafPos =
Position leafPos =
isAttack ? Position.wrap(Position.unwrap(parentPos) - 1) : Position.wrap(Position.unwrap(parentPos) + 1);
isAttack ? Position.wrap(Position.unwrap(parentPos) - 1) : Position.wrap(Position.unwrap(parentPos) + 1);
Position statePos = leafPos.traceAncestor();
Position statePos = leafPos.traceAncestor();
...
@@ -253,13 +259,11 @@ contract HonestGameSolver is GameSolver {
...
@@ -253,13 +259,11 @@ contract HonestGameSolver is GameSolver {
preStateTrace = abi.encode(parentPos.traceIndex(GAME.MAX_GAME_DEPTH()), traceAt(parentPos));
preStateTrace = abi.encode(parentPos.traceIndex(GAME.MAX_GAME_DEPTH()), traceAt(parentPos));
}
}
} else {
} else {
// TODO: Prestate trace value.
preStateTrace = absolutePrestateData;
preStateTrace = abi.encode(0xdead);
}
}
move_ = Move({
move_ = Move({
kind: MoveKind.Step,
kind: MoveKind.Step,
sender: address(this), // TODO: Change to allow for alt actors?
data: abi.encodeCall(OutputBisectionGame.step, (_challengeIndex, isAttack, preStateTrace, hex""))
data: abi.encodeCall(OutputBisectionGame.step, (_challengeIndex, isAttack, preStateTrace, hex""))
});
});
}
}
...
@@ -287,30 +291,26 @@ contract HonestGameSolver is GameSolver {
...
@@ -287,30 +291,26 @@ contract HonestGameSolver is GameSolver {
}
}
/// @notice Returns the mock output at the given position.
/// @notice Returns the mock output at the given position.
/// @dev TODO: Variability for malicious actors?
function outputAt(Position _position) public view returns (Claim claim_) {
function outputAt(Position _position) public view returns (Claim claim_) {
// Don't allow for positions that are deeper than the split depth.
// Don't allow for positions that are deeper than the split depth.
if (_position.depth() > GAME.SPLIT_DEPTH()) {
if (_position.depth() > GAME.SPLIT_DEPTH()) {
revert("GameSolver: invalid position 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.
/// @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_) {
function outputAt(uint256 _l2BlockNumber) public pure returns (Claim claim_) {
return Claim.wrap(bytes32(_l2BlockNumber));
return Claim.wrap(bytes32(_l2BlockNumber));
}
}
/// @notice Returns the state at the trace index within the player's trace.
/// @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_) {
function traceAt(Position _position) public view returns (uint256 state_) {
return traceAt(_position.traceIndex(GAME.MAX_GAME_DEPTH()));
return traceAt(_position.traceIndex(GAME.MAX_GAME_DEPTH()));
}
}
/// @notice Returns the state at the trace index within the player's trace.
/// @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_) {
function traceAt(uint256 _traceIndex) public view returns (uint256 state_) {
return uint256(uint8(_traceIndex >= trace.length ? trace[trace.length - 1] : trace[_traceIndex]));
return uint256(uint8(_traceIndex >= trace.length ? trace[trace.length - 1] : trace[_traceIndex]));
}
}
...
@@ -336,6 +336,35 @@ contract HonestGameSolver is GameSolver {
...
@@ -336,6 +336,35 @@ contract HonestGameSolver is GameSolver {
}
}
}
}
// TODO: `DishonestGameSolver`. Can remove a lot of the cruft and just throw bad claims
/// @title DisputeActor
// at the wall.
/// @notice The `DisputeActor` contract is an abstract contract that represents an actor
// TODO: Actors that utilize the `HonestGameSolver`
/// 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);
}
}
}
packages/contracts-bedrock/test/dispute/OutputBisectionGame.t.sol
View file @
0a9d6b10
...
@@ -19,16 +19,11 @@ import { LibPosition } from "src/dispute/lib/LibPosition.sol";
...
@@ -19,16 +19,11 @@ import { LibPosition } from "src/dispute/lib/LibPosition.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
import { AlphabetVM2 } from "test/mocks/AlphabetVM2.sol";
import { AlphabetVM2 } from "test/mocks/AlphabetVM2.sol";
import { DisputeActor, HonestDisputeActor } from "test/actors/OutputBisectionActors.sol";
contract OutputBisectionGame_Init is DisputeGameFactory_Init {
contract OutputBisectionGame_Init is DisputeGameFactory_Init {
/// @dev The type of the game being tested.
/// @dev The type of the game being tested.
GameType internal constant GAME_TYPE = GameType.wrap(0);
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.
/// @dev The implementation of the game.
OutputBisectionGame internal gameImpl;
OutputBisectionGame internal gameImpl;
...
@@ -40,12 +35,20 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
...
@@ -40,12 +35,20 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
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.
// Set the time to a realistic date.
vm.warp(1690906994);
vm.warp(1690906994);
// Set the extra data for the game creation
// Set the extra data for the game creation
extraData = abi.encode(
L2_BLOCK_NUMBER
);
extraData = abi.encode(
l2BlockNumber
);
AlphabetVM2 _vm = new AlphabetVM2(absolutePrestate);
AlphabetVM2 _vm = new AlphabetVM2(absolutePrestate);
...
@@ -53,8 +56,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
...
@@ -53,8 +56,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
gameImpl = new OutputBisectionGame({
gameImpl = new OutputBisectionGame({
_gameType: GAME_TYPE,
_gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate,
_absolutePrestate: absolutePrestate,
_genesisBlockNumber:
GENESIS_BLOCK_NUMBER
,
_genesisBlockNumber:
genesisBlockNumber
,
_genesisOutputRoot:
GENESIS_OUTPUT_ROOT
,
_genesisOutputRoot:
genesisOutputRoot
,
_maxGameDepth: 2 ** 3,
_maxGameDepth: 2 ** 3,
_splitDepth: 2 ** 2,
_splitDepth: 2 ** 2,
_gameDuration: Duration.wrap(7 days),
_gameDuration: Duration.wrap(7 days),
...
@@ -68,8 +71,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
...
@@ -68,8 +71,8 @@ contract OutputBisectionGame_Init is DisputeGameFactory_Init {
// Check immutables
// Check immutables
assertEq(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(GAME_TYPE));
assertEq(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(GAME_TYPE));
assertEq(Claim.unwrap(gameProxy.ABSOLUTE_PRESTATE()), Claim.unwrap(absolutePrestate));
assertEq(Claim.unwrap(gameProxy.ABSOLUTE_PRESTATE()), Claim.unwrap(absolutePrestate));
assertEq(gameProxy.GENESIS_BLOCK_NUMBER(),
GENESIS_BLOCK_NUMBER
);
assertEq(gameProxy.GENESIS_BLOCK_NUMBER(),
genesisBlockNumber
);
assertEq(Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT()), Hash.unwrap(
GENESIS_OUTPUT_ROOT
));
assertEq(Hash.unwrap(gameProxy.GENESIS_OUTPUT_ROOT()), Hash.unwrap(
genesisOutputRoot
));
assertEq(gameProxy.MAX_GAME_DEPTH(), 2 ** 3);
assertEq(gameProxy.MAX_GAME_DEPTH(), 2 ** 3);
assertEq(gameProxy.SPLIT_DEPTH(), 2 ** 2);
assertEq(gameProxy.SPLIT_DEPTH(), 2 ** 2);
assertEq(Duration.unwrap(gameProxy.GAME_DURATION()), 7 days);
assertEq(Duration.unwrap(gameProxy.GAME_DURATION()), 7 days);
...
@@ -88,7 +91,13 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
...
@@ -88,7 +91,13 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
function setUp() public override {
function setUp() public override {
super.setUp();
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 {
...
@@ -109,8 +118,8 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
new OutputBisectionGame({
new OutputBisectionGame({
_gameType: GAME_TYPE,
_gameType: GAME_TYPE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_genesisBlockNumber:
GENESIS_BLOCK_NUMBER
,
_genesisBlockNumber:
0
,
_genesisOutputRoot:
GENESIS_OUTPUT_ROOT
,
_genesisOutputRoot:
Hash.wrap(bytes32(0))
,
_maxGameDepth: 2 ** 3,
_maxGameDepth: 2 ** 3,
_splitDepth: _splitDepth,
_splitDepth: _splitDepth,
_gameDuration: Duration.wrap(7 days),
_gameDuration: Duration.wrap(7 days),
...
@@ -125,9 +134,9 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
...
@@ -125,9 +134,9 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
new OutputBisectionGame({
new OutputBisectionGame({
_gameType: GAME_TYPE,
_gameType: GAME_TYPE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_absolutePrestate: ABSOLUTE_PRESTATE,
_genesisBlockNumber: GENESIS_BLOCK_NUMBER,
_genesisOutputRoot: GENESIS_OUTPUT_ROOT,
_maxGameDepth: 2 ** 3,
_maxGameDepth: 2 ** 3,
_genesisBlockNumber: 0,
_genesisOutputRoot: Hash.wrap(bytes32(0)),
_splitDepth: _splitDepth,
_splitDepth: _splitDepth,
_gameDuration: Duration.wrap(7 days),
_gameDuration: Duration.wrap(7 days),
_vm: alphabetVM
_vm: alphabetVM
...
@@ -547,7 +556,7 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
...
@@ -547,7 +556,7 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
gameProxy.attack(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC));
gameProxy.attack(4, _changeClaimStatus(_dummyClaim(), VMStatuses.PANIC));
// Expected start/disputed claims
// Expected start/disputed claims
bytes32 startingClaim = Hash.unwrap(
GENESIS_OUTPUT_ROOT
);
bytes32 startingClaim = Hash.unwrap(
gameProxy.GENESIS_OUTPUT_ROOT()
);
bytes32 disputedClaim = bytes32(uint256(3));
bytes32 disputedClaim = bytes32(uint256(3));
Position disputedPos = LibPosition.wrap(4, 0);
Position disputedPos = LibPosition.wrap(4, 0);
...
@@ -640,3 +649,58 @@ contract OutputBisectionGame_Test is OutputBisectionGame_Init {
...
@@ -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
});
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment