Skip to content
Open
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
7 changes: 7 additions & 0 deletions .changeset/fresh-brooms-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cartesi/rollups": minor
---

- Add `outputsExecuted` counter to **Application** contract.
- Add `claimsAccepted` counters to **Consensus** (Authority + Quorum).
- Introduce view functions to retrieve these counters.
9 changes: 9 additions & 0 deletions src/consensus/AbstractConsensus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ abstract contract AbstractConsensus is IConsensus, ERC165 {

/// @notice Indexes accepted claims by application contract address.
mapping(address => mapping(bytes32 => bool)) private _validOutputsMerkleRoots;
/// @notice Number of claims accepted by the consensus.
/// @dev Must be a monotonically non-decreasing in time
uint256 _numOfAcceptedClaims;

/// @param epochLength The epoch length
/// @dev Reverts if the epoch length is zero.
Expand All @@ -39,6 +42,11 @@ abstract contract AbstractConsensus is IConsensus, ERC165 {
return _epochLength;
}

/// @inheritdoc IConsensus
function getNumberOfAcceptedClaims() external view override returns (uint256) {
return _numOfAcceptedClaims;
}

/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId)
public
Expand Down Expand Up @@ -83,5 +91,6 @@ abstract contract AbstractConsensus is IConsensus, ERC165 {
) internal {
_validOutputsMerkleRoots[appContract][outputsMerkleRoot] = true;
emit ClaimAccepted(appContract, lastProcessedBlockNumber, outputsMerkleRoot);
++_numOfAcceptedClaims;
}
}
3 changes: 3 additions & 0 deletions src/consensus/IConsensus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,7 @@ interface IConsensus is IOutputsMerkleRootValidator {
/// @dev The epoch number of a block is defined as
/// the integer division of the block number by the epoch length.
function getEpochLength() external view returns (uint256);

/// @notice Get the number of claims accepted by the consensus.
function getNumberOfAcceptedClaims() external view returns (uint256);
}
10 changes: 10 additions & 0 deletions src/dapp/Application.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ contract Application is
/// @dev See the `getDataAvailability` function.
bytes internal _dataAvailability;

/// @notice The number of outputs executed by the application.
/// @dev See the `numberOfOutputsExecuted` function.
uint256 _numOfExecutedOutputs;

/// @notice Creates an `Application` contract.
/// @param outputsMerkleRootValidator The initial outputs Merkle root validator contract
/// @param initialOwner The initial application owner
Expand Down Expand Up @@ -104,6 +108,7 @@ contract Application is
}

_executed.set(outputIndex);
++_numOfExecutedOutputs;
emit OutputExecuted(outputIndex, output);
}

Expand Down Expand Up @@ -176,6 +181,11 @@ contract Application is
return _deploymentBlockNumber;
}

/// @inheritdoc IApplication
function getNumberOfExecutedOutputs() external view override returns (uint256) {
return _numOfExecutedOutputs;
}

/// @inheritdoc Ownable
function owner() public view override(IOwnable, Ownable) returns (address) {
return super.owner();
Expand Down
3 changes: 3 additions & 0 deletions src/dapp/IApplication.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,7 @@ interface IApplication is IOwnable {

/// @notice Get number of block in which contract was deployed
function getDeploymentBlockNumber() external view returns (uint256);

/// @notice Get number of outputs executed by the application.
function getNumberOfExecutedOutputs() external view returns (uint256);
}
25 changes: 25 additions & 0 deletions test/consensus/authority/Authority.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ contract AuthorityTest is Test, ERC165Test, OwnableTest {
authority.submitClaim(appContract, lastProcessedBlockNumber, claim);
}

function testSubmitMultipleValidClaims(bytes32[] calldata claims) public {
address owner = vm.addr(1);
address app = vm.addr(2);
uint256 epochLength = 5;

IAuthority authority = new Authority(owner, epochLength);

uint256 blockNum = 0;
vm.roll(blockNum);

for (uint256 i; i < claims.length; ++i) {
blockNum = (i + 1) * epochLength;
vm.roll(blockNum);

vm.prank(owner);
authority.submitClaim(app, blockNum - 1, claims[i]);

assertEq(authority.getNumberOfAcceptedClaims(), i + 1);
}
}

function testSubmitClaimNotFirstClaim(
address owner,
uint256 epochLength,
Expand Down Expand Up @@ -152,6 +173,8 @@ contract AuthorityTest is Test, ERC165Test, OwnableTest {
);
vm.prank(owner);
authority.submitClaim(appContract, lastProcessedBlockNumber, claim2);

assertEq(authority.getNumberOfAcceptedClaims(), 1);
}

function testSubmitClaimNotEpochFinalBlock(
Expand Down Expand Up @@ -185,6 +208,7 @@ contract AuthorityTest is Test, ERC165Test, OwnableTest {
);
vm.prank(owner);
authority.submitClaim(appContract, lastProcessedBlockNumber, claim);
assertEq(authority.getNumberOfAcceptedClaims(), 0);
}

function testSubmitClaimNotPastBlock(
Expand Down Expand Up @@ -214,6 +238,7 @@ contract AuthorityTest is Test, ERC165Test, OwnableTest {
);
vm.prank(owner);
authority.submitClaim(appContract, lastProcessedBlockNumber, claim);
assertEq(authority.getNumberOfAcceptedClaims(), 0);
}

function testIsOutputsMerkleRootValid(
Expand Down
29 changes: 29 additions & 0 deletions test/consensus/quorum/Quorum.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,35 @@ contract QuorumTest is Test, ERC165Test {
quorum.submitClaim(claim);
}

function testMultipleClaimsAcceptedCounter(bytes32[] calldata claims) external {
uint256 epochLength = 5;
uint256 numOfValidators = 3;

IQuorum quorum = _deployQuorum(numOfValidators, epochLength);

Claim memory claim;
claim.appContract = vm.addr(1);

uint256 blockNum = epochLength;
vm.roll(blockNum);

for (uint256 i = 0; i < claims.length; ++i) {
claim.lastProcessedBlockNumber = blockNum - 1;
claim.outputsMerkleRoot = claims[i];

// submit claim with majority validators
for (uint256 id = 1; id <= (numOfValidators / 2 + 1); ++id) {
vm.prank(quorum.validatorById(id));
quorum.submitClaim(claim);
}

assertTrue(quorum.isOutputsMerkleRootValid(claim));
assertEq(quorum.getNumberOfAcceptedClaims(), i + 1);

blockNum += epochLength;
vm.roll(blockNum);
}
}
// Internal functions
// ------------------

Expand Down
39 changes: 36 additions & 3 deletions test/dapp/Application.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,22 @@ contract ApplicationTest is Test, OwnableTest {
);
}

function _expectNumberOfExecutedOutputsIncrement(uint256 before) internal {
assertEq(
_appContract.getNumberOfExecutedOutputs(),
before + 1,
"Should increment number of executed outputs on revert"
);
}

function _expectNumberOfExecutedOutputsSame(uint256 before) internal {
assertEq(
_appContract.getNumberOfExecutedOutputs(),
before,
"Should not increment number of executed outputs on revert"
);
}

function _expectRevertInvalidOutputHashesSiblingsArrayLength() internal {
vm.expectRevert(IApplication.InvalidOutputHashesSiblingsArrayLength.selector);
}
Expand All @@ -531,19 +547,19 @@ contract ApplicationTest is Test, OwnableTest {
function _testEtherTransfer(bytes memory output, OutputValidityProof memory proof)
internal
{
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
assertLt(
address(_appContract).balance,
_transferAmount,
"Application contract does not have enough Ether"
);

vm.expectRevert(
abi.encodeWithSelector(
IApplication.InsufficientFunds.selector, _transferAmount, 0
)
);
_appContract.executeOutput(output, proof);

_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);
vm.deal(address(_appContract), _transferAmount);

uint256 recipientBalance = _recipient.balance;
Expand All @@ -566,14 +582,15 @@ contract ApplicationTest is Test, OwnableTest {
);

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");

_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);
_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
}

function _testEtherMint(bytes memory output, OutputValidityProof memory proof)
internal
{
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
assertLt(
address(_appContract).balance,
_transferAmount,
Expand All @@ -583,6 +600,7 @@ contract ApplicationTest is Test, OwnableTest {
vm.expectRevert();
_appContract.executeOutput(output, proof);

_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);
vm.deal(address(_appContract), _transferAmount);

uint256 recipientBalance = address(_etherReceiver).balance;
Expand Down Expand Up @@ -611,6 +629,7 @@ contract ApplicationTest is Test, OwnableTest {
);

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");
_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);

_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
Expand All @@ -619,6 +638,7 @@ contract ApplicationTest is Test, OwnableTest {
function _testERC721Transfer(bytes memory output, OutputValidityProof memory proof)
internal
{
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
assertEq(
_erc721Token.ownerOf(_tokenId),
_tokenOwner,
Expand All @@ -633,6 +653,7 @@ contract ApplicationTest is Test, OwnableTest {
)
);
_appContract.executeOutput(output, proof);
_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);

vm.prank(_tokenOwner);
_erc721Token.safeTransferFrom(_tokenOwner, address(_appContract), _tokenId);
Expand All @@ -647,6 +668,7 @@ contract ApplicationTest is Test, OwnableTest {
);

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");
_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);

_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
Expand All @@ -655,6 +677,7 @@ contract ApplicationTest is Test, OwnableTest {
function _testERC20Fail(bytes memory output, OutputValidityProof memory proof)
internal
{
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
// test revert

assertLt(
Expand All @@ -672,6 +695,7 @@ contract ApplicationTest is Test, OwnableTest {
)
);
_appContract.executeOutput(output, proof);
_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);

// test return false

Expand All @@ -686,12 +710,14 @@ contract ApplicationTest is Test, OwnableTest {
)
);
_appContract.executeOutput(output, proof);
_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);
vm.clearMockedCalls();
}

function _testERC20Success(bytes memory output, OutputValidityProof memory proof)
internal
{
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
vm.prank(_tokenOwner);
_erc20Token.transfer(address(_appContract), _transferAmount);

Expand All @@ -714,6 +740,7 @@ contract ApplicationTest is Test, OwnableTest {
);

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");
_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);

_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
Expand All @@ -723,6 +750,7 @@ contract ApplicationTest is Test, OwnableTest {
bytes memory output,
OutputValidityProof memory proof
) internal {
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
vm.expectRevert(
abi.encodeWithSelector(
IERC1155Errors.ERC1155InsufficientBalance.selector,
Expand All @@ -733,6 +761,7 @@ contract ApplicationTest is Test, OwnableTest {
)
);
_appContract.executeOutput(output, proof);
_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);

vm.prank(_tokenOwner);
_erc1155SingleToken.safeTransferFrom(
Expand All @@ -758,6 +787,7 @@ contract ApplicationTest is Test, OwnableTest {
);

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");
_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);

_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
Expand All @@ -767,6 +797,7 @@ contract ApplicationTest is Test, OwnableTest {
bytes memory output,
OutputValidityProof memory proof
) internal {
uint256 numberOfExecutedOutputsBefore = _appContract.getNumberOfExecutedOutputs();
vm.expectRevert(
abi.encodeWithSelector(
IERC1155Errors.ERC1155InsufficientBalance.selector,
Expand All @@ -777,6 +808,7 @@ contract ApplicationTest is Test, OwnableTest {
)
);
_appContract.executeOutput(output, proof);
_expectNumberOfExecutedOutputsSame(numberOfExecutedOutputsBefore);

vm.prank(_tokenOwner);
_erc1155BatchToken.safeBatchTransferFrom(
Expand Down Expand Up @@ -809,6 +841,7 @@ contract ApplicationTest is Test, OwnableTest {
}

assertTrue(_wasOutputExecuted(proof), "Output should be marked as executed");
_expectNumberOfExecutedOutputsIncrement(numberOfExecutedOutputsBefore);

_expectRevertOutputNotReexecutable(output);
_appContract.executeOutput(output, proof);
Expand Down
Loading