diff --git a/.changeset/calm-panthers-retire.md b/.changeset/calm-panthers-retire.md index 434fc9a1..2f408f14 100644 --- a/.changeset/calm-panthers-retire.md +++ b/.changeset/calm-panthers-retire.md @@ -2,7 +2,10 @@ "@cartesi/rollups": major --- -Modified the `OutputValidityProof` struct: +Modified the `OutputValidityProof` struct -- Collapsed the `vouchersEpochRootHash` and `noticesEpochRootHash` fields into a single `outputsEpochRootHash` field -- Added an `inputRange` field +- Removed all fields + +- Added an `outputIndex` field + +- Added an `outputHashesSiblings` field diff --git a/.changeset/clean-gorillas-occur.md b/.changeset/clean-gorillas-occur.md index 87a43c90..4c26d912 100644 --- a/.changeset/clean-gorillas-occur.md +++ b/.changeset/clean-gorillas-occur.md @@ -4,6 +4,4 @@ Modified the `CanonicalMachine` library: -- Collapsed the `VOUCHER_METADATA_LOG2_SIZE` and `NOTICE_METADATA_LOG2_SIZE` constants into a single `OUTPUT_METADATA_LOG2_SIZE` constant (with the same value). -- Collapsed the `EPOCH_VOUCHER_LOG2_SIZE` and `EPOCH_NOTICE_LOG2_SIZE` constants into a single `EPOCH_OUTPUT_LOG2_SIZE` constant (with the same value). - Updated the value of the `INPUT_MAX_SIZE` constant to reflect a change in the off-chain machine. diff --git a/.changeset/fifty-keys-tickle.md b/.changeset/fifty-keys-tickle.md deleted file mode 100644 index c6d97f4b..00000000 --- a/.changeset/fifty-keys-tickle.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Modified the `EtherPortal` contract: - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` - - `IEtherPortal` diff --git a/.changeset/fuzzy-trainers-tan.md b/.changeset/fuzzy-trainers-tan.md index 920afbaf..a904e270 100644 --- a/.changeset/fuzzy-trainers-tan.md +++ b/.changeset/fuzzy-trainers-tan.md @@ -5,5 +5,5 @@ Modified the `AbstractConsensus` contract: - Removed the `join` function -- Implemented the `getEpochHash` function +- Implemented the `wasClaimAccepted` function - Added an internal `_acceptClaim` function diff --git a/.changeset/healthy-wasps-shout.md b/.changeset/healthy-wasps-shout.md index 57bd8b52..4cd39566 100644 --- a/.changeset/healthy-wasps-shout.md +++ b/.changeset/healthy-wasps-shout.md @@ -5,9 +5,7 @@ Added: - an `Outputs` interface -- an `InputRange` struct - a `LibAddress` library -- a `LibInputRange` library - a `LibError` library - a `LibMerkle32` library - a `Quorum` contract (which implements the `IConsensus` interface) diff --git a/.changeset/lazy-gorillas-scream.md b/.changeset/lazy-gorillas-scream.md index 9a0ea473..5e919848 100644 --- a/.changeset/lazy-gorillas-scream.md +++ b/.changeset/lazy-gorillas-scream.md @@ -7,5 +7,3 @@ Modified the `IInputRelay` interface: - Renamed it as `IPortal` - Moved it to `contracts/portals` - -- Made it inherit from `IERC165` diff --git a/.changeset/lemon-rivers-nail.md b/.changeset/lemon-rivers-nail.md deleted file mode 100644 index 7b7e7d63..00000000 --- a/.changeset/lemon-rivers-nail.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Modified the `ERC1155BatchPortal` contract: - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` - - `IERC1155BatchPortal` diff --git a/.changeset/orange-poems-fry.md b/.changeset/orange-poems-fry.md deleted file mode 100644 index 7397cbac..00000000 --- a/.changeset/orange-poems-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Moved `Proof` to a dedicated file in the `common` directory. diff --git a/.changeset/quiet-guests-greet.md b/.changeset/quiet-guests-greet.md index b7b36bf5..e706babe 100644 --- a/.changeset/quiet-guests-greet.md +++ b/.changeset/quiet-guests-greet.md @@ -5,8 +5,3 @@ Modified the `ICartesiDAppFactory` interface: - Renamed it as `IApplicationFactory`. - -- Added the following parameters to its functions and events: - - - `inputBox` - - `portals` diff --git a/.changeset/red-wasps-report.md b/.changeset/red-wasps-report.md index f1ccc79b..a63ca2aa 100644 --- a/.changeset/red-wasps-report.md +++ b/.changeset/red-wasps-report.md @@ -6,16 +6,6 @@ Modified the `CartesiDApp` contract: - Renamed it as `Application`. -- Added the following parameters to its constructor: - - - `inputBox` - - `portals` - -- Made it support the following interfaces (as in EIP-165): - - - `IApplication` - - `IERC721Receiver` - - Removed the `withdrawEther` function. - Removed the `OnlyApplication` error. diff --git a/.changeset/rude-scissors-try.md b/.changeset/rude-scissors-try.md deleted file mode 100644 index 7019dd10..00000000 --- a/.changeset/rude-scissors-try.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Modified the `ERC1155SinglePortal` contract: - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` - - `IERC1155SinglePortal` diff --git a/.changeset/silly-islands-end.md b/.changeset/silly-islands-end.md index 46de09e7..c8515122 100644 --- a/.changeset/silly-islands-end.md +++ b/.changeset/silly-islands-end.md @@ -9,7 +9,7 @@ Modified the `ICartesiDApp` interface: - Made it inherit from: - `IERC721Receiver`. - - `IERC1155Receiver` (which inherits from `IERC165`). + - `IERC1155Receiver`. - Modified the `executeVoucher` function: @@ -27,16 +27,15 @@ Modified the `ICartesiDApp` interface: - Modified the `VoucherExecuted` event: - Renamed it as `OutputExecuted`. - - Split the `voucherId` parameter into `inputIndex` and `outputIndexWithinInput` parameters. + - Removed `voucherId` parameters. + - Added an `outputIndex` parameter. - Added an `output` parameter. - Modified the `wasVoucherExecuted` function: - Renamed it as `wasOutputExecuted`. -- Added a `getInputBox` function. - -- Added a `getPortals` function. +- Added a `validateOutputHash` function. - Added an `InputIndexOutOfRange` error. @@ -44,8 +43,6 @@ Modified the `ICartesiDApp` interface: - Added an `OutputNotReexecutable` error. -- Added an `IncorrectEpochHash` error. - -- Added an `IncorrectOutputsEpochRootHash` error. +- Added an `InvalidOutputHashesSiblingsArrayLength` error. -- Added an `IncorrectOutputHashesRootHash` error. +- Added an `ClaimNotAccepted` error. diff --git a/.changeset/smooth-ducks-trade.md b/.changeset/smooth-ducks-trade.md index 8d7a1ba9..17fcb371 100644 --- a/.changeset/smooth-ducks-trade.md +++ b/.changeset/smooth-ducks-trade.md @@ -7,8 +7,3 @@ Modified the `InputRelay` contract: - Renamed it as `Portal` - Moved it to `contracts/portals` - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` diff --git a/.changeset/spotty-peas-speak.md b/.changeset/spotty-peas-speak.md index 79ea138f..0b0447d1 100644 --- a/.changeset/spotty-peas-speak.md +++ b/.changeset/spotty-peas-speak.md @@ -13,4 +13,4 @@ Modified the `Authority` contract: - Removed the `setHistory` function - Removed the `submitClaim(bytes)` function - Removed the `withdrawERC20Tokens` function -- Implemented the `submitClaim(address,(uint64,uint64),bytes32)` function +- Implemented the `submitClaim(address,bytes32)` function diff --git a/.changeset/strange-foxes-melt.md b/.changeset/strange-foxes-melt.md index f9c2156f..1d4b63db 100644 --- a/.changeset/strange-foxes-melt.md +++ b/.changeset/strange-foxes-melt.md @@ -8,6 +8,6 @@ Completely modified the `IConsensus` interface: - Removed the `getClaim` function - Removed the `ApplicationJoined` event - Added a `submitClaim` function -- Added a `getEpochHash` function +- Added a `wasClaimAccepted` function - Added a `ClaimSubmission` event - Added a `ClaimAcceptance` event diff --git a/.changeset/two-mails-marry.md b/.changeset/two-mails-marry.md deleted file mode 100644 index 7396917a..00000000 --- a/.changeset/two-mails-marry.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Modified the `ERC20Portal` contract: - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` - - `IERC20Portal` diff --git a/.changeset/wise-owls-push.md b/.changeset/wise-owls-push.md deleted file mode 100644 index 22fd44c6..00000000 --- a/.changeset/wise-owls-push.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cartesi/rollups": major ---- - -Modified the `ERC721Portal` contract: - -- Made it support the following interfaces (as in EIP-165): - - - `IERC165` - - `IPortal` - - `IERC721Portal` diff --git a/contracts/common/CanonicalMachine.sol b/contracts/common/CanonicalMachine.sol index 599976d5..dcbd3cd2 100644 --- a/contracts/common/CanonicalMachine.sol +++ b/contracts/common/CanonicalMachine.sol @@ -11,9 +11,6 @@ library CanonicalMachine { /// @notice Maximum input size (2 megabytes). uint256 constant INPUT_MAX_SIZE = 1 << 21; - /// @notice Log of maximum number of inputs per epoch. - uint256 constant LOG2_MAX_INPUTS_PER_EPOCH = 32; - - /// @notice Log of maximum number of outputs per input. - uint256 constant LOG2_MAX_OUTPUTS_PER_INPUT = 16; + /// @notice Log2 of maximum number of outputs. + uint256 constant LOG2_MAX_OUTPUTS = 63; } diff --git a/contracts/common/InputRange.sol b/contracts/common/InputRange.sol deleted file mode 100644 index f7db9beb..00000000 --- a/contracts/common/InputRange.sol +++ /dev/null @@ -1,12 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -pragma solidity ^0.8.8; - -/// @notice A range of input indices. -/// @param firstIndex The index of the first input -/// @param lastIndex The index of the last input -struct InputRange { - uint64 firstIndex; - uint64 lastIndex; -} diff --git a/contracts/common/OutputValidityProof.sol b/contracts/common/OutputValidityProof.sol index 670871dc..bd28292e 100644 --- a/contracts/common/OutputValidityProof.sol +++ b/contracts/common/OutputValidityProof.sol @@ -3,23 +3,13 @@ pragma solidity ^0.8.8; -import {InputRange} from "./InputRange.sol"; - -/// @param inputRange The range of inputs accepted during the epoch -/// @param inputIndexWithinEpoch Which input, inside the epoch, the output belongs to -/// @param outputIndexWithinInput Index of output emitted by the input -/// @param outputHashesRootHash Merkle root of hashes of outputs emitted by the input -/// @param outputsEpochRootHash Merkle root of all epoch's outputs metadata hashes -/// @param machineStateHash Hash of the machine state claimed this epoch -/// @param outputHashInOutputHashesSiblings Proof that this output metadata is in metadata memory range -/// @param outputHashesInEpochSiblings Proof that this output metadata is in epoch's output memory range +/// @notice Proof of inclusion of an output in the output Merkle tree. +/// @param outputIndex Index of output in the Merkle tree +/// @param outputHashesSiblings Siblings of the output in the Merkle tree +/// @dev From the index and siblings, one can calculate the root of the Merkle tree. +/// @dev The siblings array should have size equal to the log2 of the maximum number of outputs. +/// @dev See the `CanonicalMachine` library for constants. struct OutputValidityProof { - InputRange inputRange; - uint64 inputIndexWithinEpoch; - uint64 outputIndexWithinInput; - bytes32 outputHashesRootHash; - bytes32 outputsEpochRootHash; - bytes32 machineStateHash; - bytes32[] outputHashInOutputHashesSiblings; - bytes32[] outputHashesInEpochSiblings; + uint64 outputIndex; + bytes32[] outputHashesSiblings; } diff --git a/contracts/consensus/AbstractConsensus.sol b/contracts/consensus/AbstractConsensus.sol index 89cd8ef3..050b4f33 100644 --- a/contracts/consensus/AbstractConsensus.sol +++ b/contracts/consensus/AbstractConsensus.sol @@ -4,40 +4,31 @@ pragma solidity ^0.8.8; import {IConsensus} from "./IConsensus.sol"; -import {InputRange} from "../common/InputRange.sol"; -/// @notice Stores epoch hashes for several applications and input ranges. +/// @notice Stores accepted claims for several applications. /// @dev This contract was designed to be inherited by implementations of the `IConsensus` interface -/// that only need a simple mechanism of storage and retrieval of epoch hashes. +/// that only need a simple mechanism of storage and retrieval of accepted claims. abstract contract AbstractConsensus is IConsensus { - /// @notice Indexes epoch hashes by application contract address, first input index and last input index. - mapping(address => mapping(uint256 => mapping(uint256 => bytes32))) - private _epochHashes; + /// @notice Indexes accepted claims by application contract address. + mapping(address => mapping(bytes32 => bool)) private _acceptedClaims; - /// @notice Get the epoch hash for a certain application and input range. + /// @notice Check if an output Merkle root hash was ever accepted by the consensus + /// for a particular application. /// @param appContract The application contract address - /// @param r The input range - /// @return epochHash The epoch hash - /// @dev For claimed epochs, returns the epoch hash of the last accepted claim. - /// @dev For unclaimed epochs, returns `bytes32(0)`. - function getEpochHash( + /// @param claim The output Merkle root hash + function wasClaimAccepted( address appContract, - InputRange calldata r - ) public view override returns (bytes32 epochHash) { - epochHash = _epochHashes[appContract][r.firstIndex][r.lastIndex]; + bytes32 claim + ) public view override returns (bool) { + return _acceptedClaims[appContract][claim]; } /// @notice Accept a claim. /// @param appContract The application contract address - /// @param r The input range - /// @param epochHash The epoch hash - /// @dev On successs, emits a `ClaimAcceptance` event. - function _acceptClaim( - address appContract, - InputRange calldata r, - bytes32 epochHash - ) internal { - _epochHashes[appContract][r.firstIndex][r.lastIndex] = epochHash; - emit ClaimAcceptance(appContract, r, epochHash); + /// @param claim The output Merkle root hash + /// @dev Emits a `ClaimAcceptance` event. + function _acceptClaim(address appContract, bytes32 claim) internal { + _acceptedClaims[appContract][claim] = true; + emit ClaimAcceptance(appContract, claim); } } diff --git a/contracts/consensus/IConsensus.sol b/contracts/consensus/IConsensus.sol index 4de5026b..826a2d15 100644 --- a/contracts/consensus/IConsensus.sol +++ b/contracts/consensus/IConsensus.sol @@ -3,13 +3,11 @@ pragma solidity ^0.8.8; -import {InputRange} from "../common/InputRange.sol"; - -/// @notice Provides data availability of epoch hashes for applications. -/// @notice An epoch hash is produced after the machine processes a range of inputs and the epoch is finalized. -/// This hash can be later used to prove that any given output was produced by the machine during the epoch. +/// @notice Provides consensus over the set of valid output Merkle root hashes for applications. +/// @notice The latest output Merkle root hash is available after the machine processes every input. +/// This hash can be later used to prove that any given output was ever produced by the machine. /// @notice After an epoch is finalized, a validator may submit a claim containing the application contract address, -/// the range of inputs accepted during the epoch, and the epoch hash. +/// and the output Merkle root hash. /// @notice Validators may synchronize epoch finalization, but such mechanism is not specified by this interface. /// @notice A validator should be able to save transaction fees by not submitting a claim if it was... /// - already submitted by the validator (see the `ClaimSubmission` event) or; @@ -18,53 +16,38 @@ import {InputRange} from "../common/InputRange.sol"; /// For example, a claim may be accepted if it was... /// - submitted by an authority or; /// - submitted by the majority of a quorum or; -/// - submitted and not proven wrong after some period of time. +/// - submitted and not proven wrong after some period of time or; +/// - submitted and proven correct through an on-chain tournament. interface IConsensus { /// @notice MUST trigger when a claim is submitted. /// @param submitter The submitter address /// @param appContract The application contract address - /// @param inputRange The input range - /// @param epochHash The epoch hash - /// @dev Overwrites any previous submissions regarding `submitter`, `appContract` and `inputRange`. + /// @param claim The output Merkle root hash event ClaimSubmission( address indexed submitter, address indexed appContract, - InputRange inputRange, - bytes32 epochHash + bytes32 claim ); /// @notice MUST trigger when a claim is accepted. /// @param appContract The application contract address - /// @param inputRange The input range - /// @param epochHash The epoch hash - /// @dev MUST be triggered after some `ClaimSubmission` event regarding `appContract`, `inputRange` and `epochHash`. - /// @dev Overwrites any previous acceptances regarding `appContract` and `inputRange`. - event ClaimAcceptance( - address indexed appContract, - InputRange inputRange, - bytes32 epochHash - ); + /// @param claim The output Merkle root hash + /// @dev MUST be triggered after some `ClaimSubmission` event regarding `appContract`. + event ClaimAcceptance(address indexed appContract, bytes32 claim); /// @notice Submit a claim to the consensus. /// @param appContract The application contract address - /// @param inputRange The input range - /// @param epochHash The epoch hash + /// @param claim The output Merkle root hash /// @dev MUST fire a `ClaimSubmission` event. /// @dev MAY fire a `ClaimAcceptance` event, if the acceptance criteria is met. - function submitClaim( - address appContract, - InputRange calldata inputRange, - bytes32 epochHash - ) external; + function submitClaim(address appContract, bytes32 claim) external; - /// @notice Get the epoch hash for a certain application and input range. + /// @notice Check if an output Merkle root hash was ever accepted by the consensus + /// for a particular application. /// @param appContract The application contract address - /// @param inputRange The input range - /// @return epochHash The epoch hash - /// @dev For claimed epochs, must return the epoch hash of the last accepted claim. - /// @dev For unclaimed epochs, MUST either revert or return `bytes32(0)`. - function getEpochHash( + /// @param claim The output Merkle root hash + function wasClaimAccepted( address appContract, - InputRange calldata inputRange - ) external view returns (bytes32 epochHash); + bytes32 claim + ) external view returns (bool); } diff --git a/contracts/consensus/authority/Authority.sol b/contracts/consensus/authority/Authority.sol index a47b74a3..78b0bc25 100644 --- a/contracts/consensus/authority/Authority.sol +++ b/contracts/consensus/authority/Authority.sol @@ -7,7 +7,6 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IConsensus} from "../IConsensus.sol"; import {AbstractConsensus} from "../AbstractConsensus.sol"; -import {InputRange} from "../../common/InputRange.sol"; /// @notice A consensus contract controlled by a single address, the owner. /// @dev This contract inherits from OpenZeppelin's `Ownable` contract. @@ -18,16 +17,14 @@ contract Authority is AbstractConsensus, Ownable { /// @notice Submit a claim. /// @param appContract The application contract address - /// @param inputRange The input range - /// @param epochHash The epoch hash + /// @param claim The output Merkle root hash /// @dev Fires a `ClaimSubmission` event and a `ClaimAcceptance` event. /// @dev Can only be called by the owner. function submitClaim( address appContract, - InputRange calldata inputRange, - bytes32 epochHash - ) external override onlyOwner { - emit ClaimSubmission(msg.sender, appContract, inputRange, epochHash); - _acceptClaim(appContract, inputRange, epochHash); + bytes32 claim + ) external onlyOwner { + emit ClaimSubmission(msg.sender, appContract, claim); + _acceptClaim(appContract, claim); } } diff --git a/contracts/consensus/quorum/Quorum.sol b/contracts/consensus/quorum/Quorum.sol index 772aea39..2a717850 100644 --- a/contracts/consensus/quorum/Quorum.sol +++ b/contracts/consensus/quorum/Quorum.sol @@ -6,12 +6,11 @@ pragma solidity ^0.8.8; import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import {AbstractConsensus} from "../AbstractConsensus.sol"; -import {InputRange} from "../../common/InputRange.sol"; /// @notice A consensus model controlled by a small, immutable set of `n` validators. /// @notice You can know the value of `n` by calling the `numOfValidators` function. /// @notice Upon construction, each validator is assigned a unique number between 1 and `n`. -/// These numbers are used internally instead of addresses for optimization reasons. +/// These numbers are used internally instead of addresses for gas optimization reasons. /// @notice You can list the validators in the quorum by calling the `validatorById` /// function for each ID from 1 to `n`. contract Quorum is AbstractConsensus { @@ -41,11 +40,9 @@ contract Quorum is AbstractConsensus { BitMaps.BitMap inFavorById; } - /// @notice Votes indexed by claim - /// (application contract address, first input index, last input index, and epoch hash). + /// @notice Votes indexed by application contract address and claim. /// @dev See the `numOfValidatorsInFavorOf` and `isValidatorInFavorOf` functions. - mapping(address => mapping(uint256 => mapping(uint256 => mapping(bytes32 => Votes)))) - private _votes; + mapping(address => mapping(bytes32 => Votes)) private _votes; /// @param validators The array of validator addresses /// @dev Duplicates in the `validators` array are ignored. @@ -63,27 +60,23 @@ contract Quorum is AbstractConsensus { } /// @notice Submit a claim. - /// @notice If the majority of the quorum submit a claim, it is accepted. /// @param appContract The application contract address - /// @param r The input range - /// @param epochHash The epoch hash + /// @param claim The output Merkle root hash + /// @dev Fires a `ClaimSubmission` event if the message sender is a validator. + /// @dev Fires a `ClaimAcceptance` event if the claim reaches a majority. /// @dev Can only be called by a validator. - function submitClaim( - address appContract, - InputRange calldata r, - bytes32 epochHash - ) external { + function submitClaim(address appContract, bytes32 claim) external { uint256 id = _validatorId[msg.sender]; require(id > 0, "Quorum: caller is not validator"); - emit ClaimSubmission(msg.sender, appContract, r, epochHash); + emit ClaimSubmission(msg.sender, appContract, claim); - Votes storage votes = _getVotes(appContract, r, epochHash); + Votes storage votes = _getVotes(appContract, claim); if (!votes.inFavorById.get(id)) { votes.inFavorById.set(id); if (++votes.inFavorCount == 1 + _numOfValidators / 2) { - _acceptClaim(appContract, r, epochHash); + _acceptClaim(appContract, claim); } } } @@ -111,43 +104,37 @@ contract Quorum is AbstractConsensus { /// @notice Get the number of validators in favor of a claim. /// @param appContract The application contract address - /// @param r The input range - /// @param epochHash The epoch hash + /// @param claim The output Merkle root hash /// @return Number of validators in favor of claim function numOfValidatorsInFavorOf( address appContract, - InputRange calldata r, - bytes32 epochHash + bytes32 claim ) external view returns (uint256) { - return _getVotes(appContract, r, epochHash).inFavorCount; + return _getVotes(appContract, claim).inFavorCount; } /// @notice Check whether a validator is in favor of a claim. /// @param appContract The application contract address - /// @param r The input range - /// @param epochHash The epoch hash + /// @param claim The output Merkle root hash /// @param id The ID of the validator /// @return Whether validator is in favor of claim /// @dev Assumes the provided ID is valid. function isValidatorInFavorOf( address appContract, - InputRange calldata r, - bytes32 epochHash, + bytes32 claim, uint256 id ) external view returns (bool) { - return _getVotes(appContract, r, epochHash).inFavorById.get(id); + return _getVotes(appContract, claim).inFavorById.get(id); } /// @notice Get a `Votes` structure from storage from a given claim. /// @param appContract The application contract address - /// @param r The input range - /// @param epochHash The epoch hash - /// @return The `Votes` structure related to given claim + /// @param claim The output Merkle root hash + /// @return The `Votes` structure related to a given claim function _getVotes( address appContract, - InputRange calldata r, - bytes32 epochHash + bytes32 claim ) internal view returns (Votes storage) { - return _votes[appContract][r.firstIndex][r.lastIndex][epochHash]; + return _votes[appContract][claim]; } } diff --git a/contracts/dapp/Application.sol b/contracts/dapp/Application.sol index 42131f3a..f7cb1956 100644 --- a/contracts/dapp/Application.sol +++ b/contracts/dapp/Application.sol @@ -5,13 +5,9 @@ pragma solidity ^0.8.8; import {IApplication} from "./IApplication.sol"; import {IConsensus} from "../consensus/IConsensus.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; import {LibOutputValidityProof} from "../library/LibOutputValidityProof.sol"; import {OutputValidityProof} from "../common/OutputValidityProof.sol"; import {Outputs} from "../common/Outputs.sol"; -import {InputRange} from "../common/InputRange.sol"; -import {LibInputRange} from "../library/LibInputRange.sol"; import {LibAddress} from "../library/LibAddress.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -19,7 +15,6 @@ import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Hol import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; contract Application is @@ -32,7 +27,6 @@ contract Application is using BitMaps for BitMaps.BitMap; using LibAddress for address; using LibOutputValidityProof for OutputValidityProof; - using LibInputRange for InputRange; /// @notice The initial machine state hash. /// @dev See the `getTemplateHash` function. @@ -40,39 +34,23 @@ contract Application is /// @notice Keeps track of which outputs have been executed. /// @dev See the `wasOutputExecuted` function. - mapping(uint256 => BitMaps.BitMap) internal _executed; + BitMaps.BitMap internal _executed; /// @notice The current consensus contract. /// @dev See the `getConsensus` and `migrateToConsensus` functions. IConsensus internal _consensus; - /// @notice The input box contract. - /// @dev See the `getInputBox` function. - IInputBox internal immutable _inputBox; - - /// @notice The portals supported by the application. - /// @dev See the `getPortals` function. - IPortal[] internal _portals; - /// @notice Creates an `Application` contract. /// @param consensus The initial consensus contract - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param initialOwner The initial application owner /// @param templateHash The initial machine state hash constructor( IConsensus consensus, - IInputBox inputBox, - IPortal[] memory portals, address initialOwner, bytes32 templateHash ) Ownable(initialOwner) { _templateHash = templateHash; _consensus = consensus; - _inputBox = inputBox; - for (uint256 i; i < portals.length; ++i) { - _portals.push(portals[i]); - } } /// @notice Accept Ether transfers. @@ -86,10 +64,7 @@ contract Application is ) external override nonReentrant { validateOutput(output, proof); - uint256 inputIndex = proof.calculateInputIndex(); - uint64 outputIndexWithinInput = proof.outputIndexWithinInput; - - BitMaps.BitMap storage bitmap = _executed[outputIndexWithinInput]; + uint64 outputIndex = proof.outputIndex; if (output.length < 4) { revert OutputNotExecutable(output); @@ -99,12 +74,12 @@ contract Application is bytes calldata arguments = output[4:]; if (selector == Outputs.Voucher.selector) { - if (bitmap.get(inputIndex)) { + if (_executed.get(outputIndex)) { revert OutputNotReexecutable(output); } _executeVoucher(arguments); } else if (selector == Outputs.DelegateCallVoucher.selector) { - if (bitmap.get(inputIndex)) { + if (_executed.get(outputIndex)) { revert OutputNotReexecutable(output); } _executeDelegateCallVoucher(arguments); @@ -112,8 +87,8 @@ contract Application is revert OutputNotExecutable(output); } - bitmap.set(inputIndex); - emit OutputExecuted(uint64(inputIndex), outputIndexWithinInput, output); + _executed.set(outputIndex); + emit OutputExecuted(outputIndex, output); } function migrateToConsensus( @@ -124,36 +99,30 @@ contract Application is } function wasOutputExecuted( - uint256 inputIndex, - uint256 outputIndexWithinInput + uint256 outputIndex ) external view override returns (bool) { - return _executed[outputIndexWithinInput].get(inputIndex); + return _executed.get(outputIndex); } function validateOutput( bytes calldata output, OutputValidityProof calldata proof ) public view override { - uint256 inputIndex = proof.calculateInputIndex(); - - if (!proof.inputRange.contains(inputIndex)) { - revert InputIndexOutOfRange(inputIndex, proof.inputRange); - } - - bytes32 outputHash = keccak256(output); - - if (!proof.isOutputHashesRootHashValid(outputHash)) { - revert IncorrectOutputHashesRootHash(); - } + validateOutputHash(keccak256(output), proof); + } - if (!proof.isOutputsEpochRootHashValid()) { - revert IncorrectOutputsEpochRootHash(); + function validateOutputHash( + bytes32 outputHash, + OutputValidityProof calldata proof + ) public view override { + if (!proof.isSiblingsArrayLengthValid()) { + revert InvalidOutputHashesSiblingsArrayLength(); } - bytes32 epochHash = _getEpochHash(proof.inputRange); + bytes32 claim = proof.computeClaim(outputHash); - if (!proof.isEpochHashValid(epochHash)) { - revert IncorrectEpochHash(); + if (!_wasClaimAccepted(claim)) { + revert ClaimNotAccepted(claim); } } @@ -165,30 +134,10 @@ contract Application is return _consensus; } - function getInputBox() external view override returns (IInputBox) { - return _inputBox; - } - - function getPortals() external view override returns (IPortal[] memory) { - return _portals; - } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC1155Holder, IERC165) returns (bool) { - return - interfaceId == type(IApplication).interfaceId || - interfaceId == type(IERC721Receiver).interfaceId || - super.supportsInterface(interfaceId); - } - - /// @notice Get the epoch hash regarding an input range from the current consensus. - /// @param inputRange The input range - /// @return The epoch hash - function _getEpochHash( - InputRange calldata inputRange - ) internal view returns (bytes32) { - return _consensus.getEpochHash(address(this), inputRange); + /// @notice Check if an output Merkle root hash was ever accepted by the current consensus. + /// @param claim The output Merkle root hash + function _wasClaimAccepted(bytes32 claim) internal view returns (bool) { + return _consensus.wasClaimAccepted(address(this), claim); } /// @notice Executes a voucher diff --git a/contracts/dapp/ApplicationFactory.sol b/contracts/dapp/ApplicationFactory.sol index db55d4be..3cc5b3d4 100644 --- a/contracts/dapp/ApplicationFactory.sol +++ b/contracts/dapp/ApplicationFactory.sol @@ -7,8 +7,6 @@ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {IApplicationFactory} from "./IApplicationFactory.sol"; import {IConsensus} from "../consensus/IConsensus.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; import {Application} from "./Application.sol"; /// @title Application Factory @@ -16,63 +14,39 @@ import {Application} from "./Application.sol"; contract ApplicationFactory is IApplicationFactory { function newApplication( IConsensus consensus, - IInputBox inputBox, - IPortal[] memory portals, address appOwner, bytes32 templateHash ) external override returns (Application) { Application appContract = new Application( consensus, - inputBox, - portals, appOwner, templateHash ); - emit ApplicationCreated( - consensus, - inputBox, - portals, - appOwner, - templateHash, - appContract - ); + emit ApplicationCreated(consensus, appOwner, templateHash, appContract); return appContract; } function newApplication( IConsensus consensus, - IInputBox inputBox, - IPortal[] memory portals, address appOwner, bytes32 templateHash, bytes32 salt ) external override returns (Application) { Application appContract = new Application{salt: salt}( consensus, - inputBox, - portals, appOwner, templateHash ); - emit ApplicationCreated( - consensus, - inputBox, - portals, - appOwner, - templateHash, - appContract - ); + emit ApplicationCreated(consensus, appOwner, templateHash, appContract); return appContract; } function calculateApplicationAddress( IConsensus consensus, - IInputBox inputBox, - IPortal[] memory portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -83,13 +57,7 @@ contract ApplicationFactory is IApplicationFactory { keccak256( abi.encodePacked( type(Application).creationCode, - abi.encode( - consensus, - inputBox, - portals, - appOwner, - templateHash - ) + abi.encode(consensus, appOwner, templateHash) ) ) ); diff --git a/contracts/dapp/IApplication.sol b/contracts/dapp/IApplication.sol index af9e6c61..16f79c59 100644 --- a/contracts/dapp/IApplication.sol +++ b/contracts/dapp/IApplication.sol @@ -7,14 +7,11 @@ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Re import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IConsensus} from "../consensus/IConsensus.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; import {OutputValidityProof} from "../common/OutputValidityProof.sol"; -import {InputRange} from "../common/InputRange.sol"; /// @notice The base layer incarnation of an application running on the execution layer. -/// @notice The state of the application advances through inputs sent to an `IInputBox` contract (see the `getInputBox` function). -/// @notice These inputs can be sent either directly, or indirectly through portals (see the `getPortals` function). +/// @notice The state of the application advances through inputs sent to an `IInputBox` contract. +/// @notice These inputs can be sent either directly, or indirectly through portals. /// @notice Reader nodes can retrieve inputs sent to the `IInputBox` contract through events, and feed them into the machine. /// @notice Validator nodes can also submit claims to the `IConsensus` contract (see the `getConsensus` function). /// @notice Once accepted, claims can be used to validate outputs generated by the machine. @@ -38,23 +35,12 @@ interface IApplication is IERC721Receiver, IERC1155Receiver { event NewConsensus(IConsensus newConsensus); /// @notice MUST trigger when an output is executed. - /// @param inputIndex The index of the input that emitted the output - /// @param outputIndexWithinInput The index of the output amongst all outputs emitted by the input + /// @param outputIndex The index of the output /// @param output The output - event OutputExecuted( - uint64 inputIndex, - uint64 outputIndexWithinInput, - bytes output - ); + event OutputExecuted(uint64 outputIndex, bytes output); // Errors - /// @notice Could not validate an output because the input - /// that generated it is outside the input range of the epoch. - /// @param inputIndex The input index - /// @param inputRange The input range - error InputIndexOutOfRange(uint256 inputIndex, InputRange inputRange); - /// @notice Could not execute an output, because the application contract doesn't know how to. /// @param output The output error OutputNotExecutable(bytes output); @@ -63,17 +49,12 @@ interface IApplication is IERC721Receiver, IERC1155Receiver { /// @param output The output error OutputNotReexecutable(bytes output); - /// @notice Raised when some `OutputValidityProof` variables does not match - /// the presented finalized epoch. - error IncorrectEpochHash(); - - /// @notice Raised when `OutputValidityProof` metadata memory range is NOT - /// contained in epoch's output memory range. - error IncorrectOutputsEpochRootHash(); + /// @notice Raised when the output hashes siblings array has an invalid size. + /// @dev Please consult `CanonicalMachine` for the maximum number of outputs. + error InvalidOutputHashesSiblingsArrayLength(); - /// @notice Raised when Merkle root of output hash is NOT contained - /// in the output metadata array memory range. - error IncorrectOutputHashesRootHash(); + /// @notice Raised when the required claim was not accepted by the current consensus. + error ClaimNotAccepted(bytes32 claim); // Permissioned functions @@ -87,7 +68,7 @@ interface IApplication is IERC721Receiver, IERC1155Receiver { /// @notice Execute an output. /// @param output The output /// @param proof The proof used to validate the output against - /// a claim submitted by the current consensus contract + /// a claim submitted to the current consensus contract /// @dev On a successful execution, emits a `OutputExecuted` event. /// @dev May raise any of the errors raised by `validateOutput`, /// as well as `OutputNotExecutable` and `OutputNotReexecutable`. @@ -97,25 +78,33 @@ interface IApplication is IERC721Receiver, IERC1155Receiver { ) external; /// @notice Check whether an output has been executed. - /// @param inputIndex The index of the input in the input box - /// @param outputIndexWithinInput The index of output emitted by the input + /// @param outputIndex The index of output /// @return Whether the output has been executed before function wasOutputExecuted( - uint256 inputIndex, - uint256 outputIndexWithinInput + uint256 outputIndex ) external view returns (bool); /// @notice Validate an output. /// @param output The output /// @param proof The proof used to validate the output against - /// a claim submitted by the current consensus contract - /// @dev May raise `InputIndexOutOfRange`, `IncorrectEpochHash`, - /// `IncorrectOutputsEpochRootHash`, or `IncorrectOutputHashesRootHash`. + /// a claim submitted to the current consensus contract + /// @dev May raise any of the errors raised by `validateOutputHash`. function validateOutput( bytes calldata output, OutputValidityProof calldata proof ) external view; + /// @notice Validate an output hash. + /// @param outputHash The output hash + /// @param proof The proof used to validate the output against + /// a claim submitted to the current consensus contract + /// @dev May raise `InvalidOutputHashesSiblingsArrayLength` + /// or `ClaimNotAccepted`. + function validateOutputHash( + bytes32 outputHash, + OutputValidityProof calldata proof + ) external view; + /// @notice Get the application's template hash. /// @return The application's template hash function getTemplateHash() external view returns (bytes32); @@ -123,12 +112,4 @@ interface IApplication is IERC721Receiver, IERC1155Receiver { /// @notice Get the current consensus. /// @return The current consensus function getConsensus() external view returns (IConsensus); - - /// @notice Get the input box that the application is listening to. - /// @return The input box - function getInputBox() external view returns (IInputBox); - - /// @notice Get the portals that the application expects inputs from. - /// @return The portals. - function getPortals() external view returns (IPortal[] memory); } diff --git a/contracts/dapp/IApplicationFactory.sol b/contracts/dapp/IApplicationFactory.sol index be75fb17..637bb172 100644 --- a/contracts/dapp/IApplicationFactory.sol +++ b/contracts/dapp/IApplicationFactory.sol @@ -5,8 +5,6 @@ pragma solidity ^0.8.8; import {Application} from "./Application.sol"; import {IConsensus} from "../consensus/IConsensus.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; /// @title Application Factory interface interface IApplicationFactory { @@ -14,16 +12,12 @@ interface IApplicationFactory { /// @notice A new application was deployed. /// @param consensus The initial consensus contract - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial application owner /// @param templateHash The initial machine state hash /// @param appContract The application contract /// @dev MUST be triggered on a successful call to `newApplication`. event ApplicationCreated( IConsensus indexed consensus, - IInputBox inputBox, - IPortal[] portals, address appOwner, bytes32 templateHash, Application appContract @@ -33,24 +27,18 @@ interface IApplicationFactory { /// @notice Deploy a new application. /// @param consensus The initial consensus contract - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial application owner /// @param templateHash The initial machine state hash /// @return The application /// @dev On success, MUST emit an `ApplicationCreated` event. function newApplication( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash ) external returns (Application); /// @notice Deploy a new application deterministically. /// @param consensus The initial consensus contract - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial application owner /// @param templateHash The initial machine state hash /// @param salt The salt used to deterministically generate the application contract address @@ -58,8 +46,6 @@ interface IApplicationFactory { /// @dev On success, MUST emit an `ApplicationCreated` event. function newApplication( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -67,8 +53,6 @@ interface IApplicationFactory { /// @notice Calculate the address of an application contract to be deployed deterministically. /// @param consensus The initial consensus contract - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial application owner /// @param templateHash The initial machine state hash /// @param salt The salt used to deterministically generate the application contract address @@ -77,8 +61,6 @@ interface IApplicationFactory { /// is able to deterministically deploy an application. function calculateApplicationAddress( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt diff --git a/contracts/dapp/ISelfHostedApplicationFactory.sol b/contracts/dapp/ISelfHostedApplicationFactory.sol index 580d679c..02f1486e 100644 --- a/contracts/dapp/ISelfHostedApplicationFactory.sol +++ b/contracts/dapp/ISelfHostedApplicationFactory.sol @@ -7,8 +7,6 @@ import {Authority} from "../consensus/authority/Authority.sol"; import {IAuthorityFactory} from "../consensus/authority/IAuthorityFactory.sol"; import {Application} from "./Application.sol"; import {IApplicationFactory} from "./IApplicationFactory.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; /// @title Self-hosted Application Factory interface interface ISelfHostedApplicationFactory { @@ -25,8 +23,6 @@ interface ISelfHostedApplicationFactory { /// @notice Deploy new application and authority contracts deterministically. /// @param authorityOwner The initial authority owner - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial Application owner /// @param templateHash The initial machine state hash /// @param salt The salt used to deterministically generate the addresses @@ -34,8 +30,6 @@ interface ISelfHostedApplicationFactory { /// @return The authority contract function deployContracts( address authorityOwner, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -44,8 +38,6 @@ interface ISelfHostedApplicationFactory { /// @notice Calculate the addresses of the application and authority contracts /// to be deployed deterministically. /// @param authorityOwner The initial authority owner - /// @param inputBox The input box contract - /// @param portals The portals supported by the application /// @param appOwner The initial Application owner /// @param templateHash The initial machine state hash /// @param salt The salt used to deterministically generate the addresses @@ -53,8 +45,6 @@ interface ISelfHostedApplicationFactory { /// @return The authority address function calculateAddresses( address authorityOwner, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt diff --git a/contracts/dapp/SelfHostedApplicationFactory.sol b/contracts/dapp/SelfHostedApplicationFactory.sol index cc57f8e6..bef712ea 100644 --- a/contracts/dapp/SelfHostedApplicationFactory.sol +++ b/contracts/dapp/SelfHostedApplicationFactory.sol @@ -9,8 +9,6 @@ import {IAuthorityFactory} from "../consensus/authority/IAuthorityFactory.sol"; import {Application} from "./Application.sol"; import {IApplicationFactory} from "./IApplicationFactory.sol"; import {ISelfHostedApplicationFactory} from "./ISelfHostedApplicationFactory.sol"; -import {IInputBox} from "../inputs/IInputBox.sol"; -import {IPortal} from "../portals/IPortal.sol"; /// @title Self-hosted Application Factory /// @notice Allows anyone to reliably deploy a new Authority contract, @@ -49,8 +47,6 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory { function deployContracts( address authorityOwner, - IInputBox inputBox, - IPortal[] memory portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -59,8 +55,6 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory { application = _applicationFactory.newApplication( authority, - inputBox, - portals, appOwner, templateHash, salt @@ -69,8 +63,6 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory { function calculateAddresses( address authorityOwner, - IInputBox inputBox, - IPortal[] memory portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -82,8 +74,6 @@ contract SelfHostedApplicationFactory is ISelfHostedApplicationFactory { application = _applicationFactory.calculateApplicationAddress( IConsensus(authority), - inputBox, - portals, appOwner, templateHash, salt diff --git a/contracts/library/LibInputRange.sol b/contracts/library/LibInputRange.sol deleted file mode 100644 index eb0e88cf..00000000 --- a/contracts/library/LibInputRange.sol +++ /dev/null @@ -1,19 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -pragma solidity ^0.8.8; - -import {InputRange} from "../common/InputRange.sol"; - -library LibInputRange { - /// @notice Check if an input range contains an input. - /// @param r The input range - /// @param inputIndex The input index - /// @return Whether the input range contains the input. - function contains( - InputRange calldata r, - uint256 inputIndex - ) internal pure returns (bool) { - return r.firstIndex <= inputIndex && inputIndex <= r.lastIndex; - } -} diff --git a/contracts/library/LibOutputValidityProof.sol b/contracts/library/LibOutputValidityProof.sol index 9784c0aa..00fd67dc 100644 --- a/contracts/library/LibOutputValidityProof.sol +++ b/contracts/library/LibOutputValidityProof.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.8; import {CanonicalMachine} from "../common/CanonicalMachine.sol"; -import {Outputs} from "../common/Outputs.sol"; import {OutputValidityProof} from "../common/OutputValidityProof.sol"; import {LibMerkle32} from "./LibMerkle32.sol"; @@ -12,56 +11,21 @@ import {LibMerkle32} from "./LibMerkle32.sol"; library LibOutputValidityProof { using LibMerkle32 for bytes32[]; - /// @notice Check if epoch hash is valid - /// @param v The output validity proof - /// @param epochHash The epoch hash - function isEpochHashValid( - OutputValidityProof calldata v, - bytes32 epochHash - ) internal pure returns (bool) { - return - epochHash == - keccak256(abi.encode(v.outputsEpochRootHash, v.machineStateHash)); - } - - /// @notice Check if the outputs epoch root hash is valid - /// @param v The output validity proof - function isOutputsEpochRootHashValid( + function isSiblingsArrayLengthValid( OutputValidityProof calldata v ) internal pure returns (bool) { - bytes32[] calldata siblings = v.outputHashesInEpochSiblings; return - (siblings.length == CanonicalMachine.LOG2_MAX_INPUTS_PER_EPOCH) && - (v.outputsEpochRootHash == - siblings.merkleRootAfterReplacement( - v.inputIndexWithinEpoch, - v.outputHashesRootHash - )); + v.outputHashesSiblings.length == CanonicalMachine.LOG2_MAX_OUTPUTS; } - /// @notice Check if the output hashes root hash is valid - /// @param v The output validity proof - /// @param outputHash The output hash - function isOutputHashesRootHashValid( + function computeClaim( OutputValidityProof calldata v, bytes32 outputHash - ) internal pure returns (bool) { - bytes32[] calldata siblings = v.outputHashInOutputHashesSiblings; + ) internal pure returns (bytes32) { return - (siblings.length == CanonicalMachine.LOG2_MAX_OUTPUTS_PER_INPUT) && - (v.outputHashesRootHash == - siblings.merkleRootAfterReplacement( - v.outputIndexWithinInput, - outputHash - )); - } - - /// @notice Calculate the input index - /// @param v The output validity proof - /// @return The input index - function calculateInputIndex( - OutputValidityProof calldata v - ) internal pure returns (uint256) { - return uint256(v.inputRange.firstIndex) + v.inputIndexWithinEpoch; + v.outputHashesSiblings.merkleRootAfterReplacement( + v.outputIndex, + outputHash + ); } } diff --git a/contracts/portals/ERC1155BatchPortal.sol b/contracts/portals/ERC1155BatchPortal.sol index 2a1ee918..40ba273c 100644 --- a/contracts/portals/ERC1155BatchPortal.sol +++ b/contracts/portals/ERC1155BatchPortal.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.8; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC1155BatchPortal} from "./IERC1155BatchPortal.sol"; import {Portal} from "./Portal.sol"; @@ -47,12 +46,4 @@ contract ERC1155BatchPortal is IERC1155BatchPortal, Portal { _inputBox.addInput(appContract, payload); } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(IERC165, Portal) returns (bool) { - return - interfaceId == type(IERC1155BatchPortal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/contracts/portals/ERC1155SinglePortal.sol b/contracts/portals/ERC1155SinglePortal.sol index b4b48189..ddedfa7d 100644 --- a/contracts/portals/ERC1155SinglePortal.sol +++ b/contracts/portals/ERC1155SinglePortal.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.8; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC1155SinglePortal} from "./IERC1155SinglePortal.sol"; import {Portal} from "./Portal.sol"; @@ -47,12 +46,4 @@ contract ERC1155SinglePortal is IERC1155SinglePortal, Portal { _inputBox.addInput(appContract, payload); } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(IERC165, Portal) returns (bool) { - return - interfaceId == type(IERC1155SinglePortal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/contracts/portals/ERC20Portal.sol b/contracts/portals/ERC20Portal.sol index 201213e1..0c0f141f 100644 --- a/contracts/portals/ERC20Portal.sol +++ b/contracts/portals/ERC20Portal.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.8; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC20Portal} from "./IERC20Portal.sol"; import {Portal} from "./Portal.sol"; @@ -41,12 +40,4 @@ contract ERC20Portal is IERC20Portal, Portal { _inputBox.addInput(appContract, payload); } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(IERC165, Portal) returns (bool) { - return - interfaceId == type(IERC20Portal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/contracts/portals/ERC721Portal.sol b/contracts/portals/ERC721Portal.sol index 6d44dd23..6a74ac35 100644 --- a/contracts/portals/ERC721Portal.sol +++ b/contracts/portals/ERC721Portal.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.8; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC721Portal} from "./IERC721Portal.sol"; import {Portal} from "./Portal.sol"; @@ -39,12 +38,4 @@ contract ERC721Portal is IERC721Portal, Portal { _inputBox.addInput(appContract, payload); } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(IERC165, Portal) returns (bool) { - return - interfaceId == type(IERC721Portal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/contracts/portals/EtherPortal.sol b/contracts/portals/EtherPortal.sol index 08edee11..b179ce07 100644 --- a/contracts/portals/EtherPortal.sol +++ b/contracts/portals/EtherPortal.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.8; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - import {IEtherPortal} from "./IEtherPortal.sol"; import {Portal} from "./Portal.sol"; import {IInputBox} from "../inputs/IInputBox.sol"; @@ -37,12 +35,4 @@ contract EtherPortal is IEtherPortal, Portal { _inputBox.addInput(appContract, payload); } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(IERC165, Portal) returns (bool) { - return - interfaceId == type(IEtherPortal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/contracts/portals/IPortal.sol b/contracts/portals/IPortal.sol index 3f002caf..fd42d955 100644 --- a/contracts/portals/IPortal.sol +++ b/contracts/portals/IPortal.sol @@ -4,10 +4,9 @@ pragma solidity ^0.8.8; import {IInputBox} from "../inputs/IInputBox.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title Portal interface -interface IPortal is IERC165 { +interface IPortal { // Permissionless functions /// @notice Get the input box used by this portal. diff --git a/contracts/portals/Portal.sol b/contracts/portals/Portal.sol index 02312ab5..d1086ec4 100644 --- a/contracts/portals/Portal.sol +++ b/contracts/portals/Portal.sol @@ -5,11 +5,10 @@ pragma solidity ^0.8.8; import {IPortal} from "./IPortal.sol"; import {IInputBox} from "../inputs/IInputBox.sol"; -import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; /// @title Portal /// @notice This contract serves as a base for all the other portals. -contract Portal is IPortal, ERC165 { +contract Portal is IPortal { /// @notice The input box used by the portal. IInputBox internal immutable _inputBox; @@ -22,12 +21,4 @@ contract Portal is IPortal, ERC165 { function getInputBox() external view override returns (IInputBox) { return _inputBox; } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC165, IERC165) returns (bool) { - return - interfaceId == type(IPortal).interfaceId || - super.supportsInterface(interfaceId); - } } diff --git a/hardhat.config.ts b/hardhat.config.ts index 0e1ea8bd..d1f28346 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -120,27 +120,6 @@ const config: HardhatUserConfig = { }, ], }, - external: { - contracts: [ - { - artifacts: ppath("@cartesi/util", "/export/artifacts"), - deploy: ppath("@cartesi/util", "/dist/deploy"), - }, - ], - deployments: { - localhost: ["deployments/localhost"], - arbitrum: [ppath("@cartesi/util", "/deployments/arbitrum")], - arbitrum_sepolia: [ - ppath("@cartesi/util", "/deployments/arbitrum_sepolia"), - ], - mainnet: [ppath("@cartesi/util", "/deployments/mainnet")], - optimism: [ppath("@cartesi/util", "/deployments/optimism")], - optimism_sepolia: [ - ppath("@cartesi/util", "/deployments/optimism_sepolia"), - ], - sepolia: [ppath("@cartesi/util", "/deployments/sepolia")], - }, - }, namedAccounts: { deployer: { default: 0, diff --git a/package.json b/package.json index 1b3a01c9..9cce8af8 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "tsc:prod": "tsc -p tsconfig.prod.json" }, "dependencies": { - "@cartesi/util": "6.3.0", "@openzeppelin/contracts": "5.0.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05d88d26..3f22f517 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,9 +5,6 @@ settings: excludeLinksFromLockfile: false dependencies: - '@cartesi/util': - specifier: 6.3.0 - version: 6.3.0 '@openzeppelin/contracts': specifier: 5.0.2 version: 5.0.2 @@ -138,10 +135,6 @@ packages: regenerator-runtime: 0.14.1 dev: true - /@cartesi/util@6.3.0: - resolution: {integrity: sha512-UgsyTklI4mf3ZbnPHQTuYJcMyldM3y9Xm+ib9xBgDzDq6TxMO81xkrPv/gwA23seHL2/bkJ5flXJEhCE8poW8Q==} - dev: false - /@changesets/apply-release-plan@7.0.0: resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==} dependencies: @@ -2885,6 +2878,7 @@ packages: /glob@7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 diff --git a/test/foundry/consensus/authority/Authority.t.sol b/test/foundry/consensus/authority/Authority.t.sol index ff754565..397299f2 100644 --- a/test/foundry/consensus/authority/Authority.t.sol +++ b/test/foundry/consensus/authority/Authority.t.sol @@ -9,14 +9,11 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Authority} from "contracts/consensus/authority/Authority.sol"; import {IConsensus} from "contracts/consensus/IConsensus.sol"; -import {InputRange} from "contracts/common/InputRange.sol"; -import {LibInputRange} from "contracts/library/LibInputRange.sol"; import {TestBase} from "../../util/TestBase.sol"; import {LibTopic} from "../../util/LibTopic.sol"; contract AuthorityTest is TestBase { - using LibInputRange for InputRange; using LibTopic for address; function testConstructor(address owner) public { @@ -64,8 +61,7 @@ contract AuthorityTest is TestBase { address owner, address notOwner, address appContract, - InputRange calldata inputRange, - bytes32 epochHash + bytes32 claim ) public { vm.assume(owner != address(0)); vm.assume(owner != notOwner); @@ -80,84 +76,48 @@ contract AuthorityTest is TestBase { ); vm.prank(notOwner); - authority.submitClaim(appContract, inputRange, epochHash); + authority.submitClaim(appContract, claim); } function testSubmitClaim( address owner, address appContract, - InputRange calldata inputRange, - bytes32[2] calldata epochHashes + bytes32 claim ) public { vm.assume(owner != address(0)); Authority authority = new Authority(owner); - // First claim - - _expectClaimEvents( - authority, - owner, - appContract, - inputRange, - epochHashes[0] - ); + _expectClaimEvents(authority, owner, appContract, claim); vm.prank(owner); - authority.submitClaim(appContract, inputRange, epochHashes[0]); - - assertEq( - authority.getEpochHash(appContract, inputRange), - epochHashes[0] - ); - - // Second claim + authority.submitClaim(appContract, claim); - _expectClaimEvents( - authority, - owner, - appContract, - inputRange, - epochHashes[1] - ); - - vm.prank(owner); - authority.submitClaim(appContract, inputRange, epochHashes[1]); - - assertEq( - authority.getEpochHash(appContract, inputRange), - epochHashes[1] - ); + assertTrue(authority.wasClaimAccepted(appContract, claim)); } - function testGetEpochHash( + function testWasClaimAccepted( address owner, address appContract, - InputRange calldata inputRange + bytes32 claim ) public { vm.assume(owner != address(0)); Authority authority = new Authority(owner); - assertEq(authority.getEpochHash(appContract, inputRange), bytes32(0)); + assertFalse(authority.wasClaimAccepted(appContract, claim)); } function _expectClaimEvents( Authority authority, address owner, address appContract, - InputRange calldata inputRange, - bytes32 epochHash + bytes32 claim ) internal { vm.expectEmit(true, true, false, true, address(authority)); - emit IConsensus.ClaimSubmission( - owner, - appContract, - inputRange, - epochHash - ); + emit IConsensus.ClaimSubmission(owner, appContract, claim); vm.expectEmit(true, false, false, true, address(authority)); - emit IConsensus.ClaimAcceptance(appContract, inputRange, epochHash); + emit IConsensus.ClaimAcceptance(appContract, claim); } } diff --git a/test/foundry/consensus/quorum/Quorum.t.sol b/test/foundry/consensus/quorum/Quorum.t.sol index 9db56fa8..4d8fa454 100644 --- a/test/foundry/consensus/quorum/Quorum.t.sol +++ b/test/foundry/consensus/quorum/Quorum.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.22; -import {InputRange} from "contracts/common/InputRange.sol"; import {Quorum} from "contracts/consensus/quorum/Quorum.sol"; import {IConsensus} from "contracts/consensus/IConsensus.sol"; @@ -14,8 +13,7 @@ import {Vm} from "forge-std/Vm.sol"; struct Claim { address appContract; - InputRange inputRange; - bytes32 epochHash; + bytes32 outputHashesRootHash; } library LibQuorum { @@ -26,8 +24,7 @@ library LibQuorum { return quorum.numOfValidatorsInFavorOf( claim.appContract, - claim.inputRange, - claim.epochHash + claim.outputHashesRootHash ); } @@ -39,18 +36,24 @@ library LibQuorum { return quorum.isValidatorInFavorOf( claim.appContract, - claim.inputRange, - claim.epochHash, + claim.outputHashesRootHash, id ); } function submitClaim(Quorum quorum, Claim calldata claim) internal { - quorum.submitClaim( - claim.appContract, - claim.inputRange, - claim.epochHash - ); + quorum.submitClaim(claim.appContract, claim.outputHashesRootHash); + } + + function wasClaimAccepted( + Quorum quorum, + Claim calldata claim + ) internal view returns (bool) { + return + quorum.wasClaimAccepted( + claim.appContract, + claim.outputHashesRootHash + ); } } @@ -246,15 +249,14 @@ contract QuorumTest is TestBase { entry.emitter == address(quorum) && entry.topics[0] == IConsensus.ClaimSubmission.selector ) { - (InputRange memory inputRange, bytes32 epochHash) = abi.decode( + bytes32 outputHashesRootHash = abi.decode( entry.data, - (InputRange, bytes32) + (bytes32) ); assertEq(entry.topics[1], validator.asTopic()); assertEq(entry.topics[2], claim.appContract.asTopic()); - assertEq(inputRange, claim.inputRange); - assertEq(epochHash, claim.epochHash); + assertEq(outputHashesRootHash, claim.outputHashesRootHash); ++numOfSubmissions; } @@ -263,14 +265,13 @@ contract QuorumTest is TestBase { entry.emitter == address(quorum) && entry.topics[0] == IConsensus.ClaimAcceptance.selector ) { - (InputRange memory inputRange, bytes32 epochHash) = abi.decode( + bytes32 outputHashesRootHash = abi.decode( entry.data, - (InputRange, bytes32) + (bytes32) ); assertEq(entry.topics[1], claim.appContract.asTopic()); - assertEq(inputRange, claim.inputRange); - assertEq(epochHash, claim.epochHash); + assertEq(outputHashesRootHash, claim.outputHashesRootHash); ++numOfAcceptances; } @@ -287,19 +288,9 @@ contract QuorumTest is TestBase { assertEq(numOfAcceptances, 0); } - if (inFavorCount > (numOfValidators / 2)) { - assertEq( - quorum.getEpochHash(claim.appContract, claim.inputRange), - claim.epochHash - ); - } - } - - function assertEq( - InputRange memory r1, - InputRange memory r2 - ) internal pure { - assertEq(r1.firstIndex, r2.firstIndex); - assertEq(r1.lastIndex, r2.lastIndex); + assertEq( + quorum.wasClaimAccepted(claim), + inFavorCount > (numOfValidators / 2) + ); } } diff --git a/test/foundry/dapp/Application.t.sol b/test/foundry/dapp/Application.t.sol index 8a7303cc..930e4cc2 100644 --- a/test/foundry/dapp/Application.t.sol +++ b/test/foundry/dapp/Application.t.sol @@ -6,40 +6,30 @@ pragma solidity ^0.8.22; import {Application} from "contracts/dapp/Application.sol"; import {Authority} from "contracts/consensus/authority/Authority.sol"; import {CanonicalMachine} from "contracts/common/CanonicalMachine.sol"; -import {ERC1155BatchPortal} from "contracts/portals/ERC1155BatchPortal.sol"; -import {ERC1155SinglePortal} from "contracts/portals/ERC1155SinglePortal.sol"; -import {ERC20Portal} from "contracts/portals/ERC20Portal.sol"; -import {ERC721Portal} from "contracts/portals/ERC721Portal.sol"; -import {EtherPortal} from "contracts/portals/EtherPortal.sol"; import {IApplication} from "contracts/dapp/IApplication.sol"; import {IConsensus} from "contracts/consensus/IConsensus.sol"; -import {IInputBox} from "contracts/inputs/IInputBox.sol"; -import {IPortal} from "contracts/portals/IPortal.sol"; -import {InputBox} from "contracts/inputs/InputBox.sol"; -import {InputRange} from "contracts/common/InputRange.sol"; import {OutputValidityProof} from "contracts/common/OutputValidityProof.sol"; import {Outputs} from "contracts/common/Outputs.sol"; import {SafeERC20Transfer} from "contracts/delegatecall/SafeERC20Transfer.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {IERC20Errors, IERC721Errors, IERC1155Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; import {EtherReceiver} from "../util/EtherReceiver.sol"; +import {ExternalLibMerkle32} from "../library/LibMerkle32.t.sol"; import {LibEmulator} from "../util/LibEmulator.sol"; import {SimpleERC20} from "../util/SimpleERC20.sol"; import {SimpleERC721} from "../util/SimpleERC721.sol"; import {SimpleSingleERC1155, SimpleBatchERC1155} from "../util/SimpleERC1155.sol"; -import {ExternalLibMerkle32} from "../library/LibMerkle32.t.sol"; -contract ApplicationTest is ERC165Test { +contract ApplicationTest is TestBase { using LibEmulator for LibEmulator.State; using ExternalLibMerkle32 for bytes32[]; @@ -50,8 +40,6 @@ contract ApplicationTest is ERC165Test { IERC721 _erc721Token; IERC1155 _erc1155SingleToken; IERC1155 _erc1155BatchToken; - IInputBox _inputBox; - IPortal[] _portals; SafeERC20Transfer _safeERC20Transfer; LibEmulator.State _emulator; @@ -64,7 +52,7 @@ contract ApplicationTest is ERC165Test { uint256[] _tokenIds; uint256[] _initialSupplies; uint256[] _transferAmounts; - mapping(string => LibEmulator.OutputId) _outputIdsByName; + mapping(string => LibEmulator.OutputIndex) _outputIndexByName; bytes32 constant _templateHash = keccak256("templateHash"); uint256 constant _initialSupply = 1000000000000000000000000000000000000; @@ -75,7 +63,7 @@ contract ApplicationTest is ERC165Test { _initVariables(); _deployContracts(); _addOutputs(); - _submitClaims(); + _submitClaim(); } // ----------- @@ -89,19 +77,11 @@ contract ApplicationTest is ERC165Test { address(0) ) ); - new Application( - _consensus, - _inputBox, - _portals, - address(0), - _templateHash - ); + new Application(_consensus, address(0), _templateHash); } function testConstructor( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address owner, bytes32 templateHash ) external { @@ -112,17 +92,13 @@ contract ApplicationTest is ERC165Test { Application appContract = new Application( consensus, - inputBox, - portals, owner, templateHash ); assertEq(address(appContract.getConsensus()), address(consensus)); - assertEq(address(appContract.getInputBox()), address(inputBox)); assertEq(appContract.owner(), owner); assertEq(appContract.getTemplateHash(), templateHash); - assertEq(appContract.getPortals(), portals); } // ------------------- @@ -156,56 +132,55 @@ contract ApplicationTest is ERC165Test { // output validation // ----------------- - function testValidateOutput() external view { + function testValidateOutputAndOutputHash() external view { for (uint256 i; i < _outputNames.length; ++i) { string memory name = _outputNames[i]; bytes memory output = _getOutput(name); OutputValidityProof memory proof = _getProof(name); _appContract.validateOutput(output, proof); + _appContract.validateOutputHash(keccak256(output), proof); } } - function testValidateFakeOutput() external { + function testRevertsInvalidOutputHashesSiblingsArrayLength() external { string memory name = "HelloWorldNotice"; + bytes memory output = _getOutput(name); OutputValidityProof memory proof = _getProof(name); - bytes memory fakeOutput = _encodeNotice("Goodbye, World!"); + proof.outputHashesSiblings = new bytes32[](0); - vm.expectRevert(IApplication.IncorrectOutputHashesRootHash.selector); - _appContract.validateOutput(fakeOutput, proof); + _expectRevertInvalidOutputHashesSiblingsArrayLength(); + _appContract.validateOutput(output, proof); - proof.outputHashesRootHash = proof - .outputHashInOutputHashesSiblings - .merkleRootAfterReplacement( - proof.outputIndexWithinInput, - keccak256(fakeOutput) - ); + _expectRevertInvalidOutputHashesSiblingsArrayLength(); + _appContract.validateOutputHash(keccak256(output), proof); + } - vm.expectRevert(IApplication.IncorrectOutputsEpochRootHash.selector); - _appContract.validateOutput(fakeOutput, proof); + function testRevertsClaimNotAccepted() external { + string memory name = "HelloWorldNotice"; + OutputValidityProof memory proof = _getProof(name); - proof.outputsEpochRootHash = proof - .outputHashesInEpochSiblings + bytes memory fakeOutput = _encodeNotice("Goodbye, World!"); + bytes32 fakeClaim = proof + .outputHashesSiblings .merkleRootAfterReplacement( - proof.inputIndexWithinEpoch, - proof.outputHashesRootHash + proof.outputIndex, + keccak256(fakeOutput) ); - vm.expectRevert(IApplication.IncorrectEpochHash.selector); + _expectRevertClaimNotAccepted(fakeClaim); _appContract.validateOutput(fakeOutput, proof); + + _expectRevertClaimNotAccepted(fakeClaim); + _appContract.validateOutputHash(keccak256(fakeOutput), proof); } // ---------------- // output execution // ---------------- - function testWasOutputExecuted( - uint256 inputIndex, - uint256 outputIndexWithinInput - ) external view { - assertFalse( - _appContract.wasOutputExecuted(inputIndex, outputIndexWithinInput) - ); + function testWasOutputExecuted(uint256 outputIndex) external view { + assertFalse(_appContract.wasOutputExecuted(outputIndex)); } function testExecuteEtherTransferVoucher() external { @@ -315,23 +290,6 @@ contract ApplicationTest is ERC165Test { _testERC20Success(output, proof); } - // ------- - // ERC-165 - // ------- - - function getERC165Contract() public view override returns (IERC165) { - return _appContract; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - // ------------------ // internal functions // ------------------ @@ -367,20 +325,8 @@ contract ApplicationTest is ERC165Test { _tokenIds, _initialSupplies ); - _inputBox = new InputBox(); - _portals.push(new EtherPortal(_inputBox)); - _portals.push(new ERC20Portal(_inputBox)); - _portals.push(new ERC721Portal(_inputBox)); - _portals.push(new ERC1155SinglePortal(_inputBox)); - _portals.push(new ERC1155BatchPortal(_inputBox)); _consensus = new Authority(_authorityOwner); - _appContract = new Application( - _consensus, - _inputBox, - _portals, - _appOwner, - _templateHash - ); + _appContract = new Application(_consensus, _appOwner, _templateHash); _safeERC20Transfer = new SafeERC20Transfer(); } @@ -390,15 +336,10 @@ contract ApplicationTest is ERC165Test { "HelloWorldNotice", _addOutput(_encodeNotice("Hello, world!")) ); - _finishInput(); - _nameOutput( "MyOutput", _addOutput(abi.encodeWithSignature("MyOutput()")) ); - _finishInput(); - _finishEpoch(); - _nameOutput( "EtherTransferVoucher", _addOutput( @@ -415,9 +356,6 @@ contract ApplicationTest is ERC165Test { ) ) ); - _finishInput(); - _finishEpoch(); - _nameOutput( "ERC20TransferVoucher", _addOutput( @@ -484,9 +422,6 @@ contract ApplicationTest is ERC165Test { ) ) ); - _finishInput(); - _finishEpoch(); - _nameOutput( "ERC20DelegateCallVoucher", _addOutput( @@ -499,12 +434,6 @@ contract ApplicationTest is ERC165Test { ) ) ); - _finishInput(); - _finishEpoch(); - - // Test input with no outputs - _finishInput(); - _finishEpoch(); } function _encodeNotice( @@ -531,59 +460,34 @@ contract ApplicationTest is ERC165Test { function _addOutput( bytes memory output - ) internal returns (LibEmulator.OutputId memory) { + ) internal returns (LibEmulator.OutputIndex) { return _emulator.addOutput(output); } function _nameOutput( string memory name, - LibEmulator.OutputId memory oid + LibEmulator.OutputIndex outputIndex ) internal { - _outputIdsByName[name] = oid; + _outputIndexByName[name] = outputIndex; _outputNames.push(name); } function _getOutput( string memory name ) internal view returns (bytes storage) { - return _emulator.getOutput(_outputIdsByName[name]); + return _emulator.getOutput(_outputIndexByName[name]); } function _getProof( string memory name ) internal view returns (OutputValidityProof memory) { - return _emulator.getOutputValidityProof(_outputIdsByName[name]); - } - - function _finishInput() internal { - _emulator.finishInput(); - } - - function _finishEpoch() internal { - _emulator.finishEpoch(_getMachineStateHash(_emulator.epochCount)); + return _emulator.getOutputValidityProof(_outputIndexByName[name]); } - function _getMachineStateHash( - uint256 epochIndex - ) internal pure returns (bytes32) { - return keccak256(abi.encode("machineStateHash", epochIndex)); - } - - function _submitClaims() internal { - uint256 epochCount = _emulator.epochCount; - for (uint256 epochIndex; epochIndex < epochCount; ++epochIndex) { - InputRange memory inputRange = _emulator.getInputRange(epochIndex); - bytes32 epochHash = _emulator.getEpochHash(epochIndex); - _submitClaim(inputRange, epochHash); - } - } - - function _submitClaim( - InputRange memory inputRange, - bytes32 epochHash - ) internal { + function _submitClaim() internal { + bytes32 claim = _emulator.getClaim(); vm.prank(_authorityOwner); - _consensus.submitClaim(address(_appContract), inputRange, epochHash); + _consensus.submitClaim(address(_appContract), claim); } function _expectEmitOutputExecuted( @@ -591,11 +495,7 @@ contract ApplicationTest is ERC165Test { OutputValidityProof memory proof ) internal { vm.expectEmit(false, false, false, true, address(_appContract)); - emit IApplication.OutputExecuted( - proof.inputRange.firstIndex + proof.inputIndexWithinEpoch, - proof.outputIndexWithinInput, - output - ); + emit IApplication.OutputExecuted(proof.outputIndex, output); } function _expectRevertOutputNotExecutable(bytes memory output) internal { @@ -616,21 +516,25 @@ contract ApplicationTest is ERC165Test { ); } + function _expectRevertInvalidOutputHashesSiblingsArrayLength() internal { + vm.expectRevert( + IApplication.InvalidOutputHashesSiblingsArrayLength.selector + ); + } + + function _expectRevertClaimNotAccepted(bytes32 claim) internal { + vm.expectRevert( + abi.encodeWithSelector( + IApplication.ClaimNotAccepted.selector, + claim + ) + ); + } + function _wasOutputExecuted( OutputValidityProof memory proof ) internal view returns (bool) { - return - _appContract.wasOutputExecuted( - proof.inputRange.firstIndex + proof.inputIndexWithinEpoch, - proof.outputIndexWithinInput - ); - } - - function assertEq(IPortal[] memory a, IPortal[] memory b) internal pure { - assertEq(a.length, b.length); - for (uint256 i; i < a.length; ++i) { - assertEq(address(a[i]), address(b[i])); - } + return _appContract.wasOutputExecuted(proof.outputIndex); } function _testEtherTransfer( diff --git a/test/foundry/dapp/ApplicationFactory.t.sol b/test/foundry/dapp/ApplicationFactory.t.sol index 2533fed4..590aa452 100644 --- a/test/foundry/dapp/ApplicationFactory.t.sol +++ b/test/foundry/dapp/ApplicationFactory.t.sol @@ -8,8 +8,6 @@ import {TestBase} from "../util/TestBase.sol"; import {ApplicationFactory, IApplicationFactory} from "contracts/dapp/ApplicationFactory.sol"; import {Application} from "contracts/dapp/Application.sol"; import {IConsensus} from "contracts/consensus/IConsensus.sol"; -import {IInputBox} from "contracts/inputs/IInputBox.sol"; -import {IPortal} from "contracts/portals/IPortal.sol"; import {Vm} from "forge-std/Vm.sol"; contract ApplicationFactoryTest is TestBase { @@ -21,8 +19,6 @@ contract ApplicationFactoryTest is TestBase { function testNewApplication( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash ) public { @@ -30,24 +26,17 @@ contract ApplicationFactoryTest is TestBase { Application appContract = _factory.newApplication( consensus, - inputBox, - portals, appOwner, templateHash ); assertEq(address(appContract.getConsensus()), address(consensus)); - assertEq(address(appContract.getInputBox()), address(inputBox)); - // abi.encode is used instead of a loop - assertEq(abi.encode(appContract.getPortals()), abi.encode(portals)); assertEq(appContract.owner(), appOwner); assertEq(appContract.getTemplateHash(), templateHash); } function testNewApplicationDeterministic( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -56,8 +45,6 @@ contract ApplicationFactoryTest is TestBase { address precalculatedAddress = _factory.calculateApplicationAddress( consensus, - inputBox, - portals, appOwner, templateHash, salt @@ -65,8 +52,6 @@ contract ApplicationFactoryTest is TestBase { Application appContract = _factory.newApplication( consensus, - inputBox, - portals, appOwner, templateHash, salt @@ -76,15 +61,11 @@ contract ApplicationFactoryTest is TestBase { assertEq(precalculatedAddress, address(appContract)); assertEq(address(appContract.getConsensus()), address(consensus)); - assertEq(address(appContract.getInputBox()), address(inputBox)); - assertEq(abi.encode(appContract.getPortals()), abi.encode(portals)); assertEq(appContract.owner(), appOwner); assertEq(appContract.getTemplateHash(), templateHash); precalculatedAddress = _factory.calculateApplicationAddress( consensus, - inputBox, - portals, appOwner, templateHash, salt @@ -95,20 +76,11 @@ contract ApplicationFactoryTest is TestBase { // Cannot deploy an application with the same salt twice vm.expectRevert(bytes("")); - _factory.newApplication( - consensus, - inputBox, - portals, - appOwner, - templateHash, - salt - ); + _factory.newApplication(consensus, appOwner, templateHash, salt); } function testApplicationCreatedEvent( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash ) public { @@ -118,16 +90,12 @@ contract ApplicationFactoryTest is TestBase { Application appContract = _factory.newApplication( consensus, - inputBox, - portals, appOwner, templateHash ); _testApplicationCreatedEventAux( consensus, - inputBox, - portals, appOwner, templateHash, appContract @@ -136,8 +104,6 @@ contract ApplicationFactoryTest is TestBase { function testApplicationCreatedEventDeterministic( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -148,8 +114,6 @@ contract ApplicationFactoryTest is TestBase { Application appContract = _factory.newApplication( consensus, - inputBox, - portals, appOwner, templateHash, salt @@ -157,8 +121,6 @@ contract ApplicationFactoryTest is TestBase { _testApplicationCreatedEventAux( consensus, - inputBox, - portals, appOwner, templateHash, appContract @@ -167,8 +129,6 @@ contract ApplicationFactoryTest is TestBase { function _testApplicationCreatedEventAux( IConsensus consensus, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, Application appContract @@ -193,18 +153,11 @@ contract ApplicationFactoryTest is TestBase { ); ( - IInputBox inputBox_, - IPortal[] memory portals_, address appOwner_, bytes32 templateHash_, Application app_ - ) = abi.decode( - entry.data, - (IInputBox, IPortal[], address, bytes32, Application) - ); + ) = abi.decode(entry.data, (address, bytes32, Application)); - assertEq(address(inputBox), address(inputBox_)); - assertEq(abi.encode(portals), abi.encode(portals_)); assertEq(appOwner, appOwner_); assertEq(templateHash, templateHash_); assertEq(address(appContract), address(app_)); diff --git a/test/foundry/dapp/SelfHostedApplicationFactory.t.sol b/test/foundry/dapp/SelfHostedApplicationFactory.t.sol index da73de28..44d0c928 100644 --- a/test/foundry/dapp/SelfHostedApplicationFactory.t.sol +++ b/test/foundry/dapp/SelfHostedApplicationFactory.t.sol @@ -12,8 +12,6 @@ import {ApplicationFactory} from "contracts/dapp/ApplicationFactory.sol"; import {Application} from "contracts/dapp/Application.sol"; import {ISelfHostedApplicationFactory} from "contracts/dapp/ISelfHostedApplicationFactory.sol"; import {SelfHostedApplicationFactory} from "contracts/dapp/SelfHostedApplicationFactory.sol"; -import {IInputBox} from "contracts/inputs/IInputBox.sol"; -import {IPortal} from "contracts/portals/IPortal.sol"; import {TestBase} from "../util/TestBase.sol"; contract SelfHostedApplicationFactoryTest is TestBase { @@ -46,8 +44,6 @@ contract SelfHostedApplicationFactoryTest is TestBase { function testDeployContracts( address authorityOwner, - IInputBox inputBox, - IPortal[] calldata portals, address appOwner, bytes32 templateHash, bytes32 salt @@ -60,8 +56,6 @@ contract SelfHostedApplicationFactoryTest is TestBase { (appAddr, authorityAddr) = factory.calculateAddresses( authorityOwner, - inputBox, - portals, appOwner, templateHash, salt @@ -72,8 +66,6 @@ contract SelfHostedApplicationFactoryTest is TestBase { (application, authority) = factory.deployContracts( authorityOwner, - inputBox, - portals, appOwner, templateHash, salt @@ -85,8 +77,6 @@ contract SelfHostedApplicationFactoryTest is TestBase { assertEq(authority.owner(), authorityOwner); assertEq(address(application.getConsensus()), authorityAddr); - assertEq(address(application.getInputBox()), address(inputBox)); - assertEq(abi.encode(application.getPortals()), abi.encode(portals)); assertEq(application.owner(), appOwner); assertEq(application.getTemplateHash(), templateHash); } diff --git a/test/foundry/portals/ERC1155BatchPortal.t.sol b/test/foundry/portals/ERC1155BatchPortal.t.sol index 0c3fe027..10ae600d 100644 --- a/test/foundry/portals/ERC1155BatchPortal.t.sol +++ b/test/foundry/portals/ERC1155BatchPortal.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.22; import {IERC1155, ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC1155BatchPortal} from "contracts/portals/ERC1155BatchPortal.sol"; import {IERC1155BatchPortal} from "contracts/portals/IERC1155BatchPortal.sol"; @@ -12,10 +11,10 @@ import {IInputBox} from "contracts/inputs/IInputBox.sol"; import {IPortal} from "contracts/portals/IPortal.sol"; import {InputEncoding} from "contracts/common/InputEncoding.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; import {SimpleBatchERC1155} from "../util/SimpleERC1155.sol"; -contract ERC1155BatchPortalTest is ERC165Test { +contract ERC1155BatchPortalTest is TestBase { address _alice; address _appContract; IERC1155 _token; @@ -33,23 +32,6 @@ contract ERC1155BatchPortalTest is ERC165Test { _interfaceIds.push(type(IPortal).interfaceId); } - function getERC165Contract() public view override returns (IERC165) { - return _portal; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - - function testGetInputBox() public view { - assertEq(address(_portal.getInputBox()), address(_inputBox)); - } - function testDeposit( uint256[] calldata tokenIds, uint256[] calldata values, diff --git a/test/foundry/portals/ERC1155SinglePortal.t.sol b/test/foundry/portals/ERC1155SinglePortal.t.sol index b8ac2f0c..029c083a 100644 --- a/test/foundry/portals/ERC1155SinglePortal.t.sol +++ b/test/foundry/portals/ERC1155SinglePortal.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.22; import {IERC1155, ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC1155SinglePortal} from "contracts/portals/ERC1155SinglePortal.sol"; import {IERC1155SinglePortal} from "contracts/portals/IERC1155SinglePortal.sol"; @@ -12,10 +11,10 @@ import {IInputBox} from "contracts/inputs/IInputBox.sol"; import {IPortal} from "contracts/portals/IPortal.sol"; import {InputEncoding} from "contracts/common/InputEncoding.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; import {SimpleSingleERC1155} from "../util/SimpleERC1155.sol"; -contract ERC1155SinglePortalTest is ERC165Test { +contract ERC1155SinglePortalTest is TestBase { address _alice; address _appContract; IERC1155 _token; @@ -33,19 +32,6 @@ contract ERC1155SinglePortalTest is ERC165Test { _interfaceIds.push(type(IPortal).interfaceId); } - function getERC165Contract() public view override returns (IERC165) { - return _portal; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - function testGetInputBox() public view { assertEq(address(_portal.getInputBox()), address(_inputBox)); } diff --git a/test/foundry/portals/ERC20Portal.t.sol b/test/foundry/portals/ERC20Portal.t.sol index 1e124679..6f6c81c2 100644 --- a/test/foundry/portals/ERC20Portal.t.sol +++ b/test/foundry/portals/ERC20Portal.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.22; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC20Portal} from "contracts/portals/ERC20Portal.sol"; @@ -12,10 +11,10 @@ import {IInputBox} from "contracts/inputs/IInputBox.sol"; import {IPortal} from "contracts/portals/IPortal.sol"; import {InputEncoding} from "contracts/common/InputEncoding.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; import {SimpleERC20} from "../util/SimpleERC20.sol"; -contract ERC20PortalTest is ERC165Test { +contract ERC20PortalTest is TestBase { address _alice; address _appContract; IInputBox _inputBox; @@ -33,19 +32,6 @@ contract ERC20PortalTest is ERC165Test { _interfaceIds.push(type(IPortal).interfaceId); } - function getERC165Contract() public view override returns (IERC165) { - return _portal; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - function testGetInputBox() public view { assertEq(address(_portal.getInputBox()), address(_inputBox)); } diff --git a/test/foundry/portals/ERC721Portal.t.sol b/test/foundry/portals/ERC721Portal.t.sol index 13d4890a..8db1619c 100644 --- a/test/foundry/portals/ERC721Portal.t.sol +++ b/test/foundry/portals/ERC721Portal.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.22; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC721, ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import {ERC721Portal} from "contracts/portals/ERC721Portal.sol"; @@ -12,10 +11,10 @@ import {IInputBox} from "contracts/inputs/IInputBox.sol"; import {IPortal} from "contracts/portals/IPortal.sol"; import {InputEncoding} from "contracts/common/InputEncoding.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; import {SimpleERC721} from "../util/SimpleERC721.sol"; -contract ERC721PortalTest is ERC165Test { +contract ERC721PortalTest is TestBase { address _alice; address _appContract; IERC721 _token; @@ -33,19 +32,6 @@ contract ERC721PortalTest is ERC165Test { _interfaceIds.push(type(IPortal).interfaceId); } - function getERC165Contract() public view override returns (IERC165) { - return _portal; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - function testGetInputBox() public view { assertEq(address(_portal.getInputBox()), address(_inputBox)); } diff --git a/test/foundry/portals/EtherPortal.t.sol b/test/foundry/portals/EtherPortal.t.sol index 48b797bf..79b7039c 100644 --- a/test/foundry/portals/EtherPortal.t.sol +++ b/test/foundry/portals/EtherPortal.t.sol @@ -3,17 +3,15 @@ pragma solidity ^0.8.22; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - import {EtherPortal} from "contracts/portals/EtherPortal.sol"; import {IEtherPortal} from "contracts/portals/IEtherPortal.sol"; import {IInputBox} from "contracts/inputs/IInputBox.sol"; import {IPortal} from "contracts/portals/IPortal.sol"; import {InputEncoding} from "contracts/common/InputEncoding.sol"; -import {ERC165Test} from "../util/ERC165Test.sol"; +import {TestBase} from "../util/TestBase.sol"; -contract EtherPortalTest is ERC165Test { +contract EtherPortalTest is TestBase { address _alice; address _appContract; IInputBox _inputBox; @@ -29,19 +27,6 @@ contract EtherPortalTest is ERC165Test { _interfaceIds.push(type(IPortal).interfaceId); } - function getERC165Contract() public view override returns (IERC165) { - return _portal; - } - - function getSupportedInterfaces() - public - view - override - returns (bytes4[] memory) - { - return _interfaceIds; - } - function testGetInputBox() public view { assertEq(address(_portal.getInputBox()), address(_inputBox)); } diff --git a/test/foundry/util/ERC165Test.sol b/test/foundry/util/ERC165Test.sol deleted file mode 100644 index e7b2212d..00000000 --- a/test/foundry/util/ERC165Test.sol +++ /dev/null @@ -1,40 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -pragma solidity ^0.8.8; - -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import {TestBase} from "./TestBase.sol"; - -/// @notice Tests contracts that implement ERC-165 -abstract contract ERC165Test is TestBase { - /// @notice Get ERC-165 contract to be tested - function getERC165Contract() public virtual returns (IERC165); - - /// @notice Get array of IDs of supported interfaces - function getSupportedInterfaces() public virtual returns (bytes4[] memory); - - function testSupportsInterface() public { - IERC165 erc165 = getERC165Contract(); - assertTrue(erc165.supportsInterface(type(IERC165).interfaceId)); - assertFalse(erc165.supportsInterface(0xffffffff)); - - bytes4[] memory supportedInterfaces = getSupportedInterfaces(); - for (uint256 i; i < supportedInterfaces.length; ++i) { - assertTrue(erc165.supportsInterface(supportedInterfaces[i])); - } - } - - function testSupportsInterface(bytes4 interfaceId) public { - vm.assume(interfaceId != type(IERC165).interfaceId); - - bytes4[] memory supportedInterfaces = getSupportedInterfaces(); - for (uint256 i; i < supportedInterfaces.length; ++i) { - vm.assume(interfaceId != supportedInterfaces[i]); - } - - IERC165 erc165 = getERC165Contract(); - assertFalse(erc165.supportsInterface(interfaceId)); - } -} diff --git a/test/foundry/util/LibEmulator.sol b/test/foundry/util/LibEmulator.sol index 39325605..3bd86966 100644 --- a/test/foundry/util/LibEmulator.sol +++ b/test/foundry/util/LibEmulator.sol @@ -7,26 +7,17 @@ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {CanonicalMachine} from "contracts/common/CanonicalMachine.sol"; import {OutputValidityProof} from "contracts/common/OutputValidityProof.sol"; -import {InputRange} from "contracts/common/InputRange.sol"; import {LibMerkle32} from "contracts/library/LibMerkle32.sol"; library LibEmulator { using SafeCast for uint256; using LibMerkle32 for bytes32[]; - using LibEmulator for LibEmulator.State; struct State { - uint256 epochCount; - mapping(uint256 => uint64) inputCount; - mapping(uint256 => bytes32) machineStateHashes; - mapping(uint256 => mapping(uint256 => bytes[])) outputs; + bytes[] outputs; } - struct OutputId { - uint256 epochIndex; - uint64 inputIndexWithinEpoch; - uint64 outputIndexWithinInput; - } + type OutputIndex is uint64; // ------------- // state changes @@ -35,190 +26,74 @@ library LibEmulator { function addOutput( State storage state, bytes memory output - ) internal returns (OutputId memory oid) { - bytes[] storage outputs; - oid.epochIndex = state.epochCount; - oid.inputIndexWithinEpoch = state.inputCount[oid.epochIndex]; - outputs = state.outputs[oid.epochIndex][oid.inputIndexWithinEpoch]; - oid.outputIndexWithinInput = outputs.length.toUint64(); + ) internal returns (OutputIndex outputIndex) { + bytes[] storage outputs = state.outputs; + outputIndex = OutputIndex.wrap(outputs.length.toUint64()); outputs.push(output); } - function finishInput(State storage state) internal { - uint256 epochIndex = state.epochCount; - ++state.inputCount[epochIndex]; - } - - function finishEpoch( - State storage state, - bytes32 machineStateHash - ) internal { - uint256 epochIndex = state.epochCount; - require( - state.inputCount[epochIndex] > 0, - "LibEmulator: cannot finish epoch with zero inputs" - ); - state.machineStateHashes[epochIndex] = machineStateHash; - ++state.epochCount; - } - // ------------- // state queries // ------------- function getOutput( State storage state, - OutputId memory oid + OutputIndex outputIndex ) internal view returns (bytes storage) { - // prettier-ignore - return - state.outputs - [oid.epochIndex] - [oid.inputIndexWithinEpoch] - [oid.outputIndexWithinInput]; - } - - function getOutputs( - State storage state, - uint256 epochIndex, - uint64 inputIndexWithinEpoch - ) internal view returns (bytes[] storage) { - return state.outputs[epochIndex][inputIndexWithinEpoch]; - } - - function getMachineStateHash( - State storage state, - uint256 epochIndex - ) internal view returns (bytes32) { - return state.machineStateHashes[epochIndex]; + return state.outputs[OutputIndex.unwrap(outputIndex)]; } function getOutputValidityProof( State storage state, - OutputId memory oid + OutputIndex outputIndex ) internal view returns (OutputValidityProof memory) { - bytes[] memory outputs; bytes32[] memory outputHashes; - bytes32[] memory outputHashInOutputHashesSiblings; - bytes32[] memory outputHashesInEpoch; - bytes32[] memory outputHashesInEpochSiblings; - - outputs = state.getOutputs(oid.epochIndex, oid.inputIndexWithinEpoch); - - outputHashes = getOutputHashes(outputs); - - outputHashesInEpoch = state.getOutputHashesInEpoch(oid.epochIndex); - outputHashInOutputHashesSiblings = getOutputHashInOutputHashesSiblings( - outputHashes, - oid.outputIndexWithinInput - ); - - outputHashesInEpochSiblings = getOutputHashesInEpochSiblings( - outputHashesInEpoch, - oid.inputIndexWithinEpoch - ); + outputHashes = getOutputHashes(state.outputs); return OutputValidityProof( - state.getInputRange(oid.epochIndex), - oid.inputIndexWithinEpoch, - oid.outputIndexWithinInput, - getOutputHashesRootHash(outputHashes), - getOutputsEpochRootHash(outputHashesInEpoch), - state.getMachineStateHash(oid.epochIndex), - outputHashInOutputHashesSiblings, - outputHashesInEpochSiblings - ); - } - - function getInputRange( - State storage state, - uint256 epochIndex - ) internal view returns (InputRange memory r) { - for (uint256 i; i < epochIndex; ++i) { - r.firstIndex += state.inputCount[i]; - } - r.lastIndex = r.firstIndex + state.inputCount[epochIndex] - 1; - } - - function getEpochHash( - State storage state, - uint256 epochIndex - ) internal view returns (bytes32) { - bytes32[] memory outputHashesInEpoch; - outputHashesInEpoch = state.getOutputHashesInEpoch(epochIndex); - return - keccak256( - abi.encode( - getOutputsEpochRootHash(outputHashesInEpoch), - state.machineStateHashes[epochIndex] + OutputIndex.unwrap(outputIndex), + getOutputHashesSiblings( + outputHashes, + OutputIndex.unwrap(outputIndex) ) ); } - // ---------------- - // outputs in epoch - // ---------------- - - function getOutputsEpochRootHash( - bytes32[] memory outputHashesInEpoch - ) internal pure returns (bytes32) { - return - outputHashesInEpoch.merkleRoot( - CanonicalMachine.LOG2_MAX_INPUTS_PER_EPOCH - ); - } + function getClaim(State storage state) internal view returns (bytes32) { + bytes32[] memory outputHashes; - function getOutputHashesInEpochSiblings( - bytes32[] memory outputHashesInEpoch, - uint64 inputIndexWithinEpoch - ) internal pure returns (bytes32[] memory) { - return - outputHashesInEpoch.siblings( - inputIndexWithinEpoch, - CanonicalMachine.LOG2_MAX_INPUTS_PER_EPOCH - ); - } + outputHashes = getOutputHashes(state.outputs); - function getOutputHashesInEpoch( - State storage state, - uint256 epochIndex - ) internal view returns (bytes32[] memory leaves) { - leaves = new bytes32[](state.inputCount[epochIndex]); - for (uint64 i; i < leaves.length; ++i) { - bytes[] memory outputs; - bytes32[] memory outputHashes; - outputs = state.getOutputs(epochIndex, i); - outputHashes = getOutputHashes(outputs); - leaves[i] = getOutputHashesRootHash(outputHashes); - } + return getOutputHashesRootHash(outputHashes); } - // ---------------- - // outputs in input - // ---------------- + // ----------------- + // Merkle operations + // ----------------- function getOutputHashesRootHash( bytes32[] memory outputHashes ) internal pure returns (bytes32) { - return - outputHashes.merkleRoot( - CanonicalMachine.LOG2_MAX_OUTPUTS_PER_INPUT - ); + return outputHashes.merkleRoot(CanonicalMachine.LOG2_MAX_OUTPUTS); } - function getOutputHashInOutputHashesSiblings( + function getOutputHashesSiblings( bytes32[] memory outputHashes, - uint64 outputIndexWithinInput + uint64 outputIndex ) internal pure returns (bytes32[] memory) { return outputHashes.siblings( - outputIndexWithinInput, - CanonicalMachine.LOG2_MAX_OUTPUTS_PER_INPUT + outputIndex, + CanonicalMachine.LOG2_MAX_OUTPUTS ); } + // --------------- + // Hash operations + // --------------- + function getOutputHashes( bytes[] memory outputs ) internal pure returns (bytes32[] memory leaves) {