From 9c9289754360a14f2efe4f19e48aa01411a737a4 Mon Sep 17 00:00:00 2001 From: jatZama Date: Wed, 22 Jan 2025 18:59:52 +0100 Subject: [PATCH 1/2] feat: delegation and revoke functions and events --- contracts/contracts/ACL.sol | 102 +++++++++++++++++++++++++++-------- contracts/test/acl/acl.t.sol | 28 ++++++---- 2 files changed, 98 insertions(+), 32 deletions(-) diff --git a/contracts/contracts/ACL.sol b/contracts/contracts/ACL.sol index a5c4ccbe..aa4061da 100644 --- a/contracts/contracts/ACL.sol +++ b/contracts/contracts/ACL.sol @@ -18,33 +18,51 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { error AlreadyDelegated(); /// @notice Returned if the sender is the delegatee address. - error SenderCannotBeDelegateeAddress(); + error SenderCannotBeContractAddress(); + + /// @notice Returned if the contractAddresses array is empty. + error ContractAddressesIsEmpty(); + + /// @notice Maximum length of contractAddresses array exceeded. + error ContractAddressesMaxLengthExceeded(); + + /// @notice Returned if the handlesList array is empty. + error HandlesListIsEmpty(); + + /// @notice Returned if the the delegatee contract is not already delegatee for sender & delegator addresses. + error NotDelegatedYet(); /// @notice Returned if the sender address is not allowed for allow operations. /// @param sender Sender address. error SenderNotAllowed(address sender); - /// @notice Emitted when a list of handles is allowed for decryption. - /// @param caller account calling the allowForDecryption function. - /// @param handlesList List of handles allowed for decryption. - event AllowedForDecryption(address indexed caller, uint256[] handlesList); - /// @notice Emitted when a handle is allowed. /// @param caller account calling the allow function. /// @param account account being allowed for the handle. /// @param handle handle being allowed. event Allowed(address indexed caller, address indexed account, uint256 handle); - /// @notice Emitted when a new delegate address is added. - /// @param sender Sender address + /// @notice Emitted when a list of handles is allowed for decryption. + /// @param caller account calling the allowForDecryption function. + /// @param handlesList List of handles allowed for decryption. + event AllowedForDecryption(address indexed caller, uint256[] handlesList); + + /// @notice Emitted when a new delegatee address is added. + /// @param caller caller address /// @param delegatee Delegatee address. - /// @param contractAddress Contract address. - event NewDelegation(address indexed sender, address indexed delegatee, address indexed contractAddress); + /// @param contractAddresses Contract addresses. + event NewDelegation(address indexed caller, address indexed delegatee, address[] contractAddresses); + + /// @notice Emitted when a delegatee address is revoked. + /// @param caller caller address + /// @param delegatee Delegatee address. + /// @param contractAddresses Contract addresses. + event RevokedDelegation(address indexed caller, address indexed delegatee, address[] contractAddresses); /// @custom:storage-location erc7201:fhevm.storage.ACL struct ACLStorage { mapping(uint256 handle => mapping(address account => bool isAllowed)) persistedAllowedPairs; - mapping(uint256 => bool) allowedForDecryption; + mapping(uint256 handle => bool isAllowedForDecryption) allowedForDecryption; mapping(address account => mapping(address delegatee => mapping(address contractAddress => bool isDelegate))) delegates; } @@ -63,6 +81,9 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { /// @notice TFHEExecutor address. address private constant tfheExecutorAddress = tfheExecutorAdd; + /// @notice maximum length of contractAddresses array during delegation. + uint256 private constant MAX_NUM_CONTRACT_ADDRESSES = 10; + /// @dev keccak256(abi.encode(uint256(keccak256("fhevm.storage.ACL")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ACLStorageLocation = 0xa688f31953c2015baaf8c0a488ee1ee22eb0e05273cc1fd31ea4cbee42febc00; @@ -91,9 +112,13 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { * @param handlesList List of handles. */ function allowForDecryption(uint256[] memory handlesList) public virtual { - uint256 len = handlesList.length; + uint256 lenHandlesList = handlesList.length; + if (lenHandlesList == 0) { + revert HandlesListIsEmpty(); + } + ACLStorage storage $ = _getACLStorage(); - for (uint256 k = 0; k < len; k++) { + for (uint256 k = 0; k < lenHandlesList; k++) { uint256 handle = handlesList[k]; if (!isAllowed(handle, msg.sender)) { revert SenderNotAllowed(msg.sender); @@ -128,23 +153,56 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { } /** - * @notice Delegates the access of `handles` in the context of account abstraction for issuing + * @notice Delegates the access of handles in the context of account abstraction for issuing * reencryption requests from a smart contract account. * @param delegatee Delegatee address. - * @param delegateeContract Delegatee contract. + * @param contractAddresses Contract addresses. */ - function delegateAccount(address delegatee, address delegateeContract) public virtual { - if (delegateeContract == msg.sender) { - revert SenderCannotBeDelegateeAddress(); + function delegateAccount(address delegatee, address[] memory contractAddresses) public virtual { + uint256 lenghtContractAddresses = contractAddresses.length; + if (lenghtContractAddresses == 0) { + revert ContractAddressesIsEmpty(); + } + if (lenghtContractAddresses > MAX_NUM_CONTRACT_ADDRESSES) { + revert ContractAddressesMaxLengthExceeded(); } ACLStorage storage $ = _getACLStorage(); - if ($.delegates[msg.sender][delegatee][delegateeContract]) { - revert AlreadyDelegated(); + for (uint256 k = 0; k < lenghtContractAddresses; k++) { + if (contractAddresses[k] == msg.sender) { + revert SenderCannotBeContractAddress(); + } + if ($.delegates[msg.sender][delegatee][contractAddresses[k]]) { + revert AlreadyDelegated(); + } + $.delegates[msg.sender][delegatee][contractAddresses[k]] = true; + } + + emit NewDelegation(msg.sender, delegatee, contractAddresses); + } + + /** + * @notice Revokes delegated access of handles in the context of account abstraction for issuing + * reencryption requests from a smart contract account. + * @param delegatee Delegatee address. + * @param contractAddresses Contract addresses. + */ + function revokeDelegation(address delegatee, address[] memory contractAddresses) public virtual { + uint256 lenghtContractAddresses = contractAddresses.length; + if (lenghtContractAddresses == 0) { + revert ContractAddressesIsEmpty(); + } + + ACLStorage storage $ = _getACLStorage(); + + for (uint256 k = 0; k < lenghtContractAddresses; k++) { + if ($.delegates[msg.sender][delegatee][contractAddresses[k]]) { + revert NotDelegatedYet(); + } + $.delegates[msg.sender][delegatee][contractAddresses[k]] = false; } - $.delegates[msg.sender][delegatee][delegateeContract] = true; - emit NewDelegation(msg.sender, delegatee, delegateeContract); + emit RevokedDelegation(msg.sender, delegatee, contractAddresses); } /** diff --git a/contracts/test/acl/acl.t.sol b/contracts/test/acl/acl.t.sol index dc753b15..23d3d801 100644 --- a/contracts/test/acl/acl.t.sol +++ b/contracts/test/acl/acl.t.sol @@ -108,8 +108,11 @@ contract ACLTest is Test { vm.prank(sender); vm.expectEmit(address(acl)); - emit ACL.NewDelegation(sender, delegatee, delegateeContract); - acl.delegateAccount(delegatee, delegateeContract); + + address[] memory contractAddresses = new address[](1); + contractAddresses[0] = delegateeContract; + emit ACL.NewDelegation(sender, delegatee, contractAddresses); + acl.delegateAccount(delegatee, contractAddresses); vm.assertFalse(acl.allowedOnBehalf(delegatee, handle, delegateeContract, sender)); /// @dev The sender and the delegatee contract must be allowed to use the handle before it delegates. @@ -135,13 +138,17 @@ contract ACLTest is Test { vm.prank(sender); vm.expectRevert(ACL.AlreadyDelegated.selector); - acl.delegateAccount(delegatee, delegateeContract); + address[] memory contractAddresses = new address[](1); + contractAddresses[0] = delegateeContract; + acl.delegateAccount(delegatee, contractAddresses); } function test_CannotDelegateIfSenderIsDelegateeContract(address sender, address delegatee) public { vm.prank(sender); - vm.expectRevert(ACL.SenderCannotBeDelegateeAddress.selector); - acl.delegateAccount(delegatee, sender); + vm.expectRevert(ACL.SenderCannotBeContractAddress.selector); + address[] memory contractAddresses = new address[](1); + contractAddresses[0] = sender; + acl.delegateAccount(delegatee, contractAddresses); } function test_CanDelegateAccountIfAccountNotAllowed( @@ -156,17 +163,18 @@ contract ACLTest is Test { vm.prank(sender); vm.expectEmit(address(acl)); - emit ACL.NewDelegation(sender, delegatee, delegateeContract); - acl.delegateAccount(delegatee, delegateeContract); + address[] memory contractAddresses = new address[](1); + contractAddresses[0] = delegateeContract; + emit ACL.NewDelegation(sender, delegatee, contractAddresses); + acl.delegateAccount(delegatee, contractAddresses); vm.assertFalse(acl.allowedOnBehalf(delegatee, handle, delegateeContract, sender)); } - function test_AnyoneCanAllowForDecryptionIfEmptyList(address sender) public { + function test_NoOneCanAllowForDecryptionIfEmptyList(address sender) public { uint256[] memory handlesList = new uint256[](0); vm.prank(sender); - vm.expectEmit(address(acl)); - emit ACL.AllowedForDecryption(address(sender), handlesList); + vm.expectRevert(ACL.HandlesListIsEmpty.selector); acl.allowForDecryption(handlesList); } From 3ca6a52bb01d0c74eb5e39e8d40cbe0aaddc9f4a Mon Sep 17 00:00:00 2001 From: jatZama Date: Wed, 22 Jan 2025 19:09:12 +0100 Subject: [PATCH 2/2] chore: clearer errors --- contracts/contracts/ACL.sol | 16 ++++++++++------ contracts/test/acl/acl.t.sol | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/contracts/contracts/ACL.sol b/contracts/contracts/ACL.sol index aa4061da..782a3e3f 100644 --- a/contracts/contracts/ACL.sol +++ b/contracts/contracts/ACL.sol @@ -15,10 +15,12 @@ import {tfheExecutorAdd} from "../addresses/TFHEExecutorAddress.sol"; */ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { /// @notice Returned if the delegatee contract is already delegatee for sender & delegator addresses. - error AlreadyDelegated(); + /// @param delegatee delegatee address. + /// @param contractAddress contract address. + error AlreadyDelegated(address delegatee, address contractAddress); /// @notice Returned if the sender is the delegatee address. - error SenderCannotBeContractAddress(); + error SenderCannotBeContractAddress(address contractAddress); /// @notice Returned if the contractAddresses array is empty. error ContractAddressesIsEmpty(); @@ -30,7 +32,9 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { error HandlesListIsEmpty(); /// @notice Returned if the the delegatee contract is not already delegatee for sender & delegator addresses. - error NotDelegatedYet(); + /// @param delegatee delegatee address. + /// @param contractAddress contract address. + error NotDelegatedYet(address delegatee, address contractAddress); /// @notice Returned if the sender address is not allowed for allow operations. /// @param sender Sender address. @@ -170,10 +174,10 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { ACLStorage storage $ = _getACLStorage(); for (uint256 k = 0; k < lenghtContractAddresses; k++) { if (contractAddresses[k] == msg.sender) { - revert SenderCannotBeContractAddress(); + revert SenderCannotBeContractAddress(contractAddresses[k]); } if ($.delegates[msg.sender][delegatee][contractAddresses[k]]) { - revert AlreadyDelegated(); + revert AlreadyDelegated(delegatee, contractAddresses[k]); } $.delegates[msg.sender][delegatee][contractAddresses[k]] = true; } @@ -197,7 +201,7 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable { for (uint256 k = 0; k < lenghtContractAddresses; k++) { if ($.delegates[msg.sender][delegatee][contractAddresses[k]]) { - revert NotDelegatedYet(); + revert NotDelegatedYet(delegatee, contractAddresses[k]); } $.delegates[msg.sender][delegatee][contractAddresses[k]] = false; } diff --git a/contracts/test/acl/acl.t.sol b/contracts/test/acl/acl.t.sol index 23d3d801..bd607e42 100644 --- a/contracts/test/acl/acl.t.sol +++ b/contracts/test/acl/acl.t.sol @@ -137,7 +137,7 @@ contract ACLTest is Test { ); vm.prank(sender); - vm.expectRevert(ACL.AlreadyDelegated.selector); + vm.expectPartialRevert(ACL.AlreadyDelegated.selector); address[] memory contractAddresses = new address[](1); contractAddresses[0] = delegateeContract; acl.delegateAccount(delegatee, contractAddresses); @@ -145,7 +145,7 @@ contract ACLTest is Test { function test_CannotDelegateIfSenderIsDelegateeContract(address sender, address delegatee) public { vm.prank(sender); - vm.expectRevert(ACL.SenderCannotBeContractAddress.selector); + vm.expectPartialRevert(ACL.SenderCannotBeContractAddress.selector); address[] memory contractAddresses = new address[](1); contractAddresses[0] = sender; acl.delegateAccount(delegatee, contractAddresses);