Skip to content

Commit 25cb28f

Browse files
committed
feat!: tournament result staging
1 parent 6dfa2e5 commit 25cb28f

9 files changed

Lines changed: 734 additions & 156 deletions

File tree

cartesi-rollups/contracts/src/DaveAppFactory.sol

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,28 @@ contract DaveAppFactory is IDaveAppFactory {
3434
TOURNAMENT_FACTORY = tournamentFactory;
3535
}
3636

37-
function newDaveApp(bytes32 templateHash, WithdrawalConfig calldata withdrawalConfig, bytes32 salt)
38-
external
39-
override
40-
returns (IApplication appContract, IDaveConsensus daveConsensus)
41-
{
37+
function newDaveApp(
38+
bytes32 templateHash,
39+
uint256 claimStagingPeriod,
40+
WithdrawalConfig calldata withdrawalConfig,
41+
bytes32 salt
42+
) external override returns (IApplication appContract, IDaveConsensus daveConsensus) {
4243
appContract = _newApplication(templateHash, withdrawalConfig, salt);
43-
daveConsensus = _newDaveConsensus(address(appContract), templateHash, salt);
44+
daveConsensus = _newDaveConsensus(address(appContract), templateHash, claimStagingPeriod, salt);
4445
appContract.migrateToOutputsMerkleRootValidator(daveConsensus);
4546
appContract.renounceOwnership();
4647
emit DaveAppCreated(appContract, daveConsensus);
4748
}
4849

49-
function calculateDaveAppAddress(bytes32 templateHash, WithdrawalConfig calldata withdrawalConfig, bytes32 salt)
50-
external
51-
view
52-
override
53-
returns (address appContractAddress, address daveConsensusAddress)
54-
{
50+
function calculateDaveAppAddress(
51+
bytes32 templateHash,
52+
uint256 claimStagingPeriod,
53+
WithdrawalConfig calldata withdrawalConfig,
54+
bytes32 salt
55+
) external view override returns (address appContractAddress, address daveConsensusAddress) {
5556
appContractAddress = _calculateApplicationAddress(templateHash, withdrawalConfig, salt);
56-
daveConsensusAddress = _calculateDaveConsensusAddress(appContractAddress, templateHash, salt);
57+
daveConsensusAddress =
58+
_calculateDaveConsensusAddress(appContractAddress, templateHash, claimStagingPeriod, salt);
5759
}
5860

5961
/// @notice Encode the data availability blob for applications that only use the input box as DA.
@@ -76,12 +78,14 @@ contract DaveAppFactory is IDaveAppFactory {
7678
}
7779

7880
/// @notice Instantiate a new `DaveConsensus` contract.
79-
function _newDaveConsensus(address appContract, bytes32 templateHash, bytes32 salt)
81+
function _newDaveConsensus(address appContract, bytes32 templateHash, uint256 claimStagingPeriod, bytes32 salt)
8082
internal
8183
returns (DaveConsensus)
8284
{
8385
Machine.Hash initialMachineStateHash = Machine.Hash.wrap(templateHash);
84-
return new DaveConsensus{salt: salt}(INPUT_BOX, appContract, TOURNAMENT_FACTORY, initialMachineStateHash);
86+
return new DaveConsensus{salt: salt}(
87+
INPUT_BOX, appContract, TOURNAMENT_FACTORY, initialMachineStateHash, claimStagingPeriod
88+
);
8589
}
8690

8791
/// @notice Calculates the address of an application contract.
@@ -97,17 +101,18 @@ contract DaveAppFactory is IDaveAppFactory {
97101
}
98102

99103
/// @notice Calculates the address of a `DaveConsensus` contract.
100-
function _calculateDaveConsensusAddress(address appContract, bytes32 templateHash, bytes32 salt)
101-
internal
102-
view
103-
returns (address)
104-
{
104+
function _calculateDaveConsensusAddress(
105+
address appContract,
106+
bytes32 templateHash,
107+
uint256 claimStagingPeriod,
108+
bytes32 salt
109+
) internal view returns (address) {
105110
return Create2.computeAddress(
106111
salt,
107112
keccak256(
108113
abi.encodePacked(
109114
type(DaveConsensus).creationCode,
110-
abi.encode(INPUT_BOX, appContract, TOURNAMENT_FACTORY, templateHash)
115+
abi.encode(INPUT_BOX, appContract, TOURNAMENT_FACTORY, templateHash, claimStagingPeriod)
111116
)
112117
)
113118
);

cartesi-rollups/contracts/src/DaveConsensus.sol

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,6 @@ import {Memory} from "step/src/Memory.sol";
2727

2828
import {IDaveConsensus} from "./IDaveConsensus.sol";
2929

30-
/// @notice Consensus contract with Dave tournaments.
31-
///
32-
/// @notice This contract validates only one application,
33-
/// which read inputs from the InputBox contract.
34-
///
35-
/// @notice This contract also manages epoch boundaries, which
36-
/// are defined in terms of block numbers. We represent them
37-
/// as intervals of the form [a,b). They are also identified by
38-
/// incremental numbers that start from 0.
39-
///
40-
/// @notice Off-chain nodes can listen to `EpochSealed` events
41-
/// to know where epochs start and end, and which epochs have been
42-
/// settled already and which one is open for challenges still.
43-
/// Anyone can settle an epoch by calling `settle`.
44-
/// One can also check if it can be settled by calling `canSettle`.
45-
///
46-
/// @notice At any given time, there is always one sealed epoch.
47-
/// Prior to it, every epoch has been settled.
48-
/// After it, the next epoch is accumulating inputs. Once this epoch is settled,
49-
/// the accumlating epoch will be sealed, and a new
50-
/// accumulating epoch will be created.
51-
///
5230
contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
5331
using LibMath for uint256;
5432
using LibBinaryMerkleTree for bytes;
@@ -63,6 +41,9 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
6341
/// @notice The contract used to instantiate tournaments
6442
ITournamentFactory immutable _TOURNAMENT_FACTORY;
6543

44+
/// @notice The claim staging period
45+
uint256 immutable _CLAIM_STAGING_PERIOD;
46+
6647
/// @notice Deployment block number
6748
uint256 immutable _DEPLOYMENT_BLOCK_NUMBER = block.number;
6849

@@ -78,6 +59,21 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
7859
/// @notice Current sealed epoch tournament
7960
ITournament _tournament;
8061

62+
/// @notice Whether the result of the current sealed epoch tournament is staged
63+
bool _isTournamentResultStaged;
64+
65+
/// @notice The number of the block in which the tournament result was staged
66+
/// @dev Only meaningful if _isTournamentResultStaged is true.
67+
uint256 _stagingBlockNumber;
68+
69+
/// @notice The staged post-epoch machine state hash
70+
/// @dev Only meaningful if _isTournamentResultStaged is true.
71+
Machine.Hash _stagedPostEpochMachineStateHash;
72+
73+
/// @notice The staged post-epoch outputs Merkle root
74+
/// @dev Only meaningful if _isTournamentResultStaged is true.
75+
bytes32 _stagedPostEpochOutputsMerkleRoot;
76+
8177
/// @notice Settled output trees' merkle root hash
8278
mapping(bytes32 => bool) _outputsMerkleRoots;
8379

@@ -88,13 +84,15 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
8884
IInputBox inputBox,
8985
address appContract,
9086
ITournamentFactory tournamentFactory,
91-
Machine.Hash initialMachineStateHash
87+
Machine.Hash initialMachineStateHash,
88+
uint256 claimStagingPeriod
9289
) {
9390
// Initialize immutable variables
9491
_INPUT_BOX = inputBox;
9592
_APP_CONTRACT = appContract;
9693
_TOURNAMENT_FACTORY = tournamentFactory;
97-
emit ConsensusCreation(inputBox, appContract, tournamentFactory);
94+
_CLAIM_STAGING_PERIOD = claimStagingPeriod;
95+
emit ConsensusCreation(inputBox, appContract, tournamentFactory, claimStagingPeriod);
9896

9997
// Initialize first sealed epoch
10098
uint256 inputIndexUpperBound = inputBox.getNumberOfInputs(appContract);
@@ -104,39 +102,102 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
104102
emit EpochSealed(0, 0, inputIndexUpperBound, initialMachineStateHash, bytes32(0), tournament);
105103
}
106104

107-
function canSettle()
105+
function canStageTournamentResult()
108106
external
109107
view
110108
override
111-
returns (bool isFinished, uint256 epochNumber, Tree.Node winnerCommitment)
109+
returns (
110+
bool isFinished,
111+
bool isTournamentResultStaged,
112+
uint256 epochNumber,
113+
Tree.Node winnerCommitment,
114+
Machine.Hash winnerPostEpochMachineStateHash
115+
)
112116
{
113-
(isFinished, winnerCommitment,) = _tournament.arbitrationResult();
114117
epochNumber = _epochNumber;
118+
isTournamentResultStaged = _isTournamentResultStaged;
119+
(isFinished, winnerCommitment, winnerPostEpochMachineStateHash) = _tournament.arbitrationResult();
115120
}
116121

117-
function settle(uint256 epochNumber, bytes32 outputsMerkleRoot, bytes32[] calldata proof)
122+
function stageTournamentResult(uint256 epochNumber, bytes32 outputsMerkleRoot, bytes32[] calldata proof)
118123
external
119124
override
120125
notForeclosed(_APP_CONTRACT)
121126
{
122127
// Check tournament settlement
123128
require(epochNumber == _epochNumber, IncorrectEpochNumber(epochNumber, _epochNumber));
124129

130+
// Check whether the tournament result is staged
131+
require(!_isTournamentResultStaged, TournamentResultAlreadyStaged());
132+
125133
// Check tournament finished
126134
(bool isFinished,, Machine.Hash finalMachineStateHash) = _tournament.arbitrationResult();
127135
require(isFinished, TournamentNotFinishedYet());
128-
ITournament oldTournament = _tournament;
129-
_tournament = ITournament(address(0));
130136

131137
// Check outputs Merkle root
132138
_validateOutputTree(finalMachineStateHash, outputsMerkleRoot, proof);
133139

140+
// Stage tournament result, and store the current block number for
141+
// later checking whether the claim staging period has elapsed
142+
_stagingBlockNumber = block.number;
143+
_stagedPostEpochMachineStateHash = finalMachineStateHash;
144+
_stagedPostEpochOutputsMerkleRoot = outputsMerkleRoot;
145+
_isTournamentResultStaged = true;
146+
147+
// Try recovering bond for tournament winner
148+
try _tournament.tryRecoveringBond() {} catch {}
149+
150+
emit EpochStaged(epochNumber, finalMachineStateHash, outputsMerkleRoot);
151+
}
152+
153+
function canAcceptStagedTournamentResult()
154+
external
155+
view
156+
override
157+
returns (
158+
bool isTournamentResultStaged,
159+
bool isClaimStagingPeriodOver,
160+
uint256 epochNumber,
161+
Machine.Hash stagedPostEpochMachineStateHash,
162+
bytes32 stagedPostEpochOutputsMerkleRoot
163+
)
164+
{
165+
epochNumber = _epochNumber;
166+
isTournamentResultStaged = _isTournamentResultStaged;
167+
if (_isTournamentResultStaged) {
168+
isClaimStagingPeriodOver = ((block.number - _stagingBlockNumber) >= _CLAIM_STAGING_PERIOD);
169+
stagedPostEpochMachineStateHash = _stagedPostEpochMachineStateHash;
170+
stagedPostEpochOutputsMerkleRoot = _stagedPostEpochOutputsMerkleRoot;
171+
}
172+
}
173+
174+
function acceptStagedTournamentResult(uint256 epochNumber) external override notForeclosed(_APP_CONTRACT) {
175+
// Check tournament settlement
176+
require(epochNumber == _epochNumber, IncorrectEpochNumber(epochNumber, _epochNumber));
177+
178+
// Check whether the tournament result is staged
179+
require(_isTournamentResultStaged, TournamentResultNotStaged());
180+
181+
// Check whether the claim staging period has elapsed
182+
{
183+
uint256 numberOfBlocksAfterStaging = block.number - _stagingBlockNumber;
184+
require(
185+
numberOfBlocksAfterStaging >= _CLAIM_STAGING_PERIOD,
186+
ClaimStagingPeriodNotOverYet(numberOfBlocksAfterStaging, _CLAIM_STAGING_PERIOD)
187+
);
188+
}
189+
190+
// Get staged tournament result
191+
Machine.Hash finalMachineStateHash = _stagedPostEpochMachineStateHash;
192+
bytes32 outputsMerkleRoot = _stagedPostEpochOutputsMerkleRoot;
193+
134194
// Seal current accumulating epoch, save settled output tree and machine state hash
135195
_epochNumber++;
136196
_inputIndexLowerBound = _inputIndexUpperBound;
137197
_inputIndexUpperBound = _INPUT_BOX.getNumberOfInputs(_APP_CONTRACT);
138198
_outputsMerkleRoots[outputsMerkleRoot] = true;
139199
_lastFinalizedMachineStateHash = finalMachineStateHash;
200+
_isTournamentResultStaged = false;
140201

141202
// Start new tournament
142203
_tournament = _TOURNAMENT_FACTORY.instantiate(finalMachineStateHash, this);
@@ -149,8 +210,6 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
149210
outputsMerkleRoot,
150211
_tournament
151212
);
152-
153-
oldTournament.tryRecoveringBond();
154213
}
155214

156215
function getCurrentSealedEpoch()
@@ -161,13 +220,23 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
161220
uint256 epochNumber,
162221
uint256 inputIndexLowerBound,
163222
uint256 inputIndexUpperBound,
164-
ITournament tournament
223+
ITournament tournament,
224+
bool isTournamentResultStaged,
225+
uint256 stagingBlockNumber,
226+
Machine.Hash stagedPostEpochMachineStateHash,
227+
bytes32 stagedPostEpochOutputsMerkleRoot
165228
)
166229
{
167230
epochNumber = _epochNumber;
168231
inputIndexLowerBound = _inputIndexLowerBound;
169232
inputIndexUpperBound = _inputIndexUpperBound;
170233
tournament = _tournament;
234+
isTournamentResultStaged = _isTournamentResultStaged;
235+
if (_isTournamentResultStaged) {
236+
stagingBlockNumber = _stagingBlockNumber;
237+
stagedPostEpochMachineStateHash = _stagedPostEpochMachineStateHash;
238+
stagedPostEpochOutputsMerkleRoot = _stagedPostEpochOutputsMerkleRoot;
239+
}
171240
}
172241

173242
function getInputBox() external view override returns (IInputBox) {
@@ -182,6 +251,10 @@ contract DaveConsensus is IDaveConsensus, ERC165, ApplicationChecker {
182251
return _TOURNAMENT_FACTORY;
183252
}
184253

254+
function getClaimStagingPeriod() external view override returns (uint256) {
255+
return _CLAIM_STAGING_PERIOD;
256+
}
257+
185258
function provideMerkleRootOfInput(uint256 inputIndexWithinEpoch, bytes calldata input)
186259
external
187260
view

cartesi-rollups/contracts/src/IDaveAppFactory.sol

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {IDaveConsensus} from "./IDaveConsensus.sol";
1111

1212
/// @title Dave-App Pair Factory
1313
/// @notice Allows anyone to reliably deploy an application
14-
/// validated a newly-deployed `IDaveConsensus` contract.
14+
/// validated by a newly-deployed `IDaveConsensus` contract.
1515
interface IDaveAppFactory is IApplicationFactoryErrors {
1616
/// @notice A Dave-App pair was created.
1717
/// @param appContract The application contract
@@ -20,22 +20,29 @@ interface IDaveAppFactory is IApplicationFactoryErrors {
2020

2121
/// @notice Deploy a new Dave-App pair deterministically.
2222
/// @param templateHash The application template hash
23+
/// @param claimStagingPeriod The claim staging period
2324
/// @param withdrawalConfig The withdrawal configuration
2425
/// @param salt A 32-byte value used to add entropy to the addresses
2526
/// @return appContract The application contract
2627
/// @return daveConsensus The Dave consensus contract
27-
function newDaveApp(bytes32 templateHash, WithdrawalConfig calldata withdrawalConfig, bytes32 salt)
28-
external
29-
returns (IApplication appContract, IDaveConsensus daveConsensus);
28+
function newDaveApp(
29+
bytes32 templateHash,
30+
uint256 claimStagingPeriod,
31+
WithdrawalConfig calldata withdrawalConfig,
32+
bytes32 salt
33+
) external returns (IApplication appContract, IDaveConsensus daveConsensus);
3034

3135
/// @notice Calculate the address of a Dave-App pair.
3236
/// @param templateHash The application template hash
37+
/// @param claimStagingPeriod The claim staging period
3338
/// @param withdrawalConfig The withdrawal configuration
3439
/// @param salt A 32-byte value used to add entropy to the addresses
3540
/// @return appContractAddress The application contract address
3641
/// @return daveConsensusAddress The Dave consensus contract address
37-
function calculateDaveAppAddress(bytes32 templateHash, WithdrawalConfig calldata withdrawalConfig, bytes32 salt)
38-
external
39-
view
40-
returns (address appContractAddress, address daveConsensusAddress);
42+
function calculateDaveAppAddress(
43+
bytes32 templateHash,
44+
uint256 claimStagingPeriod,
45+
WithdrawalConfig calldata withdrawalConfig,
46+
bytes32 salt
47+
) external view returns (address appContractAddress, address daveConsensusAddress);
4148
}

0 commit comments

Comments
 (0)