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

Refactor and advance core framework capabilities #9

Merged
merged 19 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e629d29
feat(contracts)!: add base and advanced Checker and Excubia contracts
0xjei Nov 6, 2024
1389b2d
refactor(contracts): add mapping of mapping to handle multiple status…
0xjei Nov 22, 2024
9f4e9a0
refactor(contracts): add return to checks
0xjei Nov 22, 2024
c1ec974
refactor(contracts): remove extensions
0xjei Nov 25, 2024
53ce02c
refactor(contracts): apply new naming conventions to contracts
0xjei Nov 25, 2024
9a6d052
test(contracts): add example implementation and tests for BaseChecker…
0xjei Nov 26, 2024
9a05be6
chore(contracts): group base test contracts under base folder
0xjei Nov 26, 2024
c4be681
chore(contracts): move contracts tests to the same place
0xjei Nov 26, 2024
0b07847
test(contracts): add missing base tests to reach max code coverage
0xjei Nov 27, 2024
39b1b30
test(contracts): add harness for checker and policy to hardhat testing
0xjei Nov 28, 2024
ab8f3fa
test(contracts): add advanced ts tests
0xjei Nov 28, 2024
1c2416d
test(contracts): add advanced tests for max code coverage
0xjei Nov 29, 2024
cad1311
refactor(contracts): make advanced policy flags immutable
0xjei Nov 29, 2024
c74c32d
refactor(contracts): improve contracts and test documentation; small …
0xjei Dec 2, 2024
23d6eed
refactor(contracts): improve custom error name of advanced contracts
0xjei Dec 3, 2024
2eb2033
refactor(contracts): move advanced control flags for checks from chec…
0xjei Dec 3, 2024
3e595b8
refactor(contracts): switched to pragma for compatibility; minor fix
0xjei Dec 3, 2024
64ae098
refactor(contracts): change setTarget() visibility from public to ext…
0xjei Dec 3, 2024
688259b
refactor(contracts): remove redundant if case; move struct to interface
0xjei Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
node_modules
.env
/cache

# Hardhat files
/cache
/cache-hh
/artifacts

# Forge files
Expand Down
3 changes: 2 additions & 1 deletion packages/contracts/.solcover.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {

Check warning on line 1 in packages/contracts/.solcover.js

View workflow job for this annotation

GitHub Actions / style

File ignored by default.
istanbulFolder: "../../coverage/contracts"
istanbulFolder: "../../coverage/contracts",
skipFiles: ["test"]
}
2 changes: 1 addition & 1 deletion packages/contracts/.solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"code-complexity": ["error", 7],
"code-complexity": ["error", 12],
"compiler-version": ["error", ">=0.8.0"],
"var-name-mixedcase": "off",
"const-name-snakecase": "off",
Expand Down
4 changes: 3 additions & 1 deletion packages/contracts/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"license": "MIT",
"files": [
"*.sol",
"**/*.sol"
"!test/*",
"README.md",
"LICENSE"
],
"keywords": [
"blockchain",
Expand Down
61 changes: 61 additions & 0 deletions packages/contracts/contracts/src/AdvancedChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IAdvancedChecker, Check, CheckStatus} from "./interfaces/IAdvancedChecker.sol";

/// @title AdvancedChecker.
/// @notice Multi-phase validation checker with pre, main, and post checks.
/// @dev Base contract for implementing complex validation logic with configurable check phases.
abstract contract AdvancedChecker is IAdvancedChecker {
/// @notice Entry point for validation checks.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @param checkType Type of check (PRE, MAIN, POST).
/// @return checked Validation result.
function check(
address subject,
bytes memory evidence,
Check checkType
) external view override returns (bool checked) {
return _check(subject, evidence, checkType);
}

/// @notice Core validation logic router.
/// @dev Directs to appropriate check based on type and configuration.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @param checkType Check type to perform.
/// @return checked Validation result.
function _check(address subject, bytes memory evidence, Check checkType) internal view returns (bool checked) {
if (checkType == Check.PRE) {
return _checkPre(subject, evidence);
}

if (checkType == Check.POST) {
return _checkPost(subject, evidence);
}

return _checkMain(subject, evidence);
}

/// @notice Pre-condition validation implementation.
/// @dev Override to implement pre-check logic.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @return checked Validation result.
function _checkPre(address subject, bytes memory evidence) internal view virtual returns (bool checked) {}

/// @notice Main validation implementation.
/// @dev Override to implement main check logic.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @return checked Validation result.
function _checkMain(address subject, bytes memory evidence) internal view virtual returns (bool checked) {}

/// @notice Post-condition validation implementation.
/// @dev Override to implement post-check logic.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @return checked Validation result.
function _checkPost(address subject, bytes memory evidence) internal view virtual returns (bool checked) {}
}
100 changes: 100 additions & 0 deletions packages/contracts/contracts/src/AdvancedPolicy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Policy} from "./Policy.sol";
import {IAdvancedPolicy, Check} from "./interfaces/IAdvancedPolicy.sol";
import {AdvancedChecker, CheckStatus} from "./AdvancedChecker.sol";

/// @title AdvancedPolicy.
/// @notice Implements advanced policy checks with pre, main, and post validation stages.
/// @dev Extends Policy contract with multi-stage validation capabilities.
abstract contract AdvancedPolicy is IAdvancedPolicy, Policy {
/// @notice Reference to the validation checker contract.
/// @dev Immutable to ensure checker cannot be changed after deployment.
AdvancedChecker public immutable ADVANCED_CHECKER;

/// @notice Controls whether pre-condition checks are required.
bool public immutable SKIP_PRE;

/// @notice Controls whether post-condition checks are required.
bool public immutable SKIP_POST;

/// @notice Controls whether main check can be executed multiple times.
bool public immutable ALLOW_MULTIPLE_MAIN;

/// @notice Tracks validation status for each subject per target.
/// @dev Maps target => subject => CheckStatus.
mapping(address => mapping(address => CheckStatus)) public enforced;

/// @notice Initializes contract with an AdvancedChecker instance and checks configs.
/// @param _advancedChecker Address of the AdvancedChecker contract.
/// @param _skipPre Skip pre-condition validation.
/// @param _skipPost Skip post-condition validation.
/// @param _allowMultipleMain Allow multiple main validations.
constructor(AdvancedChecker _advancedChecker, bool _skipPre, bool _skipPost, bool _allowMultipleMain) {
ADVANCED_CHECKER = _advancedChecker;
SKIP_PRE = _skipPre;
SKIP_POST = _skipPost;
ALLOW_MULTIPLE_MAIN = _allowMultipleMain;
}

/// @notice Enforces policy check for a subject.
/// @dev Only callable by target contract.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @param checkType Type of check (PRE, MAIN, POST).
function enforce(address subject, bytes calldata evidence, Check checkType) external override onlyTarget {
_enforce(subject, evidence, checkType);
}

/// @notice Internal check enforcement logic.
/// @dev Handles different check types and their dependencies.
/// @param subject Address to validate.
/// @param evidence Validation data.
/// @param checkType Type of check to perform.
/// @custom:throws CannotPreCheckWhenSkipped If PRE check attempted when skipped.
/// @custom:throws CannotPostCheckWhenSkipped If POST check attempted when skipped.
/// @custom:throws UnsuccessfulCheck If validation fails.
/// @custom:throws AlreadyEnforced If check was already completed.
/// @custom:throws PreCheckNotEnforced If PRE check is required but not done.
/// @custom:throws MainCheckNotEnforced If MAIN check is required but not done.
/// @custom:throws MainCheckAlreadyEnforced If multiple MAIN checks not allowed.
function _enforce(address subject, bytes calldata evidence, Check checkType) internal {
if (!ADVANCED_CHECKER.check(subject, evidence, checkType)) {
revert UnsuccessfulCheck();
}

CheckStatus storage status = enforced[msg.sender][subject];

// Handle PRE check.
if (checkType == Check.PRE) {
if (SKIP_PRE) revert CannotPreCheckWhenSkipped();
if (status.pre) {
revert AlreadyEnforced();
}

status.pre = true;
} else if (checkType == Check.POST) {
// Handle POST check.
if (SKIP_POST) revert CannotPostCheckWhenSkipped();
if (status.main == 0) {
revert MainCheckNotEnforced();
}
if (status.post) {
revert AlreadyEnforced();
}
status.post = true;
} else {
// Handle MAIN check.
if (!SKIP_PRE && !status.pre) {
revert PreCheckNotEnforced();
}
if (!ALLOW_MULTIPLE_MAIN && status.main > 0) {
revert MainCheckAlreadyEnforced();
}
status.main += 1;
}

emit Enforced(subject, target, evidence, checkType);
}
}
26 changes: 26 additions & 0 deletions packages/contracts/contracts/src/BaseChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IBaseChecker} from "./interfaces/IBaseChecker.sol";

/// @title BaseChecker
/// @notice Abstract base contract for implementing validation checks.
/// @dev Provides a standardized interface for implementing custom validation logic
/// through the internal _check method.
abstract contract BaseChecker is IBaseChecker {
/// @notice Validates evidence for a given subject address.
/// @dev External view function that delegates to internal _check implementation.
/// @param subject Address to validate.
/// @param evidence Custom validation data.
/// @return checked Boolean indicating if the check passed.
function check(address subject, bytes memory evidence) external view override returns (bool checked) {
return _check(subject, evidence);
}

/// @notice Internal validation logic implementation.
/// @dev Must be implemented by derived contracts.
/// @param subject Address to validate.
/// @param evidence Custom validation data.
/// @return checked Boolean indicating if the check passed.
function _check(address subject, bytes memory evidence) internal view virtual returns (bool checked) {}
}
58 changes: 58 additions & 0 deletions packages/contracts/contracts/src/BasePolicy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IBasePolicy} from "./interfaces/IBasePolicy.sol";
import {Policy} from "./Policy.sol";
import {BaseChecker} from "./BaseChecker.sol";

/// @title BasePolicy
/// @notice Abstract base contract for implementing specific policy checks.
/// @dev Inherits from Policy and implements IBasePolicy interface.
///
/// Provides core functionality for enforcing policy checks through a BaseChecker
/// contract. Each specific policy implementation should extend this contract
/// and implement its custom checking logic.
abstract contract BasePolicy is Policy, IBasePolicy {
/// @notice Reference to the BaseChecker contract used for validation.
/// @dev Immutable to ensure checker cannot be changed after deployment.
BaseChecker public immutable BASE_CHECKER;

/// @notice Tracks enforcement status for each subject per target.
/// @dev Maps target => subject => enforcement status.
mapping(address => mapping(address => bool)) public enforced;
0xjei marked this conversation as resolved.
Show resolved Hide resolved

/// @notice Initializes the contract with a BaseChecker instance.
/// @param _baseChecker Address of the BaseChecker contract.
/// @dev The BaseChecker address cannot be changed after deployment.
constructor(BaseChecker _baseChecker) {
BASE_CHECKER = _baseChecker;
}

/// @notice External function to enforce policy checks.
/// @dev Only callable by the target contract.
/// @param subject Address to enforce the check on.
/// @param evidence Additional data required for verification.
/// @custom:throws AlreadyEnforced if check was previously enforced.
/// @custom:throws UnsuccessfulCheck if the check fails.
/// @custom:emits Enforced when check succeeds.
function enforce(address subject, bytes calldata evidence) external override onlyTarget {
_enforce(subject, evidence);
}

/// @notice Internal implementation of enforcement logic.
/// @dev Performs the actual check using BASE_CHECKER.
/// @param subject Address to enforce the check on.
/// @param evidence Additional data required for verification.
/// @custom:throws AlreadyEnforced if already enforced for this subject.
/// @custom:throws UnsuccessfulCheck if BASE_CHECKER.check returns false.
function _enforce(address subject, bytes calldata evidence) internal {
bool checked = BASE_CHECKER.check(subject, evidence);

if (enforced[msg.sender][subject]) revert AlreadyEnforced();
if (!checked) revert UnsuccessfulCheck();

enforced[msg.sender][subject] = checked;

emit Enforced(subject, target, evidence);
}
}
49 changes: 49 additions & 0 deletions packages/contracts/contracts/src/Policy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IPolicy} from "./interfaces/IPolicy.sol";

/// @title Policy
/// @notice Implements a base policy contract that protects access to a target contract
/// @dev Inherits from OpenZeppelin's Ownable and implements IPolicy interface
///
/// This contract serves as a base for implementing specific policy checks that must be
/// satisfied before interacting with a protected target contract. It provides core
/// functionality for managing the protected target address and access control.
abstract contract Policy is IPolicy, Ownable(msg.sender) {
/// @notice The policy-protected contract address.
/// @dev This address can only be set once by the owner.
/// For example, the target is a Semaphore group that requires the subject
/// to meet certain criteria in order to join the group.
address internal target;

/// @notice Restricts function access to only the target contract.
/// @dev Throws TargetOnly error if called by any other address.
modifier onlyTarget() {
if (msg.sender != target) revert TargetOnly();
_;
}

/// @notice Sets the target contract address.
/// @dev Can only be called once by the owner.
/// @param _target Address of the contract to be protected by this policy.
/// @custom:throws ZeroAddress if _target is the zero address.
/// @custom:throws TargetAlreadySet if target has already been set.
/// @custom:emits TargetSet when target is successfully set.
function setTarget(address _target) external virtual onlyOwner {
if (_target == address(0)) revert ZeroAddress();
if (target != address(0)) revert TargetAlreadySet();

target = _target;

emit TargetSet(_target);
}

/// @notice Retrieves the current target contract address.
/// @return address The address of the policy-protected contract.
/// @dev Returns zero address if target hasn't been set yet.
function getTarget() public view returns (address) {
return target;
}
}
Loading
Loading