diff --git a/.github/workflows/contracts-deployment-test.yml b/.github/workflows/contracts-deployment-test.yml new file mode 100644 index 000000000..6a7154b9f --- /dev/null +++ b/.github/workflows/contracts-deployment-test.yml @@ -0,0 +1,44 @@ +name: Contracts Deployment Test + +on: + push: + branches: + - main + - dev + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '18' # Adjust this as needed for your project + + - name: Install dependencies + run: cd contracts && npm install + + - name: Install Foundry + run: | + cd contracts + curl -L https://foundry.paradigm.xyz | bash + export PATH="$PATH:/home/runner/.config/.foundry/bin" + foundryup + forge install + + - name: Generate Ethereum Private Key and Create .env File and run make + run: | + cd contracts + PRIVATE_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") + echo "export PRIVATE_KEY=0x$PRIVATE_KEY" > .env + echo "export RPC_URL=http://127.0.0.1:1337" >> .env + echo "export CHAIN_ID=1337" >> .env + export PATH="$PATH:/home/runner/.config/.foundry/bin" + npx ganache-cli -g0 -p1337 --account 0x$PRIVATE_KEY,1001901919191919191 & + sleep 5 + make deploy-ipc diff --git a/.github/workflows/contracts-npm-audit.yaml b/.github/workflows/contracts-npm-audit.yaml index caa0bb8bb..6c716e4ba 100644 --- a/.github/workflows/contracts-npm-audit.yaml +++ b/.github/workflows/contracts-npm-audit.yaml @@ -1,4 +1,4 @@ -name: NPM Audit +name: Contracts NPM Audit # This workflow is triggered from the main CI workflow. on: diff --git a/.github/workflows/contracts-prettier.yaml b/.github/workflows/contracts-prettier.yaml index 0ce2733b1..1316308c5 100644 --- a/.github/workflows/contracts-prettier.yaml +++ b/.github/workflows/contracts-prettier.yaml @@ -1,6 +1,5 @@ # .github/workflows/prettier.yml - -name: Prettier +name: Contracts Prettier # This workflow is triggered from the main CI workflow. on: diff --git a/.github/workflows/contracts-sast.yaml b/.github/workflows/contracts-sast.yaml index fa2791e2d..d29cbaa60 100644 --- a/.github/workflows/contracts-sast.yaml +++ b/.github/workflows/contracts-sast.yaml @@ -1,4 +1,4 @@ -name: Static analysis +name: Contracts Static analysis # This workflow is triggered from the main CI workflow. on: diff --git a/.github/workflows/contracts-storage.yaml b/.github/workflows/contracts-storage.yaml index 6a64c715b..f2f04492c 100644 --- a/.github/workflows/contracts-storage.yaml +++ b/.github/workflows/contracts-storage.yaml @@ -1,4 +1,4 @@ -name: Storage check +name: Contracts Storage check # This workflow is triggered from the main CI workflow. on: diff --git a/.github/workflows/contracts-test.yaml b/.github/workflows/contracts-test.yaml index 5859d8714..87a155426 100644 --- a/.github/workflows/contracts-test.yaml +++ b/.github/workflows/contracts-test.yaml @@ -1,4 +1,4 @@ -name: Tests +name: Contracts Tests # This workflow is triggered from the main CI workflow. on: diff --git a/contracts/Makefile b/contracts/Makefile index 7d9a7f0e9..422089778 100644 --- a/contracts/Makefile +++ b/contracts/Makefile @@ -10,6 +10,12 @@ OUTPUT ?= out deploy-ipc: ./ops/deploy.sh $(NETWORK) +deploy-subnet-registry: + ./ops/deploy-subnet-registry.sh $(NETWORK) + +deploy-subnet: + ./ops/deploy-subnet.sh $(NETWORK) + upgrade-gw-diamond: ./ops/upgrade-gw-diamond.sh $(NETWORK) @@ -106,7 +112,6 @@ coverage-for-mac: | forge prepare: build-selector-library fmt lint test slither - build-selector-library: | forge python scripts/python/build_selector_library.py npx prettier -w test/helpers/SelectorLibrary.sol diff --git a/contracts/README.md b/contracts/README.md index b4048b92f..2f600a618 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -49,6 +49,48 @@ To deploy the contracts in some other network configured in the Hardhat config y make deploy-ipc NETWORK= ``` +# Upgrading IPC Solidity Contracts + +This repository's contracts use the Diamond pattern for upgradability, allowing new features to be added or issues to be corrected without a full redeployment. The upgrade process is automated and includes bytecode verification to ensure the integrity of the changes. + +## Automated Upgrade and Bytecode Verification + +When you run an upgrade command, the repository's scripts handle several tasks: + +1. **Bytecode Verification**: The scripts fetch the bytecode of the currently deployed contracts on an FEVM-powered IPC network using the details stored in local JSON files in the root directory of the git repository. They compare this with the bytecode generated after applying the intended changes on a temporary Ganache network. + +2. **Conditional Upgrades**: If the bytecode verification process detects changes that align with the intended upgrades, the `make` command conditionally triggers other scripts to perform the actual upgrade on the network. + +## Upgrade Commands + +To upgrade a contract, you may use the following commands. The NETWORK parameter is optional; if not specified, the scripts will default to "auto": + +- **Gateway Diamond Upgrade**: + + ```bash + make upgrade-gw-diamond [NETWORK=] + ``` + +- **Subnet Actor Diamond Upgrade**: + + ```bash + make upgrade-sa-diamond [NETWORK=] + ``` + +- **Subnet Registry Diamond Upgrade**: + ```bash + make upgrade-sr-diamond [NETWORK=] + ``` + +After running any of these commands, the scripts will provide transaction details for verification. Check the transaction on the appropriate block explorer to confirm the upgrade's success. + +## Important Notes + +- The upgrade commands are intended for use by authorized personnel with a deep understanding of the contracts' functionality. +- Ensure that your local repository is up to date with the latest contract code and JSON files before initiating an upgrade. +- Backup all contract data and thoroughly test any new code in a controlled environment prior to an upgrade. +- Monitor the output of the upgrade process carefully for transaction details and to verify its successful completion. + ## Branching Strategy ### Production branch diff --git a/contracts/binding/build.rs b/contracts/binding/build.rs index 8a31ae3d4..8c26dcbae 100644 --- a/contracts/binding/build.rs +++ b/contracts/binding/build.rs @@ -47,9 +47,12 @@ fn main() { "TopDownFinalityFacet", "XnetMessagingFacet", "GatewayMessengerFacet", + "SubnetActorCheckpointingFacet", "SubnetActorDiamond", "SubnetActorGetterFacet", "SubnetActorManagerFacet", + "SubnetActorPauseFacet", + "SubnetActorRewardFacet", "SubnetRegistryDiamond", "RegisterSubnetFacet", "SubnetGetterFacet", @@ -86,7 +89,8 @@ fn main() { "BottomUpRouterFacet", "XnetMessagingFacet", "GatewayMessengerFacet", - "SubnetActorManagerFacet", + "SubnetActorCheckpointingFacet", + "SubnetActorGetterFacet", "LibGateway", ]; @@ -101,7 +105,6 @@ fn main() { ) .unwrap(); let common_type_conversion = vec![ - ("GatewayGetterFacet", "SubnetActorManagerFacet"), ("SubnetActorGetterFacet", "BottomUpRouterFacet"), ("SubnetActorGetterFacet", "CheckpointingFacet"), ("SubnetActorGetterFacet", "XnetMessagingFacet"), diff --git a/contracts/binding/src/lib.rs b/contracts/binding/src/lib.rs index b2cbdee69..1137f5d44 100644 --- a/contracts/binding/src/lib.rs +++ b/contracts/binding/src/lib.rs @@ -24,12 +24,18 @@ pub mod xnet_messaging_facet; #[allow(clippy::all)] pub mod gateway_messenger_facet; #[allow(clippy::all)] +pub mod subnet_actor_checkpointing_facet; +#[allow(clippy::all)] pub mod subnet_actor_diamond; #[allow(clippy::all)] pub mod subnet_actor_getter_facet; #[allow(clippy::all)] pub mod subnet_actor_manager_facet; #[allow(clippy::all)] +pub mod subnet_actor_pause_facet; +#[allow(clippy::all)] +pub mod subnet_actor_reward_facet; +#[allow(clippy::all)] pub mod subnet_registry_diamond; #[allow(clippy::all)] pub mod register_subnet_facet; @@ -50,11 +56,11 @@ fvm_address_conversion!(gateway_getter_facet); fvm_address_conversion!(bottom_up_router_facet); fvm_address_conversion!(xnet_messaging_facet); fvm_address_conversion!(gateway_messenger_facet); -fvm_address_conversion!(subnet_actor_manager_facet); +fvm_address_conversion!(subnet_actor_checkpointing_facet); +fvm_address_conversion!(subnet_actor_getter_facet); fvm_address_conversion!(lib_gateway); // The list of contracts that need to convert common types between each other -common_type_conversion!(gateway_getter_facet, subnet_actor_manager_facet); common_type_conversion!(subnet_actor_getter_facet, bottom_up_router_facet); common_type_conversion!(subnet_actor_getter_facet, checkpointing_facet); common_type_conversion!(subnet_actor_getter_facet, xnet_messaging_facet); diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index f24c5be46..d2d4f07f5 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -1,14 +1,12 @@ -import { HardhatUserConfig, task } from 'hardhat/config' -import '@typechain/hardhat' -import 'hardhat-storage-layout-changes' - import '@nomicfoundation/hardhat-foundry' import '@nomiclabs/hardhat-ethers' -import 'hardhat-deploy' -import 'hardhat-contract-sizer' - +import '@typechain/hardhat' import dotenv from 'dotenv' import fs from 'fs' +import 'hardhat-contract-sizer' +import 'hardhat-deploy' +import 'hardhat-storage-layout-changes' +import { HardhatUserConfig, task } from 'hardhat/config' import { HardhatRuntimeEnvironment } from 'hardhat/types' dotenv.config() @@ -193,27 +191,6 @@ task( }, ) -task( - 'deploy-subnet', - 'Builds and deploys the SubnetActor contract on the selected network', - async (args, hre: HardhatRuntimeEnvironment) => { - const network = hre.network.name - - const deployments = await getDeployments(network) - const { deploy } = await lazyImport('./scripts/deploy-subnet') - - // remove unused lib - delete deployments.libs['StorableMsgHelper'] - - const subnetDeployment = await deploy( - deployments.Gateway, - deployments.libs, - ) - - await saveDeployments(network, subnetDeployment) - }, -) - task( 'deploy-gw-diamond-and-facets', 'Builds and deploys Gateway Actor diamond and its facets', diff --git a/contracts/ops/deploy-subnet-registry.sh b/contracts/ops/deploy-subnet-registry.sh new file mode 100755 index 000000000..995586c6f --- /dev/null +++ b/contracts/ops/deploy-subnet-registry.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Upgrades IPC Gateway Diamond Facets on an EVM-compatible subnet using hardhat +set -e + +if [ $# -ne 1 ] +then + echo "Expected a single argument with the name of the network to deploy (localnet, calibrationnet, mainnet)" + exit 1 +fi + +NETWORK=$1 + +if [ "$NETWORK" = "auto" ]; then + echo "[*] Automatically getting chainID for network" + source ops/chain-id.sh +fi + + +npx hardhat deploy-subnet-registry --network ${NETWORK} diff --git a/contracts/ops/deploy-subnet.sh b/contracts/ops/deploy-subnet.sh new file mode 100755 index 000000000..6dce9915b --- /dev/null +++ b/contracts/ops/deploy-subnet.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Upgrades IPC Gateway Diamond Facets on an EVM-compatible subnet using hardhat +set -e + +if [ $# -ne 1 ] +then + echo "Expected a single argument with the name of the network to deploy (localnet, calibrationnet, mainnet)" + exit 1 +fi + +NETWORK=$1 + +if [ "$NETWORK" = "auto" ]; then + echo "[*] Automatically getting chainID for network" + source ops/chain-id.sh +fi + + +npx hardhat deploy-sa-diamond-and-facets --network ${NETWORK} + diff --git a/contracts/scripts/deploy-registry.template.ts b/contracts/scripts/deploy-registry.template.ts index 8d9ac6811..170f7c240 100644 --- a/contracts/scripts/deploy-registry.template.ts +++ b/contracts/scripts/deploy-registry.template.ts @@ -1,5 +1,5 @@ -import { ethers } from 'hardhat' import { deployContractWithDeployer, getTransactionFees } from './util' +import { ethers } from 'hardhat' const { getSelectors, FacetCutAction } = require('./js/diamond.js') @@ -26,18 +26,38 @@ export async function deploy() { txArgs, ) const getterSelectors = getSelectors(getterFacet) - // console.log("getter address:", getterFacet.address); const managerFacet = await deployContractWithDeployer( deployer, 'SubnetActorManagerFacet', - { - LibStaking: LIBMAP['LibStaking'], - }, + {}, txArgs, ) const managerSelectors = getSelectors(managerFacet) - // console.log("manager address:", managerFacet.address); + + const pauserFacet = await deployContractWithDeployer( + deployer, + 'SubnetActorPauseFacet', + {}, + txArgs, + ) + const pauserSelectors = getSelectors(pauserFacet) + + const rewarderFacet = await deployContractWithDeployer( + deployer, + 'SubnetActorRewardFacet', + { LibStaking: LIBMAP['LibStaking'] }, + txArgs, + ) + const rewarderSelectors = getSelectors(rewarderFacet) + + const checkpointerFacet = await deployContractWithDeployer( + deployer, + 'SubnetActorCheckpointingFacet', + {}, + txArgs, + ) + const checkpointerSelectors = getSelectors(checkpointerFacet) //deploy subnet registry diamond const registry = await ethers.getContractFactory('SubnetRegistryDiamond', { @@ -48,8 +68,14 @@ export async function deploy() { gateway: gatewayAddress, getterFacet: getterFacet.address, managerFacet: managerFacet.address, - subnetGetterSelectors: getterSelectors, - subnetManagerSelectors: managerSelectors, + rewarderFacet: rewarderFacet.address, + checkpointerFacet: checkpointerFacet.address, + pauserFacet: pauserFacet.address, + subnetActorGetterSelectors: getterSelectors, + subnetActorManagerSelectors: managerSelectors, + subnetActorRewarderSelectors: rewarderSelectors, + subnetActorCheckpointerSelectors: checkpointerSelectors, + subnetActorPauserSelectors: pauserSelectors, } const facetCuts = [] //TODO diff --git a/contracts/scripts/deploy-sa-diamond.ts b/contracts/scripts/deploy-sa-diamond.ts index 7a799fda3..0cc5fa439 100644 --- a/contracts/scripts/deploy-sa-diamond.ts +++ b/contracts/scripts/deploy-sa-diamond.ts @@ -1,5 +1,5 @@ -import hre, { ethers } from 'hardhat' import { deployContractWithDeployer, getTransactionFees } from './util' +import hre, { ethers } from 'hardhat' const { getSelectors, FacetCutAction } = require('./js/diamond.js') @@ -26,15 +26,22 @@ async function deploySubnetActorDiamond( SubnetIDHelper: libs['SubnetIDHelper'], } - const managerFacetLibs: Libraries = { - LibStaking: libs['LibStaking'], - } + const managerFacetLibs: Libraries = {} + + const rewarderFacetLibs: Libraries = {} + + const pauserFacetLibs: Libraries = {} + + const checkpointerFacetLibs: Libraries = {} const facets = [ { name: 'DiamondLoupeFacet', libs: {} }, { name: 'DiamondCutFacet', libs: {} }, { name: 'SubnetActorGetterFacet', libs: getterFacetLibs }, { name: 'SubnetActorManagerFacet', libs: managerFacetLibs }, + { name: 'SubnetActorRewardFacet', libs: rewarderFacetLibs }, + { name: 'SubnetActorCheckpointingFacet', libs: checkpointerFacetLibs }, + { name: 'SubnetActorPauseFacet', libs: pauserFacetLibs }, ] // The `facetCuts` variable is the FacetCut[] that contains the functions to add during diamond deployment const facetCuts = [] diff --git a/contracts/scripts/deploy-subnet.ts b/contracts/scripts/deploy-subnet.ts deleted file mode 100644 index fd5458df5..000000000 --- a/contracts/scripts/deploy-subnet.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* global ethers */ -/* eslint prefer-const: "off" */ - -import { hre, ethers } from 'hardhat' -import { deployContractWithDeployer, getTransactionFees } from './util' - -export async function deploy( - gatewayAddress: string, - libs: { [key in string]: string }, -) { - if (!gatewayAddress) throw new Error(`Gateway is missing`) - if (!libs || Object.keys(libs).length === 0) - throw new Error(`Libraries are missing`) - - await hre.run('compile') - - const [deployer] = await ethers.getSigners() - const balance = await ethers.provider.getBalance(deployer.address) - console.log( - 'Deploying subnet with the account:', - deployer.address, - ' balance:', - ethers.utils.formatEther(balance), - ) - - const txArgs = await getTransactionFees() - - const gateway = await ethers.getContractAt( - 'Gateway', - gatewayAddress, - deployer, - ) - const parentId = await gateway.getNetworkName() - - console.log(parentId) - - const constructorParams = { - parentId, - name: ethers.utils.formatBytes32String('Subnet'), - ipcGatewayAddr: gatewayAddress, - consensus: 0, - minActivationCollateral: ethers.utils.parseEther('1'), - minValidators: 3, - bottomUpCheckPeriod: 10, - topDownCheckPeriod: 10, - majorityPercentage: 66, - genesis: 0, - permissioned: false, - } - - const { address: subnetAddress } = await deployContractWithDeployer( - deployer, - 'SubnetActor', - libs, - constructorParams, - txArgs, - ) - - return { - SubnetActor: subnetAddress, - } -} diff --git a/contracts/scripts/python/build_selector_library.py b/contracts/scripts/python/build_selector_library.py index 04a22b2e6..2cb4785db 100644 --- a/contracts/scripts/python/build_selector_library.py +++ b/contracts/scripts/python/build_selector_library.py @@ -81,6 +81,9 @@ def main(): 'src/gateway/router/XnetMessagingFacet.sol', 'src/subnet/SubnetActorGetterFacet.sol', 'src/subnet/SubnetActorManagerFacet.sol', + 'src/subnet/SubnetActorPauseFacet.sol', + 'src/subnet/SubnetActorRewardFacet.sol', + 'src/subnet/SubnetActorCheckpointingFacet.sol', 'src/subnetregistry/RegisterSubnetFacet.sol', 'src/subnetregistry/SubnetGetterFacet.sol', 'test/helpers/ERC20PresetFixedSupply.sol', @@ -88,7 +91,7 @@ def main(): 'test/helpers/NumberContractFacetSeven.sol', 'test/helpers/SelectorLibrary.sol', 'test/helpers/TestUtils.sol', - 'test/mocks/SubnetActorManagerFacetMock.sol', + 'test/mocks/SubnetActorMock.sol', ] for filepath in filepaths_to_target: diff --git a/contracts/scripts/util.ts b/contracts/scripts/util.ts index 94d02771c..ea03d797a 100644 --- a/contracts/scripts/util.ts +++ b/contracts/scripts/util.ts @@ -1,8 +1,9 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { providers, Wallet, ContractFactory, Contract } from 'ethers' -import { Contract, ethers } from 'hardhat' import ganache from 'ganache' +import { ethers } from 'hardhat' import * as linker from 'solc/linker' + const { getSelectors, FacetCutAction } = require('./js/diamond.js') const fs = require('fs') diff --git a/contracts/src/SubnetRegistryDiamond.sol b/contracts/src/SubnetRegistryDiamond.sol index d022b5e8a..a1a2e6b7a 100644 --- a/contracts/src/SubnetRegistryDiamond.sol +++ b/contracts/src/SubnetRegistryDiamond.sol @@ -17,8 +17,14 @@ contract SubnetRegistryDiamond { address gateway; address getterFacet; address managerFacet; - bytes4[] subnetGetterSelectors; - bytes4[] subnetManagerSelectors; + address rewarderFacet; + address checkpointerFacet; + address pauserFacet; + bytes4[] subnetActorGetterSelectors; + bytes4[] subnetActorManagerSelectors; + bytes4[] subnetActorRewarderSelectors; + bytes4[] subnetActorCheckpointerSelectors; + bytes4[] subnetActorPauserSelectors; } constructor(IDiamond.FacetCut[] memory _diamondCut, ConstructorParams memory params) { @@ -31,6 +37,15 @@ contract SubnetRegistryDiamond { if (params.managerFacet == address(0)) { revert FacetCannotBeZero(); } + if (params.rewarderFacet == address(0)) { + revert FacetCannotBeZero(); + } + if (params.checkpointerFacet == address(0)) { + revert FacetCannotBeZero(); + } + if (params.pauserFacet == address(0)) { + revert FacetCannotBeZero(); + } LibDiamond.setContractOwner(msg.sender); LibDiamond.diamondCut({_diamondCut: _diamondCut, _init: address(0), _calldata: new bytes(0)}); @@ -42,11 +57,17 @@ contract SubnetRegistryDiamond { ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; s.GATEWAY = params.gateway; - s.SUBNET_GETTER_FACET = params.getterFacet; - s.SUBNET_MANAGER_FACET = params.managerFacet; + s.SUBNET_ACTOR_GETTER_FACET = params.getterFacet; + s.SUBNET_ACTOR_MANAGER_FACET = params.managerFacet; + s.SUBNET_ACTOR_REWARD_FACET = params.rewarderFacet; + s.SUBNET_ACTOR_CHECKPOINTING_FACET = params.checkpointerFacet; + s.SUBNET_ACTOR_PAUSE_FACET = params.pauserFacet; - s.subnetGetterSelectors = params.subnetGetterSelectors; - s.subnetManagerSelectors = params.subnetManagerSelectors; + s.subnetActorGetterSelectors = params.subnetActorGetterSelectors; + s.subnetActorManagerSelectors = params.subnetActorManagerSelectors; + s.subnetActorRewarderSelectors = params.subnetActorRewarderSelectors; + s.subnetActorCheckpointerSelectors = params.subnetActorCheckpointerSelectors; + s.subnetActorPauserSelectors = params.subnetActorPauserSelectors; } function _fallback() internal { diff --git a/contracts/src/gateway/router/BottomUpRouterFacet.sol b/contracts/src/gateway/router/BottomUpRouterFacet.sol index 6e47ecbdf..f48523685 100644 --- a/contracts/src/gateway/router/BottomUpRouterFacet.sol +++ b/contracts/src/gateway/router/BottomUpRouterFacet.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.19; -import {ISubnetActor} from "../../interfaces/ISubnetActor.sol"; +import {IRelayerRewardDistributor} from "../../interfaces/ISubnetActor.sol"; import {GatewayActorModifiers} from "../../lib/LibGatewayActorStorage.sol"; import {BottomUpMsgBatch} from "../../structs/CrossNet.sol"; import {LibGateway} from "../../lib/LibGateway.sol"; @@ -73,7 +73,7 @@ contract BottomUpRouterFacet is GatewayActorModifiers { Address.functionCallWithValue({ target: msg.sender, data: abi.encodeCall( - ISubnetActor.distributeRewardToRelayers, + IRelayerRewardDistributor.distributeRewardToRelayers, (block.number, totalFee, QuorumObjKind.Checkpoint) ), value: totalFee diff --git a/contracts/src/gateway/router/CheckpointingFacet.sol b/contracts/src/gateway/router/CheckpointingFacet.sol index 15ef47979..ab37e51c7 100644 --- a/contracts/src/gateway/router/CheckpointingFacet.sol +++ b/contracts/src/gateway/router/CheckpointingFacet.sol @@ -8,7 +8,7 @@ import {LibQuorum} from "../../lib/LibQuorum.sol"; import {Subnet} from "../../structs/Subnet.sol"; import {QuorumObjKind} from "../../structs/Quorum.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; -import {ISubnetActor} from "../../interfaces/ISubnetActor.sol"; +import {IRelayerRewardDistributor} from "../../interfaces/ISubnetActor.sol"; import {InvalidBatchSource, NotEnoughBalance, MaxMsgsPerBatchExceeded, BatchWithNoMessages, InvalidCheckpointSource, InvalidCrossMsgNonce, InvalidCrossMsgDstSubnet, CheckpointAlreadyExists} from "../../errors/IPCErrors.sol"; import {NotRegisteredSubnet, SubnetNotActive, SubnetNotFound, InvalidSubnet, CheckpointNotCreated} from "../../errors/IPCErrors.sol"; @@ -42,7 +42,7 @@ contract CheckpointingFacet is GatewayActorModifiers { Address.functionCallWithValue({ target: msg.sender, data: abi.encodeCall( - ISubnetActor.distributeRewardToRelayers, + IRelayerRewardDistributor.distributeRewardToRelayers, (checkpoint.blockHeight, 0, QuorumObjKind.Checkpoint) ), value: 0 diff --git a/contracts/src/interfaces/ISubnetActor.sol b/contracts/src/interfaces/ISubnetActor.sol index 5f155aa41..b1f236236 100644 --- a/contracts/src/interfaces/ISubnetActor.sol +++ b/contracts/src/interfaces/ISubnetActor.sol @@ -4,47 +4,7 @@ pragma solidity 0.8.19; import {BottomUpCheckpoint} from "../structs/CrossNet.sol"; import {QuorumObjKind} from "../structs/Quorum.sol"; -/// @title Subnet Actor interface -/// @author LimeChain team -interface ISubnetActor { - /// Called by peers looking to join a subnet. - /// - /// It implements the basic logic to onboard new peers to the subnet. - function join(bytes calldata metadata) external payable; - - /// Called by peers looking to leave a subnet. - function leave() external; - - /// Method that allows a validator to increase their stake - function stake() external payable; - - /// Method that allows to pre-fund an address in the subnet before it bootstraps. - function preFund() external payable; - - /// Method that allows to recover initial balance for an address from a subnet that hasn't bootstrapped yet. - function preRelease(uint256 amount) external; - - /// Method that allows a validator to unstake their collateral from a subnet - function unstake(uint256 amount) external; - - /// Unregister the subnet from the hierarchy, making it no longer discoverable. - function kill() external; - - /// Validator claims released collateral - function claim() external; - - /// Relayer claims a reward - function claimRewardForRelayer() external; - - /// Executes the checkpoint if it is valid. - /// It triggers the commitment of the checkpoint, the execution of related cross-net messages, - /// and any other side-effects that need to be triggered by the checkpoint such as relayer reward book keeping. - function submitCheckpoint( - BottomUpCheckpoint calldata checkpoint, - address[] calldata signatories, - bytes[] calldata signatures - ) external; - +interface IRelayerRewardDistributor { /// reward the relayers for processing checkpoint at height `height`. /// The reword includes the fixed reward for a relayer defined in the contract and `amount` of fees from the cross-messages. function distributeRewardToRelayers(uint256 height, uint256 amount, QuorumObjKind kind) external payable; diff --git a/contracts/src/lib/LibPausable.sol b/contracts/src/lib/LibPausable.sol index 78a73c93b..3e880ee20 100644 --- a/contracts/src/lib/LibPausable.sol +++ b/contracts/src/lib/LibPausable.sol @@ -16,7 +16,7 @@ abstract contract Pausable { event Paused(address account); /** - * @dev Emitted when the pause is lifted by `account`. + * @dev Emitted when the unpause is triggered by `account`. */ event Unpaused(address account); @@ -42,11 +42,23 @@ abstract contract Pausable { _; } + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + /** * @dev Throws if the contract is paused. */ - function _requireNotPaused() internal view virtual { - if (paused()) { + function _requireNotPaused() internal view { + if (_paused()) { revert EnforcedPause(); } } @@ -54,14 +66,14 @@ abstract contract Pausable { /** * @dev Throws if the contract is not paused. */ - function _requirePaused() internal view virtual { - if (!paused()) { + function _requirePaused() internal view { + if (!_paused()) { revert ExpectedPause(); } } - /// @notice sets if to pause the contract - function paused() public view returns(bool) { + /// @notice returns true if the contract is paused + function _paused() internal view returns(bool) { PausableStorage storage s = pausableStorage(); return s.paused; } @@ -73,10 +85,11 @@ abstract contract Pausable { * * - The contract must not be paused. */ - function _pause() internal whenNotPaused { + function _pause() internal { + _requireNotPaused(); PausableStorage storage s = pausableStorage(); s.paused = true; - emit Unpaused(msg.sender); + emit Paused(msg.sender); } /** @@ -86,7 +99,7 @@ abstract contract Pausable { * * - The contract must be paused. */ - function _unpause() internal { + function _unpause() internal { _requirePaused(); PausableStorage storage s = pausableStorage(); s.paused = false; diff --git a/contracts/src/lib/LibSubnetRegistryStorage.sol b/contracts/src/lib/LibSubnetRegistryStorage.sol index 248cdbb9e..58b18ec67 100644 --- a/contracts/src/lib/LibSubnetRegistryStorage.sol +++ b/contracts/src/lib/LibSubnetRegistryStorage.sol @@ -6,13 +6,25 @@ struct SubnetRegistryActorStorage { address GATEWAY; /// The getter and manager facet shared by diamond // solhint-disable-next-line var-name-mixedcase - address SUBNET_GETTER_FACET; + address SUBNET_ACTOR_GETTER_FACET; // solhint-disable-next-line var-name-mixedcase - address SUBNET_MANAGER_FACET; - /// The subnet getter facet functions selectors - bytes4[] subnetGetterSelectors; - /// The subnet manager facet functions selectors - bytes4[] subnetManagerSelectors; + address SUBNET_ACTOR_MANAGER_FACET; + // solhint-disable-next-line var-name-mixedcase + address SUBNET_ACTOR_REWARD_FACET; + // solhint-disable-next-line var-name-mixedcase + address SUBNET_ACTOR_CHECKPOINTING_FACET; + // solhint-disable-next-line var-name-mixedcase + address SUBNET_ACTOR_PAUSE_FACET; + /// The subnet actor getter facet functions selectors + bytes4[] subnetActorGetterSelectors; + /// The subnet actor manager facet functions selectors + bytes4[] subnetActorManagerSelectors; + /// The subnet actor reward facet functions selectors + bytes4[] subnetActorRewarderSelectors; + /// The subnet actor checkpointing facet functions selectors + bytes4[] subnetActorCheckpointerSelectors; + /// The subnet actor pause facet functions selectors + bytes4[] subnetActorPauserSelectors; /// @notice Mapping that tracks the deployed subnet actors per user. /// Key is the hash of Subnet ID, values are addresses. /// mapping owner => nonce => subnet diff --git a/contracts/src/structs/Subnet.sol b/contracts/src/structs/Subnet.sol index df9219349..06118f4bf 100644 --- a/contracts/src/structs/Subnet.sol +++ b/contracts/src/structs/Subnet.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.19; -import {SubnetID} from "./Subnet.sol"; import {FvmAddress} from "./FvmAddress.sol"; import {MaxPQ} from "../lib/priority/LibMaxPQ.sol"; import {MinPQ} from "../lib/priority/LibMinPQ.sol"; diff --git a/contracts/src/subnet/SubnetActorCheckpointingFacet.sol b/contracts/src/subnet/SubnetActorCheckpointingFacet.sol new file mode 100644 index 000000000..5f6da4472 --- /dev/null +++ b/contracts/src/subnet/SubnetActorCheckpointingFacet.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {InvalidBatchEpoch, MaxMsgsPerBatchExceeded, BatchWithNoMessages, InvalidSignatureErr, InvalidCheckpointEpoch} from "../errors/IPCErrors.sol"; +import {IGateway} from "../interfaces/IGateway.sol"; +import {BottomUpCheckpoint, BottomUpMsgBatch, BottomUpMsgBatchInfo} from "../structs/CrossNet.sol"; +import {Validator, ValidatorSet} from "../structs/Subnet.sol"; +import {MultisignatureChecker} from "../lib/LibMultisignatureChecker.sol"; +import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; +import {SubnetActorModifiers} from "../lib/LibSubnetActorStorage.sol"; +import {LibValidatorSet, LibStaking} from "../lib/LibStaking.sol"; +import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; +import {LibSubnetActor} from "../lib/LibSubnetActor.sol"; +import {Pausable} from "../lib/LibPausable.sol"; + +contract SubnetActorCheckpointingFacet is SubnetActorModifiers, ReentrancyGuard, Pausable { + using EnumerableSet for EnumerableSet.AddressSet; + using LibValidatorSet for ValidatorSet; + + /// @notice Submits a checkpoint commitment for execution. + /// @dev It triggers the commitment of the checkpoint and any other side-effects that + /// need to be triggered by the checkpoint such as relayer reward book keeping. + /// @param checkpoint The executed bottom-up checkpoint. + /// @param signatories The addresses of validators signing the checkpoint. + /// @param signatures The signatures of validators on the checkpoint. + function submitCheckpoint( + BottomUpCheckpoint calldata checkpoint, + address[] calldata signatories, + bytes[] calldata signatures + ) external whenNotPaused { + // the checkpoint height must be equal to the last bottom-up checkpoint height or + // the next one + if ( + checkpoint.blockHeight != s.lastBottomUpCheckpointHeight + s.bottomUpCheckPeriod && + checkpoint.blockHeight != s.lastBottomUpCheckpointHeight + ) { + revert InvalidCheckpointEpoch(); + } + bytes32 checkpointHash = keccak256(abi.encode(checkpoint)); + + if (checkpoint.blockHeight == s.lastBottomUpCheckpointHeight + s.bottomUpCheckPeriod) { + // validate signatures and quorum threshold, revert if validation fails + validateActiveQuorumSignatures({signatories: signatories, hash: checkpointHash, signatures: signatures}); + + // If the checkpoint height is the next expected height then this is a new checkpoint which must be executed + // in the Gateway Actor, the checkpoint and the relayer must be stored, last bottom-up checkpoint updated. + s.committedCheckpoints[checkpoint.blockHeight] = checkpoint; + + // slither-disable-next-line unused-return + s.relayerRewards.checkpointRewarded[checkpoint.blockHeight].add(msg.sender); + + s.lastBottomUpCheckpointHeight = checkpoint.blockHeight; + + // Commit in gateway to distribute rewards + IGateway(s.ipcGatewayAddr).commitCheckpoint(checkpoint); + + // confirming the changes in membership in the child + LibStaking.confirmChange(checkpoint.nextConfigurationNumber); + } else if (checkpoint.blockHeight == s.lastBottomUpCheckpointHeight) { + // If the checkpoint height is equal to the last checkpoint height, then this is a repeated submission. + // We should store the relayer, but not to execute checkpoint again. + // In this case, we do not verify the signatures for this checkpoint again, + // but we add the relayer to the list of all relayers for this checkpoint to be rewarded later. + // The reason for comparing hashes instead of verifying signatures is the following: + // once the checkpoint is executed, the active validator set changes + // and can only be used to validate the next checkpoint, not another instance of the last one. + bytes32 lastCheckpointHash = keccak256(abi.encode(s.committedCheckpoints[checkpoint.blockHeight])); + if (checkpointHash == lastCheckpointHash) { + // slither-disable-next-line unused-return + s.relayerRewards.checkpointRewarded[checkpoint.blockHeight].add(msg.sender); + } + } + } + + /// @notice Submits a batch of bottom-up messages for execution. + /// @dev It triggers the execution of a cross-net message batch. + /// @param batch The batch of bottom-up messages. + /// @param signatories The addresses of validators signing the batch. + /// @param signatures The signatures of validators on the batch. + function submitBottomUpMsgBatch( + BottomUpMsgBatch calldata batch, + address[] calldata signatories, + bytes[] calldata signatures + ) external { + // forbid the submission of batches from the past + if (batch.blockHeight < s.lastBottomUpBatch.blockHeight) { + revert InvalidBatchEpoch(); + } + if (batch.msgs.length > s.maxMsgsPerBottomUpBatch) { + revert MaxMsgsPerBatchExceeded(); + } + // if the batch height is not max, we only supoprt batch submission in period epochs + if (batch.msgs.length != s.maxMsgsPerBottomUpBatch && batch.blockHeight % s.bottomUpMsgBatchPeriod != 0) { + revert InvalidBatchEpoch(); + } + if (batch.msgs.length == 0) { + revert BatchWithNoMessages(); + } + + bytes32 batchHash = keccak256(abi.encode(batch)); + + if (batch.blockHeight == s.lastBottomUpBatch.blockHeight) { + // If the batch info is equal to the last batch info, then this is a repeated submission. + // We should store the relayer, but not to execute batch again following the same reward logic + // used for checkpoints. + if (batchHash == s.lastBottomUpBatch.hash) { + // slither-disable-next-line unused-return + s.relayerRewards.batchRewarded[batch.blockHeight].add(msg.sender); + } + } else { + // validate signatures and quorum threshold, revert if validation fails + validateActiveQuorumSignatures({signatories: signatories, hash: batchHash, signatures: signatures}); + + // If the checkpoint height is the next expected height then this is a new batch, + // and should be forwarded to the gateway for execution. + s.lastBottomUpBatch = BottomUpMsgBatchInfo({blockHeight: batch.blockHeight, hash: batchHash}); + + // slither-disable-next-line unused-return + s.relayerRewards.batchRewarded[batch.blockHeight].add(msg.sender); + + // Execute messages. + IGateway(s.ipcGatewayAddr).execBottomUpMsgBatch(batch); + } + } + + /// @notice Checks whether the signatures are valid for the provided signatories and hash within the current validator set. + /// Reverts otherwise. + /// @dev Signatories in `signatories` and their signatures in `signatures` must be provided in the same order. + /// Having it public allows external users to perform sanity-check verification if needed. + /// @param signatories The addresses of the signatories. + /// @param hash The hash of the checkpoint. + /// @param signatures The packed signatures of the checkpoint. + function validateActiveQuorumSignatures( + address[] memory signatories, + bytes32 hash, + bytes[] memory signatures + ) public view { + // This call reverts if at least one of the signatories (validator) is not in the active validator set. + uint256[] memory collaterals = s.validatorSet.getTotalPowerOfValidators(signatories); + uint256 activeCollateral = s.validatorSet.getTotalActivePower(); + + uint256 threshold = (activeCollateral * s.majorityPercentage) / 100; + + (bool valid, MultisignatureChecker.Error err) = MultisignatureChecker.isValidWeightedMultiSignature({ + signatories: signatories, + weights: collaterals, + threshold: threshold, + hash: hash, + signatures: signatures + }); + + if (!valid) { + revert InvalidSignatureErr(uint8(err)); + } + } +} diff --git a/contracts/src/subnet/SubnetActorManagerFacet.sol b/contracts/src/subnet/SubnetActorManagerFacet.sol index 7a6dae3e2..e079d00fb 100644 --- a/contracts/src/subnet/SubnetActorManagerFacet.sol +++ b/contracts/src/subnet/SubnetActorManagerFacet.sol @@ -2,150 +2,24 @@ pragma solidity 0.8.19; import {VALIDATOR_SECP256K1_PUBLIC_KEY_LENGTH} from "../constants/Constants.sol"; -import {ERR_PERMISSIONED_AND_BOOTSTRAPPED, ERR_VALIDATOR_JOINED, ERR_VALIDATOR_NOT_JOINED} from "../errors/IPCErrors.sol"; -import {InvalidBatchEpoch, MaxMsgsPerBatchExceeded, BatchWithNoMessages, InvalidFederationPayload, SubnetAlreadyBootstrapped, NotEnoughFunds, CollateralIsZero, CannotReleaseZero, NotOwnerOfPublicKey, EmptyAddress, NotEnoughBalance, NotEnoughCollateral, NotValidator, NotAllValidatorsHaveLeft, NotStakedBefore, InvalidSignatureErr, InvalidCheckpointEpoch, InvalidPublicKeyLength, MethodNotAllowed} from "../errors/IPCErrors.sol"; +import {ERR_VALIDATOR_JOINED, ERR_VALIDATOR_NOT_JOINED} from "../errors/IPCErrors.sol"; +import {InvalidFederationPayload, SubnetAlreadyBootstrapped, NotEnoughFunds, CollateralIsZero, CannotReleaseZero, NotOwnerOfPublicKey, EmptyAddress, NotEnoughBalance, NotEnoughCollateral, NotValidator, NotAllValidatorsHaveLeft, InvalidPublicKeyLength, MethodNotAllowed} from "../errors/IPCErrors.sol"; import {IGateway} from "../interfaces/IGateway.sol"; -import {ISubnetActor} from "../interfaces/ISubnetActor.sol"; -import {QuorumObjKind} from "../structs/Quorum.sol"; -import {BottomUpCheckpoint, BottomUpMsgBatch, BottomUpMsgBatchInfo} from "../structs/CrossNet.sol"; -import {Validator, ValidatorSet, PermissionMode} from "../structs/Subnet.sol"; -import {Pausable} from "../lib/LibPausable.sol"; +import {Validator, ValidatorSet} from "../structs/Subnet.sol"; import {LibDiamond} from "../lib/LibDiamond.sol"; -import {MultisignatureChecker} from "../lib/LibMultisignatureChecker.sol"; import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; import {SubnetActorModifiers} from "../lib/LibSubnetActorStorage.sol"; import {LibValidatorSet, LibStaking} from "../lib/LibStaking.sol"; import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {LibSubnetActor} from "../lib/LibSubnetActor.sol"; +import {Pausable} from "../lib/LibPausable.sol"; -contract SubnetActorManagerFacet is ISubnetActor, SubnetActorModifiers, Pausable, ReentrancyGuard { +contract SubnetActorManagerFacet is SubnetActorModifiers, ReentrancyGuard, Pausable { using EnumerableSet for EnumerableSet.AddressSet; using LibValidatorSet for ValidatorSet; using Address for address payable; - event BottomUpCheckpointSubmitted(BottomUpCheckpoint checkpoint, address submitter); - event BottomUpCheckpointExecuted(uint256 epoch, address submitter); - event NextBottomUpCheckpointExecuted(uint256 epoch, address submitter); - - /// @notice Pauses all contract functions with the `whenNotPaused` modifier. - function pause() external { - LibDiamond.enforceIsContractOwner(); - _pause(); - } - - /// @notice Unpauses all contract functions with the `whenNotPaused` modifier. - function unpause() external { - LibDiamond.enforceIsContractOwner(); - _unpause(); - } - - /// @notice Submits a checkpoint commitment for execution. - /// @dev It triggers the commitment of the checkpoint and any other side-effects that - /// need to be triggered by the checkpoint such as relayer reward book keeping. - /// @param checkpoint The executed bottom-up checkpoint. - /// @param signatories The addresses of validators signing the checkpoint. - /// @param signatures The signatures of validators on the checkpoint. - function submitCheckpoint( - BottomUpCheckpoint calldata checkpoint, - address[] calldata signatories, - bytes[] calldata signatures - ) external whenNotPaused { - // the checkpoint height must be equal to the last bottom-up checkpoint height or - // the next one - if ( - checkpoint.blockHeight != s.lastBottomUpCheckpointHeight + s.bottomUpCheckPeriod && - checkpoint.blockHeight != s.lastBottomUpCheckpointHeight - ) { - revert InvalidCheckpointEpoch(); - } - bytes32 checkpointHash = keccak256(abi.encode(checkpoint)); - - if (checkpoint.blockHeight == s.lastBottomUpCheckpointHeight + s.bottomUpCheckPeriod) { - // validate signatures and quorum threshold, revert if validation fails - validateActiveQuorumSignatures({signatories: signatories, hash: checkpointHash, signatures: signatures}); - - // If the checkpoint height is the next expected height then this is a new checkpoint which must be executed - // in the Gateway Actor, the checkpoint and the relayer must be stored, last bottom-up checkpoint updated. - s.committedCheckpoints[checkpoint.blockHeight] = checkpoint; - - // slither-disable-next-line unused-return - s.relayerRewards.checkpointRewarded[checkpoint.blockHeight].add(msg.sender); - - s.lastBottomUpCheckpointHeight = checkpoint.blockHeight; - - // Commit in gateway to distribute rewards - IGateway(s.ipcGatewayAddr).commitCheckpoint(checkpoint); - - // confirming the changes in membership in the child - LibStaking.confirmChange(checkpoint.nextConfigurationNumber); - } else if (checkpoint.blockHeight == s.lastBottomUpCheckpointHeight) { - // If the checkpoint height is equal to the last checkpoint height, then this is a repeated submission. - // We should store the relayer, but not to execute checkpoint again. - // In this case, we do not verify the signatures for this checkpoint again, - // but we add the relayer to the list of all relayers for this checkpoint to be rewarded later. - // The reason for comparing hashes instead of verifying signatures is the following: - // once the checkpoint is executed, the active validator set changes - // and can only be used to validate the next checkpoint, not another instance of the last one. - bytes32 lastCheckpointHash = keccak256(abi.encode(s.committedCheckpoints[checkpoint.blockHeight])); - if (checkpointHash == lastCheckpointHash) { - // slither-disable-next-line unused-return - s.relayerRewards.checkpointRewarded[checkpoint.blockHeight].add(msg.sender); - } - } - } - - /// @notice Submits a batch of bottom-up messages for execution. - /// @dev It triggers the execution of a cross-net message batch. - /// @param batch The batch of bottom-up messages. - /// @param signatories The addresses of validators signing the batch. - /// @param signatures The signatures of validators on the batch. - function submitBottomUpMsgBatch( - BottomUpMsgBatch calldata batch, - address[] calldata signatories, - bytes[] calldata signatures - ) external { - // forbid the submission of batches from the past - if (batch.blockHeight < s.lastBottomUpBatch.blockHeight) { - revert InvalidBatchEpoch(); - } - if (batch.msgs.length > s.maxMsgsPerBottomUpBatch) { - revert MaxMsgsPerBatchExceeded(); - } - // if the batch height is not max, we only supoprt batch submission in period epochs - if (batch.msgs.length != s.maxMsgsPerBottomUpBatch && batch.blockHeight % s.bottomUpMsgBatchPeriod != 0) { - revert InvalidBatchEpoch(); - } - if (batch.msgs.length == 0) { - revert BatchWithNoMessages(); - } - - bytes32 batchHash = keccak256(abi.encode(batch)); - - if (batch.blockHeight == s.lastBottomUpBatch.blockHeight) { - // If the batch info is equal to the last batch info, then this is a repeated submission. - // We should store the relayer, but not to execute batch again following the same reward logic - // used for checkpoints. - if (batchHash == s.lastBottomUpBatch.hash) { - // slither-disable-next-line unused-return - s.relayerRewards.batchRewarded[batch.blockHeight].add(msg.sender); - } - } else { - // validate signatures and quorum threshold, revert if validation fails - validateActiveQuorumSignatures({signatories: signatories, hash: batchHash, signatures: signatures}); - - // If the checkpoint height is the next expected height then this is a new batch, - // and should be forwarded to the gateway for execution. - s.lastBottomUpBatch = BottomUpMsgBatchInfo({blockHeight: batch.blockHeight, hash: batchHash}); - - // slither-disable-next-line unused-return - s.relayerRewards.batchRewarded[batch.blockHeight].add(msg.sender); - - // Execute messages. - IGateway(s.ipcGatewayAddr).execBottomUpMsgBatch(batch); - } - } - /// @notice method to add some initial balance into a subnet that hasn't yet bootstrapped. /// @dev This balance is added to user addresses in genesis, and becomes part of the genesis /// circulating supply. @@ -216,9 +90,17 @@ contract SubnetActorManagerFacet is ISubnetActor, SubnetActorModifiers, Pausable } if (s.bootstrapped) { - LibSubnetActor.postBootstrapSetFederatedPower(validators, publicKeys, powers); + LibSubnetActor.postBootstrapSetFederatedPower({ + validators: validators, + publicKeys: publicKeys, + powers: powers + }); } else { - LibSubnetActor.preBootstrapSetFederatedPower(validators, publicKeys, powers); + LibSubnetActor.preBootstrapSetFederatedPower({ + validators: validators, + publicKeys: publicKeys, + powers: powers + }); } } @@ -371,16 +253,6 @@ contract SubnetActorManagerFacet is ISubnetActor, SubnetActorModifiers, Pausable IGateway(s.ipcGatewayAddr).kill(); } - /// @notice Validator claims their released collateral. - function claim() external nonReentrant whenNotPaused { - LibStaking.claimCollateral(msg.sender); - } - - /// @notice Relayer claims its reward. - function claimRewardForRelayer() external nonReentrant whenNotPaused { - LibStaking.claimRewardForRelayer(msg.sender); - } - /// @notice Add a bootstrap node. /// @param netAddress The network address of the new bootstrap node. function addBootstrapNode(string memory netAddress) external whenNotPaused { @@ -394,82 +266,4 @@ contract SubnetActorManagerFacet is ISubnetActor, SubnetActorModifiers, Pausable // slither-disable-next-line unused-return s.bootstrapOwners.add(msg.sender); } - - /// @notice Distributes rewards to relayers for a specific checkpoint. - /// @param height The height of the checkpoint for which rewards are being distributed. - /// @param reward The total amount of reward to be distributed. - /// @param kind The type of object for which rewards are being distributed. - function distributeRewardToRelayers( - uint256 height, - uint256 reward, - QuorumObjKind kind - ) external payable whenNotPaused onlyGateway { - if (reward == 0) { - return; - } - - // get rewarded addresses - address[] memory relayers = new address[](0); - if (kind == QuorumObjKind.Checkpoint) { - relayers = LibSubnetActor.checkpointRewardedAddrs(height); - } else if (kind == QuorumObjKind.BottomUpMsgBatch) { - // FIXME: The distribution of rewards for batches can't be done - // as for checkpoints (due to how they are submitted). As - // we are running out of time, we'll defer this for the future. - revert MethodNotAllowed("rewards not defined for batches"); - } else { - revert MethodNotAllowed("rewards not defined for object kind"); - } - - // comupte reward - // we are not distributing equally, this logic should be decoupled - // into different reward policies. - uint256 relayersLength = relayers.length; - if (relayersLength == 0) { - return; - } - if (reward < relayersLength) { - return; - } - uint256 relayerReward = reward / relayersLength; - - // distribute reward - for (uint256 i; i < relayersLength; ) { - s.relayerRewards.rewards[relayers[i]] += relayerReward; - unchecked { - ++i; - } - } - } - - /// @notice Checks whether the signatures are valid for the provided signatories and hash within the current validator set. - /// Reverts otherwise. - /// @dev Signatories in `signatories` and their signatures in `signatures` must be provided in the same order. - /// Having it public allows external users to perform sanity-check verification if needed. - /// @param signatories The addresses of the signatories. - /// @param hash The hash of the checkpoint. - /// @param signatures The packed signatures of the checkpoint. - function validateActiveQuorumSignatures( - address[] memory signatories, - bytes32 hash, - bytes[] memory signatures - ) public view { - // This call reverts if at least one of the signatories (validator) is not in the active validator set. - uint256[] memory collaterals = s.validatorSet.getTotalPowerOfValidators(signatories); - uint256 activeCollateral = s.validatorSet.getTotalActivePower(); - - uint256 threshold = (activeCollateral * s.majorityPercentage) / 100; - - (bool valid, MultisignatureChecker.Error err) = MultisignatureChecker.isValidWeightedMultiSignature({ - signatories: signatories, - weights: collaterals, - threshold: threshold, - hash: hash, - signatures: signatures - }); - - if (!valid) { - revert InvalidSignatureErr(uint8(err)); - } - } } diff --git a/contracts/src/subnet/SubnetActorPauseFacet.sol b/contracts/src/subnet/SubnetActorPauseFacet.sol new file mode 100644 index 000000000..fbfb48ec1 --- /dev/null +++ b/contracts/src/subnet/SubnetActorPauseFacet.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {LibDiamond} from "../lib/LibDiamond.sol"; +import {Pausable} from "../lib/LibPausable.sol"; + +contract SubnetActorPauseFacet is Pausable { + /// @notice Pauses all contract functions with the `whenNotPaused` modifier. + function pause() external { + LibDiamond.enforceIsContractOwner(); + _pause(); + } + + /// @notice Unpauses all contract functions with the `whenNotPaused` modifier. + function unpause() external { + LibDiamond.enforceIsContractOwner(); + _unpause(); + } + + /// @notice Returns true if the SubnetActor contract is paused. + function paused() external view returns (bool) { + return _paused(); + } +} diff --git a/contracts/src/subnet/SubnetActorRewardFacet.sol b/contracts/src/subnet/SubnetActorRewardFacet.sol new file mode 100644 index 000000000..cedfce1d4 --- /dev/null +++ b/contracts/src/subnet/SubnetActorRewardFacet.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {MethodNotAllowed} from "../errors/IPCErrors.sol"; +import {IRelayerRewardDistributor} from "../interfaces/ISubnetActor.sol"; +import {QuorumObjKind} from "../structs/Quorum.sol"; +import {Pausable} from "../lib/LibPausable.sol"; +import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; +import {SubnetActorModifiers} from "../lib/LibSubnetActorStorage.sol"; +import {LibStaking} from "../lib/LibStaking.sol"; +import {LibSubnetActor} from "../lib/LibSubnetActor.sol"; + +contract SubnetActorRewardFacet is IRelayerRewardDistributor, SubnetActorModifiers, ReentrancyGuard, Pausable { + /// @notice Validator claims their released collateral. + function claim() external nonReentrant whenNotPaused { + LibStaking.claimCollateral(msg.sender); + } + + /// @notice Relayer claims its reward. + function claimRewardForRelayer() external nonReentrant whenNotPaused { + LibStaking.claimRewardForRelayer(msg.sender); + } + + /// @notice Distributes rewards to relayers for a specific checkpoint. + /// @param height The height of the checkpoint for which rewards are being distributed. + /// @param reward The total amount of reward to be distributed. + /// @param kind The type of object for which rewards are being distributed. + function distributeRewardToRelayers( + uint256 height, + uint256 reward, + QuorumObjKind kind + ) external payable whenNotPaused onlyGateway { + if (reward == 0) { + return; + } + + // get rewarded addresses + address[] memory relayers = new address[](0); + if (kind == QuorumObjKind.Checkpoint) { + relayers = LibSubnetActor.checkpointRewardedAddrs(height); + } else if (kind == QuorumObjKind.BottomUpMsgBatch) { + // FIXME: The distribution of rewards for batches can't be done + // as for checkpoints (due to how they are submitted). As + // we are running out of time, we'll defer this for the future. + revert MethodNotAllowed("rewards not defined for batches"); + } else { + revert MethodNotAllowed("rewards not defined for object kind"); + } + + // comupte reward + // we are not distributing equally, this logic should be decoupled + // into different reward policies. + uint256 relayersLength = relayers.length; + if (relayersLength == 0) { + return; + } + if (reward < relayersLength) { + return; + } + uint256 relayerReward = reward / relayersLength; + + // distribute reward + for (uint256 i; i < relayersLength; ) { + s.relayerRewards.rewards[relayers[i]] += relayerReward; + unchecked { + ++i; + } + } + } +} diff --git a/contracts/src/subnetregistry/RegisterSubnetFacet.sol b/contracts/src/subnetregistry/RegisterSubnetFacet.sol index 112c2dc5a..e13f6faa6 100644 --- a/contracts/src/subnetregistry/RegisterSubnetFacet.sol +++ b/contracts/src/subnetregistry/RegisterSubnetFacet.sol @@ -23,20 +23,38 @@ contract RegisterSubnetFacet is ReentrancyGuard { revert WrongGateway(); } - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](2); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](5); // set the diamond cut for subnet getter diamondCut[0] = IDiamond.FacetCut({ - facetAddress: s.SUBNET_GETTER_FACET, + facetAddress: s.SUBNET_ACTOR_GETTER_FACET, action: IDiamond.FacetCutAction.Add, - functionSelectors: s.subnetGetterSelectors + functionSelectors: s.subnetActorGetterSelectors }); // set the diamond cut for subnet manager diamondCut[1] = IDiamond.FacetCut({ - facetAddress: s.SUBNET_MANAGER_FACET, + facetAddress: s.SUBNET_ACTOR_MANAGER_FACET, action: IDiamond.FacetCutAction.Add, - functionSelectors: s.subnetManagerSelectors + functionSelectors: s.subnetActorManagerSelectors + }); + + diamondCut[2] = IDiamond.FacetCut({ + facetAddress: s.SUBNET_ACTOR_REWARD_FACET, + action: IDiamond.FacetCutAction.Add, + functionSelectors: s.subnetActorRewarderSelectors + }); + + diamondCut[3] = IDiamond.FacetCut({ + facetAddress: s.SUBNET_ACTOR_CHECKPOINTING_FACET, + action: IDiamond.FacetCutAction.Add, + functionSelectors: s.subnetActorCheckpointerSelectors + }); + + diamondCut[4] = IDiamond.FacetCut({ + facetAddress: s.SUBNET_ACTOR_PAUSE_FACET, + action: IDiamond.FacetCutAction.Add, + functionSelectors: s.subnetActorPauserSelectors }); // slither-disable-next-line reentrancy-benign diff --git a/contracts/src/subnetregistry/SubnetGetterFacet.sol b/contracts/src/subnetregistry/SubnetGetterFacet.sol index fc2caa235..d2150bdf8 100644 --- a/contracts/src/subnetregistry/SubnetGetterFacet.sol +++ b/contracts/src/subnetregistry/SubnetGetterFacet.sol @@ -51,22 +51,52 @@ contract SubnetGetterFacet { /// @notice Returns the address of the SUBNET_GETTER_FACET. function getSubnetActorGetterFacet() external view returns (address) { - return s.SUBNET_GETTER_FACET; + return s.SUBNET_ACTOR_GETTER_FACET; } /// @notice Returns the address of the SUBNET_MANAGER_FACET. function getSubnetActorManagerFacet() external view returns (address) { - return s.SUBNET_MANAGER_FACET; + return s.SUBNET_ACTOR_MANAGER_FACET; } - /// @notice Returns the subnet getter selectors. + /// @notice Returns the address of the SUBNET_ACTOR_REWARDER_FACET. + function getSubnetActorRewarderFacet() external view returns (address) { + return s.SUBNET_ACTOR_REWARD_FACET; + } + + /// @notice Returns the address of the SUBNET_ACTOR_CHECKPOINTER_FACET. + function getSubnetActorCheckpointerFacet() external view returns (address) { + return s.SUBNET_ACTOR_CHECKPOINTING_FACET; + } + + /// @notice Returns the address of the SUBNET_ACTOR_PAUSER_FACET. + function getSubnetActorPauserFacet() external view returns (address) { + return s.SUBNET_ACTOR_PAUSE_FACET; + } + + /// @notice Returns the subnet actor getter selectors. function getSubnetActorGetterSelectors() external view returns (bytes4[] memory) { - return s.subnetGetterSelectors; + return s.subnetActorGetterSelectors; } - /// @notice Returns the subnet manager selectors. + /// @notice Returns the subnet actor manager selectors. function getSubnetActorManagerSelectors() external view returns (bytes4[] memory) { - return s.subnetManagerSelectors; + return s.subnetActorManagerSelectors; + } + + /// @notice Returns the subnet actor rewarder selectors. + function getSubnetActorRewarderSelectors() external view returns (bytes4[] memory) { + return s.subnetActorRewarderSelectors; + } + + /// @notice Returns the subnet actor checkpointer selectors. + function getSubnetActorCheckpointerSelectors() external view returns (bytes4[] memory) { + return s.subnetActorCheckpointerSelectors; + } + + /// @notice Returns the subnet actor pauser selectors. + function getSubnetActorPauserSelectors() external view returns (bytes4[] memory) { + return s.subnetActorPauserSelectors; } /// @notice Updates references to the subnet contract components, including facets and selector sets. @@ -92,9 +122,10 @@ contract SubnetGetterFacet { } // Update the storage variables - s.SUBNET_GETTER_FACET = newGetterFacet; - s.SUBNET_MANAGER_FACET = newManagerFacet; - s.subnetGetterSelectors = newSubnetGetterSelectors; - s.subnetManagerSelectors = newSubnetManagerSelectors; + s.SUBNET_ACTOR_GETTER_FACET = newGetterFacet; + s.SUBNET_ACTOR_MANAGER_FACET = newManagerFacet; + + s.subnetActorGetterSelectors = newSubnetGetterSelectors; + s.subnetActorManagerSelectors = newSubnetManagerSelectors; } } diff --git a/contracts/test/IntegrationTestBase.sol b/contracts/test/IntegrationTestBase.sol index f2c5b376d..32686321e 100644 --- a/contracts/test/IntegrationTestBase.sol +++ b/contracts/test/IntegrationTestBase.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.19; import "forge-std/Test.sol"; -import "forge-std/StdInvariant.sol"; import "../src/errors/IPCErrors.sol"; import {EMPTY_BYTES, METHOD_SEND} from "../src/constants/Constants.sol"; @@ -27,12 +26,17 @@ import {XnetMessagingFacet} from "../src/gateway/router/XnetMessagingFacet.sol"; import {TopDownFinalityFacet} from "../src/gateway/router/TopDownFinalityFacet.sol"; import {BottomUpRouterFacet} from "../src/gateway/router/BottomUpRouterFacet.sol"; -import {SubnetActorManagerFacetMock} from "./mocks/SubnetActorManagerFacetMock.sol"; +import {SubnetActorMock} from "./mocks/SubnetActorMock.sol"; import {SubnetActorManagerFacet} from "../src/subnet/SubnetActorManagerFacet.sol"; +import {SubnetActorPauseFacet} from "../src/subnet/SubnetActorPauseFacet.sol"; +import {SubnetActorCheckpointingFacet} from "../src/subnet/SubnetActorCheckpointingFacet.sol"; +import {SubnetActorRewardFacet} from "../src/subnet/SubnetActorRewardFacet.sol"; import {SubnetActorGetterFacet} from "../src/subnet/SubnetActorGetterFacet.sol"; + import {SubnetRegistryDiamond} from "../src/SubnetRegistryDiamond.sol"; import {RegisterSubnetFacet} from "../src/subnetregistry/RegisterSubnetFacet.sol"; import {SubnetGetterFacet} from "../src/subnetregistry/SubnetGetterFacet.sol"; + import {DiamondLoupeFacet} from "../src/diamond/DiamondLoupeFacet.sol"; import {DiamondCutFacet} from "../src/diamond/DiamondCutFacet.sol"; import {SupplySourceHelper} from "../src/lib/SupplySourceHelper.sol"; @@ -119,39 +123,36 @@ contract TestGatewayActor is Test, TestParams { gwCutterSelectors = SelectorLibrary.resolveSelectors("DiamondCutFacet"); gwLoupeSelectors = SelectorLibrary.resolveSelectors("DiamondLoupeFacet"); } - - function defaultGatewayParams() internal pure virtual returns (GatewayDiamond.ConstructorParams memory) { - GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: DEFAULT_CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, - genesisValidators: new Validator[](0), - activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT - }); - - return params; - } } contract TestSubnetActor is Test, TestParams { bytes4[] saGetterSelectors; bytes4[] saManagerSelectors; + bytes4[] saPauserSelectors; + bytes4[] saRewarderSelectors; + bytes4[] saCheckpointerSelectors; bytes4[] saManagerMockedSelectors; bytes4[] saCutterSelectors; bytes4[] saLouperSelectors; SubnetActorDiamond saDiamond; SubnetActorManagerFacet saManager; - SubnetActorManagerFacetMock saMockedManager; + SubnetActorMock saMock; SubnetActorGetterFacet saGetter; + SubnetActorRewardFacet saRewarder; + SubnetActorPauseFacet saPauser; + SubnetActorCheckpointingFacet saCheckpointer; + DiamondCutFacet saCutter; DiamondLoupeFacet saLouper; constructor() { saGetterSelectors = SelectorLibrary.resolveSelectors("SubnetActorGetterFacet"); saManagerSelectors = SelectorLibrary.resolveSelectors("SubnetActorManagerFacet"); - saManagerMockedSelectors = SelectorLibrary.resolveSelectors("SubnetActorManagerFacetMock"); + saPauserSelectors = SelectorLibrary.resolveSelectors("SubnetActorPauseFacet"); + saRewarderSelectors = SelectorLibrary.resolveSelectors("SubnetActorRewardFacet"); + saCheckpointerSelectors = SelectorLibrary.resolveSelectors("SubnetActorCheckpointingFacet"); + saManagerMockedSelectors = SelectorLibrary.resolveSelectors("SubnetActorMock"); saCutterSelectors = SelectorLibrary.resolveSelectors("DiamondCutFacet"); saLouperSelectors = SelectorLibrary.resolveSelectors("DiamondLoupeFacet"); } @@ -216,12 +217,28 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, saDiamond = createSubnetActor(saConstructorParams); saManager = SubnetActorManagerFacet(address(saDiamond)); saGetter = SubnetActorGetterFacet(address(saDiamond)); + saPauser = SubnetActorPauseFacet(address(saDiamond)); + saRewarder = SubnetActorRewardFacet(address(saDiamond)); + saCheckpointer = SubnetActorCheckpointingFacet(address(saDiamond)); saLouper = DiamondLoupeFacet(address(saDiamond)); saCutter = DiamondCutFacet(address(saDiamond)); addValidator(TOPDOWN_VALIDATOR_1, 100); } + function defaultGatewayParams() internal pure virtual returns (GatewayDiamond.ConstructorParams memory) { + GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ + networkName: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), + bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, + msgFee: DEFAULT_CROSS_MSG_FEE, + majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, + genesisValidators: new Validator[](0), + activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT + }); + + return params; + } + function createGatewayDiamond(GatewayDiamond.ConstructorParams memory params) public returns (GatewayDiamond) { CheckpointingFacet checkpointingFacet = new CheckpointingFacet(); XnetMessagingFacet xnetMessagingFacet = new XnetMessagingFacet(); @@ -315,14 +332,17 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, function createSubnetActorDiamondWithFaucets( SubnetActorDiamond.ConstructorParams memory params, - address getterFaucet, - address managerFaucet + address getter, + address manager, + address pauser, + address rewarder, + address checkpointer ) public returns (SubnetActorDiamond) { - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](2); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](5); diamondCut[0] = ( IDiamond.FacetCut({ - facetAddress: getterFaucet, + facetAddress: getter, action: IDiamond.FacetCutAction.Add, functionSelectors: saGetterSelectors }) @@ -330,12 +350,36 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, diamondCut[1] = ( IDiamond.FacetCut({ - facetAddress: managerFaucet, + facetAddress: manager, action: IDiamond.FacetCutAction.Add, functionSelectors: saManagerSelectors }) ); + diamondCut[2] = ( + IDiamond.FacetCut({ + facetAddress: pauser, + action: IDiamond.FacetCutAction.Add, + functionSelectors: saPauserSelectors + }) + ); + + diamondCut[3] = ( + IDiamond.FacetCut({ + facetAddress: rewarder, + action: IDiamond.FacetCutAction.Add, + functionSelectors: saRewarderSelectors + }) + ); + + diamondCut[4] = ( + IDiamond.FacetCut({ + facetAddress: checkpointer, + action: IDiamond.FacetCutAction.Add, + functionSelectors: saCheckpointerSelectors + }) + ); + saDiamond = new SubnetActorDiamond(diamondCut, params); return saDiamond; } @@ -343,10 +387,14 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, function createSubnetActor(SubnetActorDiamond.ConstructorParams memory params) public returns (SubnetActorDiamond) { SubnetActorManagerFacet manager = new SubnetActorManagerFacet(); SubnetActorGetterFacet getter = new SubnetActorGetterFacet(); + SubnetActorPauseFacet pauser = new SubnetActorPauseFacet(); + SubnetActorRewardFacet rewarder = new SubnetActorRewardFacet(); + SubnetActorCheckpointingFacet checkpointer = new SubnetActorCheckpointingFacet(); + DiamondLoupeFacet louper = new DiamondLoupeFacet(); DiamondCutFacet cutter = new DiamondCutFacet(); - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](4); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](7); diamondCut[0] = ( IDiamond.FacetCut({ @@ -366,17 +414,41 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, diamondCut[2] = ( IDiamond.FacetCut({ - facetAddress: address(cutter), + facetAddress: address(pauser), action: IDiamond.FacetCutAction.Add, - functionSelectors: gwCutterSelectors + functionSelectors: saPauserSelectors }) ); diamondCut[3] = ( + IDiamond.FacetCut({ + facetAddress: address(rewarder), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saRewarderSelectors + }) + ); + + diamondCut[4] = ( + IDiamond.FacetCut({ + facetAddress: address(checkpointer), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saCheckpointerSelectors + }) + ); + + diamondCut[5] = ( + IDiamond.FacetCut({ + facetAddress: address(cutter), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saCutterSelectors + }) + ); + + diamondCut[6] = ( IDiamond.FacetCut({ facetAddress: address(louper), action: IDiamond.FacetCutAction.Add, - functionSelectors: gwLoupeSelectors + functionSelectors: saLouperSelectors }) ); @@ -419,10 +491,13 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, saManager = new SubnetActorManagerFacet(); saGetter = new SubnetActorGetterFacet(); + saPauser = new SubnetActorPauseFacet(); + saCheckpointer = new SubnetActorCheckpointingFacet(); + saRewarder = new SubnetActorRewardFacet(); saCutter = new DiamondCutFacet(); saLouper = new DiamondLoupeFacet(); - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](4); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](7); diamondCut[0] = ( IDiamond.FacetCut({ @@ -441,6 +516,30 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, ); diamondCut[2] = ( + IDiamond.FacetCut({ + facetAddress: address(saPauser), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saPauserSelectors + }) + ); + + diamondCut[3] = ( + IDiamond.FacetCut({ + facetAddress: address(saRewarder), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saRewarderSelectors + }) + ); + + diamondCut[4] = ( + IDiamond.FacetCut({ + facetAddress: address(saCheckpointer), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saCheckpointerSelectors + }) + ); + + diamondCut[5] = ( IDiamond.FacetCut({ facetAddress: address(saCutter), action: IDiamond.FacetCutAction.Add, @@ -448,7 +547,7 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, }) ); - diamondCut[3] = ( + diamondCut[6] = ( IDiamond.FacetCut({ facetAddress: address(saLouper), action: IDiamond.FacetCutAction.Add, @@ -475,13 +574,16 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, ); saManager = SubnetActorManagerFacet(address(saDiamond)); + saPauser = SubnetActorPauseFacet(address(saDiamond)); + saRewarder = SubnetActorRewardFacet(address(saDiamond)); + saCheckpointer = SubnetActorCheckpointingFacet(address(saDiamond)); saGetter = SubnetActorGetterFacet(address(saDiamond)); saCutter = DiamondCutFacet(address(saDiamond)); saLouper = DiamondLoupeFacet(address(saDiamond)); } function createMockedSubnetActorWithGateway(address gw) public returns (SubnetActorDiamond) { - SubnetActorManagerFacetMock mockedManager = new SubnetActorManagerFacetMock(); + SubnetActorMock mockedManager = new SubnetActorMock(); SubnetActorGetterFacet getter = new SubnetActorGetterFacet(); IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](2); @@ -749,7 +851,7 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, for (uint256 i = 0; i < n; i++) { vm.prank(validators[i]); - saManager.submitCheckpoint(checkpoint, validators, signatures); + saCheckpointer.submitCheckpoint(checkpoint, validators, signatures); } } diff --git a/contracts/test/IntegrationTestPresets.sol b/contracts/test/IntegrationTestPresets.sol new file mode 100644 index 000000000..3f267738f --- /dev/null +++ b/contracts/test/IntegrationTestPresets.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {SubnetID, Subnet, IPCAddress, Validator} from "../src/structs/Subnet.sol"; +import {DiamondCutFacet} from "../src/diamond/DiamondCutFacet.sol"; +import {DiamondLoupeFacet} from "../src/diamond/DiamondLoupeFacet.sol"; +import {GatewayDiamond} from "../src/GatewayDiamond.sol"; +import {GatewayGetterFacet} from "../src/gateway/GatewayGetterFacet.sol"; +import {GatewayManagerFacet} from "../src/gateway/GatewayManagerFacet.sol"; +import {GatewayMessengerFacet} from "../src/gateway/GatewayMessengerFacet.sol"; +import {XnetMessagingFacet} from "../src/gateway/router/XnetMessagingFacet.sol"; +import {IntegrationTestBase} from "./IntegrationTestBase.sol"; + +contract L1GatewayActorDiamond is IntegrationTestBase { + function setUp() public virtual override { + GatewayDiamond.ConstructorParams memory gwConstructorParams = defaultGatewayParams(); + gatewayDiamond = createGatewayDiamond(gwConstructorParams); + + gwGetter = GatewayGetterFacet(address(gatewayDiamond)); + gwManager = GatewayManagerFacet(address(gatewayDiamond)); + gwXnetMessagingFacet = XnetMessagingFacet(address(gatewayDiamond)); + gwMessenger = GatewayMessengerFacet(address(gatewayDiamond)); + gwLouper = DiamondLoupeFacet(address(gatewayDiamond)); + gwCutter = DiamondCutFacet(address(gatewayDiamond)); + } + + function defaultGatewayParams() internal pure override returns (GatewayDiamond.ConstructorParams memory) { + address[] memory path = new address[](1); + path[0] = CHILD_NETWORK_ADDRESS; + + GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ + networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), + bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, + msgFee: DEFAULT_CROSS_MSG_FEE, + majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, + genesisValidators: new Validator[](0), + activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT + }); + + return params; + } +} + +contract L2GatewayActorDiamond is IntegrationTestBase { + function setUp() public virtual override { + GatewayDiamond.ConstructorParams memory gwConstructorParams = defaultGatewayParams(); + gatewayDiamond = createGatewayDiamond(gwConstructorParams); + + gwGetter = GatewayGetterFacet(address(gatewayDiamond)); + gwManager = GatewayManagerFacet(address(gatewayDiamond)); + gwXnetMessagingFacet = XnetMessagingFacet(address(gatewayDiamond)); + gwMessenger = GatewayMessengerFacet(address(gatewayDiamond)); + gwLouper = DiamondLoupeFacet(address(gatewayDiamond)); + gwCutter = DiamondCutFacet(address(gatewayDiamond)); + } + + function defaultGatewayParams() internal pure override returns (GatewayDiamond.ConstructorParams memory) { + address[] memory path = new address[](2); + path[0] = CHILD_NETWORK_ADDRESS; + path[1] = CHILD_NETWORK_ADDRESS_2; + + GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ + networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), + bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, + msgFee: DEFAULT_CROSS_MSG_FEE, + majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, + genesisValidators: new Validator[](0), + activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT + }); + + return params; + } +} + +contract L3GatewayActorDiamond is IntegrationTestBase { + address constant CHILD_NETWORK_ADDRESS_3 = address(31); + + function setUp() public virtual override { + GatewayDiamond.ConstructorParams memory gwConstructorParams = defaultGatewayParams(); + gatewayDiamond = createGatewayDiamond(gwConstructorParams); + + gwGetter = GatewayGetterFacet(address(gatewayDiamond)); + gwManager = GatewayManagerFacet(address(gatewayDiamond)); + gwXnetMessagingFacet = XnetMessagingFacet(address(gatewayDiamond)); + gwMessenger = GatewayMessengerFacet(address(gatewayDiamond)); + gwLouper = DiamondLoupeFacet(address(gatewayDiamond)); + gwCutter = DiamondCutFacet(address(gatewayDiamond)); + } + + function defaultGatewayParams() internal pure override returns (GatewayDiamond.ConstructorParams memory) { + address[] memory path = new address[](3); + path[0] = CHILD_NETWORK_ADDRESS; + path[1] = CHILD_NETWORK_ADDRESS_2; + path[1] = CHILD_NETWORK_ADDRESS_2; + + GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ + networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), + bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, + msgFee: DEFAULT_CROSS_MSG_FEE, + majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, + genesisValidators: new Validator[](0), + activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT + }); + + return params; + } +} diff --git a/contracts/test/helpers/SelectorLibrary.sol b/contracts/test/helpers/SelectorLibrary.sol index a17aa04f8..84a8b0e0d 100644 --- a/contracts/test/helpers/SelectorLibrary.sol +++ b/contracts/test/helpers/SelectorLibrary.sol @@ -41,7 +41,7 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("GatewayGetterFacet"))) { return abi.decode( - hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000268789f83b0000000000000000000000000000000000000000000000000000000006c46853000000000000000000000000000000000000000000000000000000002da5794a00000000000000000000000000000000000000000000000000000000dd81b5cf0000000000000000000000000000000000000000000000000000000069e737fd0000000000000000000000000000000000000000000000000000000041b6a2e80000000000000000000000000000000000000000000000000000000024729425000000000000000000000000000000000000000000000000000000009e530b57000000000000000000000000000000000000000000000000000000006547cd6400000000000000000000000000000000000000000000000000000000b9ee584200000000000000000000000000000000000000000000000000000000a9294bdd000000000000000000000000000000000000000000000000000000002218059400000000000000000000000000000000000000000000000000000000b3ab3f7400000000000000000000000000000000000000000000000000000000ac12d763000000000000000000000000000000000000000000000000000000004aa8f8a500000000000000000000000000000000000000000000000000000000ca41d5ce00000000000000000000000000000000000000000000000000000000d6c5c39700000000000000000000000000000000000000000000000000000000544dddff000000000000000000000000000000000000000000000000000000006ad21bb000000000000000000000000000000000000000000000000000000000a517218f000000000000000000000000000000000000000000000000000000009704276600000000000000000000000000000000000000000000000000000000767ee5f400000000000000000000000000000000000000000000000000000000335eb62a00000000000000000000000000000000000000000000000000000000b1ba49b000000000000000000000000000000000000000000000000000000000f3229131000000000000000000000000000000000000000000000000000000000338150f0000000000000000000000000000000000000000000000000000000094074b03000000000000000000000000000000000000000000000000000000007edeac920000000000000000000000000000000000000000000000000000000006572c1a00000000000000000000000000000000000000000000000000000000c66c66a1000000000000000000000000000000000000000000000000000000009d3070b5000000000000000000000000000000000000000000000000000000005d02968500000000000000000000000000000000000000000000000000000000599c7bd10000000000000000000000000000000000000000000000000000000005aff0b300000000000000000000000000000000000000000000000000000000375b3c0a000000000000000000000000000000000000000000000000000000008cfd78e70000000000000000000000000000000000000000000000000000000002e30f9a00000000000000000000000000000000000000000000000000000000a2b6715800000000000000000000000000000000000000000000000000000000", + hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000258789f83b0000000000000000000000000000000000000000000000000000000006c46853000000000000000000000000000000000000000000000000000000002da5794a00000000000000000000000000000000000000000000000000000000dd81b5cf0000000000000000000000000000000000000000000000000000000069e737fd0000000000000000000000000000000000000000000000000000000041b6a2e80000000000000000000000000000000000000000000000000000000024729425000000000000000000000000000000000000000000000000000000009e530b57000000000000000000000000000000000000000000000000000000006547cd6400000000000000000000000000000000000000000000000000000000b9ee584200000000000000000000000000000000000000000000000000000000a9294bdd000000000000000000000000000000000000000000000000000000002218059400000000000000000000000000000000000000000000000000000000b3ab3f7400000000000000000000000000000000000000000000000000000000ac12d763000000000000000000000000000000000000000000000000000000004aa8f8a500000000000000000000000000000000000000000000000000000000ca41d5ce00000000000000000000000000000000000000000000000000000000d6c5c39700000000000000000000000000000000000000000000000000000000544dddff000000000000000000000000000000000000000000000000000000006ad21bb000000000000000000000000000000000000000000000000000000000a517218f000000000000000000000000000000000000000000000000000000009704276600000000000000000000000000000000000000000000000000000000767ee5f400000000000000000000000000000000000000000000000000000000335eb62a00000000000000000000000000000000000000000000000000000000b1ba49b000000000000000000000000000000000000000000000000000000000f3229131000000000000000000000000000000000000000000000000000000000338150f0000000000000000000000000000000000000000000000000000000094074b03000000000000000000000000000000000000000000000000000000007edeac920000000000000000000000000000000000000000000000000000000006572c1a00000000000000000000000000000000000000000000000000000000c66c66a1000000000000000000000000000000000000000000000000000000009d3070b5000000000000000000000000000000000000000000000000000000005d02968500000000000000000000000000000000000000000000000000000000599c7bd10000000000000000000000000000000000000000000000000000000005aff0b3000000000000000000000000000000000000000000000000000000008cfd78e70000000000000000000000000000000000000000000000000000000002e30f9a00000000000000000000000000000000000000000000000000000000a2b6715800000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } @@ -97,7 +97,28 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorManagerFacet"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001210fd4261000000000000000000000000000000000000000000000000000000004e71d92d00000000000000000000000000000000000000000000000000000000ed7c4da1000000000000000000000000000000000000000000000000000000004c860af6000000000000000000000000000000000000000000000000000000006170b1620000000000000000000000000000000000000000000000000000000041c0e1b500000000000000000000000000000000000000000000000000000000d66d9e19000000000000000000000000000000000000000000000000000000008456cb59000000000000000000000000000000000000000000000000000000005c975abb000000000000000000000000000000000000000000000000000000000b7fbe600000000000000000000000000000000000000000000000000000000066783c9b00000000000000000000000000000000000000000000000000000000da5d09ee000000000000000000000000000000000000000000000000000000003a4b66f1000000000000000000000000000000000000000000000000000000002681193600000000000000000000000000000000000000000000000000000000b9ee2bb9000000000000000000000000000000000000000000000000000000003f4ba83a000000000000000000000000000000000000000000000000000000002e17de7800000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", + hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000910fd4261000000000000000000000000000000000000000000000000000000006170b1620000000000000000000000000000000000000000000000000000000041c0e1b500000000000000000000000000000000000000000000000000000000d66d9e19000000000000000000000000000000000000000000000000000000000b7fbe600000000000000000000000000000000000000000000000000000000066783c9b00000000000000000000000000000000000000000000000000000000da5d09ee000000000000000000000000000000000000000000000000000000003a4b66f1000000000000000000000000000000000000000000000000000000002e17de7800000000000000000000000000000000000000000000000000000000", + (bytes4[]) + ); + } + if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorPauseFacet"))) { + return + abi.decode( + hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000038456cb59000000000000000000000000000000000000000000000000000000005c975abb000000000000000000000000000000000000000000000000000000003f4ba83a00000000000000000000000000000000000000000000000000000000", + (bytes4[]) + ); + } + if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorRewardFacet"))) { + return + abi.decode( + hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034e71d92d00000000000000000000000000000000000000000000000000000000ed7c4da1000000000000000000000000000000000000000000000000000000004c860af600000000000000000000000000000000000000000000000000000000", + (bytes4[]) + ); + } + if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorCheckpointingFacet"))) { + return + abi.decode( + hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000032681193600000000000000000000000000000000000000000000000000000000b9ee2bb900000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } @@ -150,7 +171,7 @@ library SelectorLibrary { (bytes4[]) ); } - if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorManagerFacetMock"))) { + if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorMock"))) { return abi.decode( hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001410fd4261000000000000000000000000000000000000000000000000000000004e71d92d00000000000000000000000000000000000000000000000000000000ed7c4da100000000000000000000000000000000000000000000000000000000350a14bf00000000000000000000000000000000000000000000000000000000c7ebdaef000000000000000000000000000000000000000000000000000000004c860af6000000000000000000000000000000000000000000000000000000006170b1620000000000000000000000000000000000000000000000000000000041c0e1b500000000000000000000000000000000000000000000000000000000d66d9e19000000000000000000000000000000000000000000000000000000008456cb59000000000000000000000000000000000000000000000000000000005c975abb000000000000000000000000000000000000000000000000000000000b7fbe600000000000000000000000000000000000000000000000000000000066783c9b00000000000000000000000000000000000000000000000000000000da5d09ee000000000000000000000000000000000000000000000000000000003a4b66f1000000000000000000000000000000000000000000000000000000002681193600000000000000000000000000000000000000000000000000000000b9ee2bb9000000000000000000000000000000000000000000000000000000003f4ba83a000000000000000000000000000000000000000000000000000000002e17de7800000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", diff --git a/contracts/test/integration/GatewayDiamondToken.t.sol b/contracts/test/integration/GatewayDiamondToken.t.sol index 067a2ce5e..2e82ae9de 100644 --- a/contracts/test/integration/GatewayDiamondToken.t.sol +++ b/contracts/test/integration/GatewayDiamondToken.t.sol @@ -4,33 +4,20 @@ pragma solidity 0.8.19; import "forge-std/Test.sol"; import "../../src/errors/IPCErrors.sol"; -import {NumberContractFacetSeven} from "../helpers/NumberContractFacetSeven.sol"; -import {NumberContractFacetEight} from "../helpers/NumberContractFacetEight.sol"; import {EMPTY_BYTES, METHOD_SEND, EMPTY_HASH} from "../../src/constants/Constants.sol"; -import {IERC165} from "../../src/interfaces/IERC165.sol"; -import {IDiamond} from "../../src/interfaces/IDiamond.sol"; -import {IDiamondLoupe} from "../../src/interfaces/IDiamondLoupe.sol"; -import {IDiamondCut} from "../../src/interfaces/IDiamondCut.sol"; -import {CrossMsg, BottomUpMsgBatch, BottomUpCheckpoint, StorableMsg, ParentFinality} from "../../src/structs/CrossNet.sol"; +import {CrossMsg, BottomUpMsgBatch, StorableMsg} from "../../src/structs/CrossNet.sol"; import {FvmAddress} from "../../src/structs/FvmAddress.sol"; -import {SubnetID, Subnet, SupplySource, SupplyKind, IPCAddress, Membership, Validator, StakingChange, StakingChangeRequest, StakingOperation} from "../../src/structs/Subnet.sol"; +import {SubnetID, Subnet, SupplySource, SupplyKind, Validator} from "../../src/structs/Subnet.sol"; import {SubnetIDHelper} from "../../src/lib/SubnetIDHelper.sol"; import {FvmAddressHelper} from "../../src/lib/FvmAddressHelper.sol"; import {CrossMsgHelper} from "../../src/lib/CrossMsgHelper.sol"; import {SupplySourceHelper} from "../../src/lib/SupplySourceHelper.sol"; import {StorableMsgHelper} from "../../src/lib/StorableMsgHelper.sol"; import {FilAddress} from "fevmate/utils/FilAddress.sol"; -import {GatewayDiamond, FunctionNotFound} from "../../src/GatewayDiamond.sol"; -import {SubnetActorDiamond} from "../../src/SubnetActorDiamond.sol"; -import {GatewayGetterFacet} from "../../src/gateway/GatewayGetterFacet.sol"; -import {GatewayManagerFacet} from "../../src/gateway/GatewayManagerFacet.sol"; -import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; -import {LibDiamond} from "../../src/lib/LibDiamond.sol"; +import {GatewayDiamond} from "../../src/GatewayDiamond.sol"; import {LibGateway} from "../../src/lib/LibGateway.sol"; -import {MerkleTreeHelper} from "../helpers/MerkleTreeHelper.sol"; import {TestUtils} from "../helpers/TestUtils.sol"; import {IntegrationTestBase} from "../IntegrationTestBase.sol"; - import {SubnetActorDiamond} from "../../src/SubnetActorDiamond.sol"; import {GatewayGetterFacet} from "../../src/gateway/GatewayGetterFacet.sol"; import {GatewayMessengerFacet} from "../../src/gateway/GatewayMessengerFacet.sol"; @@ -39,7 +26,6 @@ import {SubnetActorManagerFacet} from "../../src/subnet/SubnetActorManagerFacet. import {SubnetActorGetterFacet} from "../../src/subnet/SubnetActorGetterFacet.sol"; import {DiamondLoupeFacet} from "../../src/diamond/DiamondLoupeFacet.sol"; import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; -import {LibDiamond} from "../../src/lib/LibDiamond.sol"; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; import {ERC20PresetFixedSupply} from "../helpers/ERC20PresetFixedSupply.sol"; diff --git a/contracts/test/integration/GatewayL2Diamond.t.sol b/contracts/test/integration/L2GatewayDiamond.t.sol similarity index 78% rename from contracts/test/integration/GatewayL2Diamond.t.sol rename to contracts/test/integration/L2GatewayDiamond.t.sol index 3d09403f0..59e19fa3d 100644 --- a/contracts/test/integration/GatewayL2Diamond.t.sol +++ b/contracts/test/integration/L2GatewayDiamond.t.sol @@ -19,45 +19,13 @@ import {GatewayMessengerFacet} from "../../src/gateway/GatewayMessengerFacet.sol import {DiamondLoupeFacet} from "../../src/diamond/DiamondLoupeFacet.sol"; import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; import {IntegrationTestBase} from "../IntegrationTestBase.sol"; +import {L2GatewayActorDiamond} from "../IntegrationTestPresets.sol"; import {FilAddress} from "fevmate/utils/FilAddress.sol"; -contract GatewayL2ActorDiamondTest is Test, IntegrationTestBase { +contract L2GatewayActorDiamondTest is Test, L2GatewayActorDiamond { using SubnetIDHelper for SubnetID; using CrossMsgHelper for CrossMsg; - function setUp() public override { - address[] memory path2 = new address[](2); - path2[0] = CHILD_NETWORK_ADDRESS; - path2[1] = CHILD_NETWORK_ADDRESS_2; - - GatewayDiamond.ConstructorParams memory gwConstructorParams = defaultGatewayParams(); - gatewayDiamond = createGatewayDiamond(gwConstructorParams); - - gwGetter = GatewayGetterFacet(address(gatewayDiamond)); - gwManager = GatewayManagerFacet(address(gatewayDiamond)); - gwXnetMessagingFacet = XnetMessagingFacet(address(gatewayDiamond)); - gwMessenger = GatewayMessengerFacet(address(gatewayDiamond)); - gwLouper = DiamondLoupeFacet(address(gatewayDiamond)); - gwCutter = DiamondCutFacet(address(gatewayDiamond)); - } - - function defaultGatewayParams() internal pure override returns (GatewayDiamond.ConstructorParams memory) { - address[] memory path2 = new address[](2); - path2[0] = CHILD_NETWORK_ADDRESS; - path2[1] = CHILD_NETWORK_ADDRESS_2; - - GatewayDiamond.ConstructorParams memory params = GatewayDiamond.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path2}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: DEFAULT_CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, - genesisValidators: new Validator[](0), - activeValidatorsLimit: DEFAULT_ACTIVE_VALIDATORS_LIMIT - }); - - return params; - } - function testGatewayDiamond_CommitParentFinality_BigNumberOfMessages() public { uint256 n = 2000; FvmAddress[] memory validators = new FvmAddress[](1); diff --git a/contracts/test/integration/SubnetActorDiamond.t.sol b/contracts/test/integration/SubnetActorDiamond.t.sol index 9bcca7cef..0e1acb37f 100644 --- a/contracts/test/integration/SubnetActorDiamond.t.sol +++ b/contracts/test/integration/SubnetActorDiamond.t.sol @@ -26,6 +26,9 @@ import {SubnetActorDiamond, FunctionNotFound} from "../../src/SubnetActorDiamond import {FEATURE_CHECKPOINT_RELAYER_REWARDS} from "../../src/GatewayDiamond.sol"; import {SubnetActorManagerFacet} from "../../src/subnet/SubnetActorManagerFacet.sol"; import {SubnetActorGetterFacet} from "../../src/subnet/SubnetActorGetterFacet.sol"; +import {SubnetActorPauseFacet} from "../../src/subnet/SubnetActorPauseFacet.sol"; +import {SubnetActorCheckpointingFacet} from "../../src/subnet/SubnetActorCheckpointingFacet.sol"; +import {SubnetActorRewardFacet} from "../../src/subnet/SubnetActorRewardFacet.sol"; import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; import {FilAddress} from "fevmate/utils/FilAddress.sol"; import {LibStaking} from "../../src/lib/LibStaking.sol"; @@ -68,7 +71,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { } function testSubnetActorDiamondReal_LoupeFunction() public view { - require(saLouper.facets().length == 4, "unexpected length"); + require(saLouper.facets().length == 7, "unexpected length"); require(saLouper.supportsInterface(type(IERC165).interfaceId) == true, "IERC165 not supported"); require(saLouper.supportsInterface(type(IDiamondCut).interfaceId) == true, "IDiamondCut not supported"); require(saLouper.supportsInterface(type(IDiamondLoupe).interfaceId) == true, "IDiamondLoupe not supported"); @@ -263,7 +266,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { // ======== Step. Claim collateral ====== uint256 b1 = validator1.balance; vm.prank(validator1); - saManager.claim(); + saRewarder.claim(); uint256 b2 = validator1.balance; require(b2 - b1 == validator1Stake + stake, "collateral not received"); } @@ -298,8 +301,11 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { function testSubnetActorDiamond_Deployments_Fail_GatewayCannotBeZero() public { SubnetActorManagerFacet saDupMangerFaucet = new SubnetActorManagerFacet(); - SubnetActorGetterFacet saDupGetterFaucet = new SubnetActorGetterFacet(); + SubnetActorPauseFacet saDupPauserFaucet = new SubnetActorPauseFacet(); + SubnetActorRewardFacet saDupRewardFaucet = new SubnetActorRewardFacet(); + SubnetActorCheckpointingFacet saDupCheckpointerFaucet = new SubnetActorCheckpointingFacet(); + SupplySource memory native = SupplySourceHelper.native(); vm.expectRevert(GatewayCannotBeZero.selector); @@ -319,7 +325,10 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { supplySource: native }), address(saDupGetterFaucet), - address(saDupMangerFaucet) + address(saDupMangerFaucet), + address(saDupPauserFaucet), + address(saDupRewardFaucet), + address(saDupCheckpointerFaucet) ); } @@ -492,7 +501,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { saManager.join{value: 10}(pubKeys[i]); } - saManager.validateActiveQuorumSignatures(validators, hash, signatures); + saCheckpointer.validateActiveQuorumSignatures(validators, hash, signatures); } function testSubnetActorDiamond_validateActiveQuorumSignatures_InvalidWeightSum() public { @@ -522,7 +531,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { MultisignatureChecker.Error.WeightsSumLessThanThreshold ) ); - saManager.validateActiveQuorumSignatures(subValidators, hash, signatures); + saCheckpointer.validateActiveQuorumSignatures(subValidators, hash, signatures); } function testSubnetActorDiamond_validateActiveQuorumSignatures_InvalidSignature() public { @@ -549,7 +558,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert( abi.encodeWithSelector(InvalidSignatureErr.selector, MultisignatureChecker.Error.InvalidSignature) ); - saManager.validateActiveQuorumSignatures(validators, hash, signatures); + saCheckpointer.validateActiveQuorumSignatures(validators, hash, signatures); } function testSubnetActorDiamond_validateActiveQuorumSignatures_EmptySignatures() public { @@ -570,7 +579,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert( abi.encodeWithSelector(InvalidSignatureErr.selector, MultisignatureChecker.Error.EmptySignatures) ); - saManager.validateActiveQuorumSignatures(validators, hash, signatures); + saCheckpointer.validateActiveQuorumSignatures(validators, hash, signatures); } function testSubnetActorDiamond_validateActiveQuorumSignatures_InvalidArrayLength() public { @@ -591,7 +600,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert( abi.encodeWithSelector(InvalidSignatureErr.selector, MultisignatureChecker.Error.InvalidArrayLength) ); - saManager.validateActiveQuorumSignatures(validators, hash, signatures); + saCheckpointer.validateActiveQuorumSignatures(validators, hash, signatures); } function testSubnetActorDiamond_validateActiveQuorumSignatures_InvalidSignatory() public { @@ -623,7 +632,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert( abi.encodeWithSelector(InvalidSignatureErr.selector, MultisignatureChecker.Error.InvalidSignatory) ); - saManager.validateActiveQuorumSignatures(validators, hash0, signatures); + saCheckpointer.validateActiveQuorumSignatures(validators, hash0, signatures); } function testSubnetActorDiamond_submitCheckpoint_basic() public { @@ -683,11 +692,11 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert(InvalidCheckpointEpoch.selector); vm.prank(validators[0]); - saManager.submitCheckpoint(checkpointWithIncorrectHeight, validators, signatures); + saCheckpointer.submitCheckpoint(checkpointWithIncorrectHeight, validators, signatures); vm.expectCall(gatewayAddress, abi.encodeCall(IGateway.commitCheckpoint, (checkpoint)), 1); vm.prank(validators[0]); - saManager.submitCheckpoint(checkpoint, validators, signatures); + saCheckpointer.submitCheckpoint(checkpoint, validators, signatures); require(saGetter.hasSubmittedInLastBottomUpCheckpointHeight(validators[0]), "validator rewarded"); require( @@ -696,7 +705,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { ); vm.prank(validators[0]); - saManager.submitCheckpoint(checkpoint, validators, signatures); + saCheckpointer.submitCheckpoint(checkpoint, validators, signatures); require(saGetter.hasSubmittedInLastBottomUpCheckpointHeight(validators[0]), "validator rewarded"); require( saGetter.lastBottomUpCheckpointHeight() == saGetter.bottomUpCheckPeriod(), @@ -766,7 +775,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectCall(gatewayAddress, abi.encodeCall(IGateway.commitCheckpoint, (checkpoint)), 1); vm.prank(validators[0]); - saManager.submitCheckpoint(checkpoint, validators, signatures); + saCheckpointer.submitCheckpoint(checkpoint, validators, signatures); require(saGetter.hasSubmittedInLastBottomUpCheckpointHeight(validators[0]), "validator rewarded"); require( @@ -809,7 +818,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { } vm.prank(validators[0]); - saManager.submitCheckpoint(checkpoint, validators, signatures); + saCheckpointer.submitCheckpoint(checkpoint, validators, signatures); require(saGetter.getRelayerReward(validators[1]) == 0, "unexpected reward"); require(saGetter.getRelayerReward(validators[2]) == 0, "unexpected reward"); @@ -821,7 +830,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { // disable the claim of rewards if the fee is zero if (DEFAULT_CROSS_MSG_FEE != 0) { vm.startPrank(validators[0]); - saManager.claimRewardForRelayer(); + saRewarder.claimRewardForRelayer(); uint256 b2 = validators[0].balance; require(b2 - b1 == validator0Reward, "reward received"); } @@ -885,23 +894,23 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { vm.expectRevert(InvalidBatchEpoch.selector); vm.prank(validators[0]); - saManager.submitBottomUpMsgBatch(batchIncorrectHeight, validators, signatures); + saCheckpointer.submitBottomUpMsgBatch(batchIncorrectHeight, validators, signatures); vm.prank(validators[0]); batchIncorrectHeight.msgs = new CrossMsg[](0); batchIncorrectHeight.blockHeight = saGetter.bottomUpMsgBatchPeriod(); vm.expectRevert(BatchWithNoMessages.selector); - saManager.submitBottomUpMsgBatch(batchIncorrectHeight, validators, signatures); + saCheckpointer.submitBottomUpMsgBatch(batchIncorrectHeight, validators, signatures); vm.expectCall(gatewayAddress, abi.encodeCall(IGateway.execBottomUpMsgBatch, (batch)), 1); vm.prank(validators[0]); - saManager.submitBottomUpMsgBatch(batch, validators, signatures); + saCheckpointer.submitBottomUpMsgBatch(batch, validators, signatures); require(saGetter.hasSubmittedInLastBottomUpMsgBatchHeight(validators[0]), "validator rewarded"); require(saGetter.lastBottomUpMsgBatchHeight() == saGetter.bottomUpMsgBatchPeriod(), " batch height correct"); vm.prank(validators[1]); - saManager.submitBottomUpMsgBatch(batch, validators, signatures); + saCheckpointer.submitBottomUpMsgBatch(batch, validators, signatures); require(saGetter.hasSubmittedInLastBottomUpMsgBatchHeight(validators[0]), "validator rewarded"); require(saGetter.lastBottomUpMsgBatchHeight() == saGetter.bottomUpMsgBatchPeriod(), " batch height correct"); } @@ -1531,15 +1540,15 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { } function testSubnetActorDiamond_Pausable_SetPaused() public { - saManager.pause(); - require(saManager.paused()); + saPauser.pause(); + require(saPauser.paused()); - saManager.unpause(); - require(!saManager.paused()); + saPauser.unpause(); + require(!saPauser.paused()); } function testSubnetActorDiamond_Pausable_EnforcedPause() public { - saManager.pause(); + saPauser.pause(); uint256 n = 1; (address[] memory validators, , bytes[] memory publicKeys) = TestUtils.newValidators(n); @@ -1552,18 +1561,18 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { function testSubnetActorDiamond_Pausable_NotOwner() public { vm.startPrank(address(1)); vm.expectRevert(LibDiamond.NotOwner.selector); - saManager.pause(); + saPauser.pause(); } function testSubnetActorDiamond_Pausable_CannotPauseAgain() public { - saManager.pause(); + saPauser.pause(); vm.expectRevert(Pausable.EnforcedPause.selector); - saManager.pause(); + saPauser.pause(); } function testSubnetActorDiamond_Pausable_CannotUnpauseAgain() public { vm.expectRevert(Pausable.ExpectedPause.selector); - saManager.unpause(); + saPauser.unpause(); } } diff --git a/contracts/test/integration/SubnetRegistry.t.sol b/contracts/test/integration/SubnetRegistry.t.sol index 570aedecb..25c4e3bce 100644 --- a/contracts/test/integration/SubnetRegistry.t.sol +++ b/contracts/test/integration/SubnetRegistry.t.sol @@ -14,6 +14,9 @@ import {LibDiamond} from "../../src/lib/LibDiamond.sol"; import {SubnetActorGetterFacet} from "../../src/subnet/SubnetActorGetterFacet.sol"; import {SubnetActorManagerFacet} from "../../src/subnet/SubnetActorManagerFacet.sol"; +import {SubnetActorPauseFacet} from "../../src/subnet/SubnetActorPauseFacet.sol"; +import {SubnetActorCheckpointingFacet} from "../../src/subnet/SubnetActorCheckpointingFacet.sol"; +import {SubnetActorRewardFacet} from "../../src/subnet/SubnetActorRewardFacet.sol"; import {SubnetActorDiamond} from "../../src/SubnetActorDiamond.sol"; import {SubnetID, PermissionMode} from "../../src/structs/Subnet.sol"; import {SubnetRegistryDiamond} from "../../src/SubnetRegistryDiamond.sol"; @@ -36,12 +39,29 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { bytes4[] memory mockedSelectors2 = new bytes4[](1); mockedSelectors2[0] = 0x133f74ea; + bytes4[] memory mockedSelectors3 = new bytes4[](1); + mockedSelectors3[0] = 0x433f74ea; + + bytes4[] memory mockedSelectors4 = new bytes4[](1); + mockedSelectors4[0] = 0x333f74ea; + + bytes4[] memory mockedSelectors5 = new bytes4[](1); + mockedSelectors5[0] = 0x233f74ea; + SubnetRegistryDiamond.ConstructorParams memory params; params.gateway = DEFAULT_IPC_GATEWAY_ADDR; + params.getterFacet = address(new SubnetActorGetterFacet()); params.managerFacet = address(new SubnetActorManagerFacet()); - params.subnetGetterSelectors = mockedSelectors; - params.subnetManagerSelectors = mockedSelectors2; + params.rewarderFacet = address(new SubnetActorRewardFacet()); + params.checkpointerFacet = address(new SubnetActorCheckpointingFacet()); + params.pauserFacet = address(new SubnetActorPauseFacet()); + + params.subnetActorGetterSelectors = mockedSelectors; + params.subnetActorManagerSelectors = mockedSelectors2; + params.subnetActorRewarderSelectors = mockedSelectors3; + params.subnetActorCheckpointerSelectors = mockedSelectors4; + params.subnetActorPauserSelectors = mockedSelectors5; registryDiamond = createSubnetRegistry(params); registryLouper = DiamondLoupeFacet(address(registryDiamond)); @@ -73,30 +93,34 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { ); } - function test_Registry_Deployment_ZeroGetterFacet() public { + function test_Registry_Deployment_ZeroAddressFacet() public { SubnetRegistryDiamond.ConstructorParams memory params; params.gateway = DEFAULT_IPC_GATEWAY_ADDR; - params.getterFacet = address(0); - params.managerFacet = address(1); - params.subnetGetterSelectors = empty; - params.subnetManagerSelectors = empty; + params.subnetActorGetterSelectors = empty; + params.subnetActorManagerSelectors = empty; IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](0); vm.expectRevert(FacetCannotBeZero.selector); new SubnetRegistryDiamond(diamondCut, params); - } - function test_Registry_Deployment_ZeroManagerFacet() public { - SubnetRegistryDiamond.ConstructorParams memory params; - params.gateway = DEFAULT_IPC_GATEWAY_ADDR; params.getterFacet = address(1); - params.managerFacet = address(0); - params.subnetGetterSelectors = empty; - params.subnetManagerSelectors = empty; + vm.expectRevert(FacetCannotBeZero.selector); + new SubnetRegistryDiamond(diamondCut, params); - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](0); + params.managerFacet = address(2); + vm.expectRevert(FacetCannotBeZero.selector); + new SubnetRegistryDiamond(diamondCut, params); + + params.rewarderFacet = address(3); vm.expectRevert(FacetCannotBeZero.selector); new SubnetRegistryDiamond(diamondCut, params); + + params.checkpointerFacet = address(4); + vm.expectRevert(FacetCannotBeZero.selector); + new SubnetRegistryDiamond(diamondCut, params); + + params.pauserFacet = address(5); + new SubnetRegistryDiamond(diamondCut, params); } function test_Registry_Deployment_ZeroGateway() public { @@ -104,8 +128,8 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { params.gateway = address(0); params.getterFacet = address(1); params.managerFacet = address(1); - params.subnetGetterSelectors = empty; - params.subnetManagerSelectors = empty; + params.subnetActorGetterSelectors = empty; + params.subnetActorManagerSelectors = empty; IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](0); vm.expectRevert(GatewayCannotBeZero.selector); @@ -203,7 +227,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { bytes4[] memory newSubnetGetterSelectors = new bytes4[](1); newSubnetGetterSelectors[0] = 0x12345678; // Mocked selector bytes4[] memory newSubnetManagerSelectors = new bytes4[](1); - newSubnetManagerSelectors[0] = 0x87654321; // Mocked selector + newSubnetManagerSelectors[0] = 0x87654322; // Mocked selector registrySubnetGetterFacet.updateReferenceSubnetContract( newGetterFacet, diff --git a/contracts/test/invariants/GatewayActorInvariantTests.t.sol b/contracts/test/invariants/GatewayActorInvariantTests.t.sol new file mode 100644 index 000000000..28749e6cc --- /dev/null +++ b/contracts/test/invariants/GatewayActorInvariantTests.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {StdInvariant} from "forge-std/StdInvariant.sol"; +import {GatewayDiamond} from "../../src/GatewayDiamond.sol"; +import {L1GatewayActorDiamond, L2GatewayActorDiamond, L3GatewayActorDiamond} from "../IntegrationTestPresets.sol"; +import {GatewayActorHandler} from "./handlers/GatewayActorHandler.sol"; +import {GatewayActorBasicProperties} from "./GatewayActorProperties.sol"; + +contract GatewayActorInvariantTests is StdInvariant, L1GatewayActorDiamond, GatewayActorBasicProperties { + GatewayActorHandler private gatewayActorHandler; + + function setUp() public override { + L1GatewayActorDiamond.setUp(); + gatewayActorHandler = new GatewayActorHandler(gatewayDiamond); + targetContract(address(gatewayActorHandler)); + + // assert specific properties of the infrastructure. + assertEq(gwGetter.getNetworkName().route.length, 1); + } +} + +contract L2GatewayActorInvariantTests is L2GatewayActorDiamond, GatewayActorBasicProperties { + GatewayActorHandler private gatewayActorHandler; + + function setUp() public override { + L2GatewayActorDiamond.setUp(); + gatewayActorHandler = new GatewayActorHandler(gatewayDiamond); + targetContract(address(gatewayActorHandler)); + + // assert specific properties of the infrastructure. + assertEq(gwGetter.getNetworkName().route.length, 2); + } +} + +contract L3GatewayActorInvariantTests is L3GatewayActorDiamond, GatewayActorBasicProperties { + GatewayActorHandler private gatewayActorHandler; + + function setUp() public override { + L3GatewayActorDiamond.setUp(); + gatewayActorHandler = new GatewayActorHandler(gatewayDiamond); + targetContract(address(gatewayActorHandler)); + + // assert specific properties of the infrastructure. + assertEq(gwGetter.getNetworkName().route.length, 3); + } +} diff --git a/contracts/test/invariants/GatewayActorProperties.sol b/contracts/test/invariants/GatewayActorProperties.sol new file mode 100644 index 000000000..9b8bb8aea --- /dev/null +++ b/contracts/test/invariants/GatewayActorProperties.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import {StdAssertions} from "forge-std/StdAssertions.sol"; +import {GatewayGetterFacet} from "../../src/gateway/GatewayGetterFacet.sol"; +import {IntegrationTestBase, TestGatewayActor} from "../IntegrationTestBase.sol"; + +/// @title GatewayActor properties. +/// @dev It is suggested that all properties are defined here. +/// To check that a concrete GatewayActor instance holds the properties that target contract should inherit from this contract. +/// This contract must be abstract. +abstract contract GatewayActorBasicProperties is StdAssertions, TestGatewayActor { + /// @notice The number of subnets is consistent within GatewayActor mechanisms. + function invariant_GA_01_consistent_subnet_number() public virtual { + assertEq(gwGetter.totalSubnets(), gwGetter.listSubnets().length, "the number of subnets is not consistent"); + } +} diff --git a/contracts/test/invariants/SubnetActorInvariants.t.sol b/contracts/test/invariants/SubnetActorInvariants.t.sol index 6f3fab732..837ee44ac 100644 --- a/contracts/test/invariants/SubnetActorInvariants.t.sol +++ b/contracts/test/invariants/SubnetActorInvariants.t.sol @@ -10,7 +10,7 @@ import {GatewayGetterFacet} from "../../src/gateway/GatewayGetterFacet.sol"; import {GatewayMessengerFacet} from "../../src/gateway/GatewayMessengerFacet.sol"; import {GatewayManagerFacet} from "../../src/gateway/GatewayManagerFacet.sol"; import {SubnetActorHandler, ETH_SUPPLY} from "./handlers/SubnetActorHandler.sol"; -import {SubnetActorManagerFacetMock} from "../mocks/SubnetActorManagerFacetMock.sol"; +import {SubnetActorMock} from "../mocks/SubnetActorMock.sol"; import {SubnetActorGetterFacet} from "../../src/subnet/SubnetActorGetterFacet.sol"; import {IntegrationTestBase} from "../IntegrationTestBase.sol"; import {SupplySourceHelper} from "../../src/lib/SupplySourceHelper.sol"; @@ -34,7 +34,7 @@ contract SubnetActorInvariants is StdInvariant, IntegrationTestBase { saDiamond = createMockedSubnetActorWithGateway(gatewayAddress); - saMockedManager = SubnetActorManagerFacetMock(address(saDiamond)); + saMock = SubnetActorMock(address(saDiamond)); saGetter = SubnetActorGetterFacet(address(saDiamond)); subnetActorHandler = new SubnetActorHandler(saDiamond); @@ -120,8 +120,8 @@ contract SubnetActorInvariants is StdInvariant, IntegrationTestBase { uint256 balanceBefore = validator.balance; vm.prank(validator); - saMockedManager.claim(); - saMockedManager.confirmNextChange(); + saMock.claim(); + saMock.confirmNextChange(); uint256 balanceAfter = validator.balance; uint256 subnetBalanceAfter = address(saDiamond).balance; diff --git a/contracts/test/invariants/SubnetRegistryInvariants.t.sol b/contracts/test/invariants/SubnetRegistryInvariants.t.sol index 945121606..6734abd61 100644 --- a/contracts/test/invariants/SubnetRegistryInvariants.t.sol +++ b/contracts/test/invariants/SubnetRegistryInvariants.t.sol @@ -9,6 +9,9 @@ import {SubnetRegistryDiamond} from "../../src/SubnetRegistryDiamond.sol"; import {SubnetIDHelper} from "../../src/lib/SubnetIDHelper.sol"; import {SubnetActorGetterFacet} from "../../src/subnet/SubnetActorGetterFacet.sol"; import {SubnetActorManagerFacet} from "../../src/subnet/SubnetActorManagerFacet.sol"; +import {SubnetActorPauseFacet} from "../../src/subnet/SubnetActorPauseFacet.sol"; +import {SubnetActorCheckpointingFacet} from "../../src/subnet/SubnetActorCheckpointingFacet.sol"; +import {SubnetActorRewardFacet} from "../../src/subnet/SubnetActorRewardFacet.sol"; import {SubnetID} from "../../src/structs/Subnet.sol"; import {RegisterSubnetFacet} from "../../src/subnetregistry/RegisterSubnetFacet.sol"; import {SubnetGetterFacet} from "../../src/subnetregistry/SubnetGetterFacet.sol"; @@ -26,12 +29,29 @@ contract SubnetRegistryInvariants is StdInvariant, Test, TestRegistry, Integrati bytes4[] memory mockedSelectors2 = new bytes4[](1); mockedSelectors2[0] = 0x133f74ea; + bytes4[] memory mockedSelectors3 = new bytes4[](1); + mockedSelectors3[0] = 0x433f74ea; + + bytes4[] memory mockedSelectors4 = new bytes4[](1); + mockedSelectors4[0] = 0x333f74ea; + + bytes4[] memory mockedSelectors5 = new bytes4[](1); + mockedSelectors5[0] = 0x233f74ea; + SubnetRegistryDiamond.ConstructorParams memory params; params.gateway = DEFAULT_IPC_GATEWAY_ADDR; + params.getterFacet = address(new SubnetActorGetterFacet()); params.managerFacet = address(new SubnetActorManagerFacet()); - params.subnetGetterSelectors = mockedSelectors; - params.subnetManagerSelectors = mockedSelectors2; + params.rewarderFacet = address(new SubnetActorRewardFacet()); + params.checkpointerFacet = address(new SubnetActorCheckpointingFacet()); + params.pauserFacet = address(new SubnetActorPauseFacet()); + + params.subnetActorGetterSelectors = mockedSelectors; + params.subnetActorManagerSelectors = mockedSelectors2; + params.subnetActorRewarderSelectors = mockedSelectors3; + params.subnetActorCheckpointerSelectors = mockedSelectors4; + params.subnetActorPauserSelectors = mockedSelectors5; registryDiamond = createSubnetRegistry(params); registryHandler = new SubnetRegistryHandler(registryDiamond); diff --git a/contracts/test/invariants/handlers/GatewayActorHandler.sol b/contracts/test/invariants/handlers/GatewayActorHandler.sol new file mode 100644 index 000000000..3667a9ad9 --- /dev/null +++ b/contracts/test/invariants/handlers/GatewayActorHandler.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.19; + +import "forge-std/StdUtils.sol"; +import "forge-std/StdCheats.sol"; +import {CommonBase} from "forge-std/Base.sol"; +import {GatewayDiamond} from "../../../src/GatewayDiamond.sol"; +import {BottomUpRouterFacet} from "../../../src/gateway/router/BottomUpRouterFacet.sol"; +import {GatewayManagerFacet} from "../../../src/gateway/GatewayManagerFacet.sol"; +import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; + +uint256 constant ETH_SUPPLY = 129_590_000 ether; + +contract GatewayActorHandler is CommonBase, StdCheats, StdUtils { + GatewayManagerFacet managerFacet; + + uint256 private constant DEFAULT_MIN_VALIDATOR_STAKE = 10 ether; + + constructor(GatewayDiamond _gw) { + managerFacet = GatewayManagerFacet(address(_gw)); + + deal(address(this), ETH_SUPPLY); + } + + function register(uint256 amount) public { + amount = bound(amount, 0, 3 * DEFAULT_MIN_VALIDATOR_STAKE); + managerFacet.register(amount); + } + + function stake(uint256 amount) public { + amount = bound(amount, 0, 3 * DEFAULT_MIN_VALIDATOR_STAKE); + managerFacet.addStake{value: amount}(); + } + + function _pay(address to, uint256 amount) internal { + (bool s, ) = to.call{value: amount}(""); + require(s, "pay() failed"); + } + + receive() external payable {} +} diff --git a/contracts/test/invariants/handlers/SubnetActorHandler.sol b/contracts/test/invariants/handlers/SubnetActorHandler.sol index a860b8beb..54804f763 100644 --- a/contracts/test/invariants/handlers/SubnetActorHandler.sol +++ b/contracts/test/invariants/handlers/SubnetActorHandler.sol @@ -6,7 +6,7 @@ import "forge-std/StdCheats.sol"; import {CommonBase} from "forge-std/Base.sol"; import {SubnetActorDiamond} from "../../../src/SubnetActorDiamond.sol"; import {SubnetActorGetterFacet} from "../../../src/subnet/SubnetActorGetterFacet.sol"; -import {SubnetActorManagerFacetMock} from "../../mocks/SubnetActorManagerFacetMock.sol"; +import {SubnetActorMock} from "../../mocks/SubnetActorMock.sol"; import {TestUtils} from "../../helpers/TestUtils.sol"; import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; @@ -15,7 +15,7 @@ uint256 constant ETH_SUPPLY = 129_590_000 ether; contract SubnetActorHandler is CommonBase, StdCheats, StdUtils { using EnumerableSet for EnumerableSet.AddressSet; - SubnetActorManagerFacetMock private managerFacet; + SubnetActorMock private managerFacet; SubnetActorGetterFacet private getterFacet; uint256 private constant DEFAULT_MIN_VALIDATOR_STAKE = 10 ether; @@ -32,7 +32,7 @@ contract SubnetActorHandler is CommonBase, StdCheats, StdUtils { uint256 public ghost_unstakedSum; constructor(SubnetActorDiamond _subnetActor) { - managerFacet = SubnetActorManagerFacetMock(address(_subnetActor)); + managerFacet = SubnetActorMock(address(_subnetActor)); getterFacet = SubnetActorGetterFacet(address(_subnetActor)); deal(address(this), ETH_SUPPLY); diff --git a/contracts/test/mocks/SubnetActorManagerFacetMock.sol b/contracts/test/mocks/SubnetActorMock.sol similarity index 56% rename from contracts/test/mocks/SubnetActorManagerFacetMock.sol rename to contracts/test/mocks/SubnetActorMock.sol index 70685b49b..8950dedda 100644 --- a/contracts/test/mocks/SubnetActorManagerFacetMock.sol +++ b/contracts/test/mocks/SubnetActorMock.sol @@ -3,8 +3,16 @@ pragma solidity 0.8.19; import {SubnetActorManagerFacet} from "../../src/subnet/SubnetActorManagerFacet.sol"; import {LibStaking} from "../../src/lib/LibStaking.sol"; +import {SubnetActorPauseFacet} from "../../src/subnet/SubnetActorPauseFacet.sol"; +import {SubnetActorRewardFacet} from "../../src/subnet/SubnetActorRewardFacet.sol"; +import {SubnetActorCheckpointingFacet} from "../../src/subnet/SubnetActorCheckpointingFacet.sol"; -contract SubnetActorManagerFacetMock is SubnetActorManagerFacet { +contract SubnetActorMock is + SubnetActorPauseFacet, + SubnetActorManagerFacet, + SubnetActorRewardFacet, + SubnetActorCheckpointingFacet +{ function confirmChange(uint64 _configurationNumber) external { LibStaking.confirmChange(_configurationNumber); } diff --git a/fendermint/fendermint/eth/hardhat/src/lib.rs b/fendermint/fendermint/eth/hardhat/src/lib.rs index 8b5e80d57..f732b0323 100644 --- a/fendermint/fendermint/eth/hardhat/src/lib.rs +++ b/fendermint/fendermint/eth/hardhat/src/lib.rs @@ -345,6 +345,9 @@ mod tests { "GatewayMessengerFacet", "SubnetActorGetterFacet", "SubnetActorManagerFacet", + "SubnetActorRewardFacet", + "SubnetActorCheckpointingFacet", + "SubnetActorPauseFacet", ] .into_iter() .map(|c| (format!("{c}.sol"), c)) diff --git a/fendermint/fendermint/testing/contract-test/src/ipc/subnet.rs b/fendermint/fendermint/testing/contract-test/src/ipc/subnet.rs index 9574a971a..cb7e91502 100644 --- a/fendermint/fendermint/testing/contract-test/src/ipc/subnet.rs +++ b/fendermint/fendermint/testing/contract-test/src/ipc/subnet.rs @@ -12,16 +12,23 @@ use fendermint_vm_message::conv::{from_eth, from_fvm}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::crypto::signature::SECP_SIG_LEN; use fvm_shared::econ::TokenAmount; +use ipc_actors_abis::subnet_actor_checkpointing_facet::{ + self as checkpointer, SubnetActorCheckpointingFacet, +}; use ipc_actors_abis::subnet_actor_getter_facet::{self as getter, SubnetActorGetterFacet}; -use ipc_actors_abis::subnet_actor_manager_facet::{self as manager, SubnetActorManagerFacet}; +use ipc_actors_abis::subnet_actor_manager_facet::SubnetActorManagerFacet; pub use ipc_actors_abis::register_subnet_facet::ConstructorParams as SubnetConstructorParams; +use ipc_actors_abis::subnet_actor_reward_facet::SubnetActorRewardFacet; #[derive(Clone)] pub struct SubnetCaller { addr: EthAddress, getter: ContractCaller, NoRevert>, manager: ContractCaller, SubnetActorErrors>, + rewarder: ContractCaller, SubnetActorErrors>, + checkpointer: + ContractCaller, SubnetActorErrors>, } impl SubnetCaller { @@ -30,6 +37,8 @@ impl SubnetCaller { addr, getter: ContractCaller::new(addr, SubnetActorGetterFacet::new), manager: ContractCaller::new(addr, SubnetActorManagerFacet::new), + rewarder: ContractCaller::new(addr, SubnetActorRewardFacet::new), + checkpointer: ContractCaller::new(addr, SubnetActorCheckpointingFacet::new), } } @@ -102,15 +111,15 @@ impl SubnetCaller { /// Claim any refunds. pub fn try_claim(&self, state: &mut FvmExecState, addr: &EthAddress) -> TryCallResult<()> { - self.manager.try_call(state, |c| c.claim().from(addr)) + self.rewarder.try_call(state, |c| c.claim().from(addr)) } /// Submit a bottom-up checkpoint. pub fn try_submit_checkpoint( &self, state: &mut FvmExecState, - checkpoint: manager::BottomUpCheckpoint, - _messages: Vec, + checkpoint: checkpointer::BottomUpCheckpoint, + _messages: Vec, signatures: Vec<(EthAddress, [u8; SECP_SIG_LEN])>, ) -> TryCallResult<()> { let mut addrs = Vec::new(); @@ -119,7 +128,7 @@ impl SubnetCaller { addrs.push(ethers::types::Address::from(addr)); sigs.push(sig.into()); } - self.manager + self.checkpointer .try_call(state, |c| c.submit_checkpoint(checkpoint, addrs, sigs)) } diff --git a/fendermint/fendermint/testing/contract-test/tests/staking/machine.rs b/fendermint/fendermint/testing/contract-test/tests/staking/machine.rs index fb1c6b476..1d89f8f14 100644 --- a/fendermint/fendermint/testing/contract-test/tests/staking/machine.rs +++ b/fendermint/fendermint/testing/contract-test/tests/staking/machine.rs @@ -25,7 +25,7 @@ use fvm_ipld_blockstore::Blockstore; use fvm_shared::bigint::Integer; use fvm_shared::econ::TokenAmount; use fvm_shared::{address::Address, bigint::BigInt}; -use ipc_actors_abis::subnet_actor_manager_facet as subnet_manager; +use ipc_actors_abis::subnet_actor_checkpointing_facet as checkpointer; use ipc_sdk::subnet_id::SubnetID; use super::{ @@ -280,8 +280,8 @@ impl StateMachine for StakingMachine { let (root, route) = subnet_id_to_eth(&system.subnet_id).unwrap(); - let checkpoint = subnet_manager::BottomUpCheckpoint { - subnet_id: subnet_manager::SubnetID { root, route }, + let checkpoint = checkpointer::BottomUpCheckpoint { + subnet_id: checkpointer::SubnetID { root, route }, block_height: ethers::types::U256::from(*block_height), block_hash: *block_hash, next_configuration_number: *next_configuration_number, diff --git a/fendermint/fendermint/vm/actor_interface/src/ipc.rs b/fendermint/fendermint/vm/actor_interface/src/ipc.rs index d06fe163c..dbe192bb2 100644 --- a/fendermint/fendermint/vm/actor_interface/src/ipc.rs +++ b/fendermint/fendermint/vm/actor_interface/src/ipc.rs @@ -88,6 +88,18 @@ lazy_static! { abi: ia::subnet_actor_manager_facet::SUBNETACTORMANAGERFACET_ABI .to_owned(), }, + EthFacet { + name: "SubnetActorRewardFacet", + abi: ia::subnet_actor_reward_facet::SUBNETACTORREWARDFACET_ABI.to_owned(), + }, + EthFacet { + name: "SubnetActorCheckpointingFacet", + abi: ia::subnet_actor_checkpointing_facet::SUBNETACTORCHECKPOINTINGFACET_ABI.to_owned(), + }, + EthFacet { + name: "SubnetActorPauseFacet", + abi: ia::subnet_actor_pause_facet::SUBNETACTORPAUSEFACET_ABI.to_owned(), + }, // The registry has its own facets: // https://github.com/consensus-shipyard/ipc-solidity-actors/blob/b01a2dffe367745f55111a65536a3f6fea9165f5/scripts/deploy-registry.template.ts#L58-L67 EthFacet { @@ -140,6 +152,18 @@ lazy_static! { name: "SubnetActorManagerFacet", abi: ia::subnet_actor_manager_facet::SUBNETACTORMANAGERFACET_ABI.to_owned(), }, + EthFacet { + name: "SubnetActorRewardFacet", + abi: ia::subnet_actor_reward_facet::SUBNETACTORREWARDFACET_ABI.to_owned(), + }, + EthFacet { + name: "SubnetActorCheckpointingFacet", + abi: ia::subnet_actor_checkpointing_facet::SUBNETACTORCHECKPOINTINGFACET_ABI.to_owned(), + }, + EthFacet { + name: "SubnetActorPauseFacet", + abi: ia::subnet_actor_pause_facet::SUBNETACTORPAUSEFACET_ABI.to_owned(), + }, ], }, ), @@ -269,9 +293,9 @@ macro_rules! abi_hash { } abi_hash!(struct ipc_actors_abis::checkpointing_facet::BottomUpCheckpoint); -abi_hash!(struct ipc_actors_abis::subnet_actor_manager_facet::BottomUpCheckpoint); +abi_hash!(struct ipc_actors_abis::subnet_actor_checkpointing_facet::BottomUpCheckpoint); abi_hash!(Vec); -abi_hash!(Vec); +abi_hash!(Vec); abi_hash!(Vec); pub mod gateway { @@ -424,8 +448,14 @@ pub mod registry { pub gateway: Address, pub getter_facet: Address, pub manager_facet: Address, + pub rewarder_facet: Address, + pub pauser_facet: Address, + pub checkpointer_facet: Address, pub subnet_getter_selectors: Vec, pub subnet_manager_selectors: Vec, + pub subnet_rewarder_selectors: Vec, + pub subnet_pauser_selectors: Vec, + pub subnet_checkpointer_selectors: Vec, } } @@ -434,7 +464,10 @@ pub mod subnet { use ipc_actors_abis::bottom_up_router_facet::BottomUpRouterFacetErrors; use ipc_actors_abis::checkpointing_facet::CheckpointingFacetErrors; use ipc_actors_abis::gateway_manager_facet::GatewayManagerFacetErrors; + use ipc_actors_abis::subnet_actor_checkpointing_facet::SubnetActorCheckpointingFacetErrors; use ipc_actors_abis::subnet_actor_manager_facet::SubnetActorManagerFacetErrors; + use ipc_actors_abis::subnet_actor_pause_facet::SubnetActorPauseFacetErrors; + use ipc_actors_abis::subnet_actor_reward_facet::SubnetActorRewardFacetErrors; use ipc_actors_abis::top_down_finality_facet::TopDownFinalityFacetErrors; pub const CONTRACT_NAME: &str = "SubnetActorDiamond"; @@ -443,6 +476,9 @@ pub mod subnet { revert_errors! { SubnetActorErrors { SubnetActorManagerFacetErrors, + SubnetActorRewardFacetErrors, + SubnetActorPauseFacetErrors, + SubnetActorCheckpointingFacetErrors, GatewayManagerFacetErrors, CheckpointingFacetErrors, BottomUpRouterFacetErrors, @@ -454,7 +490,7 @@ pub mod subnet { mod tests { use ethers::abi::{AbiType, Tokenize}; use ethers::core::types::Bytes; - use ipc_actors_abis::subnet_actor_manager_facet::{BottomUpCheckpoint, SubnetID}; + use ipc_actors_abis::subnet_actor_checkpointing_facet::{BottomUpCheckpoint, SubnetID}; #[test] fn checkpoint_abi() { diff --git a/fendermint/fendermint/vm/interpreter/src/fvm/genesis.rs b/fendermint/fendermint/vm/interpreter/src/fvm/genesis.rs index e411cde24..4dde742a6 100644 --- a/fendermint/fendermint/vm/interpreter/src/fvm/genesis.rs +++ b/fendermint/fendermint/vm/interpreter/src/fvm/genesis.rs @@ -290,7 +290,7 @@ where )? }; - // IPC SubnetRegistry actory. + // IPC SubnetRegistry actor. { use ipc::registry::ConstructorParameters; @@ -300,6 +300,9 @@ where let getter_facet = facets.remove(0); let manager_facet = facets.remove(0); + let rewarder_facet = facets.remove(0); + let checkpointer_facet = facets.remove(0); + let pauser_facet = facets.remove(0); debug_assert_eq!(facets.len(), 4, "SubnetRegistry has 4 facets of its own"); @@ -307,8 +310,14 @@ where gateway: gateway_addr, getter_facet: getter_facet.facet_address, manager_facet: manager_facet.facet_address, + rewarder_facet: rewarder_facet.facet_address, + checkpointer_facet: checkpointer_facet.facet_address, + pauser_facet: pauser_facet.facet_address, subnet_getter_selectors: getter_facet.function_selectors, subnet_manager_selectors: manager_facet.function_selectors, + subnet_rewarder_selectors: rewarder_facet.function_selectors, + subnet_checkpointer_selectors: checkpointer_facet.function_selectors, + subnet_pauser_selectors: pauser_facet.function_selectors, }; deployer.deploy_contract( diff --git a/ipc/ipc/provider/src/manager/evm/manager.rs b/ipc/ipc/provider/src/manager/evm/manager.rs index ffe1b5374..0effc3393 100644 --- a/ipc/ipc/provider/src/manager/evm/manager.rs +++ b/ipc/ipc/provider/src/manager/evm/manager.rs @@ -10,7 +10,8 @@ use ethers_contract::{ContractError, EthLogDecode, LogMeta}; use ipc_actors_abis::{ bottom_up_router_facet, gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, lib_gateway, lib_quorum, lib_staking_change_log, register_subnet_facet, - subnet_actor_getter_facet, subnet_actor_manager_facet, + subnet_actor_checkpointing_facet, subnet_actor_getter_facet, subnet_actor_manager_facet, + subnet_actor_reward_facet, }; use ipc_sdk::evm::{fil_to_eth_amount, payload_to_evm_address, subnet_id_to_evm_addresses}; use ipc_sdk::validator::from_contract_validators; @@ -504,7 +505,7 @@ impl SubnetManager for EthSubnetManager { let signer = Arc::new(self.get_signer(&from)?); let contract = - subnet_actor_manager_facet::SubnetActorManagerFacet::new(address, signer.clone()); + subnet_actor_reward_facet::SubnetActorRewardFacet::new(address, signer.clone()); call_with_premium_estimation(signer, contract.claim()) .await? @@ -521,7 +522,7 @@ impl SubnetManager for EthSubnetManager { let signer = Arc::new(self.get_signer(&from)?); let contract = - subnet_actor_manager_facet::SubnetActorManagerFacet::new(address, signer.clone()); + subnet_actor_reward_facet::SubnetActorRewardFacet::new(address, signer.clone()); call_with_premium_estimation(signer, contract.claim_reward_for_relayer()) .await? @@ -812,7 +813,7 @@ impl EthManager for EthSubnetManager { async fn bottom_up_checkpoint( &self, epoch: ChainEpoch, - ) -> Result { + ) -> Result { let gateway_contract = gateway_getter_facet::GatewayGetterFacet::new( self.ipc_contract_info.gateway_addr, Arc::new(self.ipc_contract_info.provider.clone()), @@ -823,7 +824,7 @@ impl EthManager for EthSubnetManager { .await?; log::debug!("raw bottom up checkpoint from gateway: {checkpoint:?}"); let token = checkpoint.into_token(); - let c = subnet_actor_manager_facet::BottomUpCheckpoint::from_token(token)?; + let c = subnet_actor_checkpointing_facet::BottomUpCheckpoint::from_token(token)?; Ok(c) } @@ -1011,11 +1012,14 @@ impl BottomUpCheckpointRelayer for EthSubnetManager { // .into_iter() // .map(subnet_actor_manager_facet::CrossMsg::try_from) // .collect::, _>>()?; - let checkpoint = subnet_actor_manager_facet::BottomUpCheckpoint::try_from(checkpoint)?; + let checkpoint = + subnet_actor_checkpointing_facet::BottomUpCheckpoint::try_from(checkpoint)?; let signer = Arc::new(self.get_signer(submitter)?); - let contract = - subnet_actor_manager_facet::SubnetActorManagerFacet::new(address, signer.clone()); + let contract = subnet_actor_checkpointing_facet::SubnetActorCheckpointingFacet::new( + address, + signer.clone(), + ); let call = contract.submit_checkpoint(checkpoint, signatories, signatures); let call = call_with_premium_estimation(signer, call).await?; @@ -1159,10 +1163,12 @@ impl BottomUpCheckpointRelayer for EthSubnetManager { .collect::, _>>()?; let signer = Arc::new(self.get_signer(submitter)?); - let contract = - subnet_actor_manager_facet::SubnetActorManagerFacet::new(address, signer.clone()); + let contract = subnet_actor_checkpointing_facet::SubnetActorCheckpointingFacet::new( + address, + signer.clone(), + ); let call = contract.submit_bottom_up_msg_batch( - subnet_actor_manager_facet::BottomUpMsgBatch::try_from(batch)?, + subnet_actor_checkpointing_facet::BottomUpMsgBatch::try_from(batch)?, signatories, signatures, ); diff --git a/ipc/ipc/provider/src/manager/evm/mod.rs b/ipc/ipc/provider/src/manager/evm/mod.rs index 1a61087af..646004540 100644 --- a/ipc/ipc/provider/src/manager/evm/mod.rs +++ b/ipc/ipc/provider/src/manager/evm/mod.rs @@ -10,7 +10,7 @@ use ipc_sdk::subnet_id::SubnetID; use super::subnet::SubnetManager; pub use manager::EthSubnetManager; -use ipc_actors_abis::subnet_actor_manager_facet; +use ipc_actors_abis::subnet_actor_checkpointing_facet; #[async_trait] pub trait EthManager: SubnetManager { @@ -21,7 +21,7 @@ pub trait EthManager: SubnetManager { async fn bottom_up_checkpoint( &self, epoch: ChainEpoch, - ) -> anyhow::Result; + ) -> anyhow::Result; /// Get the latest applied top down nonce async fn get_applied_top_down_nonce(&self, subnet_id: &SubnetID) -> anyhow::Result; diff --git a/ipc/ipc/sdk/src/evm.rs b/ipc/ipc/sdk/src/evm.rs index 06954831a..14a2b4a8f 100644 --- a/ipc/ipc/sdk/src/evm.rs +++ b/ipc/ipc/sdk/src/evm.rs @@ -21,8 +21,8 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::MethodNum; use ipc_actors_abis::{ bottom_up_router_facet, gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, - lib_gateway, register_subnet_facet, subnet_actor_diamond, subnet_actor_getter_facet, - subnet_actor_manager_facet, top_down_finality_facet, xnet_messaging_facet, + lib_gateway, register_subnet_facet, subnet_actor_checkpointing_facet, subnet_actor_diamond, + subnet_actor_getter_facet, top_down_finality_facet, xnet_messaging_facet, }; /// The type conversion for IPC structs to evm solidity contracts. We need this convenient macro because @@ -206,7 +206,7 @@ base_type_conversion!(bottom_up_router_facet); base_type_conversion!(xnet_messaging_facet); base_type_conversion!(subnet_actor_getter_facet); base_type_conversion!(gateway_manager_facet); -base_type_conversion!(subnet_actor_manager_facet); +base_type_conversion!(subnet_actor_checkpointing_facet); base_type_conversion!(gateway_getter_facet); base_type_conversion!(gateway_messenger_facet); base_type_conversion!(lib_gateway); @@ -215,11 +215,11 @@ cross_msg_types!(gateway_getter_facet); cross_msg_types!(bottom_up_router_facet); cross_msg_types!(xnet_messaging_facet); cross_msg_types!(gateway_messenger_facet); -cross_msg_types!(subnet_actor_manager_facet); +cross_msg_types!(subnet_actor_checkpointing_facet); cross_msg_types!(lib_gateway); bottom_up_type_conversion!(gateway_getter_facet); -bottom_up_type_conversion!(subnet_actor_manager_facet); +bottom_up_type_conversion!(subnet_actor_checkpointing_facet); impl TryFrom for subnet_actor_diamond::SupplySource { type Error = anyhow::Error;