From f7b67ea65d3371d4f5f873e8974df86d1287625a Mon Sep 17 00:00:00 2001 From: Stephen Chen <20940639+stephenctw@users.noreply.github.com> Date: Mon, 10 Feb 2025 19:57:24 +0800 Subject: [PATCH] test(prt-contracts): improve `LeafTournament` coverage --- prt/contracts/test/BottomTournament.t.sol | 359 +++++++++++++++++++++- prt/contracts/test/Util.sol | 51 +++ 2 files changed, 408 insertions(+), 2 deletions(-) diff --git a/prt/contracts/test/BottomTournament.t.sol b/prt/contracts/test/BottomTournament.t.sol index d5f5af8..badd76b 100644 --- a/prt/contracts/test/BottomTournament.t.sol +++ b/prt/contracts/test/BottomTournament.t.sol @@ -38,7 +38,7 @@ contract BottomTournamentTest is Util, Test { function setUp() public {} - function testBottom1() public { + function testComputeStep() public { topTournament = Util.initializePlayer0Tournament(factory); // pair commitment, expect a match @@ -148,7 +148,7 @@ contract BottomTournamentTest is Util, Test { Util.winLeafMatch(bottomTournament, _matchId, _playerToSeal); } - function testBottom2() public { + function testComputeReset() public { topTournament = Util.initializePlayer0Tournament(factory); // pair commitment, expect a match @@ -278,4 +278,359 @@ contract BottomTournamentTest is Util, Test { // win match, expect revert Util.winLeafMatch(bottomTournament, _matchId, _playerToSeal); } + + function testRollupsCmio() public { + topTournament = Util.initializePlayer0RollupsTournament(factory); + + // pair commitment, expect a match + // player 1 joins tournament + uint256 _opponent = 1; + uint64 _height = 0; + Util.joinTournament(topTournament, _opponent); + + Match.Id memory _matchId = Util.matchId(_opponent, _height); + Match.State memory _match = + topTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + uint256 _playerToSeal = + Util.advanceMatch(topTournament, _matchId, _opponent); + + // expect new inner created + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + topTournament, _matchId, _playerToSeal + ); + _height += 1; + + assertEq( + topTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + Vm.Log[] memory _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + middleTournament = MiddleTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(middleTournament, 0); + Util.joinTournament(middleTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = middleTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + _playerToSeal = Util.advanceMatch(middleTournament, _matchId, _opponent); + + // expect new inner created (middle 2) + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + middleTournament, _matchId, _playerToSeal + ); + _height += 1; + + assertEq( + middleTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + bottomTournament = BottomTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(bottomTournament, 0); + Util.joinTournament(bottomTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = bottomTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + _playerToSeal = Util.advanceMatch(bottomTournament, _matchId, _opponent); + + // seal match + Util.sealLeafMatch(bottomTournament, _matchId, _playerToSeal); + + assertEq( + bottomTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + vm.expectRevert(); + // win match, expect revert + Util.winLeafMatchRollupsWithInput( + bottomTournament, _matchId, _playerToSeal + ); + } + + function testRollupsStep() public { + topTournament = Util.initializePlayer0RollupsTournament(factory); + + // pair commitment, expect a match + // player 1 joins tournament + uint256 _opponent = 1; + uint64 _height = 0; + Util.joinTournament(topTournament, _opponent); + + Match.Id memory _matchId = Util.matchId(_opponent, _height); + Match.State memory _match = + topTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + uint256 _playerToSeal = + Util.advanceMatch(topTournament, _matchId, _opponent); + + // expect new inner created + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + topTournament, _matchId, _playerToSeal + ); + _height += 1; + + assertEq( + topTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + Vm.Log[] memory _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + middleTournament = MiddleTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(middleTournament, 0); + Util.joinTournament(middleTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = middleTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + _playerToSeal = Util.advanceMatch(middleTournament, _matchId, _opponent); + + // expect new inner created (middle 2) + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + middleTournament, _matchId, _playerToSeal + ); + _height += 1; + + assertEq( + middleTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + bottomTournament = BottomTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(bottomTournament, 0); + Util.joinTournament(bottomTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = bottomTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to left tree + _playerToSeal = Util.advanceMatch(bottomTournament, _matchId, _opponent); + + // seal match + Util.sealLeafMatch(bottomTournament, _matchId, _playerToSeal); + + assertEq( + bottomTournament.getMatchCycle(_matchId.hashFromId()), + 0, + "agree cycle should be zero" + ); + + vm.expectRevert(); + // win match, expect revert + Util.winLeafMatchRollupsWithoutInput( + bottomTournament, _matchId, _playerToSeal + ); + } + + function testRollupsReset() public { + topTournament = Util.initializePlayer0RollupsTournament(factory); + + // pair commitment, expect a match + // player 2 joins tournament + uint256 _opponent = 2; + uint64 _height = 0; + Util.joinTournament(topTournament, _opponent); + + Match.Id memory _matchId = Util.matchId(_opponent, _height); + Match.State memory _match = + topTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to right tree + uint256 _playerToSeal = + Util.advanceMatch(topTournament, _matchId, _opponent); + + // expect new inner created + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + topTournament, _matchId, _playerToSeal + ); + _height += 1; + + uint256 cycle = ( + 1 + << ( + ArbitrationConstants.height(0) + + ArbitrationConstants.log2step(0) + ) + ) - (1 << ArbitrationConstants.log2step(0)); + assertEq( + topTournament.getMatchCycle(_matchId.hashFromId()), + cycle, + "agree cycle should be 4951760157141503507410452480" + ); + + Vm.Log[] memory _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + middleTournament = MiddleTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(middleTournament, 0); + Util.joinTournament(middleTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = middleTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to right tree + _playerToSeal = Util.advanceMatch(middleTournament, _matchId, _opponent); + + // expect new inner created (middle 2) + vm.recordLogs(); + + // seal match + Util.sealInnerMatchAndCreateInnerTournament( + middleTournament, _matchId, _playerToSeal + ); + _height += 1; + + cycle = ( + 1 + << ( + ArbitrationConstants.height(0) + + ArbitrationConstants.log2step(0) + ) + ) - (1 << ArbitrationConstants.log2step(1)); + assertEq( + middleTournament.getMatchCycle(_matchId.hashFromId()), + cycle, + "agree cycle should be 4951760157141521099462279168" + ); + + _entries = vm.getRecordedLogs(); + assertEq(_entries[0].topics.length, 2); + assertEq( + _entries[0].topics[0], + keccak256("newInnerTournament(bytes32,address)") + ); + assertEq( + _entries[0].topics[1], Match.IdHash.unwrap(_matchId.hashFromId()) + ); + + bottomTournament = BottomTournament( + address(bytes20(bytes32(_entries[0].data) << (12 * 8))) + ); + + Util.joinTournament(bottomTournament, 0); + Util.joinTournament(bottomTournament, _opponent); + + _matchId = Util.matchId(_opponent, _height); + _match = bottomTournament.getMatch(_matchId.hashFromId()); + assertTrue(_match.exists(), "match should exist"); + + // advance match to end, this match will always advance to right tree + _playerToSeal = Util.advanceMatch(bottomTournament, _matchId, _opponent); + + // seal match + Util.sealLeafMatch(bottomTournament, _matchId, _playerToSeal); + + cycle = ( + 1 + << ( + ArbitrationConstants.height(0) + + ArbitrationConstants.log2step(0) + ) + ) - (1 << ArbitrationConstants.log2step(2)); + assertEq( + bottomTournament.getMatchCycle(_matchId.hashFromId()), + cycle, + "agree cycle should be 4951760157141521099596496895" + ); + + vm.expectRevert(); + // win match, expect revert + Util.winLeafMatch(bottomTournament, _matchId, _playerToSeal); + } } diff --git a/prt/contracts/test/Util.sol b/prt/contracts/test/Util.sol index a4a84cd..0254007 100644 --- a/prt/contracts/test/Util.sol +++ b/prt/contracts/test/Util.sol @@ -151,6 +151,21 @@ contract Util { joinTournament(_topTournament, 0); } + // create new _topTournament and player 0 joins it + function initializePlayer0RollupsTournament( + MultiLevelTournamentFactory _factory + ) internal returns (TopTournament _topTournament) { + _topTournament = TopTournament( + address( + _factory.instantiate( + ONE_STATE, IDataProvider(address(0x12345678901234567890)) + ) + ) + ); + // player 0 joins tournament + joinTournament(_topTournament, 0); + } + // _player joins _tournament at _level function joinTournament(Tournament _tournament, uint256 _player) internal { (,,, uint64 height) = _tournament.tournamentLevelConstants(); @@ -198,6 +213,42 @@ contract Util { _tournament.winLeafMatch(_matchId, _left, _right, new bytes(0)); } + function winLeafMatchRollupsWithInput( + LeafTournament _tournament, + Match.Id memory _matchId, + uint256 _player + ) internal { + Tree.Node _left = _player == 1 ? playerNodes[1][0] : playerNodes[0][0]; + Tree.Node _right = playerNodes[_player][0]; + // Machine.Hash state = _player == 1 ? ONE_STATE : Machine.ZERO_STATE; + + uint256 length = 20; + _tournament.winLeafMatch( + _matchId, + _left, + _right, + abi.encodePacked(abi.encodePacked(length), new bytes(20)) + ); + } + + function winLeafMatchRollupsWithoutInput( + LeafTournament _tournament, + Match.Id memory _matchId, + uint256 _player + ) internal { + Tree.Node _left = _player == 1 ? playerNodes[1][0] : playerNodes[0][0]; + Tree.Node _right = playerNodes[_player][0]; + // Machine.Hash state = _player == 1 ? ONE_STATE : Machine.ZERO_STATE; + + uint256 length = 0; + _tournament.winLeafMatch( + _matchId, + _left, + _right, + abi.encodePacked(abi.encodePacked(length), new bytes(0)) + ); + } + function sealInnerMatchAndCreateInnerTournament( NonLeafTournament _tournament, Match.Id memory _matchId,