From 567808a1c313af33c333d5f4b77d9ca9d99e46e4 Mon Sep 17 00:00:00 2001 From: Alan Donoso Naumczuk Date: Fri, 20 Dec 2024 16:12:21 -0300 Subject: [PATCH] feat: Restricted signers rules updated after interface changes --- .../core/libraries/EIP712EncodingLib.sol | 99 +++++++++++++++++++ .../rules/base/RestrictedSignersRule.sol | 32 +++--- .../rules/feed/RestrictedSignersFeedRule.sol | 77 +++++++++++++++ .../feed/RestrictedSignersFeedRule.sol.bak | 53 ---------- .../graph/RestrictedSignersGraphRule.sol | 64 ++++++++++++ .../graph/RestrictedSignersGraphRule.sol.bak | 39 -------- 6 files changed, 260 insertions(+), 104 deletions(-) create mode 100644 contracts/rules/feed/RestrictedSignersFeedRule.sol delete mode 100644 contracts/rules/feed/RestrictedSignersFeedRule.sol.bak create mode 100644 contracts/rules/graph/RestrictedSignersGraphRule.sol delete mode 100644 contracts/rules/graph/RestrictedSignersGraphRule.sol.bak diff --git a/contracts/core/libraries/EIP712EncodingLib.sol b/contracts/core/libraries/EIP712EncodingLib.sol index 63363ebf..bf7cab81 100644 --- a/contracts/core/libraries/EIP712EncodingLib.sol +++ b/contracts/core/libraries/EIP712EncodingLib.sol @@ -2,6 +2,9 @@ // Copyright (C) 2024 Lens Labs. All Rights Reserved. pragma solidity ^0.8.17; +import {KeyValue, RuleChange, RuleConfigurationChange, RuleSelectorChange} from "../types/Types.sol"; +import {CreatePostParams, EditPostParams} from "../interfaces/IFeed.sol"; + library EIP712EncodingLib { function encodeForEIP712(bytes[] memory bytesArray) internal pure returns (bytes32) { bytes32[] memory bytesArrayEncodedElements = new bytes32[](bytesArray.length); @@ -33,6 +36,102 @@ library EIP712EncodingLib { return keccak256(abi.encodePacked(bytes32Array)); } + function encodeForEIP712(KeyValue[] memory keyValueArray) internal pure returns (bytes32) { + bytes32[] memory keyValueEncodedElements = new bytes32[](keyValueArray.length); + for (uint256 i = 0; i < keyValueArray.length; i++) { + keyValueEncodedElements[i] = encodeForEIP712(keyValueArray[i]); + } + return encodeForEIP712(keyValueEncodedElements); + } + + function encodeForEIP712(RuleChange[] memory ruleChangeArray) internal pure returns (bytes32) { + bytes32[] memory ruleChangeEncodedElements = new bytes32[](ruleChangeArray.length); + for (uint256 i = 0; i < ruleChangeArray.length; i++) { + ruleChangeEncodedElements[i] = encodeForEIP712(ruleChangeArray[i]); + } + return encodeForEIP712(ruleChangeEncodedElements); + } + + function encodeForEIP712(RuleSelectorChange[] memory ruleSelectorChangeArray) internal pure returns (bytes32) { + bytes32[] memory ruleSelectorChangeEncodedElements = new bytes32[](ruleSelectorChangeArray.length); + for (uint256 i = 0; i < ruleSelectorChangeArray.length; i++) { + ruleSelectorChangeEncodedElements[i] = encodeForEIP712(ruleSelectorChangeArray[i]); + } + return encodeForEIP712(ruleSelectorChangeEncodedElements); + } + + function encodeForEIP712(KeyValue memory keyValue) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256("KeyValue(bytes32 key,bytes value)"), // Type Hash + keyValue.key, + encodeForEIP712(keyValue.value) + ) + ); + } + + function encodeForEIP712(RuleChange memory ruleChange) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256( + "RuleChange(address ruleAddress,bytes32 configSalt,RuleConfigurationChange configurationChanges,RuleSelectorChange[] selectorChanges)" + ), // Type Hash + ruleChange.ruleAddress, + ruleChange.configSalt, + encodeForEIP712(ruleChange.configurationChanges), + encodeForEIP712(ruleChange.selectorChanges) + ) + ); + } + + function encodeForEIP712(RuleConfigurationChange memory ruleConfigurationChange) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256("RuleConfigurationChange(bool configure,KeyValue[] ruleParams)"), // Type Hash + ruleConfigurationChange.configure, + encodeForEIP712(ruleConfigurationChange.ruleParams) + ) + ); + } + + function encodeForEIP712(RuleSelectorChange memory ruleSelectorChange) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256("RuleSelectorChange(bytes4 ruleSelector,bool isRequired,bool enabled)"), // Type Hash + ruleSelectorChange.ruleSelector, + ruleSelectorChange.isRequired, + ruleSelectorChange.enabled + ) + ); + } + + function encodeForEIP712(CreatePostParams memory createPostParams) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256( + "CreatePostParams(address author,string contentURI,uint256 repostedPostId,uint256 quotedPostId,uint256 repliedPostId,RuleChange[] ruleChanges,KeyValue[] extraData)" + ), // Type Hash + createPostParams.author, + encodeForEIP712(createPostParams.contentURI), + createPostParams.repostedPostId, + createPostParams.quotedPostId, + createPostParams.repliedPostId, + encodeForEIP712(createPostParams.ruleChanges), + encodeForEIP712(createPostParams.extraData) + ) + ); + } + + function encodeForEIP712(EditPostParams memory editPostParams) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + keccak256("EditPostParams(string contentURI,KeyValue[] extraData)"), // Type Hash + encodeForEIP712(editPostParams.contentURI), + encodeForEIP712(editPostParams.extraData) + ) + ); + } + function encodeForEIP712(string memory stringValue) internal pure returns (bytes32) { return keccak256(bytes(stringValue)); } diff --git a/contracts/rules/base/RestrictedSignersRule.sol b/contracts/rules/base/RestrictedSignersRule.sol index b97c8e76..ede9c756 100644 --- a/contracts/rules/base/RestrictedSignersRule.sol +++ b/contracts/rules/base/RestrictedSignersRule.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import {EIP712EncodingLib} from "./../../core/libraries/EIP712EncodingLib.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {KeyValue} from "./../../core/types/Types.sol"; // Move to types struct EIP712Signature { @@ -28,7 +29,7 @@ abstract contract RestrictedSignersRule { event Lens_RestrictedSignersRule_SignerNonceUsed(address indexed signer, uint256 indexed nonce); struct RulesStorage { - mapping(address => InnerStorage) rulesStorage; + mapping(address => mapping(bytes32 => InnerStorage)) rulesStorage; } struct InnerStorage { @@ -39,15 +40,19 @@ abstract contract RestrictedSignersRule { // keccak256('lens.rule.restricted.storage') bytes32 constant RESTRICTED_RULE_STORAGE_SLOT = 0xcf6ecf8730d498cbf6701bc1140f2b12e988e1c416a85799d241dcfbb3ed90df; - function $rulesStorage() private pure returns (mapping(address => InnerStorage) storage _storage) { + function $rulesStorage() + private + pure + returns (mapping(address => mapping(bytes32 => InnerStorage)) storage _storage) + { assembly { _storage.slot := RESTRICTED_RULE_STORAGE_SLOT } } - function $rulesStorage(address primitiveAddress) private view returns (InnerStorage storage) { - mapping(address => InnerStorage) storage _rulesStorage = $rulesStorage(); - return _rulesStorage[primitiveAddress]; + function $rulesStorage(address primitiveAddress, bytes32 configSalt) private view returns (InnerStorage storage) { + mapping(address => mapping(bytes32 => InnerStorage)) storage _rulesStorage = $rulesStorage(); + return _rulesStorage[primitiveAddress][configSalt]; } bytes4 constant EIP1271_MAGIC_VALUE = 0x1626ba7e; @@ -59,13 +64,15 @@ abstract contract RestrictedSignersRule { "RestrictedSignerMessage(bytes4 functionSelector,bytes abiEncodedParams,uint256 nonce,uint256 deadline)" ); - function _configure(bytes calldata data) internal virtual { + function _configure(bytes32 configSalt, KeyValue[] calldata ruleParams) internal virtual { + require(ruleParams.length > 0); + require(ruleParams[0].key == "restrictedSigners"); // TODO: Use proper constant (address[] memory signers, string[] memory labels, bool[] memory isWhitelisted) = - abi.decode(data, (address[], string[], bool[])); + abi.decode(ruleParams[0].value, (address[], string[], bool[])); require(signers.length == isWhitelisted.length); require(signers.length == labels.length); for (uint256 i = 0; i < signers.length; i++) { - bool wasWhitelisted = $rulesStorage(msg.sender).isWhitelistedSigner[signers[i]]; + bool wasWhitelisted = $rulesStorage(msg.sender, configSalt).isWhitelistedSigner[signers[i]]; if (wasWhitelisted == isWhitelisted[i]) { if (isWhitelisted[i]) { // Signal removal and re-addition in order to update the label @@ -73,7 +80,7 @@ abstract contract RestrictedSignersRule { emit Lens_RestrictedSignersRule_SignerAdded(signers[i], labels[i]); } } else { - $rulesStorage(msg.sender).isWhitelistedSigner[signers[i]] = isWhitelisted[i]; + $rulesStorage(msg.sender, configSalt).isWhitelistedSigner[signers[i]] = isWhitelisted[i]; if (isWhitelisted[i]) { emit Lens_RestrictedSignersRule_SignerAdded(signers[i], labels[i]); } else { @@ -84,6 +91,7 @@ abstract contract RestrictedSignersRule { } function _validateRestrictedSignerMessage( + bytes32 configSalt, bytes4 functionSelector, bytes memory abiEncodedFunctionParams, EIP712Signature memory signature @@ -93,12 +101,12 @@ abstract contract RestrictedSignersRule { if (block.timestamp > signature.deadline) { revert("Errors.SignatureExpired()"); } - if ($rulesStorage(msg.sender).wasSignerNonceUsed[signature.signer][signature.nonce]) { + if ($rulesStorage(msg.sender, configSalt).wasSignerNonceUsed[signature.signer][signature.nonce]) { revert("Errors.SignatureNonceUsed()"); } - $rulesStorage(msg.sender).wasSignerNonceUsed[signature.signer][signature.nonce] = true; + $rulesStorage(msg.sender, configSalt).wasSignerNonceUsed[signature.signer][signature.nonce] = true; emit Lens_RestrictedSignersRule_SignerNonceUsed(signature.signer, signature.nonce); - if (!$rulesStorage(msg.sender).isWhitelistedSigner[signature.signer]) { + if (!$rulesStorage(msg.sender, configSalt).isWhitelistedSigner[signature.signer]) { revert("Errors.SignerNotWhitelisted()"); } bytes32 hashStruct = _calculateMessageHashStruct(message); diff --git a/contracts/rules/feed/RestrictedSignersFeedRule.sol b/contracts/rules/feed/RestrictedSignersFeedRule.sol new file mode 100644 index 00000000..7c091400 --- /dev/null +++ b/contracts/rules/feed/RestrictedSignersFeedRule.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (C) 2024 Lens Labs. All Rights Reserved. +pragma solidity ^0.8.0; + +import {CreatePostParams, EditPostParams} from "./../../core/interfaces/IFeed.sol"; +import {IFeedRule} from "./../../core/interfaces/IFeedRule.sol"; +import {RestrictedSignersRule, EIP712Signature} from "./../base/RestrictedSignersRule.sol"; +import {KeyValue, RuleChange} from "./../../core/types/Types.sol"; +import {EIP712EncodingLib} from "./../../core/libraries/EIP712EncodingLib.sol"; + +contract RestrictedSignersFeedRule is RestrictedSignersRule, IFeedRule { + function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external override { + _configure(configSalt, ruleParams); + } + + function processCreatePost( + bytes32 configSalt, + uint256 postId, + CreatePostParams calldata postParams, + KeyValue[] calldata primitiveParams, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IFeedRule.processCreatePost.selector, + abiEncodedFunctionParams: abi.encode( + postId, EIP712EncodingLib.encodeForEIP712(postParams), EIP712EncodingLib.encodeForEIP712(primitiveParams) + ), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } + + function processEditPost( + bytes32 configSalt, + uint256 postId, + EditPostParams calldata postParams, + KeyValue[] calldata primitiveParams, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IFeedRule.processEditPost.selector, + abiEncodedFunctionParams: abi.encode( + postId, EIP712EncodingLib.encodeForEIP712(postParams), EIP712EncodingLib.encodeForEIP712(primitiveParams) + ), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } + + function processRemovePost( + bytes32 configSalt, + uint256 postId, + KeyValue[] calldata primitiveParams, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IFeedRule.processRemovePost.selector, + abiEncodedFunctionParams: abi.encode(postId, EIP712EncodingLib.encodeForEIP712(primitiveParams)), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } + + function processPostRuleChanges( + bytes32 configSalt, + uint256 postId, + RuleChange[] calldata ruleChanges, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IFeedRule.processPostRuleChanges.selector, + abiEncodedFunctionParams: abi.encode(postId, EIP712EncodingLib.encodeForEIP712(ruleChanges)), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } +} diff --git a/contracts/rules/feed/RestrictedSignersFeedRule.sol.bak b/contracts/rules/feed/RestrictedSignersFeedRule.sol.bak deleted file mode 100644 index 5ac6cdb5..00000000 --- a/contracts/rules/feed/RestrictedSignersFeedRule.sol.bak +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// Copyright (C) 2024 Lens Labs. All Rights Reserved. -pragma solidity ^0.8.0; - -import {CreatePostParams, EditPostParams} from "./../../core/interfaces/IFeed.sol"; -import {IFeedRule} from "./../../core/interfaces/IFeedRule.sol"; -import {RestrictedSignersRule, EIP712Signature} from "./../base/RestrictedSignersRule.sol"; -import {RuleChange} from "./../../core/types/Types.sol"; - -contract RestrictedSignersFeedRule is RestrictedSignersRule, IFeedRule { - function configure(bytes calldata data) external override { - _configure(data); - } - - function processCreatePost( - uint256 postId, - CreatePostParams calldata postParams, - bytes calldata data - ) external override returns (bool) { - _validateRestrictedSignerMessage({ - functionSelector: IFeedRule.processCreatePost.selector, - abiEncodedFunctionParams: abi.encode(postId, postParams), - signature: abi.decode(data, (EIP712Signature)) - }); - return true; - } - - function processEditPost( - uint256 postId, - EditPostParams calldata editPostParams, - bytes calldata data - ) external override returns (bool) { - _validateRestrictedSignerMessage({ - functionSelector: IFeedRule.processEditPost.selector, - abiEncodedFunctionParams: abi.encode(postId, editPostParams), - signature: abi.decode(data, (EIP712Signature)) - }); - return true; - } - - function processPostRuleChanges( - uint256 postId, - RuleChange[] calldata ruleChanges, - bytes calldata data - ) external override returns (bool) { - _validateRestrictedSignerMessage({ - functionSelector: IFeedRule.processPostRuleChanges.selector, - abiEncodedFunctionParams: abi.encode(postId, ruleChanges), - signature: abi.decode(data, (EIP712Signature)) - }); - return true; - } -} diff --git a/contracts/rules/graph/RestrictedSignersGraphRule.sol b/contracts/rules/graph/RestrictedSignersGraphRule.sol new file mode 100644 index 00000000..9deb9030 --- /dev/null +++ b/contracts/rules/graph/RestrictedSignersGraphRule.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (C) 2024 Lens Labs. All Rights Reserved. +pragma solidity ^0.8.0; + +import {IGraphRule} from "./../../core/interfaces/IGraphRule.sol"; +import {RestrictedSignersRule, EIP712Signature} from "./../base/RestrictedSignersRule.sol"; +import {KeyValue, RuleChange} from "./../../core/types/Types.sol"; +import {EIP712EncodingLib} from "./../../core/libraries/EIP712EncodingLib.sol"; + +contract RestrictedSignersGraphRule is RestrictedSignersRule, IGraphRule { + function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external override { + _configure(configSalt, ruleParams); + } + + function processFollow( + bytes32 configSalt, + address originalMsgSender, + address followerAccount, + address accountToFollow, + KeyValue[] calldata primitiveParams, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IGraphRule.processFollow.selector, + abiEncodedFunctionParams: abi.encode( + originalMsgSender, followerAccount, accountToFollow, EIP712EncodingLib.encodeForEIP712(primitiveParams) + ), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } + + function processUnfollow( + bytes32 configSalt, + address originalMsgSender, + address followerAccount, + address accountToUnfollow, + KeyValue[] calldata primitiveParams, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IGraphRule.processUnfollow.selector, + abiEncodedFunctionParams: abi.encode( + originalMsgSender, followerAccount, accountToUnfollow, EIP712EncodingLib.encodeForEIP712(primitiveParams) + ), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } + + function processFollowRuleChanges( + bytes32 configSalt, + address account, + RuleChange[] calldata ruleChanges, + KeyValue[] calldata ruleParams + ) external override { + _validateRestrictedSignerMessage({ + configSalt: configSalt, + functionSelector: IGraphRule.processFollowRuleChanges.selector, + abiEncodedFunctionParams: abi.encode(account, EIP712EncodingLib.encodeForEIP712(ruleChanges)), + signature: abi.decode(ruleParams[0].value, (EIP712Signature)) + }); + } +} diff --git a/contracts/rules/graph/RestrictedSignersGraphRule.sol.bak b/contracts/rules/graph/RestrictedSignersGraphRule.sol.bak deleted file mode 100644 index 4fbae2ea..00000000 --- a/contracts/rules/graph/RestrictedSignersGraphRule.sol.bak +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// Copyright (C) 2024 Lens Labs. All Rights Reserved. -pragma solidity ^0.8.0; - -import {IGraphRule} from "./../../core/interfaces/IGraphRule.sol"; -import {RestrictedSignersRule, EIP712Signature} from "./../base/RestrictedSignersRule.sol"; -import {RuleChange} from "./../../core/types/Types.sol"; - -contract RestrictedSignersGraphRule is RestrictedSignersRule, IGraphRule { - function configure(bytes calldata data) external override { - _configure(data); - } - - function processFollow( - address followerAccount, - address accountToFollow, - bytes calldata data - ) external override returns (bool) { - _validateRestrictedSignerMessage({ - functionSelector: IGraphRule.processFollow.selector, - abiEncodedFunctionParams: abi.encode(followerAccount, accountToFollow), - signature: abi.decode(data, (EIP712Signature)) - }); - return true; - } - - function processFollowRuleChanges( - address account, - RuleChange[] calldata ruleChanges, - bytes calldata data - ) external override returns (bool) { - _validateRestrictedSignerMessage({ - functionSelector: IGraphRule.processFollowRuleChanges.selector, - abiEncodedFunctionParams: abi.encode(account, ruleChanges), - signature: abi.decode(data, (EIP712Signature)) - }); - return true; - } -}