diff --git a/contracts/contracts/coordination/BetaProgramInitiator.sol b/contracts/contracts/coordination/BetaProgramInitiator.sol deleted file mode 100644 index 32733b7b..00000000 --- a/contracts/contracts/coordination/BetaProgramInitiator.sol +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./FlatRateFeeModel.sol"; -import "./IEncryptionAuthorizer.sol"; -import "./Coordinator.sol"; - -contract BetaProgramInitiator { - using SafeERC20 for IERC20; - - event RequestRegistered( - address indexed sender, - uint256 indexed requestIndex, - address[] providers, - address authority, - uint32 duration, - IEncryptionAuthorizer accessController, - uint256 payment - ); - - event RequestCanceled(address indexed sender, uint256 indexed requestIndex); - - event RequestExecuted(uint256 indexed requestIndex, uint256 indexed ritualId); - - event FailedRequestRefunded(uint256 indexed requestIndex, uint256 refundAmount); - - struct InitiationRequest { - address[] providers; - address authority; - uint32 duration; - IEncryptionAuthorizer accessController; - address sender; - uint32 ritualId; - uint256 payment; - } - - uint32 public constant NO_RITUAL = type(uint32).max; - - Coordinator public immutable coordinator; - IERC20 public immutable currency; - address public immutable executor; // TODO transferable role? - FlatRateFeeModel public immutable feeModel; - - InitiationRequest[] public requests; - - constructor(Coordinator _coordinator, address _executor, FlatRateFeeModel _feeModel) { - require(_executor != address(0), "Invalid parameters"); - coordinator = _coordinator; - currency = _feeModel.currency(); - executor = _executor; - feeModel = _feeModel; - } - - function getRequestsLength() external view returns (uint256) { - return requests.length; - } - - function getProviders(uint256 requestIndex) external view returns (address[] memory) { - InitiationRequest storage request = requests[requestIndex]; - return request.providers; - } - - function registerInitiationRequest( - address[] calldata providers, - address authority, - uint32 duration, - IEncryptionAuthorizer accessController - ) external returns (uint256 requestIndex) { - uint256 ritualCost = feeModel.getRitualCost(providers.length, duration); - - requestIndex = requests.length; - InitiationRequest storage request = requests.push(); - request.providers = providers; - request.authority = authority; - request.duration = duration; - request.accessController = accessController; - request.sender = msg.sender; - request.ritualId = NO_RITUAL; - request.payment = ritualCost; - - emit RequestRegistered( - msg.sender, - requestIndex, - providers, - authority, - duration, - accessController, - ritualCost - ); - currency.safeTransferFrom(msg.sender, address(this), ritualCost); - - return requestIndex; - } - - function cancelInitiationRequest(uint256 requestIndex) external { - require(requestIndex < requests.length, "Non-existent request"); - InitiationRequest storage request = requests[requestIndex]; - address sender = request.sender; - require(msg.sender == sender || msg.sender == executor, "Not allowed to cancel"); - - uint256 ritualCost = request.payment; - require(request.ritualId == NO_RITUAL, "Request already executed"); - require(ritualCost != 0, "Request canceled"); - - // Erase payment and transfer refund to original sender - request.payment = 0; - emit RequestCanceled(msg.sender, requestIndex); - currency.safeTransfer(sender, ritualCost); - // TODO consider gas refund by setting zero values - } - - function executeInitiationRequest(uint256 requestIndex) external { - require(msg.sender == executor, "Only executor can call"); - - require(requestIndex < requests.length, "Non-existent request"); - InitiationRequest storage request = requests[requestIndex]; - require(request.ritualId == NO_RITUAL, "Request already executed"); - require(request.payment != 0, "Request canceled"); - - address[] memory providers = request.providers; - uint32 duration = request.duration; - uint256 ritualCost = feeModel.getRitualCost(providers.length, duration); - require(ritualCost == request.payment, "Ritual initiation cost has changed"); - currency.approve(address(feeModel), ritualCost); - - uint32 ritualId = coordinator.initiateRitual( - feeModel, - providers, - request.authority, - duration, - request.accessController - ); - request.ritualId = ritualId; - emit RequestExecuted(requestIndex, ritualId); - } - - function refundFailedRequest(uint256 requestIndex) external { - require(requestIndex < requests.length, "Non-existent request"); - InitiationRequest storage request = requests[requestIndex]; - uint32 ritualId = request.ritualId; - require(request.ritualId != NO_RITUAL, "Request is not executed"); - require(request.payment != 0, "Refund already processed"); - - Coordinator.RitualState state = coordinator.getRitualState(ritualId); - require( - state == Coordinator.RitualState.DKG_INVALID || - state == Coordinator.RitualState.DKG_TIMEOUT, - "Ritual is not failed" - ); - - // Process pending fees in Coordinator, if necessary - uint256 refundAmount = request.payment; - refundAmount -= feeModel.feeDeduction(request.payment, request.duration); - if (feeModel.pendingFees(ritualId) > 0) { - feeModel.processPendingFee(ritualId); - } - - // Erase payment and transfer refund to original sender - request.payment = 0; - currency.safeTransfer(request.sender, refundAmount); - emit FailedRequestRefunded(requestIndex, refundAmount); - // TODO consider gas refund by setting zero values - } -} diff --git a/contracts/contracts/coordination/Coordinator.sol b/contracts/contracts/coordination/Coordinator.sol index 9be31fb9..125a559f 100644 --- a/contracts/contracts/coordination/Coordinator.sol +++ b/contracts/contracts/coordination/Coordinator.sol @@ -88,7 +88,6 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable BLS12381.G2Point publicKey; } - bytes32 public constant INITIATOR_ROLE = keccak256("INITIATOR_ROLE"); bytes32 public constant TREASURY_ROLE = keccak256("TREASURY_ROLE"); ITACoChildApplication public immutable application; @@ -97,11 +96,11 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable Ritual[] public rituals; uint32 public timeout; uint16 public maxDkgSize; - bool public isInitiationPublic; + bool private stub1; // former isInitiationPublic - uint256 private stub1; // former totalPendingFees - mapping(uint256 => uint256) private stub2; // former pendingFees - address private stub3; // former feeModel + uint256 private stub2; // former totalPendingFees + mapping(uint256 => uint256) private stub3; // former pendingFees + address private stub4; // former feeModel IReimbursementPool internal reimbursementPool; mapping(address => ParticipantKey[]) internal participantKeysHistory; @@ -144,6 +143,10 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable return rituals[ritualId].accessController; } + function getFeeModel(uint32 ritualId) external view returns (IFeeModel) { + return rituals[ritualId].feeModel; + } + function getRitualState(uint32 ritualId) external view returns (RitualState) { return getRitualState(rituals[ritualId]); } @@ -190,11 +193,6 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable } } - function makeInitiationPublic() external onlyRole(DEFAULT_ADMIN_ROLE) { - isInitiationPublic = true; - _setRoleAdmin(INITIATOR_ROLE, bytes32(0)); - } - function setProviderPublicKey(BLS12381.G2Point calldata publicKey) external { uint32 lastRitualId = uint32(rituals.length); address stakingProvider = application.operatorToStakingProvider(msg.sender); @@ -280,10 +278,6 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable ) external returns (uint32) { require(authority != address(0), "Invalid authority"); - require( - isInitiationPublic || hasRole(INITIATOR_ROLE, msg.sender), - "Sender can't initiate ritual" - ); require(feeModelsRegistry[feeModel], "Fee model must be approved"); uint16 length = uint16(providers.length); require(2 <= length && length <= maxDkgSize, "Invalid number of nodes"); @@ -549,6 +543,17 @@ contract Coordinator is Initializable, AccessControlDefaultAdminRulesUpgradeable return found; } + /// @dev Deprecated, see issue #195 + function isEncryptionAuthorized( + uint32 ritualId, + bytes memory evidence, + bytes memory ciphertextHeader + ) external view returns (bool) { + Ritual storage ritual = rituals[ritualId]; + require(getRitualState(ritual) == RitualState.ACTIVE, "Ritual not active"); + return ritual.accessController.isAuthorized(ritualId, evidence, ciphertextHeader); + } + function processReimbursement(uint256 initialGasLeft) internal { if (address(reimbursementPool) != address(0)) { uint256 gasUsed = initialGasLeft - gasleft(); diff --git a/contracts/contracts/coordination/GlobalAllowList.sol b/contracts/contracts/coordination/GlobalAllowList.sol index c7658ac8..656073be 100644 --- a/contracts/contracts/coordination/GlobalAllowList.sol +++ b/contracts/contracts/coordination/GlobalAllowList.sol @@ -17,7 +17,6 @@ contract GlobalAllowList is IEncryptionAuthorizer { using ECDSA for bytes32; Coordinator public immutable coordinator; - IFeeModel public immutable feeModel; mapping(bytes32 => bool) internal authorizations; @@ -41,16 +40,11 @@ contract GlobalAllowList is IEncryptionAuthorizer { * @notice Sets the coordinator contract * @dev The coordinator contract cannot be a zero address and must have a valid number of rituals * @param _coordinator The address of the coordinator contract - * @param _feeModel The address of the fee model contract */ - constructor(Coordinator _coordinator, IFeeModel _feeModel) { - require( - address(_coordinator) != address(0) && address(_feeModel) != address(0), - "Contracts cannot be zero addresses" - ); + constructor(Coordinator _coordinator) { + require(address(_coordinator) != address(0), "Contracts cannot be zero addresses"); require(_coordinator.numberOfRituals() >= 0, "Invalid coordinator"); coordinator = _coordinator; - feeModel = _feeModel; } /** @@ -88,6 +82,7 @@ contract GlobalAllowList is IEncryptionAuthorizer { // solhint-disable-next-line no-unused-vars bytes memory ciphertextHeader ) internal view virtual { + IFeeModel feeModel = coordinator.getFeeModel(ritualId); feeModel.beforeIsAuthorized(ritualId); } @@ -120,6 +115,7 @@ contract GlobalAllowList is IEncryptionAuthorizer { address[] calldata addresses, bool value ) internal virtual { + IFeeModel feeModel = coordinator.getFeeModel(ritualId); feeModel.beforeSetAuthorization(ritualId, addresses, value); } diff --git a/contracts/contracts/coordination/ManagedAllowList.sol b/contracts/contracts/coordination/ManagedAllowList.sol index 547c8d45..1d06f077 100644 --- a/contracts/contracts/coordination/ManagedAllowList.sol +++ b/contracts/contracts/coordination/ManagedAllowList.sol @@ -39,9 +39,8 @@ contract ManagedAllowList is GlobalAllowList { */ constructor( Coordinator _coordinator, - IFeeModel _feeModel, - UpfrontSubscriptionWithEncryptorsCap _subscription - ) GlobalAllowList(_coordinator, _feeModel) { + UpfrontSubscriptionWithEncryptorsCap _subscription // TODO replace with IFeeModel subscription + ) GlobalAllowList(_coordinator) { require(address(_subscription) != address(0), "Subscription cannot be the zero address"); subscription = _subscription; } diff --git a/contracts/contracts/coordination/subscription/BqETHSubscription.sol b/contracts/contracts/coordination/subscription/BqETHSubscription.sol index 085782a6..b89b2aed 100644 --- a/contracts/contracts/coordination/subscription/BqETHSubscription.sol +++ b/contracts/contracts/coordination/subscription/BqETHSubscription.sol @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; import "./EncryptorSlotsSubscription.sol"; +import "../GlobalAllowList.sol"; /** * @title BqETH Subscription @@ -30,7 +31,7 @@ contract BqETHSubscription is EncryptorSlotsSubscription, Initializable, Ownable uint256 public immutable encryptorFeeRate; uint256 public immutable maxNodes; - IEncryptionAuthorizer public accessController; + GlobalAllowList public immutable accessController; uint32 public activeRitualId; mapping(uint256 periodNumber => Billing billing) public billingInfo; @@ -75,6 +76,7 @@ contract BqETHSubscription is EncryptorSlotsSubscription, Initializable, Ownable * @notice Sets the coordinator and fee token contracts * @dev The coordinator and fee token contracts cannot be zero addresses * @param _coordinator The address of the coordinator contract + * @param _accessController The address of the global allow list * @param _feeToken The address of the fee token contract * @param _adopter The address of the adopter * @param _baseFeeRate Fee rate per node per second @@ -86,6 +88,7 @@ contract BqETHSubscription is EncryptorSlotsSubscription, Initializable, Ownable */ constructor( Coordinator _coordinator, + GlobalAllowList _accessController, IERC20 _feeToken, address _adopter, uint256 _baseFeeRate, @@ -104,11 +107,16 @@ contract BqETHSubscription is EncryptorSlotsSubscription, Initializable, Ownable { require(address(_feeToken) != address(0), "Fee token cannot be the zero address"); require(_adopter != address(0), "Adopter cannot be the zero address"); + require( + address(_accessController) != address(0), + "Access controller cannot be the zero address" + ); feeToken = _feeToken; adopter = _adopter; baseFeeRate = _baseFeeRate; encryptorFeeRate = _encryptorFeeRate; maxNodes = _maxNodes; + accessController = _accessController; _disableInitializers(); } @@ -131,15 +139,7 @@ contract BqETHSubscription is EncryptorSlotsSubscription, Initializable, Ownable /** * @notice Initialize function for using with OpenZeppelin proxy */ - function initialize( - address _treasury, - IEncryptionAuthorizer _accessController - ) external initializer { - require( - address(accessController) == address(0) && address(_accessController) != address(0), - "Access controller not already set and parameter cannot be the zero address" - ); - accessController = _accessController; + function initialize(address _treasury) external initializer { activeRitualId = INACTIVE_RITUAL_ID; __Ownable_init(_treasury); } diff --git a/contracts/test/BetaProgramInitiatorTestSet.sol b/contracts/test/BetaProgramInitiatorTestSet.sol deleted file mode 100644 index 91cdc17f..00000000 --- a/contracts/test/BetaProgramInitiatorTestSet.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -pragma solidity ^0.8.0; - -import "../contracts/coordination/IEncryptionAuthorizer.sol"; -import "../contracts/coordination/Coordinator.sol"; -import "../contracts/coordination/IFeeModel.sol"; - -contract CoordinatorForBetaProgramInitiatorMock { - struct Ritual { - address initiator; - address[] providers; - address authority; - uint32 duration; - IEncryptionAuthorizer accessController; - Coordinator.RitualState state; - uint256 ritualCost; - IFeeModel feeModel; - } - - Ritual[] public rituals; - - function getRitualsLength() external view returns (uint256) { - return rituals.length; - } - - function getRitualState(uint32 _ritualId) external view returns (Coordinator.RitualState) { - if (_ritualId > rituals.length) { - return Coordinator.RitualState.NON_INITIATED; - } - return rituals[_ritualId].state; - } - - function getProviders(uint256 _ritualId) external view returns (address[] memory) { - Ritual storage ritual = rituals[_ritualId]; - return ritual.providers; - } - - function setRitualState(uint32 _ritualId, Coordinator.RitualState _state) external { - rituals[_ritualId].state = _state; - } - - function initiateRitual( - IFeeModel _feeModel, - address[] calldata _providers, - address _authority, - uint32 _duration, - IEncryptionAuthorizer _accessController - ) external returns (uint32 ritualId) { - Ritual storage ritual = rituals.push(); - ritual.initiator = msg.sender; - ritual.providers = _providers; - ritual.authority = _authority; - ritual.duration = _duration; - ritual.accessController = _accessController; - ritual.feeModel = _feeModel; - ritual.state = Coordinator.RitualState.DKG_AWAITING_TRANSCRIPTS; - - uint32 id = uint32(rituals.length - 1); - _feeModel.processRitualPayment(msg.sender, id, _providers.length, _duration); - - return id; - } - - function getInitiator(uint32 ritualId) external view returns (address) { - return rituals[ritualId].initiator; - } - - function getTimestamps( - uint32 ritualId - ) external view returns (uint32 initTimestamp, uint32 endTimestamp) { - initTimestamp = uint32(block.timestamp); - endTimestamp = uint32(block.timestamp + rituals[ritualId].duration); - } -} diff --git a/contracts/test/BqETHSubscriptionTestSet.sol b/contracts/test/BqETHSubscriptionTestSet.sol index a31883a8..0bbf188a 100644 --- a/contracts/test/BqETHSubscriptionTestSet.sol +++ b/contracts/test/BqETHSubscriptionTestSet.sol @@ -59,6 +59,10 @@ contract CoordinatorForBqETHSubscriptionMock { return rituals[_ritualId].state; } + function getFeeModel(uint32) external view returns (IFeeModel) { + return feeModel; + } + function getTimestamps( uint32 _ritualId ) external view returns (uint32 initTimestamp, uint32 endTimestamp) { diff --git a/scripts/grant_initiator_role.py b/scripts/grant_initiator_role.py deleted file mode 100644 index 96b2beaa..00000000 --- a/scripts/grant_initiator_role.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python3 - -import click -from ape import networks, project -from ape.cli import ConnectedProviderCommand, account_option, network_option - -from deployment.constants import SUPPORTED_TACO_DOMAINS -from deployment.params import Transactor -from deployment.registry import contracts_from_registry -from deployment.utils import check_plugins, registry_filepath_from_domain - - -@click.command(cls=ConnectedProviderCommand) -@network_option(required=True) -@account_option() -@click.option( - "--domain", - "-d", - help="TACo domain", - type=click.Choice(SUPPORTED_TACO_DOMAINS), - required=True, -) -@click.option( - "--grant-address", - "-g", - help="Address to grant initiator role", - type=str, - required=False, -) -def cli(network, account, domain, grant_address): - check_plugins() - transactor = Transactor(account) - registry_filepath = registry_filepath_from_domain(domain=domain) - deployments = contracts_from_registry( - filepath=registry_filepath, chain_id=networks.active_provider.chain_id - ) - coordinator = deployments[project.Coordinator.contract_type.name] - initiator_role_hash = coordinator.INITIATOR_ROLE() - transactor.transact( - coordinator.grantRole, - initiator_role_hash, - grant_address, # <- new initiator - ) - - -if __name__ == "__main__": - cli() diff --git a/tests/test_beta_program_initiator.py b/tests/test_beta_program_initiator.py deleted file mode 100644 index b7c4fa73..00000000 --- a/tests/test_beta_program_initiator.py +++ /dev/null @@ -1,465 +0,0 @@ -""" -This file is part of nucypher. - -nucypher is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -nucypher is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with nucypher. If not, see . -""" -from enum import IntEnum - -import ape -import pytest - -DAY_IN_SECONDS = 60 * 60 * 24 - -AUTHORITY_SLOT = 0 -DURATION_SLOT = 1 -ACCESS_CONTROLLER_SLOT = 2 -SENDER_SLOT = 3 -RITUAL_ID_SLOT = 4 -PAYMENT_SLOT = 5 -FEE_RATE = 42 - -RitualState = IntEnum( - "RitualState", - [ - "NON_INITIATED", - "DKG_AWAITING_TRANSCRIPTS", - "DKG_AWAITING_AGGREGATIONS", - "DKG_TIMEOUT", - "DKG_INVALID", - "ACTIVE", - "EXPIRED", - ], - start=0, -) - - -@pytest.fixture() -def executor(accounts): - return accounts[1] - - -@pytest.fixture() -def token(project, creator): - token = project.TestToken.deploy(0, sender=creator) - return token - - -@pytest.fixture() -def coordinator(project, creator): - contract = project.CoordinatorForBetaProgramInitiatorMock.deploy(sender=creator) - return contract - - -@pytest.fixture() -def fee_model(project, creator, coordinator, token): - contract = project.FlatRateFeeModel.deploy( - coordinator.address, token.address, FEE_RATE, sender=creator - ) - return contract - - -@pytest.fixture() -def beta_program_initiator(project, coordinator, executor, creator, fee_model): - contract = project.BetaProgramInitiator.deploy( - coordinator.address, executor, fee_model.address, sender=creator - ) - return contract - - -def test_register(accounts, beta_program_initiator, token, fee_model): - ( - initiator_1, - initiator_2, - authority, - node_1, - node_2, - access_controller, - *everyone_else, - ) = accounts[2:] - no_ritual = beta_program_initiator.NO_RITUAL() - - nodes = [node_1, node_2] - duration = DAY_IN_SECONDS - ritual_cost = fee_model.getRitualCost(len(nodes), duration) - - # Can't register request without token transfer approval - with ape.reverts(): - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_1 - ) - - # Register request - token.mint(initiator_1, 10 * ritual_cost, sender=initiator_1) - token.approve(beta_program_initiator.address, 10 * ritual_cost, sender=initiator_1) - tx = beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_1 - ) - assert beta_program_initiator.getRequestsLength() == 1 - assert beta_program_initiator.getProviders(0) == nodes - request = beta_program_initiator.requests(0) - assert request[AUTHORITY_SLOT] == authority - assert request[DURATION_SLOT] == duration - assert request[ACCESS_CONTROLLER_SLOT] == access_controller - assert request[SENDER_SLOT] == initiator_1 - assert request[RITUAL_ID_SLOT] == no_ritual - assert request[PAYMENT_SLOT] == ritual_cost - assert token.balanceOf(beta_program_initiator) == ritual_cost - - events = beta_program_initiator.RequestRegistered.from_receipt(tx) - assert len(events) == 1 - event = events[0] - assert event.sender == initiator_1 - assert event.requestIndex == 0 - assert event.providers == [n.address for n in nodes] - assert event.authority == authority - assert event.duration == duration - assert event.accessController == access_controller - assert event.payment == ritual_cost - - # Register another request - nodes = [node_1] - duration = 3 * DAY_IN_SECONDS - ritual_cost_2 = fee_model.getRitualCost(len(nodes), duration) - - token.mint(initiator_2, ritual_cost_2, sender=initiator_2) - token.approve(beta_program_initiator.address, ritual_cost_2, sender=initiator_2) - tx = beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_2 - ) - assert beta_program_initiator.getRequestsLength() == 2 - assert beta_program_initiator.getProviders(1) == nodes - request = beta_program_initiator.requests(1) - assert request[AUTHORITY_SLOT] == authority - assert request[DURATION_SLOT] == duration - assert request[ACCESS_CONTROLLER_SLOT] == access_controller - assert request[SENDER_SLOT] == initiator_2 - assert request[RITUAL_ID_SLOT] == no_ritual - assert request[PAYMENT_SLOT] == ritual_cost_2 - assert token.balanceOf(beta_program_initiator) == ritual_cost + ritual_cost_2 - - events = beta_program_initiator.RequestRegistered.from_receipt(tx) - assert len(events) == 1 - event = events[0] - assert event.sender == initiator_2 - assert event.requestIndex == 1 - assert event.providers == [n.address for n in nodes] - assert event.authority == authority - assert event.duration == duration - assert event.accessController == access_controller - assert event.payment == ritual_cost_2 - - -def test_cancel(accounts, beta_program_initiator, token, executor, fee_model): - ( - initiator_1, - initiator_2, - authority, - node_1, - node_2, - access_controller, - *everyone_else, - ) = accounts[2:] - - nodes = [node_1, node_2] - duration = DAY_IN_SECONDS - ritual_cost = fee_model.getRitualCost(len(nodes), duration) - - token.mint(initiator_1, 10 * ritual_cost, sender=initiator_1) - token.approve(beta_program_initiator.address, 10 * ritual_cost, sender=initiator_1) - token.mint(initiator_2, 10 * ritual_cost, sender=initiator_2) - token.approve(beta_program_initiator.address, 10 * ritual_cost, sender=initiator_2) - - # Can't cancel non-existent request - with ape.reverts("Non-existent request"): - beta_program_initiator.cancelInitiationRequest(0, sender=executor) - - # Register three requests - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_1 - ) - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_1 - ) - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration, access_controller, sender=initiator_2 - ) - - # Only initiator or executor can cancel request - with ape.reverts("Not allowed to cancel"): - beta_program_initiator.cancelInitiationRequest(0, sender=initiator_2) - with ape.reverts("Not allowed to cancel"): - beta_program_initiator.cancelInitiationRequest(2, sender=initiator_1) - - # Initiator cancels request - balance_before = token.balanceOf(initiator_1) - tx = beta_program_initiator.cancelInitiationRequest(0, sender=initiator_1) - assert beta_program_initiator.requests(0)[PAYMENT_SLOT] == 0 - balance_after = token.balanceOf(initiator_1) - assert balance_after - balance_before == ritual_cost - - events = beta_program_initiator.RequestCanceled.from_receipt(tx) - assert events == [beta_program_initiator.RequestCanceled(initiator_1, 0)] - - # Executor cancels request - balance_before = token.balanceOf(initiator_2) - tx = beta_program_initiator.cancelInitiationRequest(2, sender=executor) - assert beta_program_initiator.requests(2)[PAYMENT_SLOT] == 0 - balance_after = token.balanceOf(initiator_2) - assert balance_after - balance_before == ritual_cost - - events = beta_program_initiator.RequestCanceled.from_receipt(tx) - assert events == [beta_program_initiator.RequestCanceled(executor, 2)] - - # Can't cancel twice - with ape.reverts("Request canceled"): - beta_program_initiator.cancelInitiationRequest(0, sender=executor) - with ape.reverts("Request canceled"): - beta_program_initiator.cancelInitiationRequest(2, sender=initiator_2) - - # Can't cancel an executed request - beta_program_initiator.executeInitiationRequest(1, sender=executor) - with ape.reverts("Request already executed"): - beta_program_initiator.cancelInitiationRequest(1, sender=executor) - - -def test_execute(accounts, beta_program_initiator, token, coordinator, executor, fee_model): - ( - initiator_1, - initiator_2, - authority_1, - authority_2, - node_1, - node_2, - access_controller_1, - access_controller_2, - *everyone_else, - ) = accounts[2:] - no_ritual = beta_program_initiator.NO_RITUAL() - - nodes_1 = [node_1, node_2] - duration_1 = DAY_IN_SECONDS - ritual_cost_1 = fee_model.getRitualCost(len(nodes_1), duration_1) - nodes_2 = [node_1] - duration_2 = 2 * duration_1 - ritual_cost_2 = fee_model.getRitualCost(len(nodes_2), duration_2) - - token.mint(initiator_1, 10 * ritual_cost_1, sender=initiator_1) - token.approve(beta_program_initiator.address, 10 * ritual_cost_1, sender=initiator_1) - token.mint(initiator_2, 10 * ritual_cost_2, sender=initiator_2) - token.approve(beta_program_initiator.address, 10 * ritual_cost_2, sender=initiator_2) - - # Can't execute non-existent request - with ape.reverts("Non-existent request"): - beta_program_initiator.executeInitiationRequest(0, sender=executor) - - # Register three requests - beta_program_initiator.registerInitiationRequest( - nodes_1, authority_1, duration_1, access_controller_1, sender=initiator_1 - ) - beta_program_initiator.registerInitiationRequest( - nodes_2, authority_2, duration_2, access_controller_2, sender=initiator_2 - ) - beta_program_initiator.registerInitiationRequest( - nodes_2, authority_1, duration_1, access_controller_2, sender=initiator_1 - ) - - # Only executor can execute request - with ape.reverts("Only executor can call"): - beta_program_initiator.executeInitiationRequest(0, sender=initiator_1) - - # Can't execute canceled request - beta_program_initiator.cancelInitiationRequest(2, sender=initiator_1) - with ape.reverts("Request canceled"): - beta_program_initiator.executeInitiationRequest(2, sender=executor) - - # Execute request - balance_before = token.balanceOf(beta_program_initiator.address) - tx = beta_program_initiator.executeInitiationRequest(1, sender=executor) - assert beta_program_initiator.requests(1)[RITUAL_ID_SLOT] == 0 - assert beta_program_initiator.requests(0)[RITUAL_ID_SLOT] == no_ritual - balance_after = token.balanceOf(beta_program_initiator.address) - assert balance_before - balance_after == ritual_cost_2 - assert token.balanceOf(fee_model.address) == ritual_cost_2 - - assert coordinator.getRitualsLength() == 1 - assert coordinator.getProviders(0) == nodes_2 - ritual = coordinator.rituals(0) - assert ritual[0] == beta_program_initiator.address - assert ritual[1] == authority_2 - assert ritual[2] == duration_2 - assert ritual[3] == access_controller_2 - assert ritual[4] == 1 - # assert ritual[5] == ritual_cost_2 - assert ritual[6] == fee_model.address - - events = beta_program_initiator.RequestExecuted.from_receipt(tx) - assert events == [beta_program_initiator.RequestExecuted(1, 0)] - - # Can't execute twice - with ape.reverts("Request already executed"): - beta_program_initiator.executeInitiationRequest(1, sender=executor) - - # Can't execute request if ritual cost changes - # fee_rate = fee_model.feeRatePerSecond() - # coordinator.setFeeRatePerSecond(2 * fee_rate, sender=executor) - # with ape.reverts("Ritual initiation cost has changed"): - # beta_program_initiator.executeInitiationRequest(0, sender=executor) - # coordinator.setFeeRatePerSecond(fee_rate // 2, sender=executor) - # with ape.reverts("Ritual initiation cost has changed"): - # beta_program_initiator.executeInitiationRequest(0, sender=executor) - - # Return fee rate back and execute request again - # coordinator.setFeeRatePerSecond(fee_rate, sender=executor) - balance_before = token.balanceOf(beta_program_initiator.address) - tx = beta_program_initiator.executeInitiationRequest(0, sender=executor) - assert beta_program_initiator.requests(0)[RITUAL_ID_SLOT] == 1 - assert beta_program_initiator.requests(1)[RITUAL_ID_SLOT] == 0 - balance_after = token.balanceOf(beta_program_initiator.address) - assert balance_before - balance_after == ritual_cost_1 - assert token.balanceOf(fee_model.address) == ritual_cost_2 + ritual_cost_1 - - assert coordinator.getRitualsLength() == 2 - assert coordinator.getProviders(1) == nodes_1 - ritual = coordinator.rituals(1) - assert ritual[0] == beta_program_initiator.address - assert ritual[1] == authority_1 - assert ritual[2] == duration_1 - assert ritual[3] == access_controller_1 - assert ritual[4] == 1 - # assert ritual[5] == ritual_cost_1 - assert ritual[6] == fee_model.address - - events = beta_program_initiator.RequestExecuted.from_receipt(tx) - assert events == [beta_program_initiator.RequestExecuted(0, 1)] - - -def test_refund(accounts, beta_program_initiator, token, coordinator, executor, fee_model): - ( - initiator_1, - initiator_2, - authority, - node_1, - node_2, - access_controller, - *everyone_else, - ) = accounts[2:] - - nodes = [node_1, node_2] - duration_1 = DAY_IN_SECONDS - ritual_cost_1 = fee_model.getRitualCost(len(nodes), duration_1) - duration_2 = 3 * duration_1 - ritual_cost_2 = fee_model.getRitualCost(len(nodes), duration_2) - - token.mint(initiator_1, 10 * ritual_cost_1, sender=initiator_1) - token.approve(beta_program_initiator.address, 10 * ritual_cost_1, sender=initiator_1) - token.mint(initiator_2, 10 * ritual_cost_2, sender=initiator_2) - token.approve(beta_program_initiator.address, 10 * ritual_cost_2, sender=initiator_2) - - # Can't refund non-existent request - with ape.reverts("Non-existent request"): - beta_program_initiator.refundFailedRequest(0, sender=executor) - - # Register three requests - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration_1, access_controller, sender=initiator_1 - ) - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration_2, access_controller, sender=initiator_2 - ) - beta_program_initiator.registerInitiationRequest( - nodes, authority, duration_2, access_controller, sender=initiator_1 - ) - - # Can't refund not executed request - with ape.reverts("Request is not executed"): - beta_program_initiator.refundFailedRequest(0, sender=executor) - beta_program_initiator.cancelInitiationRequest(2, sender=initiator_1) - with ape.reverts("Request is not executed"): - beta_program_initiator.refundFailedRequest(2, sender=initiator_2) - - # Can't refund not failed request - beta_program_initiator.executeInitiationRequest(1, sender=executor) - beta_program_initiator.executeInitiationRequest(0, sender=executor) - request_0_ritual_id = 1 - request_1_ritual_id = 0 - for state in [ - RitualState.NON_INITIATED, - RitualState.DKG_AWAITING_TRANSCRIPTS, - RitualState.DKG_AWAITING_AGGREGATIONS, - RitualState.ACTIVE, - RitualState.EXPIRED, - ]: - coordinator.setRitualState(request_0_ritual_id, state, sender=initiator_2) - with ape.reverts("Ritual is not failed"): - beta_program_initiator.refundFailedRequest(request_0_ritual_id, sender=initiator_2) - - # Refund failed request - coordinator.setRitualState(request_0_ritual_id, RitualState.DKG_TIMEOUT, sender=initiator_2) - - assert token.balanceOf(beta_program_initiator.address) == 0 - initiator_1_balance_before = token.balanceOf(initiator_1) - fee_model_balance_before = token.balanceOf(fee_model.address) - assert fee_model_balance_before == ritual_cost_1 + ritual_cost_2 - pending_fees_1_before = fee_model.pendingFees(request_0_ritual_id) - assert pending_fees_1_before == ritual_cost_1 - - tx = beta_program_initiator.refundFailedRequest(0, sender=initiator_2) - - fee_model_balance_after = token.balanceOf(fee_model.address) - fee_deduction_1 = fee_model.feeDeduction(pending_fees_1_before, duration_1) - pending_fees_1_after = fee_model.pendingFees(request_0_ritual_id) - assert fee_model_balance_after == ritual_cost_2 + fee_deduction_1 - assert pending_fees_1_after == 0 - assert token.balanceOf(beta_program_initiator.address) == 0 - - refund_1 = ritual_cost_1 - fee_deduction_1 - initiator_1_balance_after = token.balanceOf(initiator_1) - assert initiator_1_balance_after - initiator_1_balance_before == refund_1 - assert beta_program_initiator.requests(0)[RITUAL_ID_SLOT] == request_0_ritual_id - assert beta_program_initiator.requests(0)[PAYMENT_SLOT] == 0 - - events = beta_program_initiator.FailedRequestRefunded.from_receipt(tx) - assert events == [beta_program_initiator.FailedRequestRefunded(0, refund_1)] - - # Can't refund again - with ape.reverts("Refund already processed"): - beta_program_initiator.refundFailedRequest(0, sender=executor) - - # Refund failed request without pending fees - coordinator.setRitualState(request_1_ritual_id, RitualState.DKG_INVALID, sender=initiator_2) - - assert token.balanceOf(beta_program_initiator.address) == 0 - initiator_2_balance_before = token.balanceOf(initiator_2) - fee_model_balance_before = token.balanceOf(fee_model.address) - assert fee_model_balance_before == ritual_cost_2 + fee_deduction_1 - pending_fees_2_before = fee_model.pendingFees(request_1_ritual_id) - assert pending_fees_2_before == ritual_cost_2 - - fee_model.processPendingFee(request_1_ritual_id, sender=initiator_1) - assert fee_model.pendingFees(request_1_ritual_id) == 0 - - fee_deduction_2 = fee_model.feeDeduction(pending_fees_2_before, duration_2) - refund_2 = ritual_cost_2 - fee_deduction_2 - assert token.balanceOf(beta_program_initiator.address) == refund_2 - - tx = beta_program_initiator.refundFailedRequest(1, sender=initiator_2) - - assert token.balanceOf(beta_program_initiator.address) == 0 - initiator_2_balance_after = token.balanceOf(initiator_2) - assert initiator_2_balance_after - initiator_2_balance_before == refund_2 - assert beta_program_initiator.requests(1)[RITUAL_ID_SLOT] == request_1_ritual_id - assert beta_program_initiator.requests(1)[PAYMENT_SLOT] == 0 - - events = beta_program_initiator.FailedRequestRefunded.from_receipt(tx) - assert events == [beta_program_initiator.FailedRequestRefunded(1, refund_2)] diff --git a/tests/test_bqeth_subscription.py b/tests/test_bqeth_subscription.py index d47ecbc6..4252f269 100644 --- a/tests/test_bqeth_subscription.py +++ b/tests/test_bqeth_subscription.py @@ -77,9 +77,18 @@ def coordinator(project, creator): @pytest.fixture() -def subscription(project, creator, coordinator, erc20, adopter, oz_dependency): +def global_allow_list(project, creator, coordinator): + contract = project.GlobalAllowList.deploy(coordinator.address, sender=creator) + return contract + + +@pytest.fixture() +def subscription( + project, creator, coordinator, global_allow_list, erc20, adopter, treasury, oz_dependency +): contract = project.BqETHSubscription.deploy( coordinator.address, + global_allow_list.address, erc20.address, adopter, BASE_FEE_RATE, @@ -100,18 +109,10 @@ def subscription(project, creator, coordinator, erc20, adopter, oz_dependency): ) proxy_contract = project.BqETHSubscription.at(proxy.address) coordinator.setFeeModel(proxy_contract.address, sender=creator) + proxy_contract.initialize(treasury.address, sender=treasury) return proxy_contract -@pytest.fixture() -def global_allow_list(project, creator, coordinator, subscription, treasury): - contract = project.GlobalAllowList.deploy( - coordinator.address, subscription.address, sender=creator - ) - subscription.initialize(treasury.address, contract.address, sender=treasury) - return contract - - def test_pay_subscription( erc20, subscription, coordinator, global_allow_list, adopter, treasury, chain ): @@ -511,8 +512,6 @@ def test_before_set_authorization( ritual_id = 6 number_of_providers = 7 - assert subscription.address == global_allow_list.feeModel() - with ape.reverts("Only Access Controller can call this method"): subscription.beforeSetAuthorization(0, [creator], True, sender=adopter) @@ -576,8 +575,6 @@ def test_before_is_authorized( signed_digest = w3.eth.account.sign_message(signable_message, private_key=adopter.private_key) signature = signed_digest.signature - assert subscription.address == global_allow_list.feeModel() - with ape.reverts("Only Access Controller can call this method"): subscription.beforeIsAuthorized(0, sender=adopter) diff --git a/tests/test_coordinator.py b/tests/test_coordinator.py index e8ce21dd..22b88b09 100644 --- a/tests/test_coordinator.py +++ b/tests/test_coordinator.py @@ -101,8 +101,6 @@ def coordinator(project, deployer, application, initiator, oz_dependency): sender=deployer, ) proxy_contract = project.Coordinator.at(proxy.address) - - proxy_contract.grantRole(contract.INITIATOR_ROLE(), initiator, sender=admin) return proxy_contract @@ -117,10 +115,8 @@ def fee_model(project, deployer, coordinator, erc20, treasury): @pytest.fixture() -def global_allow_list(project, deployer, coordinator, fee_model): - contract = project.GlobalAllowList.deploy( - coordinator.address, fee_model.address, sender=deployer - ) +def global_allow_list(project, deployer, coordinator): + contract = project.GlobalAllowList.deploy(coordinator.address, sender=deployer) return contract @@ -133,12 +129,6 @@ def test_initial_parameters(coordinator): def test_invalid_initiate_ritual( project, coordinator, nodes, accounts, initiator, fee_model, global_allow_list ): - with ape.reverts("Sender can't initiate ritual"): - sender = accounts[3] - coordinator.initiateRitual( - fee_model.address, nodes, sender, DURATION, global_allow_list.address, sender=sender - ) - with ape.reverts("Invalid number of nodes"): coordinator.initiateRitual( fee_model.address, diff --git a/tests/test_global_allow_list.py b/tests/test_global_allow_list.py index 72a8a0a5..1294fd3a 100644 --- a/tests/test_global_allow_list.py +++ b/tests/test_global_allow_list.py @@ -100,8 +100,6 @@ def coordinator(project, deployer, application, initiator, oz_dependency): sender=deployer, ) proxy_contract = project.Coordinator.at(proxy.address) - - proxy_contract.grantRole(contract.INITIATOR_ROLE(), initiator, sender=admin) return proxy_contract @@ -116,10 +114,8 @@ def fee_model(project, deployer, coordinator, erc20, treasury): @pytest.fixture() -def global_allow_list(project, deployer, coordinator, fee_model): - contract = project.GlobalAllowList.deploy( - coordinator.address, fee_model.address, sender=deployer - ) +def global_allow_list(project, deployer, coordinator): + contract = project.GlobalAllowList.deploy(coordinator.address, sender=deployer) return contract @@ -168,6 +164,9 @@ def test_authorize_using_global_allow_list( with ape.reverts("Only active rituals can set authorizations"): global_allow_list.authorize(0, [deployer.address], sender=initiator) + with ape.reverts("Ritual not active"): + coordinator.isEncryptionAuthorized(0, bytes(signature), bytes(digest)) + # Finalize ritual transcript = os.urandom(transcript_size(len(nodes), len(nodes))) for node in nodes: @@ -186,6 +185,7 @@ def test_authorize_using_global_allow_list( # Authorized assert global_allow_list.isAuthorized(0, bytes(signature), bytes(data)) + assert coordinator.isEncryptionAuthorized(0, bytes(signature), bytes(data)) events = global_allow_list.AddressAuthorizationSet.from_receipt(tx) assert events == [ global_allow_list.AddressAuthorizationSet( @@ -197,6 +197,7 @@ def test_authorize_using_global_allow_list( tx = global_allow_list.deauthorize(0, [deployer.address], sender=initiator) assert not global_allow_list.isAuthorized(0, bytes(signature), bytes(data)) + assert not coordinator.isEncryptionAuthorized(0, bytes(signature), bytes(data)) events = global_allow_list.AddressAuthorizationSet.from_receipt(tx) assert events == [ global_allow_list.AddressAuthorizationSet( @@ -210,8 +211,10 @@ def test_authorize_using_global_allow_list( signed_digest = w3.eth.account.sign_message(signable_message, private_key=initiator.private_key) initiator_signature = signed_digest.signature assert global_allow_list.isAuthorized(0, bytes(initiator_signature), bytes(data)) + assert coordinator.isEncryptionAuthorized(0, bytes(initiator_signature), bytes(data)) assert global_allow_list.isAuthorized(0, bytes(signature), bytes(data)) + assert coordinator.isEncryptionAuthorized(0, bytes(signature), bytes(data)) events = global_allow_list.AddressAuthorizationSet.from_receipt(tx) assert events == [ diff --git a/tests/test_managed_allow_list.py b/tests/test_managed_allow_list.py index 3f5905cd..05000d85 100644 --- a/tests/test_managed_allow_list.py +++ b/tests/test_managed_allow_list.py @@ -63,7 +63,7 @@ def fee_model(project, deployer, coordinator, fee_token): @pytest.fixture() def brand_new_managed_allow_list(project, coordinator, subscription, fee_model, authority): return project.ManagedAllowList.deploy( - coordinator.address, fee_model.address, subscription.address, sender=authority + coordinator.address, subscription.address, sender=authority ) @@ -109,6 +109,7 @@ def test_remove_administrators(managed_allow_list, authority, admin): assert managed_allow_list.authActions(RITUAL_ID) == 2 +@pytest.mark.skip(reason="finish tests when managed allow list will use fee model") def test_authorize( managed_allow_list, subscription, fee_token, deployer, authority, admin, encryptor ): @@ -140,6 +141,7 @@ def test_authorize( assert managed_allow_list.isAddressAuthorized(RITUAL_ID, encryptor) +@pytest.mark.skip(reason="finish tests when managed allow list will use fee model") def test_deauthorize( managed_allow_list, subscription, fee_token, deployer, authority, admin, encryptor ):