Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion cartesi-rollups/contracts/script/Deployment.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,25 @@ contract DeploymentScript is BaseDeploymentScript {
address inputBox = _loadDeployment(".", "InputBox");
address appFactory = _loadDeployment(".", "ApplicationFactory");
address tournamentFactory = _loadDeployment(".", "MultiLevelTournamentFactory");
address safetyGateTaskSpawner = _loadDeployment("../../prt/contracts", "SafetyGateTaskSpawner");
address securityCouncil = tx.origin;

vmSafe.startBroadcast();

_storeDeployment(
type(DaveAppFactory).name,
_create2(type(DaveAppFactory).creationCode, abi.encode(inputBox, appFactory, tournamentFactory))
_create2(
type(DaveAppFactory).creationCode,
abi.encode(inputBox, appFactory, tournamentFactory, securityCouncil)
)
);

_storeDeployment(
"DaveAppFactorySafetyGate",
_create2(
type(DaveAppFactory).creationCode,
abi.encode(inputBox, appFactory, safetyGateTaskSpawner, securityCouncil)
)
);

if (block.chainid == 31337) {
Expand Down
17 changes: 11 additions & 6 deletions cartesi-rollups/contracts/src/DaveAppFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {IApplication} from "cartesi-rollups-contracts-2.1.1/src/dapp/IApplicatio
import {IApplicationFactory} from "cartesi-rollups-contracts-2.1.1/src/dapp/IApplicationFactory.sol";
import {IInputBox} from "cartesi-rollups-contracts-2.1.1/src/inputs/IInputBox.sol";

import {ITournamentFactory} from "prt-contracts/ITournamentFactory.sol";
import {ITaskSpawner} from "prt-contracts/ITaskSpawner.sol";
import {Machine} from "prt-contracts/types/Machine.sol";

import {DaveConsensus} from "./DaveConsensus.sol";
Expand All @@ -23,14 +23,16 @@ import {IDaveConsensus} from "./IDaveConsensus.sol";
contract DaveAppFactory is IDaveAppFactory {
IInputBox immutable INPUT_BOX;
IApplicationFactory immutable APP_FACTORY;
ITournamentFactory immutable TOURNAMENT_FACTORY;
ITaskSpawner immutable TASK_SPAWNER;
address immutable SECURITY_COUNCIL;

IOutputsMerkleRootValidator constant NO_VALIDATOR = IOutputsMerkleRootValidator(address(0));

constructor(IInputBox inputBox, IApplicationFactory appFactory, ITournamentFactory tournamentFactory) {
constructor(IInputBox inputBox, IApplicationFactory appFactory, ITaskSpawner taskSpawner, address securityCouncil) {
INPUT_BOX = inputBox;
APP_FACTORY = appFactory;
TOURNAMENT_FACTORY = tournamentFactory;
TASK_SPAWNER = taskSpawner;
SECURITY_COUNCIL = securityCouncil;
}

function newDaveApp(bytes32 templateHash, bytes32 salt)
Expand Down Expand Up @@ -74,7 +76,10 @@ contract DaveAppFactory is IDaveAppFactory {
returns (DaveConsensus)
{
Machine.Hash initialMachineStateHash = Machine.Hash.wrap(templateHash);
return new DaveConsensus{salt: salt}(INPUT_BOX, appContract, TOURNAMENT_FACTORY, initialMachineStateHash);
return
new DaveConsensus{salt: salt}(
INPUT_BOX, appContract, TASK_SPAWNER, SECURITY_COUNCIL, initialMachineStateHash
);
}

/// @notice Calculates the address of an application contract.
Expand All @@ -95,7 +100,7 @@ contract DaveAppFactory is IDaveAppFactory {
keccak256(
abi.encodePacked(
type(DaveConsensus).creationCode,
abi.encode(INPUT_BOX, appContract, TOURNAMENT_FACTORY, templateHash)
abi.encode(INPUT_BOX, appContract, TASK_SPAWNER, SECURITY_COUNCIL, templateHash)
)
)
);
Expand Down
128 changes: 89 additions & 39 deletions cartesi-rollups/contracts/src/DaveConsensus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ import {IInputBox} from "cartesi-rollups-contracts-2.1.1/src/inputs/IInputBox.so
import {LibMerkle32} from "cartesi-rollups-contracts-2.1.1/src/library/LibMerkle32.sol";

import {IDataProvider} from "prt-contracts/IDataProvider.sol";
import {ITournament} from "prt-contracts/ITournament.sol";
import {ITournamentFactory} from "prt-contracts/ITournamentFactory.sol";
import {ITask} from "prt-contracts/ITask.sol";
import {ITaskSpawner} from "prt-contracts/ITaskSpawner.sol";

import {Machine} from "prt-contracts/types/Machine.sol";
import {Tree} from "prt-contracts/types/Tree.sol";

import {EmulatorConstants} from "step/src/EmulatorConstants.sol";
import {Memory} from "step/src/Memory.sol";

import {IDaveConsensus} from "./IDaveConsensus.sol";
import {Merkle} from "./Merkle.sol";

/// @notice Consensus contract with Dave tournaments.
/// @notice Consensus contract with Dave tasks.
///
/// @notice This contract validates only one application,
/// which read inputs from the InputBox contract.
Expand Down Expand Up @@ -57,8 +56,11 @@ contract DaveConsensus is IDaveConsensus, ERC165 {
/// @notice The application contract
address immutable _APP_CONTRACT;

/// @notice The contract used to instantiate tournaments
ITournamentFactory immutable _TOURNAMENT_FACTORY;
/// @notice Security council address
address immutable _SECURITY_COUNCIL;

/// @notice The contract used to instantiate tasks
ITaskSpawner _taskSpawner;

/// @notice Deployment block number
uint256 immutable _DEPLOYMENT_BLOCK_NUMBER = block.number;
Expand All @@ -72,51 +74,72 @@ contract DaveConsensus is IDaveConsensus, ERC165 {
/// @notice Input index (exclusive) upper bound of the current sealed epoch
uint256 _inputIndexUpperBound;

/// @notice Current sealed epoch tournament
ITournament _tournament;
/// @notice Current sealed epoch task
ITask _task;

/// @notice Settled output trees' merkle root hash
mapping(bytes32 => bool) _outputsMerkleRoots;

/// @notice Whether settlement is paused
bool _paused;

constructor(
IInputBox inputBox,
address appContract,
ITournamentFactory tournamentFactory,
ITaskSpawner taskSpawner,
address securityCouncil,
Machine.Hash initialMachineStateHash
) {
// Initialize immutable variables
_INPUT_BOX = inputBox;
_APP_CONTRACT = appContract;
_TOURNAMENT_FACTORY = tournamentFactory;
emit ConsensusCreation(inputBox, appContract, tournamentFactory);
_SECURITY_COUNCIL = securityCouncil;
_taskSpawner = taskSpawner;
emit ConsensusCreation(inputBox, appContract, taskSpawner);

// Initialize first sealed epoch
uint256 inputIndexUpperBound = inputBox.getNumberOfInputs(appContract);
_inputIndexUpperBound = inputIndexUpperBound;
ITournament tournament = tournamentFactory.instantiate(initialMachineStateHash, this);
_tournament = tournament;
emit EpochSealed(0, 0, inputIndexUpperBound, initialMachineStateHash, bytes32(0), tournament);
ITask task = taskSpawner.spawn(initialMachineStateHash, this);
_task = task;
emit EpochSealed(0, 0, inputIndexUpperBound, initialMachineStateHash, bytes32(0), task);
}

function _onlySecurityCouncil() internal view {
require(msg.sender == _SECURITY_COUNCIL, NotSecurityCouncil());
}

modifier onlySecurityCouncil() {
_onlySecurityCouncil();
_;
}

function canSettle()
external
view
override
returns (bool isFinished, uint256 epochNumber, Tree.Node winnerCommitment)
returns (bool isFinished, uint256 epochNumber, Machine.Hash finalState)
{
(isFinished, winnerCommitment,) = _tournament.arbitrationResult();
if (_paused) {
(isFinished, finalState) = (false, Machine.ZERO_STATE);
} else {
(isFinished, finalState) = _task.result();
}

epochNumber = _epochNumber;
}

function settle(uint256 epochNumber, bytes32 outputsMerkleRoot, bytes32[] calldata proof) external override {
// Check tournament settlement
require(!_paused, PausedError());

// Check task settlement
require(epochNumber == _epochNumber, IncorrectEpochNumber(epochNumber, _epochNumber));

// Check tournament finished
(bool isFinished,, Machine.Hash finalMachineStateHash) = _tournament.arbitrationResult();
// Check task finished
(bool isFinished, Machine.Hash finalMachineStateHash) = _task.result();
require(isFinished, TournamentNotFinishedYet());
ITournament oldTournament = _tournament;
_tournament = ITournament(address(0));
ITask oldTask = _task;
_task = ITask(address(0));

// Check outputs Merkle root
_validateOutputTree(finalMachineStateHash, outputsMerkleRoot, proof);
Expand All @@ -127,36 +150,26 @@ contract DaveConsensus is IDaveConsensus, ERC165 {
_inputIndexUpperBound = _INPUT_BOX.getNumberOfInputs(_APP_CONTRACT);
_outputsMerkleRoots[outputsMerkleRoot] = true;

// Start new tournament
_tournament = _TOURNAMENT_FACTORY.instantiate(finalMachineStateHash, this);
// Start new task
_task = _taskSpawner.spawn(finalMachineStateHash, this);

emit EpochSealed(
_epochNumber,
_inputIndexLowerBound,
_inputIndexUpperBound,
finalMachineStateHash,
outputsMerkleRoot,
_tournament
_epochNumber, _inputIndexLowerBound, _inputIndexUpperBound, finalMachineStateHash, outputsMerkleRoot, _task
);

oldTournament.tryRecoveringBond();
_tryCleanup(oldTask);
}

function getCurrentSealedEpoch()
external
view
override
returns (
uint256 epochNumber,
uint256 inputIndexLowerBound,
uint256 inputIndexUpperBound,
ITournament tournament
)
returns (uint256 epochNumber, uint256 inputIndexLowerBound, uint256 inputIndexUpperBound, ITask task)
{
epochNumber = _epochNumber;
inputIndexLowerBound = _inputIndexLowerBound;
inputIndexUpperBound = _inputIndexUpperBound;
tournament = _tournament;
task = _task;
}

function getInputBox() external view override returns (IInputBox) {
Expand All @@ -167,8 +180,37 @@ contract DaveConsensus is IDaveConsensus, ERC165 {
return _APP_CONTRACT;
}

function getTournamentFactory() external view override returns (ITournamentFactory) {
return _TOURNAMENT_FACTORY;
function getTaskSpawner() external view override returns (ITaskSpawner) {
return _taskSpawner;
}

function getSecurityCouncil() external view override returns (address) {
return _SECURITY_COUNCIL;
}

function isPaused() external view override returns (bool) {
return _paused;
}

function upgrade(Machine.Hash newInitialState, ITaskSpawner newTaskSpawner) external override onlySecurityCouncil {
_taskSpawner = newTaskSpawner;
_task = newTaskSpawner.spawn(newInitialState, this);

emit TaskUpgraded(_epochNumber, newInitialState, newTaskSpawner, _task);
}

function pause() external override onlySecurityCouncil {
if (!_paused) {
_paused = true;
emit Paused(msg.sender);
}
}

function unpause() external override onlySecurityCouncil {
if (_paused) {
_paused = false;
emit Unpaused(msg.sender);
}
}

function provideMerkleRootOfInput(uint256 inputIndexWithinEpoch, bytes calldata input)
Expand Down Expand Up @@ -227,4 +269,12 @@ contract DaveConsensus is IDaveConsensus, ERC165 {

require(machineStateHash == allegedStateHash, InvalidOutputsMerkleRootProof(finalMachineStateHash));
}

function _tryCleanup(ITask task) internal {
if (address(task) == address(0)) {
return;
}

try task.cleanup() returns (bool) {} catch {}
}
}
Loading
Loading