Files

40 lines
21 KiB
JSON

{
"contractName": "Integrity",
"sourceName": "contracts/Integrity.sol",
"compilerVersion": "v0.8.21+commit.d9974bed",
"constructorArguments": "",
"input": {
"language": "Solidity",
"sources": {
"contracts/Integrity.sol": {
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Topology.sol\";\n\n/**\n * @title Integrity, A library that validates condition integrity, and\n * adherence to the expected input structure and rules.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Integrity {\n error UnsuitableRootNode();\n\n error NotBFS();\n\n error UnsuitableParameterType(uint256 index);\n\n error UnsuitableCompValue(uint256 index);\n\n error UnsupportedOperator(uint256 index);\n\n error UnsuitableParent(uint256 index);\n\n error UnsuitableChildCount(uint256 index);\n\n error UnsuitableChildTypeTree(uint256 index);\n\n function enforce(ConditionFlat[] memory conditions) external pure {\n _root(conditions);\n for (uint256 i = 0; i < conditions.length; ++i) {\n _node(conditions[i], i);\n }\n _tree(conditions);\n }\n\n function _root(ConditionFlat[] memory conditions) private pure {\n uint256 count;\n\n for (uint256 i; i < conditions.length; ++i) {\n if (conditions[i].parent == i) ++count;\n }\n if (count != 1 || conditions[0].parent != 0) {\n revert UnsuitableRootNode();\n }\n }\n\n function _node(ConditionFlat memory condition, uint256 index) private pure {\n Operator operator = condition.operator;\n ParameterType paramType = condition.paramType;\n bytes memory compValue = condition.compValue;\n if (operator == Operator.Pass) {\n if (condition.compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator >= Operator.And && operator <= Operator.Nor) {\n if (paramType != ParameterType.None) {\n revert UnsuitableParameterType(index);\n }\n if (condition.compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Matches) {\n if (\n paramType != ParameterType.Tuple &&\n paramType != ParameterType.Array &&\n paramType != ParameterType.Calldata &&\n paramType != ParameterType.AbiEncoded\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.ArraySome ||\n operator == Operator.ArrayEvery ||\n operator == Operator.ArraySubset\n ) {\n if (paramType != ParameterType.Array) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.EqualToAvatar) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.EqualTo) {\n if (\n paramType != ParameterType.Static &&\n paramType != ParameterType.Dynamic &&\n paramType != ParameterType.Tuple &&\n paramType != ParameterType.Array\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length == 0 || compValue.length % 32 != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.GreaterThan ||\n operator == Operator.LessThan ||\n operator == Operator.SignedIntGreaterThan ||\n operator == Operator.SignedIntLessThan\n ) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Bitmask) {\n if (\n paramType != ParameterType.Static &&\n paramType != ParameterType.Dynamic\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Custom) {\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.WithinAllowance) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.EtherWithinAllowance ||\n operator == Operator.CallWithinAllowance\n ) {\n if (paramType != ParameterType.None) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else {\n revert UnsupportedOperator(index);\n }\n }\n\n function _tree(ConditionFlat[] memory conditions) private pure {\n uint256 length = conditions.length;\n // check BFS\n for (uint256 i = 1; i < length; ++i) {\n if (conditions[i - 1].parent > conditions[i].parent) {\n revert NotBFS();\n }\n }\n\n for (uint256 i = 0; i < length; ++i) {\n if (\n (conditions[i].operator == Operator.EtherWithinAllowance ||\n conditions[i].operator == Operator.CallWithinAllowance) &&\n conditions[conditions[i].parent].paramType !=\n ParameterType.Calldata\n ) {\n revert UnsuitableParent(i);\n }\n }\n\n Topology.Bounds[] memory childrenBounds = Topology.childrenBounds(\n conditions\n );\n\n for (uint256 i = 0; i < conditions.length; i++) {\n ConditionFlat memory condition = conditions[i];\n Topology.Bounds memory childBounds = childrenBounds[i];\n\n if (condition.paramType == ParameterType.None) {\n if (\n (condition.operator == Operator.EtherWithinAllowance ||\n condition.operator == Operator.CallWithinAllowance) &&\n childBounds.length != 0\n ) {\n revert UnsuitableChildCount(i);\n }\n if (\n (condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor)\n ) {\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n }\n } else if (\n condition.paramType == ParameterType.Static ||\n condition.paramType == ParameterType.Dynamic\n ) {\n if (childBounds.length != 0) {\n revert UnsuitableChildCount(i);\n }\n } else if (\n condition.paramType == ParameterType.Tuple ||\n condition.paramType == ParameterType.Calldata ||\n condition.paramType == ParameterType.AbiEncoded\n ) {\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n } else {\n assert(condition.paramType == ParameterType.Array);\n\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n\n if (\n (condition.operator == Operator.ArraySome ||\n condition.operator == Operator.ArrayEvery) &&\n childBounds.length != 1\n ) {\n revert UnsuitableChildCount(i);\n } else if (\n condition.operator == Operator.ArraySubset &&\n childBounds.length > 256\n ) {\n revert UnsuitableChildCount(i);\n }\n }\n }\n\n for (uint256 i = 0; i < conditions.length; i++) {\n ConditionFlat memory condition = conditions[i];\n if (\n ((condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor) ||\n condition.paramType == ParameterType.Array) &&\n childrenBounds[i].length > 1\n ) {\n _compatibleSiblingTypes(conditions, i, childrenBounds);\n }\n }\n\n Topology.TypeTree memory typeTree = Topology.typeTree(\n conditions,\n 0,\n childrenBounds\n );\n\n if (typeTree.paramType != ParameterType.Calldata) {\n revert UnsuitableRootNode();\n }\n }\n\n function _compatibleSiblingTypes(\n ConditionFlat[] memory conditions,\n uint256 index,\n Topology.Bounds[] memory childrenBounds\n ) private pure {\n uint256 start = childrenBounds[index].start;\n uint256 end = childrenBounds[index].end;\n\n for (uint256 j = start + 1; j < end; ++j) {\n if (\n !_isTypeMatch(conditions, start, j, childrenBounds) &&\n !_isTypeEquivalent(conditions, start, j, childrenBounds)\n ) {\n revert UnsuitableChildTypeTree(index);\n }\n }\n }\n\n function _isTypeMatch(\n ConditionFlat[] memory conditions,\n uint256 i,\n uint256 j,\n Topology.Bounds[] memory childrenBounds\n ) private pure returns (bool) {\n return\n typeTreeId(Topology.typeTree(conditions, i, childrenBounds)) ==\n typeTreeId(Topology.typeTree(conditions, j, childrenBounds));\n }\n\n function _isTypeEquivalent(\n ConditionFlat[] memory conditions,\n uint256 i,\n uint256 j,\n Topology.Bounds[] memory childrenBounds\n ) private pure returns (bool) {\n ParameterType leftParamType = Topology\n .typeTree(conditions, i, childrenBounds)\n .paramType;\n return\n (leftParamType == ParameterType.Calldata ||\n leftParamType == ParameterType.AbiEncoded) &&\n Topology.typeTree(conditions, j, childrenBounds).paramType ==\n ParameterType.Dynamic;\n }\n\n function typeTreeId(\n Topology.TypeTree memory node\n ) private pure returns (bytes32) {\n uint256 childCount = node.children.length;\n if (childCount > 0) {\n bytes32[] memory ids = new bytes32[](node.children.length);\n for (uint256 i = 0; i < childCount; ++i) {\n ids[i] = typeTreeId(node.children[i]);\n }\n\n return keccak256(abi.encodePacked(node.paramType, \"-\", ids));\n } else {\n return bytes32(uint256(node.paramType));\n }\n }\n}\n"
},
"contracts/Topology.sol": {
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Types.sol\";\n\n/**\n * @title Topology - a library that provides helper functions for dealing with\n * the flat representation of conditions.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Topology {\n struct TypeTree {\n ParameterType paramType;\n TypeTree[] children;\n }\n\n struct Bounds {\n uint256 start;\n uint256 end;\n uint256 length;\n }\n\n function childrenBounds(\n ConditionFlat[] memory conditions\n ) internal pure returns (Bounds[] memory result) {\n uint256 count = conditions.length;\n assert(count > 0);\n\n // parents are breadth-first\n result = new Bounds[](count);\n result[0].start = type(uint256).max;\n\n // first item is the root\n for (uint256 i = 1; i < count; ) {\n result[i].start = type(uint256).max;\n Bounds memory parentBounds = result[conditions[i].parent];\n if (parentBounds.start == type(uint256).max) {\n parentBounds.start = i;\n }\n parentBounds.end = i + 1;\n parentBounds.length = parentBounds.end - parentBounds.start;\n unchecked {\n ++i;\n }\n }\n }\n\n function isInline(TypeTree memory node) internal pure returns (bool) {\n ParameterType paramType = node.paramType;\n if (paramType == ParameterType.Static) {\n return true;\n } else if (\n paramType == ParameterType.Dynamic ||\n paramType == ParameterType.Array ||\n paramType == ParameterType.Calldata ||\n paramType == ParameterType.AbiEncoded\n ) {\n return false;\n } else {\n uint256 length = node.children.length;\n\n for (uint256 i; i < length; ) {\n if (!isInline(node.children[i])) {\n return false;\n }\n unchecked {\n ++i;\n }\n }\n return true;\n }\n }\n\n function typeTree(\n Condition memory condition\n ) internal pure returns (TypeTree memory result) {\n if (\n condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor\n ) {\n assert(condition.children.length > 0);\n return typeTree(condition.children[0]);\n }\n\n result.paramType = condition.paramType;\n if (condition.children.length > 0) {\n uint256 length = condition.paramType == ParameterType.Array\n ? 1\n : condition.children.length;\n result.children = new TypeTree[](length);\n\n for (uint256 i; i < length; ) {\n result.children[i] = typeTree(condition.children[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n }\n\n function typeTree(\n ConditionFlat[] memory conditions,\n uint256 index,\n Bounds[] memory bounds\n ) internal pure returns (TypeTree memory result) {\n ConditionFlat memory condition = conditions[index];\n if (\n condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor\n ) {\n assert(bounds[index].length > 0);\n return typeTree(conditions, bounds[index].start, bounds);\n }\n\n result.paramType = condition.paramType;\n if (bounds[index].length > 0) {\n uint256 start = bounds[index].start;\n uint256 end = condition.paramType == ParameterType.Array\n ? bounds[index].start + 1\n : bounds[index].end;\n result.children = new TypeTree[](end - start);\n for (uint256 i = start; i < end; ) {\n result.children[i - start] = typeTree(conditions, i, bounds);\n unchecked {\n ++i;\n }\n }\n }\n }\n}\n"
},
"contracts/Types.sol": {
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\n/**\n * @title Types - a file that contains all of the type definitions used throughout\n * the Zodiac Roles Mod.\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\n\nenum ParameterType {\n None,\n Static,\n Dynamic,\n Tuple,\n Array,\n Calldata,\n AbiEncoded\n}\n\nenum Operator {\n // 00: EMPTY EXPRESSION (default, always passes)\n // paramType: Static / Dynamic / Tuple / Array\n // ❓ children (only for paramType: Tuple / Array to describe their structure)\n // 🚫 compValue\n /* 00: */ Pass,\n // ------------------------------------------------------------\n // 01-04: LOGICAL EXPRESSIONS\n // paramType: None\n // ✅ children\n // 🚫 compValue\n /* 01: */ And,\n /* 02: */ Or,\n /* 03: */ Nor,\n /* 04: */ _Placeholder04,\n // ------------------------------------------------------------\n // 05-14: COMPLEX EXPRESSIONS\n // paramType: Calldata / AbiEncoded / Tuple / Array,\n // ✅ children\n // 🚫 compValue\n /* 05: */ Matches,\n /* 06: */ ArraySome,\n /* 07: */ ArrayEvery,\n /* 08: */ ArraySubset,\n /* 09: */ _Placeholder09,\n /* 10: */ _Placeholder10,\n /* 11: */ _Placeholder11,\n /* 12: */ _Placeholder12,\n /* 13: */ _Placeholder13,\n /* 14: */ _Placeholder14,\n // ------------------------------------------------------------\n // 15: SPECIAL COMPARISON (without compValue)\n // paramType: Static\n // 🚫 children\n // 🚫 compValue\n /* 15: */ EqualToAvatar,\n // ------------------------------------------------------------\n // 16-31: COMPARISON EXPRESSIONS\n // paramType: Static / Dynamic / Tuple / Array\n // ❓ children (only for paramType: Tuple / Array to describe their structure)\n // ✅ compValue\n /* 16: */ EqualTo, // paramType: Static / Dynamic / Tuple / Array\n /* 17: */ GreaterThan, // paramType: Static\n /* 18: */ LessThan, // paramType: Static\n /* 19: */ SignedIntGreaterThan, // paramType: Static\n /* 20: */ SignedIntLessThan, // paramType: Static\n /* 21: */ Bitmask, // paramType: Static / Dynamic\n /* 22: */ Custom, // paramType: Static / Dynamic / Tuple / Array\n /* 23: */ _Placeholder23,\n /* 24: */ _Placeholder24,\n /* 25: */ _Placeholder25,\n /* 26: */ _Placeholder26,\n /* 27: */ _Placeholder27,\n /* 28: */ WithinAllowance, // paramType: Static\n /* 29: */ EtherWithinAllowance, // paramType: None\n /* 30: */ CallWithinAllowance, // paramType: None\n /* 31: */ _Placeholder31\n}\n\nenum ExecutionOptions {\n None,\n Send,\n DelegateCall,\n Both\n}\n\nenum Clearance {\n None,\n Target,\n Function\n}\n\n// This struct is a flattened version of Condition\n// used for ABI encoding a scope config tree\n// (ABI does not support recursive types)\nstruct ConditionFlat {\n uint8 parent;\n ParameterType paramType;\n Operator operator;\n bytes compValue;\n}\n\nstruct Condition {\n ParameterType paramType;\n Operator operator;\n bytes32 compValue;\n Condition[] children;\n}\nstruct ParameterPayload {\n uint256 location;\n uint256 size;\n ParameterPayload[] children;\n}\n\nstruct TargetAddress {\n Clearance clearance;\n ExecutionOptions options;\n}\n\nstruct Role {\n mapping(address => bool) members;\n mapping(address => TargetAddress) targets;\n mapping(bytes32 => bytes32) scopeConfig;\n}\n\n/// @notice The order of members in the `Allowance` struct is significant; members updated during accrual (`balance` and `timestamp`) should be stored in the same word.\n/// @custom:member refill Amount added to balance after each period elapses.\n/// @custom:member maxRefill Refilling stops when balance reaches this value.\n/// @custom:member period Duration, in seconds, before a refill occurs. If set to 0, the allowance is for one-time use and won't be replenished.\n/// @custom:member balance Remaining allowance available for use. Decreases with usage and increases after each refill by the specified refill amount.\n/// @custom:member timestamp Timestamp when the last refill occurred.\nstruct Allowance {\n uint128 refill;\n uint128 maxRefill;\n uint64 period;\n uint128 balance;\n uint64 timestamp;\n}\n\nstruct Consumption {\n bytes32 allowanceKey;\n uint128 balance;\n uint128 consumed;\n}\n"
}
},
"settings": {
"evmVersion": "shanghai",
"optimizer": {
"enabled": true,
"runs": 100
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}
}
}