diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e757e3f7..fe290a51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,14 +9,6 @@ jobs: with: submodules: recursive - - name: Set up cache for Foundry - uses: actions/cache@v3 - with: - path: ~/.foundry - key: ${{ runner.os }}-foundry-${{ hashFiles('**/foundry.toml') }} - restore-keys: | - ${{ runner.os }}-foundry- - - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -30,16 +22,6 @@ jobs: just build-smart-contracts just test - - name: Set up cache for Cargo - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - name: Deps run: | export CARTESI_MACHINE_VERSION=0.18.1 diff --git a/cartesi-rollups/contracts/script/DaveConsensus.s.sol b/cartesi-rollups/contracts/script/DaveConsensus.s.sol index 745678ee..52d9f788 100644 --- a/cartesi-rollups/contracts/script/DaveConsensus.s.sol +++ b/cartesi-rollups/contracts/script/DaveConsensus.s.sol @@ -8,31 +8,21 @@ import {Script} from "forge-std/Script.sol"; import {Machine} from "prt-contracts/Machine.sol"; import "prt-contracts/tournament/factories/MultiLevelTournamentFactory.sol"; -import "prt-contracts/CanonicalConstants.sol"; +import "prt-contracts/CanonicalTournamentParametersProvider.sol"; import "rollups-contracts/inputs/IInputBox.sol"; import "src/DaveConsensus.sol"; contract DaveConcensusScript is Script { function run(Machine.Hash initialHash, IInputBox inputBox) external { - DisputeParameters memory disputeParameters = DisputeParameters({ - timeConstants: TimeConstants({ - matchEffort: ArbitrationConstants.MATCH_EFFORT, - maxAllowance: ArbitrationConstants.MAX_ALLOWANCE - }), - commitmentStructures: new CommitmentStructure[](ArbitrationConstants.LEVELS) - }); - - for (uint64 i; i < ArbitrationConstants.LEVELS; ++i) { - disputeParameters.commitmentStructures[i] = CommitmentStructure({ - log2step: ArbitrationConstants.log2step(i), - height: ArbitrationConstants.height(i) - }); - } vm.startBroadcast(vm.envUint("PRIVATE_KEY")); MultiLevelTournamentFactory factory = new MultiLevelTournamentFactory( - new TopTournamentFactory(), new MiddleTournamentFactory(), new BottomTournamentFactory(), disputeParameters + new TopTournamentFactory(), + new MiddleTournamentFactory(), + new BottomTournamentFactory(), + new CanonicalTournamentParametersProvider() ); + new DaveConsensus(inputBox, address(0x0), factory, initialHash); vm.stopBroadcast(); diff --git a/cartesi-rollups/contracts/test/Merkle.t.sol b/cartesi-rollups/contracts/test/Merkle.t.sol index b6ad6091..7f57e9e0 100644 --- a/cartesi-rollups/contracts/test/Merkle.t.sol +++ b/cartesi-rollups/contracts/test/Merkle.t.sol @@ -9,13 +9,17 @@ import {MerkleConstants} from "src/MerkleConstants.sol"; import {PristineMerkleTree} from "src/PristineMerkleTree.sol"; import {Merkle} from "src/Merkle.sol"; -contract PristineMerkleTreeContract { +library PristineMerkleTreeWrapper { function getNodeAtHeight(uint256 height) external pure returns (bytes32) { return PristineMerkleTree.getNodeAtHeight(height); } } -contract MerkleContract { +library MerkleWrapper { + function join(bytes32 a, bytes32 b) external pure returns (bytes32) { + return Merkle.join(a, b); + } + function getRootAfterReplacementInDrive( uint256 position, uint256 log2SizeOfReplacement, @@ -42,9 +46,6 @@ contract MerkleContract { } contract MerkleTest is Test { - PristineMerkleTreeContract pristineMerkle; - MerkleContract merkle; - uint256 constant LEAF_SIZE = (1 << MerkleConstants.LOG2_LEAF_SIZE); bytes32 constant WORD_0 = keccak256("foo"); bytes32 constant WORD_1 = keccak256("bar"); @@ -59,30 +60,25 @@ contract MerkleTest is Test { bytes32 constant ROOT = keccak256(abi.encode(NODE_01, NODE_23)); uint256 constant MAX_HEIGHT = MerkleConstants.LOG2_MEMORY_SIZE - MerkleConstants.LOG2_LEAF_SIZE; - function setUp() external { - pristineMerkle = new PristineMerkleTreeContract(); - merkle = new MerkleContract(); - } - function testJoinEquivalence(bytes32 a, bytes32 b) external pure { - assertEq(Merkle.join(a, b), keccak256(abi.encodePacked(a, b))); + assertEq(MerkleWrapper.join(a, b), keccak256(abi.encodePacked(a, b))); } function testPristineMerkleTree() external pure { bytes32 node = keccak256(abi.encode(0)); for (uint256 height; height <= MerkleConstants.TREE_HEIGHT; ++height) { assertEq(PristineMerkleTree.getNodeAtHeight(height), node); - node = Merkle.join(node, node); + node = MerkleWrapper.join(node, node); } } function testPristineMerkleTreeRevert(uint256 height) external { height = bound(height, MerkleConstants.TREE_HEIGHT + 1, type(uint256).max); vm.expectRevert("Height out of bounds"); - pristineMerkle.getNodeAtHeight(height); + PristineMerkleTreeWrapper.getNodeAtHeight(height); } - function testGetRootAfterReplacementInDrive() external view { + function testGetRootAfterReplacementInDrive() external pure { { uint256 log2SizeOfReplacement = MerkleConstants.LOG2_LEAF_SIZE; uint256 position = 0 << log2SizeOfReplacement; @@ -110,7 +106,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -142,7 +138,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -174,7 +170,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -206,7 +202,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -231,7 +227,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -256,7 +252,7 @@ contract MerkleTest is Test { assertEq( ROOT, - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ) ); @@ -273,7 +269,7 @@ contract MerkleTest is Test { siblings[0] = LEAF_1; siblings[1] = NODE_23; vm.expectRevert("Position is not aligned"); - merkle.getRootAfterReplacementInDrive( + MerkleWrapper.getRootAfterReplacementInDrive( position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings ); } @@ -291,7 +287,9 @@ contract MerkleTest is Test { vm.assume(siblings.length != log2SizeOfDrive - log2SizeOfReplacement); position = (position >> log2SizeOfReplacement) << log2SizeOfReplacement; vm.expectRevert("Proof length does not match"); - Merkle.getRootAfterReplacementInDrive(position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings); + MerkleWrapper.getRootAfterReplacementInDrive( + position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings + ); } function testGetRootAfterAnyReplacementInDrive( @@ -299,63 +297,65 @@ contract MerkleTest is Test { uint256 log2SizeOfReplacement, uint256 log2SizeOfDrive, bytes32 replacement - ) external view { + ) external pure { log2SizeOfDrive = bound(log2SizeOfDrive, MerkleConstants.LOG2_LEAF_SIZE, MerkleConstants.LOG2_MEMORY_SIZE); log2SizeOfReplacement = bound(log2SizeOfReplacement, MerkleConstants.LOG2_LEAF_SIZE, log2SizeOfDrive); bytes32[] memory siblings = new bytes32[](log2SizeOfDrive - log2SizeOfReplacement); position = (position >> log2SizeOfReplacement) << log2SizeOfReplacement; - merkle.getRootAfterReplacementInDrive(position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings); + MerkleWrapper.getRootAfterReplacementInDrive( + position, log2SizeOfReplacement, log2SizeOfDrive, replacement, siblings + ); } - function testGetMinLog2SizeOfDrive(bytes calldata data) external view { - uint256 log2SizeOfDrive = merkle.getMinLog2SizeOfDrive(data); + function testGetMinLog2SizeOfDrive(bytes calldata data) external pure { + uint256 log2SizeOfDrive = MerkleWrapper.getMinLog2SizeOfDrive(data); assertLe(data.length, 1 << log2SizeOfDrive); if (data.length <= LEAF_SIZE) { assertEq(log2SizeOfDrive, MerkleConstants.LOG2_LEAF_SIZE); } else { assertGt(data.length, 1 << (log2SizeOfDrive - 1)); } - merkle.getMerkleRootFromBytes(data, log2SizeOfDrive); + MerkleWrapper.getMerkleRootFromBytes(data, log2SizeOfDrive); } - function testGetMerkleRootFromBytes() external view { + function testGetMerkleRootFromBytes() external pure { bytes memory data = _getTestData(); bytes32 root = ROOT; for (uint256 i; MerkleConstants.LOG2_LEAF_SIZE + HEIGHT + i <= MerkleConstants.LOG2_MEMORY_SIZE; ++i) { - assertEq(merkle.getMerkleRootFromBytes(data, MerkleConstants.LOG2_LEAF_SIZE + HEIGHT + i), root); - root = Merkle.join(root, PristineMerkleTree.getNodeAtHeight(HEIGHT + i)); + assertEq(MerkleWrapper.getMerkleRootFromBytes(data, MerkleConstants.LOG2_LEAF_SIZE + HEIGHT + i), root); + root = MerkleWrapper.join(root, PristineMerkleTree.getNodeAtHeight(HEIGHT + i)); } } function testGetMerkleRootFromBytesRevertDriveSmallerThanLeaf(uint256 log2SizeOfDrive) external { log2SizeOfDrive = bound(log2SizeOfDrive, 0, MerkleConstants.LOG2_LEAF_SIZE - 1); vm.expectRevert("Drive smaller than leaf"); - merkle.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); + MerkleWrapper.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); } function testGetMerkleRootFromBytesRevertDataLargerThanDrive(uint256 log2SizeOfDrive) external { log2SizeOfDrive = MerkleConstants.LOG2_LEAF_SIZE + bound(log2SizeOfDrive, 0, HEIGHT - 1); vm.expectRevert("Data larger than drive"); - merkle.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); + MerkleWrapper.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); } function testGetMerkleRootFromBytesRevertDriveLargerThanMemory(uint256 log2SizeOfDrive) external { log2SizeOfDrive = bound(log2SizeOfDrive, MerkleConstants.LOG2_MEMORY_SIZE + 1, type(uint256).max); vm.expectRevert("Drive larger than memory"); - merkle.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); + MerkleWrapper.getMerkleRootFromBytes(_getTestData(), log2SizeOfDrive); } - function testGetHashOfLeafAtIndex() external view { + function testGetHashOfLeafAtIndex() external pure { bytes memory data = _getTestData(); - assertEq(merkle.getHashOfLeafAtIndex(data, 0), LEAF_0); - assertEq(merkle.getHashOfLeafAtIndex(data, 1), LEAF_1); - assertEq(merkle.getHashOfLeafAtIndex(data, 2), LEAF_2); + assertEq(MerkleWrapper.getHashOfLeafAtIndex(data, 0), LEAF_0); + assertEq(MerkleWrapper.getHashOfLeafAtIndex(data, 1), LEAF_1); + assertEq(MerkleWrapper.getHashOfLeafAtIndex(data, 2), LEAF_2); } function testGetHashOfLeafAtIndex(bytes calldata data, uint256 index) external pure { index = bound(index, _getNumOfLeaves(data.length), type(uint256).max); bytes32 leafHash = PristineMerkleTree.getNodeAtHeight(0); - assertEq(Merkle.getHashOfLeafAtIndex(data, index), leafHash); + assertEq(MerkleWrapper.getHashOfLeafAtIndex(data, index), leafHash); } function _getTestData() internal pure returns (bytes memory) { diff --git a/cartesi-rollups/node/blockchain-reader/src/lib.rs b/cartesi-rollups/node/blockchain-reader/src/lib.rs index c0f4a631..a286d4d8 100644 --- a/cartesi-rollups/node/blockchain-reader/src/lib.rs +++ b/cartesi-rollups/node/blockchain-reader/src/lib.rs @@ -503,7 +503,7 @@ mod blockchain_reader_tests { anvil, provider, Address::from_hex("0x5fbdb2315678afecb367f032d93f642f64180aa3").unwrap(), - Address::from_hex("0x0165878a594ca255338adfa4d48449f69242eb8f").unwrap(), + Address::from_hex("0xa513e6e4b8f2a923d98304ec87f64353c4d5c853").unwrap(), ) } diff --git a/prt/contracts/script/TopTournament.s.sol b/prt/contracts/script/TopTournament.s.sol index 89216ba3..00ed38e6 100644 --- a/prt/contracts/script/TopTournament.s.sol +++ b/prt/contracts/script/TopTournament.s.sol @@ -9,34 +9,17 @@ import {Machine} from "src/Machine.sol"; import "src/tournament/factories/MultiLevelTournamentFactory.sol"; import "src/IDataProvider.sol"; -import "src/CanonicalConstants.sol"; +import "src/CanonicalTournamentParametersProvider.sol"; contract TopTournamentScript is Script { function run(Machine.Hash initialHash) external { - DisputeParameters memory disputeParameters = DisputeParameters({ - timeConstants: TimeConstants({ - matchEffort: ArbitrationConstants.MATCH_EFFORT, - maxAllowance: ArbitrationConstants.MAX_ALLOWANCE - }), - commitmentStructures: new CommitmentStructure[]( - ArbitrationConstants.LEVELS - ) - }); - - for (uint64 i; i < ArbitrationConstants.LEVELS; ++i) { - disputeParameters.commitmentStructures[i] = CommitmentStructure({ - log2step: ArbitrationConstants.log2step(i), - height: ArbitrationConstants.height(i) - }); - } - vm.startBroadcast(vm.envUint("PRIVATE_KEY")); MultiLevelTournamentFactory factory = new MultiLevelTournamentFactory( new TopTournamentFactory(), new MiddleTournamentFactory(), new BottomTournamentFactory(), - disputeParameters + new CanonicalTournamentParametersProvider() ); factory.instantiate(initialHash, IDataProvider(address(0x0))); diff --git a/prt/contracts/src/CanonicalTournamentParametersProvider.sol b/prt/contracts/src/CanonicalTournamentParametersProvider.sol new file mode 100644 index 00000000..67973d8a --- /dev/null +++ b/prt/contracts/src/CanonicalTournamentParametersProvider.sol @@ -0,0 +1,27 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +pragma solidity ^0.8.17; + +import "./ITournamentParametersProvider.sol"; +import "./CanonicalConstants.sol"; + +contract CanonicalTournamentParametersProvider is + ITournamentParametersProvider +{ + /// @inheritdoc ITournamentParametersProvider + function tournamentParameters(uint64 level) + external + pure + override + returns (TournamentParameters memory) + { + return TournamentParameters({ + levels: ArbitrationConstants.LEVELS, + log2step: ArbitrationConstants.log2step(level), + height: ArbitrationConstants.height(level), + matchEffort: ArbitrationConstants.MATCH_EFFORT, + maxAllowance: ArbitrationConstants.MAX_ALLOWANCE + }); + } +} diff --git a/prt/contracts/src/ITournamentParametersProvider.sol b/prt/contracts/src/ITournamentParametersProvider.sol new file mode 100644 index 00000000..605c78a9 --- /dev/null +++ b/prt/contracts/src/ITournamentParametersProvider.sol @@ -0,0 +1,15 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +pragma solidity ^0.8.17; + +import {TournamentParameters} from "./TournamentParameters.sol"; + +interface ITournamentParametersProvider { + /// @notice Get tournament parameters for a given level. + /// @param level The tournament level (0 = top) + function tournamentParameters(uint64 level) + external + view + returns (TournamentParameters memory); +} diff --git a/prt/contracts/src/tournament/factories/MultiLevelTournamentFactory.sol b/prt/contracts/src/tournament/factories/MultiLevelTournamentFactory.sol index da0df69d..b21dd0f3 100644 --- a/prt/contracts/src/tournament/factories/MultiLevelTournamentFactory.sol +++ b/prt/contracts/src/tournament/factories/MultiLevelTournamentFactory.sol @@ -5,65 +5,28 @@ pragma solidity ^0.8.17; import "../../IMultiLevelTournamentFactory.sol"; import "../../TournamentParameters.sol"; +import "../../ITournamentParametersProvider.sol"; import "./multilevel/TopTournamentFactory.sol"; import "./multilevel/MiddleTournamentFactory.sol"; import "./multilevel/BottomTournamentFactory.sol"; -struct CommitmentStructure { - uint64 log2step; - uint64 height; -} - -struct TimeConstants { - Time.Duration matchEffort; - Time.Duration maxAllowance; -} - -struct DisputeParameters { - TimeConstants timeConstants; - CommitmentStructure[] commitmentStructures; -} - contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { TopTournamentFactory immutable topFactory; MiddleTournamentFactory immutable middleFactory; BottomTournamentFactory immutable bottomFactory; - uint64 immutable levels; - Time.Duration immutable matchEffort; - Time.Duration immutable maxAllowance; - uint64 immutable log2step0; - uint64 immutable height0; - CommitmentStructure[] commitmentStructures; - - error CommitmentStructuresArrayLengthTooSmall(); - error CommitmentStructuresArrayLengthTooLarge(); + ITournamentParametersProvider immutable tournamentParametersProvider; constructor( TopTournamentFactory _topFactory, MiddleTournamentFactory _middleFactory, BottomTournamentFactory _bottomFactory, - DisputeParameters memory _disputeParameters + ITournamentParametersProvider _tournamentParametersProvider ) { topFactory = _topFactory; middleFactory = _middleFactory; bottomFactory = _bottomFactory; - - require( - _disputeParameters.commitmentStructures.length >= 1, - CommitmentStructuresArrayLengthTooSmall() - ); - require( - _disputeParameters.commitmentStructures.length <= type(uint64).max, - CommitmentStructuresArrayLengthTooLarge() - ); - - levels = uint64(_disputeParameters.commitmentStructures.length); - matchEffort = _disputeParameters.timeConstants.matchEffort; - maxAllowance = _disputeParameters.timeConstants.maxAllowance; - log2step0 = _disputeParameters.commitmentStructures[0].log2step; - height0 = _disputeParameters.commitmentStructures[0].height; - commitmentStructures = _disputeParameters.commitmentStructures; + tournamentParametersProvider = _tournamentParametersProvider; } function instantiate(Machine.Hash _initialHash, IDataProvider _provider) @@ -71,13 +34,14 @@ contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { override returns (ITournament) { - TopTournament _tournament = this.instantiateTop(_initialHash, _provider); + TopTournament _tournament = instantiateTop(_initialHash, _provider); emit tournamentCreated(_tournament); return _tournament; } function instantiateTop(Machine.Hash _initialHash, IDataProvider _provider) - external + public + override returns (TopTournament) { TopTournament _tournament = topFactory.instantiate( @@ -96,7 +60,7 @@ contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { uint256 _startCycle, uint64 _level, IDataProvider _provider - ) external returns (MiddleTournament) { + ) external override returns (MiddleTournament) { MiddleTournament _tournament = middleFactory.instantiate( _initialHash, _contestedCommitmentOne, @@ -124,7 +88,7 @@ contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { uint256 _startCycle, uint64 _level, IDataProvider _provider - ) external returns (BottomTournament) { + ) external override returns (BottomTournament) { BottomTournament _tournament = bottomFactory.instantiate( _initialHash, _contestedCommitmentOne, @@ -146,13 +110,7 @@ contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { view returns (TournamentParameters memory) { - return TournamentParameters({ - levels: levels, - log2step: log2step0, - height: height0, - matchEffort: matchEffort, - maxAllowance: maxAllowance - }); + return _getTournamentParameters(0); } function _getTournamentParameters(uint64 _level) @@ -160,12 +118,6 @@ contract MultiLevelTournamentFactory is IMultiLevelTournamentFactory { view returns (TournamentParameters memory) { - return TournamentParameters({ - levels: levels, - log2step: commitmentStructures[_level].log2step, - height: commitmentStructures[_level].height, - matchEffort: matchEffort, - maxAllowance: maxAllowance - }); + return tournamentParametersProvider.tournamentParameters(_level); } } diff --git a/prt/contracts/test/Util.sol b/prt/contracts/test/Util.sol index fd770203..1e31f3cd 100644 --- a/prt/contracts/test/Util.sol +++ b/prt/contracts/test/Util.sol @@ -12,6 +12,7 @@ import "src/tournament/libs/Match.sol"; import "src/CanonicalConstants.sol"; +import "src/CanonicalTournamentParametersProvider.sol"; import "src/tournament/concretes/TopTournament.sol"; import "src/tournament/concretes/MiddleTournament.sol"; @@ -289,28 +290,11 @@ contract Util { internal returns (MultiLevelTournamentFactory) { - DisputeParameters memory disputeParameters = DisputeParameters({ - timeConstants: TimeConstants({ - matchEffort: ArbitrationConstants.MATCH_EFFORT, - maxAllowance: ArbitrationConstants.MAX_ALLOWANCE - }), - commitmentStructures: new CommitmentStructure[]( - ArbitrationConstants.LEVELS - ) - }); - - for (uint64 i; i < ArbitrationConstants.LEVELS; ++i) { - disputeParameters.commitmentStructures[i] = CommitmentStructure({ - log2step: ArbitrationConstants.log2step(i), - height: ArbitrationConstants.height(i) - }); - } - return new MultiLevelTournamentFactory( new TopTournamentFactory(), new MiddleTournamentFactory(), new BottomTournamentFactory(), - disputeParameters + new CanonicalTournamentParametersProvider() ); } }