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

M05 #27

Merged
merged 29 commits into from
Jul 31, 2023
Merged

M05 #27

Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
682ffe0
added atomic upgrade contract
sfeyal Jun 12, 2023
6e561d1
Merge branch 'L06' into L12
sfeyal Jun 12, 2023
674eba7
Merge branch 'collecting-storage-internal-functions' into L12
sfeyal Jun 19, 2023
120212d
atomic-engine-update-contract
sfeyal Jun 19, 2023
d8f3670
Merge remote-tracking branch 'origin/L11' into L12
sfeyal Jun 20, 2023
834b7ad
handle factory
sfeyal Jun 20, 2023
fe314b5
factory solution
sfeyal Jun 21, 2023
515f589
fixed typo added test
sfeyal Jun 21, 2023
c4f039f
gas report
sfeyal Jun 21, 2023
2fee41d
fixed typos
sfeyal Jun 21, 2023
5ee0b46
naming and doc string
sfeyal Jun 21, 2023
b96ebb0
operator initial value address(0)
sfeyal Jun 21, 2023
4015f68
fixed event typo
sfeyal Jun 21, 2023
5a05c3e
Achanged DD_ALLOWED_SENDER_ONCHAIN_INDEX value
sfeyal Jun 21, 2023
22fd462
added an internal function to protected to add an allowed sender
sfeyal Jun 22, 2023
39f2150
readme fix
sfeyal Jun 22, 2023
dc7f46a
another fix
sfeyal Jun 22, 2023
1f8f68b
small cr fix
sfeyal Jun 22, 2023
53335c0
deleted checks in init function
sfeyal Jun 22, 2023
c750127
fixed a bug when the engine is disabled when adding a new sender from…
sfeyal Jun 24, 2023
b4cd9e2
SphereXEngine.sol typos
ArielTM Jun 25, 2023
852aa44
addAllowedSenderOnChain returnsIfNotActivated
sfeyal Jun 29, 2023
57bc44e
added grantSenderAdderRole function
sfeyal Jun 29, 2023
74112e8
removed super and fixed test
sfeyal Jun 29, 2023
e48ae7a
audit fixes
sfeyal Jul 4, 2023
68ca9c2
rename init function
sfeyal Jul 4, 2023
8680010
deleted L12 changes
sfeyal Jul 4, 2023
f0ae51b
Merge branch 'collecting-storage-internal-functions' into M05
sfeyal Jul 4, 2023
052bb05
addAllowedSenderOnChain document flow
sfeyal Jul 4, 2023
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
39 changes: 31 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,26 +123,49 @@ graph TD

## Notes regarding SphereXProtected contract

This is an abstract contract with 2 state variable, but to make sure they do not interrupt the layout of the inherited contract's variables, they are actually saved in distinct storage slots. This is why these are not normal state variables with names. These 2 special addresses contain:
This is an abstract contract with 4 state variable, but to make sure they do not interrupt the layout of the inherited contract's variables, they are actually saved in distinct storage slots. This is why these are not normal state variables with names. These 4 special addresses contain:

1. The address of the SphereXAdmin
- This account is the only one who can change the address of the ENGINE state variable, and pass it's role to another address.
2. The address of the SphereXEngine - this is the address to which data is sent, and which classifies the transaction (at various points of execution).
- This account is the only one who can change the address of the OPERTOR state variable, and pass it's role to another address.
2. the address of the pendingSphereXAdmin
- This stte variable is used to implement a two step admin role transfer.
3. the address of the sphereXOperator
- This account is the only onw responsible for managing the protected contract, including changing the address of the ENGINE.
4. The address of the SphereXEngine - this is the address to which data is sent, and which classifies the transaction (at various points of execution).
- If this value is `address(0)`, the engine will be bypassed, essentially disabling its protection.


The contract provides functions to transfer the role of the SphereXAdmin, and update the address of the engine.
The contract provides functions to transfer the role of the SphereXAdmin, SphereXOperator, and update the address of the engine.

The heart of the contract is its modifiers - `SphereXProtected` provides 3 different modifiers to be used on functions with different visibilities:
1. `sphereXGuardInternal(int16 num)` - used for internal/private functions. The number that should be provided is an id to the function. It should be positive, and we simply use simple counting int starting with 1.
2. `sphereXGuardExternal(int16 num)` - used for external functions. The number has the same meaning and usage as in the internal.
3. `sphereXGuardPublic(int16 num, bytes4 selector)` - used fo public functions. The first param is just like in the previous 2 modifiers, and the `selector` param is the 4 bytes selector of the function.
1. `sphereXGuardInternal(int256 num)` - used for internal/private functions. The number that should be provided is an id to the function. It should be positive, and we simply use simple counting int starting with 1.
2. `sphereXGuardExternal(int256 num)` - used for external functions. The number has the same meaning and usage as in the internal.
3. `sphereXGuardPublic(int256 num, bytes4 selector)` - used fo public functions. The first param is just like in the previous 2 modifiers, and the `selector` param is the 4 bytes selector of the function.

### SphereXProtectedBase
The only difference between SphereXProtectedBase and SphereXProtected is the constructor and the init function, the SphereXProtectedBase has explicit initialization process (receives parameters), and the SphereXProtected has an implicit initialization process (initializes the admin to msg.sender and the rest to address(0)).

### Factory integration note
When integrating the protected contract with a factory pattern there a few key points to look at:
1. The contract crreated by the factory should inherit from SphereXProtectedBase
2. Upon creating the new contract the process should look something like:

```
ChildContract newChild = new ChildContract(..., sphereXAdmin(), sphereXOperator(), sphereXEngine());
_addAllowedSenderOnChain(address(newChild));
```

## Notes regarding SphereXEngine contract

This contract is the brain of the system. It is managed by an owner account (we have stripped down and simplified the OpenZeppelin `Ownable` template).
This contract is the brain of the system. It is managed by an AccessControlDefaultAdminRules pattern (inherited from OpenZeppelin).

The engine has 3 key roles:
1. The owner
- Controls the operator role address.
2. The operator
- Controls the engine managment functions.
3. The sender adder
- A unique role granted to mainly on-chain factory contracts, its sole purpose is to allow adding an address to the `allowedSenders`.
It has 3 important state variables:

1. `_engineRules` - a bytes8 variable indicating what rules (theses) are applied, 1 for CF, 2 for PrefixTxFlow, if all are off then the engine is deactivated.
Expand Down
30 changes: 16 additions & 14 deletions gas-report
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
| src/SphereXEngine.sol:SphereXEngine contract | | | | | |
|----------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1847252 | 10391 | | | | |
| 1912323 | 10716 | | | | |
| Function Name | min | avg | median | max | # calls |
| OPERATOR_ROLE | 338 | 338 | 338 | 338 | 3 |
| SENDER_ADDER_ROLE | 307 | 307 | 307 | 307 | 6 |
| acceptDefaultAdminTransfer | 27892 | 27892 | 27892 | 27892 | 1 |
| addAllowedPatterns | 5406 | 24458 | 25306 | 48304 | 123 |
| addAllowedSender | 1007 | 24848 | 25351 | 25351 | 93 |
| beginDefaultAdminTransfer | 12095 | 12095 | 12095 | 12095 | 1 |
| configureRules | 2352 | 22409 | 24252 | 26252 | 96 |
| deactivateAllRules | 2705 | 5308 | 6176 | 6176 | 4 |
| grantRole | 29689 | 31303 | 29689 | 34533 | 3 |
| removeAllowedPatterns | 2777 | 3363 | 3656 | 3656 | 3 |
| removeAllowedSender | 3006 | 5402 | 6600 | 6600 | 3 |
| addAllowedPatterns | 5406 | 24670 | 25306 | 48304 | 145 |
| addAllowedSender | 1007 | 24888 | 25351 | 25351 | 101 |
| addAllowedSenderOnchain | 2814 | 20709 | 26675 | 26675 | 8 |
| beginDefaultAdminTransfer | 12117 | 12117 | 12117 | 12117 | 1 |
| configureRules | 2352 | 22551 | 24252 | 26252 | 104 |
| deactivateAllRules | 2727 | 5330 | 6198 | 6198 | 4 |
| grantRole | 29623 | 30161 | 29623 | 34467 | 9 |
| removeAllowedPatterns | 2725 | 3311 | 3604 | 3604 | 3 |
| removeAllowedSender | 2961 | 5363 | 6564 | 6564 | 3 |
| revokeRole | 2948 | 2948 | 2948 | 2948 | 1 |
| sphereXValidateInternalPost | 458 | 2129 | 1742 | 3677 | 79 |
| sphereXValidateInternalPre | 434 | 4161 | 1962 | 8847 | 86 |
| sphereXValidatePost | 992 | 3147 | 2659 | 4659 | 86 |
| sphereXValidatePre | 926 | 5823 | 2449 | 11334 | 94 |
| supportsInterface | 435 | 435 | 435 | 435 | 49 |
| sphereXValidateInternalPost | 972 | 2640 | 2254 | 4181 | 79 |
| sphereXValidateInternalPre | 653 | 4373 | 2178 | 9063 | 86 |
| sphereXValidatePost | 992 | 3128 | 2659 | 4659 | 96 |
| sphereXValidatePre | 948 | 6134 | 2513 | 11356 | 106 |
| supportsInterface | 435 | 435 | 435 | 435 | 65 |
11 changes: 9 additions & 2 deletions src/ISphereXEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ interface ISphereXEngine is IERC165 {
bytes32[] calldata valuesBefore,
bytes32[] calldata valuesAfter
) external;
function sphereXValidateInternalPre(int256 num) external;
function sphereXValidateInternalPost(int256 num, uint256 gas) external;
function sphereXValidateInternalPre(int256 num) external returns (bytes32[] memory);
function sphereXValidateInternalPost(
int256 num,
uint256 gas,
bytes32[] calldata valuesBefore,
bytes32[] calldata valuesAfter
) external;

function addAllowedSenderOnChain(address sender) external;
ArielTM marked this conversation as resolved.
Show resolved Hide resolved
}
7 changes: 7 additions & 0 deletions src/ISphereXProtected.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: Unlicensed

pragma solidity ^0.8.17;

interface ISphereXProtected {
function changeSphereXEngine(address newSphereXEngine) external;
}
47 changes: 40 additions & 7 deletions src/SphereXEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
bytes32 internal constant DEACTIVATED = bytes32(0);
uint64 internal constant RULES_1_AND_2_TOGETHER = 3;

// the index of the addAllowedSenderOnChain in the call flow
int256 internal constant ADD_ALLOWED_SENDER_ONCHAIN_INDEX = int256(uint256(keccak256("factory.allowed.sender")));

bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant SENDER_ADDER_ROLE = keccak256("SENDER_ADDER_ROLE");

constructor() AccessControlDefaultAdminRules(1 days, msg.sender) {
grantRole(OPERATOR_ROLE, msg.sender);
Expand All @@ -43,9 +47,15 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
_;
}

modifier onlySenderAdderRole() {
require(hasRole(SENDER_ADDER_ROLE, msg.sender), "SphereX error: sender adder required");
_;
}

event TxStartedAtIrregularDepth();
event ConfigureRules(bytes8 oldRules, bytes8 newRules);
event AddedAllowedSenders(address[] senders);
event AddedAllowedSenderOnchain(address sender);
event RemovedAllowedSenders(address[] senders);
event AddedAllowedPatterns(uint216[] patterns);
event RemovedAllowedPatterns(uint216[] patterns);
Expand Down Expand Up @@ -108,6 +118,19 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
emit AddedAllowedSenders(senders);
}

/**
* Adds address that will be served by this engine. An address that was never added will get a revert if it tries to call the engine.
* @param sender address to add to the set of allowed addresses
*/
function addAllowedSenderOnChain(address sender) external returnsIfNotActivated onlySenderAdderRole {
_addCfElementFunctionEntry(ADD_ALLOWED_SENDER_ONCHAIN_INDEX);
ArielTM marked this conversation as resolved.
Show resolved Hide resolved
ArielTM marked this conversation as resolved.
Show resolved Hide resolved

_allowedSenders[sender] = true;
emit AddedAllowedSenderOnchain(sender);

_addCfElementFunctionExit(-ADD_ALLOWED_SENDER_ONCHAIN_INDEX, true);
}

/**
* Removes address so that they will not get served when calling the engine. Transaction from these addresses will get reverted.
* @param senders list of address to stop service.
Expand Down Expand Up @@ -142,6 +165,10 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
emit RemovedAllowedPatterns(patterns);
}

function grantSenderAdderRole(address newSenderAdder) external onlyOperator {
super._grantRole(SENDER_ADDER_ROLE, newSenderAdder);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is super necessary? I think it's just when you have the same function name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, and fixed test

}

// ============ CF ============

/**
Expand Down Expand Up @@ -254,7 +281,13 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
* This is used only for internal function calls (internal and private functions).
* @param num id of function to add.
*/
function sphereXValidateInternalPre(int256 num) external override returnsIfNotActivated onlyApprovedSenders {
function sphereXValidateInternalPre(int256 num)
external
override
returnsIfNotActivated
onlyApprovedSenders
returns (bytes32[] memory result)
{
_addCfElementFunctionEntry(num);
}

Expand All @@ -263,12 +296,12 @@ contract SphereXEngine is ISphereXEngine, AccessControlDefaultAdminRules {
* This is used only for internal function calls (internal and private functions).
* @param num id of function to add.
*/
function sphereXValidateInternalPost(int256 num, uint256 gas)
external
override
returnsIfNotActivated
onlyApprovedSenders
{
function sphereXValidateInternalPost(
int256 num,
uint256 gas,
bytes32[] calldata valuesBefore,
bytes32[] calldata valuesAfter
) external override returnsIfNotActivated onlyApprovedSenders {
_addCfElementFunctionExit(num, false);
}
}
21 changes: 21 additions & 0 deletions src/SphereXEngineUpdater.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Unlicensed

pragma solidity ^0.8.17;

import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
import {ISphereXEngine} from "./ISphereXEngine.sol";
import {ISphereXProtected} from "./ISphereXProtected.sol";


/**
* @title Management contract used for atomic engine update.
* It is expected for this contract to be the operator of all the protected contracts, otherwise it will revert.
*/
contract SphereXEngineUpdater is Ownable {

function update(address[] calldata protectedContracts, address newSphereXEngine) external onlyOwner {
for (uint256 i = 0; i < protectedContracts.length; ++i) {
ISphereXProtected(protectedContracts[i]).changeSphereXEngine(newSphereXEngine);
}
}
}