Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move getRequiredRole from Component to ComponentController #20

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions brownie-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ compiler:
remappings:
- "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.7.0"
- "@chainlink=smartcontractkit/chainlink@1.6.0"
- "@etherisc/gif-interface=etherisc/gif-interface@a12bb3f"
- "@etherisc/gif-interface=etherisc/gif-interface@0f870d9"

# packages below will be added to brownie
# you may use 'brownie pm list' after 'brownie compile'
Expand All @@ -31,7 +31,7 @@ dependencies:
# github dependency format: <owner>/<repository>@<release>
- OpenZeppelin/openzeppelin-contracts@4.7.0
- smartcontractkit/chainlink@1.6.0
- etherisc/gif-interface@a12bb3f
- etherisc/gif-interface@0f870d9

# exclude Ownable when calculating test coverage
# https://eth-brownie.readthedocs.io/en/v1.10.3/config.html#exclude_paths
Expand Down
12 changes: 5 additions & 7 deletions contracts/flows/PolicyDefaultFlow.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "../modules/ComponentController.sol";
import "../modules/PoolController.sol";
import "../modules/PolicyController.sol";
import "../modules/TreasuryModule.sol";
import "../shared/WithRegistry.sol";
// import "../shared/CoreController.sol";

import "@etherisc/gif-interface/contracts/modules/ILicense.sol";
import "@etherisc/gif-interface/contracts/modules/IPolicy.sol";
import "@etherisc/gif-interface/contracts/modules/IQuery.sol";
import "@etherisc/gif-interface/contracts/modules/IRegistry.sol";
Expand All @@ -24,7 +23,6 @@ import "@etherisc/gif-interface/contracts/modules/IPool.sol";

contract PolicyDefaultFlow is
WithRegistry
// CoreController
{
bytes32 public constant NAME = "PolicyDefaultFlow";

Expand Down Expand Up @@ -61,8 +59,8 @@ contract PolicyDefaultFlow is
external
returns(bytes32 processId)
{
ILicense license = getLicenseContract();
uint256 productId = license.getProductId(msg.sender);
ComponentController component = getComponentContract();
uint256 productId = component.getComponentId(msg.sender);

IPolicy policy = getPolicyContract();
processId = policy.createPolicyFlow(owner, productId, metaData);
Expand Down Expand Up @@ -265,8 +263,8 @@ contract PolicyDefaultFlow is
return policy.getPayout(processId, payoutId).data;
}

function getLicenseContract() internal view returns (ILicense) {
return ILicense(getContractFromRegistry("License"));
function getComponentContract() internal view returns (ComponentController) {
return ComponentController(getContractFromRegistry("Component"));
}

function getPoolContract() internal view returns (PoolController) {
Expand Down
28 changes: 17 additions & 11 deletions contracts/modules/ComponentController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ contract ComponentController is
mapping(bytes32 => uint256) private _componentIdByName;
mapping(address => uint256) private _componentIdByAddress;

mapping(uint256 => IComponent.ComponentType) private _componentType;
mapping(uint256 => IComponent.ComponentState) private _componentState;

uint256 [] private _products;
Expand Down Expand Up @@ -68,7 +67,6 @@ contract ComponentController is

// update component state
_changeState(id, IComponent.ComponentState.Proposed);
_componentType[id] = component.getType();
component.setId(id);

// update controller book keeping
Expand Down Expand Up @@ -196,13 +194,21 @@ contract ComponentController is
}

function getComponentType(uint256 id) public view returns (IComponent.ComponentType componentType) {
return _componentType[id];
IComponent component = _componentById[id];
componentType = component.getType();
}

function getComponentState(uint256 id) public view returns (IComponent.ComponentState componentState) {
return _componentState[id];
}

function getRequiredRole(IComponent.ComponentType componentType) external returns (bytes32) {
if (componentType == IComponent.ComponentType.Product) { return _access.productOwnerRole(); }
else if (componentType == IComponent.ComponentType.Oracle) { return _access.oracleProviderRole(); }
else if (componentType == IComponent.ComponentType.Riskpool) { return _access.riskpoolKeeperRole(); }
else { revert("ERROR:CCR-008:COMPONENT_TYPE_UNKNOWN"); }
}

function components() public view returns (uint256 count) { return _componentCount; }
function products() public view returns (uint256 count) { return _products.length; }
function oracles() public view returns (uint256 count) { return _oracles.length; }
Expand All @@ -226,31 +232,31 @@ contract ComponentController is
pure
{
require(newState != oldState,
"ERROR:CMP-011:SOURCE_AND_TARGET_STATE_IDENTICAL");
"ERROR:CCR-011:SOURCE_AND_TARGET_STATE_IDENTICAL");

if (oldState == IComponent.ComponentState.Created) {
require(newState == IComponent.ComponentState.Proposed,
"ERROR:CMP-012:CREATED_INVALID_TRANSITION");
"ERROR:CCR-012:CREATED_INVALID_TRANSITION");
} else if (oldState == IComponent.ComponentState.Proposed) {
require(newState == IComponent.ComponentState.Active
|| newState == IComponent.ComponentState.Declined,
"ERROR:CMP-013:PROPOSED_INVALID_TRANSITION");
"ERROR:CCR-013:PROPOSED_INVALID_TRANSITION");
} else if (oldState == IComponent.ComponentState.Declined) {
revert("ERROR:CMP-014:DECLINED_IS_FINAL_STATE");
revert("ERROR:CCR-014:DECLINED_IS_FINAL_STATE");
} else if (oldState == IComponent.ComponentState.Active) {
require(newState == IComponent.ComponentState.Paused
|| newState == IComponent.ComponentState.Suspended,
"ERROR:CMP-015:ACTIVE_INVALID_TRANSITION");
"ERROR:CCR-015:ACTIVE_INVALID_TRANSITION");
} else if (oldState == IComponent.ComponentState.Paused) {
require(newState == IComponent.ComponentState.Active
|| newState == IComponent.ComponentState.Archived,
"ERROR:CMP-016:PAUSED_INVALID_TRANSITION");
"ERROR:CCR-016:PAUSED_INVALID_TRANSITION");
} else if (oldState == IComponent.ComponentState.Suspended) {
require(newState == IComponent.ComponentState.Active
|| newState == IComponent.ComponentState.Archived,
"ERROR:CMP-017:SUSPENDED_INVALID_TRANSITION");
"ERROR:CCR-017:SUSPENDED_INVALID_TRANSITION");
} else {
revert("ERROR:CMP-018:INITIAL_STATE_NOT_HANDLED");
revert("ERROR:CCR-018:INITIAL_STATE_NOT_HANDLED");
}
}
}
18 changes: 5 additions & 13 deletions contracts/modules/LicenseController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,19 @@ contract LicenseController is
_component = ComponentController(_getContractAddress("Component"));
}

// ensures that calling component (productAddress) is a product
matthiaszimmermann marked this conversation as resolved.
Show resolved Hide resolved
function getAuthorizationStatus(address productAddress)
public override
view
returns (uint256 productId, bool isAuthorized, address policyFlow)
{
productId = getProductId(productAddress);
isAuthorized = _isValidCall(productAddress);
productId = _component.getComponentId(productAddress);
isAuthorized = _isValidCall(productId);
policyFlow = _getProduct(productId).getPolicyFlow();
}

function getProductId(address sender)
public override
view
returns(uint256 productId)
{
productId = _component.getComponentId(sender);
}

function _isValidCall(address componentAddress) internal view returns (bool) {
uint256 componentId = _component.getComponentId(componentAddress);
return _component.getComponentState(componentId) == IComponent.ComponentState.Active;
function _isValidCall(uint256 productId) internal view returns (bool) {
return _component.getComponentState(productId) == IComponent.ComponentState.Active;
}

function _getProduct(uint256 id) internal view returns (IProduct product) {
Expand Down
6 changes: 3 additions & 3 deletions contracts/services/ComponentOwnerService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ contract ComponentOwnerService is

modifier onlyOwnerWithRoleFromComponent(IComponent component) {
address owner = component.getOwner();
bytes32 requiredRole = component.getRequiredRole();
require(_access.hasRole(requiredRole, owner), "ERROR:COS-002:REQUIRED_ROLE_MISSING");
bytes32 requiredRole = _component.getRequiredRole(component.getType());
require(_msgSender() == owner, "ERROR:COS-001:NOT_OWNER");
require(_access.hasRole(requiredRole, owner), "ERROR:COS-002:REQUIRED_ROLE_MISSING");
_;
}

Expand All @@ -29,7 +29,7 @@ contract ComponentOwnerService is
require(address(component) != address(0), "ERROR:COS-003:COMPONENT_ID_INVALID");

address owner = component.getOwner();
bytes32 requiredRole = component.getRequiredRole();
bytes32 requiredRole = _component.getRequiredRole(component.getType());

require(_msgSender() == owner, "ERROR:COS-004:NOT_OWNER");
require(_access.hasRole(requiredRole, owner), "ERROR:COS-005:REQUIRED_ROLE_MISSING");
Expand Down
1 change: 1 addition & 0 deletions contracts/services/OracleService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ contract OracleService is
}

function respond(uint256 _requestId, bytes calldata _data) external override {
// function below enforces msg.sender to be a registered oracle
_query.respond(_requestId, _msgSender(), _data);
}
}
1 change: 1 addition & 0 deletions contracts/services/ProductService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ contract ProductService is
constructor(address _registry) WithRegistry(_registry) {}

fallback() external {
// getAuthorizationStatus enforces msg.sender to be a registered product
(uint256 id, bool isAuthorized, address policyFlow) = _license().getAuthorizationStatus(_msgSender());

require(isAuthorized, "ERROR:PRS-001:NOT_AUTHORIZED");
Expand Down
6 changes: 0 additions & 6 deletions contracts/test/TestCompromisedProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,6 @@ contract TestCompromisedProduct is
function getState() external override view returns(ComponentState) { return IComponent.ComponentState.Active; }
function getOwner() external override view returns(address) { return owner(); }

function getRequiredRole() public override view returns (bytes32) {
if (isProduct()) { return _access.productOwnerRole(); }
if (isOracle()) { return _access.oracleProviderRole(); }
if (isRiskpool()) { return _access.riskpoolKeeperRole(); }
}

function isProduct() public override view returns(bool) { return true; }
function isOracle() public override view returns(bool) { return false; }
function isRiskpool() public override view returns(bool) { return false; }
Expand Down
2 changes: 1 addition & 1 deletion tests/test_deploy_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_License(instance: GifInstance, owner):
assert license.address != 0x0

with brownie.reverts("ERROR:CCR-007:COMPONENT_UNKNOWN"):
license.getProductId(owner)
license.getAuthorizationStatus(registry.address)

with pytest.raises(AttributeError):
assert license.foo({'from': owner})
Expand Down
16 changes: 8 additions & 8 deletions tests/test_riskpool_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def test_suspend_archive(
componentOwnerService.archive(riskpoolId, {'from':riskpoolKeeper})

# ensure that component owner may not resume riskpool
with brownie.reverts("ERROR:CMP-018:INITIAL_STATE_NOT_HANDLED"):
with brownie.reverts("ERROR:CCR-018:INITIAL_STATE_NOT_HANDLED"):
instanceOperatorService.resume(riskpoolId, {'from':owner})

# ensure that instance operator may not archive archived riskpool
Expand Down Expand Up @@ -504,7 +504,7 @@ def test_pause_archive_as_owner(
riskpool.createBundle(bytes(0), 50, {'from':bundleOwner})

# ensure that owner may not unpause archived riskpool
with brownie.reverts("ERROR:CMP-018:INITIAL_STATE_NOT_HANDLED"):
with brownie.reverts("ERROR:CCR-018:INITIAL_STATE_NOT_HANDLED"):
componentOwnerService.unpause(riskpoolId, {'from':riskpoolKeeper})

assert instanceService.getComponentState(riskpoolId) == 6
Expand Down Expand Up @@ -569,7 +569,7 @@ def test_pause_archive_as_instance_operator(
riskpool.createBundle(bytes(0), 50, {'from':bundleOwner})

# ensure that owner may not unpause archived riskpool
with brownie.reverts("ERROR:CMP-018:INITIAL_STATE_NOT_HANDLED"):
with brownie.reverts("ERROR:CCR-018:INITIAL_STATE_NOT_HANDLED"):
componentOwnerService.unpause(riskpoolId, {'from':riskpoolKeeper})

assert instanceService.getComponentState(riskpoolId) == 6
Expand Down Expand Up @@ -632,19 +632,19 @@ def test_propose_decline(
assert instanceService.getComponentState(riskpoolId) == 2

# ensure that declined riskpool cannot be approved
with brownie.reverts("ERROR:CMP-014:DECLINED_IS_FINAL_STATE"):
with brownie.reverts("ERROR:CCR-014:DECLINED_IS_FINAL_STATE"):
instanceOperatorService.approve(
riskpoolId,
{'from': instance.getOwner()})

# ensure that declined riskpool cannot be suspended
with brownie.reverts("ERROR:CMP-014:DECLINED_IS_FINAL_STATE"):
with brownie.reverts("ERROR:CCR-014:DECLINED_IS_FINAL_STATE"):
instanceOperatorService.suspend(
riskpoolId,
{'from': instance.getOwner()})

# ensure that declined riskpool cannot be resumed
with brownie.reverts("ERROR:CMP-014:DECLINED_IS_FINAL_STATE"):
with brownie.reverts("ERROR:CCR-014:DECLINED_IS_FINAL_STATE"):
instanceOperatorService.resume(
riskpoolId,
{'from': instance.getOwner()})
Expand All @@ -656,13 +656,13 @@ def test_propose_decline(
{'from': instance.getOwner()})

# ensure that declined riskpool cannot be paused
with brownie.reverts("ERROR:CMP-014:DECLINED_IS_FINAL_STATE"):
with brownie.reverts("ERROR:CCR-014:DECLINED_IS_FINAL_STATE"):
componentOwnerService.pause(
riskpoolId,
{'from': riskpoolKeeper})

# ensure that declined riskpool cannot be unpaused
with brownie.reverts("ERROR:CMP-014:DECLINED_IS_FINAL_STATE"):
with brownie.reverts("ERROR:CCR-014:DECLINED_IS_FINAL_STATE"):
componentOwnerService.unpause(
riskpoolId,
{'from': riskpoolKeeper})
Expand Down