Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"examples/mock/MockNewtonPolicyClient.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import {NewtonPolicyClient} from "../../src/mixins/NewtonPolicyClient.sol";
import {NewtonMessage} from "../../src/core/NewtonMessage.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
contract MockNewtonPolicyClient is NewtonPolicyClient, OwnableUpgradeable {
// Errors
error InvalidAttestation();
error IntentExecutionFailed();
// Events
/* Token Deposits */
event Deposit(address indexed clientAddress, address token, uint256 tokenAmount);
/* Token Withdrawals */
event Withdraw(address indexed clientAddress, address token, uint256 tokenAmount);
/* Intent Execution */
event IntentExecuted(address indexed clientAddress, NewtonMessage.Intent intent);
constructor() {
_disableInitializers();
}
function initialize(
address policyTaskManager,
address policy,
address owner
) public initializer {
_initNewtonPolicyClient(policyTaskManager, policy, owner);
__Ownable_init();
_transferOwnership(owner);
}
function setOwner(
address _owner
) external onlyOwner {
_transferOwnership(_owner);
NewtonPolicyClient(address(this)).setPolicyClientOwner(_owner);
}
function deposit(address token, uint256 tokenAmount) external onlyOwner {
IERC20(token).transferFrom(msg.sender, address(this), tokenAmount);
emit Deposit(address(this), token, tokenAmount);
}
function balanceOf(
address token
) external view returns (uint256) {
return IERC20(token).balanceOf(address(this));
}
function withdraw(address token, uint256 tokenAmount) external onlyOwner {
IERC20(token).transfer(msg.sender, tokenAmount);
emit Withdraw(address(this), token, tokenAmount);
}
function executeIntent(
NewtonMessage.Attestation calldata attestation
) external returns (bytes memory) {
require(_validateAttestation(attestation), InvalidAttestation());
NewtonMessage.Intent memory intent = attestation.intent;
// Send the raw call and capture return data
(bool success, bytes memory returnData) = intent.to.call{value: intent.value}(intent.data);
if (!success) {
// Bubble up the revert error if there's return data, otherwise use generic error
if (returnData.length > 0) {
assembly {
let returnDataSize := mload(returnData)
revert(add(32, returnData), returnDataSize)
}
} else {
revert IntentExecutionFailed();
}
}
emit IntentExecuted(address(this), intent);
return returnData;
}
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return super.supportsInterface(interfaceId);
}
}
"
},
"src/mixins/NewtonPolicyClient.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {INewtonProverTaskManager} from "../interfaces/INewtonProverTaskManager.sol";
import {INewtonPolicyClient} from "../interfaces/INewtonPolicyClient.sol";
import {NewtonPolicy} from "../core/NewtonPolicy.sol";
import {NewtonMessage} from "../core/NewtonMessage.sol";
import {INewtonPolicy} from "../interfaces/INewtonPolicy.sol";
abstract contract NewtonPolicyClient is INewtonPolicyClient {
/// @notice Function to check if a contract implements an interface
/// @param interfaceId The interface identifier to check
/// @return True if the contract implements the interface, false otherwise
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId // 0x01ffc9a7
|| interfaceId == type(INewtonPolicyClient).interfaceId;
}
// error for when a call is made by an account other than the owner
error OnlyPolicyClientOwner();
// modifier to restrict functions to only the owner
modifier onlyPolicyClientOwner() {
require(
msg.sender == _getNewtonPolicyClientStorage().policyClientOwner, OnlyPolicyClientOwner()
);
_;
}
/// @notice Struct to contain stateful values for NewtonPolicyClient-type contracts
/// @custom:storage-location erc7201:newton.storage.NewtonPolicyClient
struct NewtonPolicyClientStorage {
INewtonProverTaskManager policyTaskManager;
address policy;
bytes32 policyId;
address policyClientOwner;
}
/// @notice EIP-1967 proxy storage slot for the NewtonPolicyClientStorage struct
/// @dev keccak256(abi.encode(uint256(keccak256("newton.storage.NewtonPolicyClient")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _NEWTON_POLICY_CLIENT_STORAGE_SLOT =
0xaa6954ac1e404d8f79e6eba698b90c3c7071936d683ce65dd13ddf463ffbcb00;
function _getNewtonPolicyClientStorage()
private
pure
returns (NewtonPolicyClientStorage storage $)
{
assembly {
$.slot := _NEWTON_POLICY_CLIENT_STORAGE_SLOT
}
}
function _initNewtonPolicyClient(
address policyTaskManager,
address policy,
address policyClientOwner
) internal {
NewtonPolicyClientStorage storage $ = _getNewtonPolicyClientStorage();
$.policyTaskManager = INewtonProverTaskManager(policyTaskManager);
$.policy = policy;
$.policyClientOwner = policyClientOwner;
}
/**
* @notice Only callable by the owner. Used for external policy configuration.
* @param policyClientOwner The new policy client owner.
*/
function setPolicyClientOwner(
address policyClientOwner
) external onlyPolicyClientOwner {
NewtonPolicyClientStorage storage $ = _getNewtonPolicyClientStorage();
$.policyClientOwner = policyClientOwner;
}
/**
* @notice Sets a policy for the calling address to the policyID from on chain.
* @param policyConfig The policy configuration.
* @return policyId The policyID associated with the calling address.
* @dev This function enables clients to define execution rules or parameters for tasks they submit.
* The policy governs how tasks submitted by the caller are executed, ensuring compliance with predefined rules.
*/
function _setPolicy(
INewtonPolicy.PolicyConfig memory policyConfig
) internal returns (bytes32) {
NewtonPolicyClientStorage storage $ = _getNewtonPolicyClientStorage();
bytes32 policyId = NewtonPolicy($.policy).setPolicy(policyConfig);
$.policyId = policyId;
return policyId;
}
/**
* @notice Same as _setPolicy, but only callable by the owner. Used for external policy configuration.
* @param policyConfig The policy configuration.
* @return policyId The policyID associated with the calling address.
*/
function setPolicy(
INewtonPolicy.PolicyConfig memory policyConfig
) external onlyPolicyClientOwner returns (bytes32) {
return _setPolicy(policyConfig);
}
function getPolicyAddress() external view returns (address) {
return _getPolicyAddress();
}
function _getPolicyAddress() internal view returns (address) {
return _getNewtonPolicyClientStorage().policy;
}
function getPolicyConfig() external view returns (INewtonPolicy.PolicyConfig memory) {
return _getPolicyConfig();
}
function _getPolicyConfig() internal view returns (INewtonPolicy.PolicyConfig memory) {
return NewtonPolicy(_getNewtonPolicyClientStorage().policy).getPolicyConfig(_getPolicyId());
}
function getPolicyId() external view returns (bytes32) {
return _getPolicyId();
}
function _getPolicyId() internal view returns (bytes32) {
return _getNewtonPolicyClientStorage().policyId;
}
function getNewtonPolicyTaskManager() external view returns (address) {
return _getNewtonPolicyTaskManager();
}
function _getNewtonPolicyTaskManager() internal view returns (address) {
return address(_getNewtonPolicyClientStorage().policyTaskManager);
}
/**
* @notice Validates the transaction by checking the policy evaluation task response.
* @param attestation the attestation to validate
* @return true if the attestation is valid, false otherwise
* @dev This function validates the attestation by checking the policy ID and the intent sender.
* NOTE: Attestation is valid if the policy ID matches and the intent sender is the caller.
* If either of the conditions is not met, the function reverts with an Unauthorized error.
*/
function _validateAttestation(
NewtonMessage.Attestation memory attestation
) internal returns (bool) {
NewtonPolicyClientStorage storage $ = _getNewtonPolicyClientStorage();
require(
attestation.policyId == $.policyId,
NewtonMessage.Unauthorized("Policy ID does not match")
);
require(
attestation.intent.from == msg.sender,
NewtonMessage.Unauthorized("Not authorized intent sender")
);
require(
attestation.intent.chainId == block.chainid,
NewtonMessage.Unauthorized("Chain ID does not match")
);
return $.policyTaskManager.validateAttestation(attestation);
}
}
"
},
"src/core/NewtonMessage.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
/// @notice Contract for a NewtonMessage
contract NewtonMessage {
// STRUCTS
/// @notice Intent struct for a transaction authorization
struct Intent {
// equivalent to tx.origin/from
address from;
// equivalent to to
address to;
// equivalent to msg.value
uint256 value;
// ABI-encoded calldata. function selector and arguments
bytes data;
// chain id of the chain that the transaction is on
uint256 chainId;
// encoded ABI of the function that is being called
// e.g. abi.encodePacked("function transfer(address,uint256)")
bytes functionSignature;
}
/// @notice Attestation struct for a transaction authorization
struct Attestation {
// task id
bytes32 taskId;
// policy id
bytes32 policyId;
// policy client
address policyClient;
// intent
Intent intent;
// expiration block number for the attestation
uint32 expiration;
}
/// @notice PolicyData struct for a policy data and its attestation proof
struct PolicyData {
// encoded policy data
bytes data;
// attestation proof for the policy data.
bytes attestation;
// policy data address
address policyDataAddress;
// expiration block number for the policy data
uint32 expireBlock;
}
/// @notice PolicyTaskData struct for a policy data
struct PolicyTaskData {
// policy id
bytes32 policyId;
// policy address
address policyAddress;
// policy program binary
bytes policy;
// an array of policy data with attestation
// NOTE: order matters, the first policy data is the first policy data in the policy data set of the policy.
PolicyData[] policyData;
}
/// @notice VerificationInfo struct for a policy data verification
struct VerificationInfo {
// verifier
address verifier;
// verified
bool verified;
// timestamp
uint256 timestamp;
}
/// @notice error type for unauthorized access
error Unauthorized(string reason);
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
"
},
"src/interfaces/INewtonProverTaskManager.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import "@eigenlayer-middleware/src/libraries/BN254.sol";
import "@eigenlayer-middleware/src/interfaces/IBLSSignatureChecker.sol";
import {NewtonMessage} from "../core/NewtonMessage.sol";
import {INewtonPolicy} from "./INewtonPolicy.sol";
interface INewtonProverTaskManager {
// EVENTS
event NewTaskCreated(bytes32 indexed taskId, Task task);
event TaskResponded(TaskResponse taskResponse, ResponseCertificate responseCertificate);
event TaskChallengedSuccessfully(bytes32 indexed taskId, address indexed challenger);
event TaskChallengedUnsuccessfully(bytes32 indexed taskId, address indexed challenger);
event AttestationSpent(bytes32 indexed taskId, NewtonMessage.Attestation attestation);
// STRUCTS
// task submitter decides on the criteria for a task to be completed
// note that this does not mean the task was "correctly" answered (i.e. the number was proved correctly)
// this is for the challenge logic to verify
// task is completed (and contract will accept its TaskResponse) when each quorumNumbers specified here
// are signed by at least quorumThresholdPercentage of the operators
// note that we set the quorumThresholdPercentage to be the same for all quorumNumbers, but this could be changed
struct Task {
// the unique identifier for the task
bytes32 taskId;
// policy client address
address policyClient;
// policy id
bytes32 policyId;
// the nonce of the task
uint32 nonce;
// the intent of the task
NewtonMessage.Intent intent;
// the policy task data of the task
NewtonMessage.PolicyTaskData policyTaskData;
// policy configuration for the policy program
INewtonPolicy.PolicyConfig policyConfig;
// the block number when the task was created
uint32 taskCreatedBlock;
// the quorum numbers of the task
bytes quorumNumbers;
// the quorum threshold percentage of the task
uint32 quorumThresholdPercentage;
}
// Task response is hashed and signed by operators.
// these signatures are aggregated and sent to the contract as response.
struct TaskResponse {
// Can be obtained by the operator from the event NewTaskCreated.
bytes32 taskId;
// policy client address
address policyClient;
// policy id of the task
bytes32 policyId;
// the policy address of the task
address policyAddress;
// the intent of the task
NewtonMessage.Intent intent;
// Policy evaluation result.
bytes evaluationResult;
}
// Certificate is filled by the protocol contract for each taskResponse signed by operators.
// This Certificate is used by policy clients to attest the validity of policy evaluation result
// during intent execution.
// This certificate is also used by the challenger, who monitors and if invalid, raises challenge
// with zero-knowledge proof of the policy evaluation result discrepancy.
// NOTE: this can be used as an attestation for not just single chain but multi-chain attestation.
struct ResponseCertificate {
// the block number when the response certificate is created
uint32 referenceBlock;
// the hash of the non-signers
bytes32 hashOfNonSigners;
// the non-signers and their stakes
IBLSSignatureChecker.NonSignerStakesAndSignature nonSignerStakesAndSignature;
// the block number when the task response expires
uint32 responseExpireBlock;
}
// Challenge data is submitted by the challenger.
// Contains the proof data and verification key for onchain verification of the policy evaluation result.
// TODO: add support for risc0 zk proofs, and other proof types.
struct ChallengeData {
// Can be obtained by the operator from the event NewTaskCreated.
bytes32 taskId;
// sp1 zk proof to attest the policy evaluation result of the challenger
bytes proof;
// The committed proof output to verify against the task response data.
bytes data;
}
// FUNCTIONS
// NOTE: this function creates new task.
function createNewTask(
bytes32 taskId,
address policyClient,
NewtonMessage.Intent calldata intent,
NewtonMessage.PolicyTaskData calldata policyTaskData,
bytes calldata quorumNumbers,
uint32 quorumThresholdPercentage
) external;
// NOTE: this function responds to existing tasks.
function respondToTask(
Task calldata task,
TaskResponse calldata taskResponse,
IBLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature
) external;
// NOTE: this function raises challenge to existing tasks.
function raiseAndResolveChallenge(
Task calldata task,
TaskResponse calldata taskResponse,
ResponseCertificate calldata responseCertificate,
ChallengeData calldata challenge,
BN254.G1Point[] memory pubkeysOfNonSigningOperators
) external;
// NOTE: this function authorizes existing task responses.
function validateAttestation(
NewtonMessage.Attestation calldata attestation
) external returns (bool);
}
"
},
"src/interfaces/INewtonPolicyClient.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @notice Interface for a NewtonPolicyClient-type contract that enables clients to define execution rules or parameters for tasks they submit
interface INewtonPolicyClient is IERC165 {
/// @notice error for when validate() is called with an incorrect policyID
error InvalidPolicyID();
/**
* @notice Retrieves the policyID for the calling address.
* @return The policyID associated with the calling address.
*/
function getPolicyId() external view returns (bytes32);
/**
* @notice Retrieves the policy address for the calling address.
* @return The policy address associated with the calling address.
*/
function getPolicyAddress() external view returns (address);
/**
* @notice Function for getting the Newton PolicyTaskManager
* @return address of the policy task manager
*/
function getNewtonPolicyTaskManager() external view returns (address);
}
"
},
"src/core/NewtonPolicy.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin-upgrades/contracts/utils/introspection/ERC165Upgradeable.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import {NewtonPolicyFactory} from "./NewtonPolicyFactory.sol";
import {INewtonPolicyClient} from "../interfaces/INewtonPolicyClient.sol";
import {INewtonPolicy} from "../interfaces/INewtonPolicy.sol";
contract NewtonPolicy is Initializable, OwnableUpgradeable, ERC165Upgradeable, INewtonPolicy {
/* STORAGE */
address public factory;
string public policyCid;
string public schemaCid;
string public entrypoint;
address[] public policyData;
string public metadataCid;
// mapping of policyId to per policy config
mapping(bytes32 => INewtonPolicy.PolicyConfig) private _policyIdToConfig;
// mapping of client to policyId
mapping(address => bytes32) public clientToPolicyId;
/* ERRORS */
error OnlyPolicyClient();
error InterfaceNotSupported();
/* Modifiers */
modifier onlyPolicyClient() {
bytes4 interfaceId = type(INewtonPolicyClient).interfaceId;
(bool success, bytes memory result) = msg.sender.staticcall(
abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId)
);
require(
success && result.length == 32 && abi.decode(result, (bool)), InterfaceNotSupported()
);
_;
}
function initialize(
address _factory,
string calldata _entrypoint,
string calldata _policyCid,
string calldata _schemaCid,
address[] calldata _policyData,
string calldata _metadataCid,
address _owner
) public initializer {
__Ownable_init();
_transferOwnership(_owner);
__ERC165_init();
factory = _factory;
policyCid = _policyCid;
schemaCid = _schemaCid;
policyData = _policyData;
entrypoint = _entrypoint;
metadataCid = _metadataCid;
}
// function to set policy for the msg.sender (client)
function setPolicy(
INewtonPolicy.PolicyConfig calldata policyConfig
) public onlyPolicyClient returns (bytes32) {
bytes32 policyId = keccak256(
abi.encode(
msg.sender,
address(this),
owner(),
policyCid,
schemaCid,
entrypoint,
policyConfig,
policyData,
block.timestamp
)
);
_policyIdToConfig[policyId] = policyConfig;
clientToPolicyId[msg.sender] = policyId;
emit PolicySet(
msg.sender,
policyId,
SetPolicyInfo(
policyId,
address(this),
owner(),
policyCid,
schemaCid,
entrypoint,
policyConfig,
policyData
)
);
return policyId;
}
function getPolicyId(
address client
) public view returns (bytes32) {
return clientToPolicyId[client];
}
function getMetadataCid() public view returns (string memory) {
return metadataCid;
}
function setMetadataCid(
string calldata _metadataCid
) public onlyOwner {
metadataCid = _metadataCid;
emit policyMetadataCidUpdated(_metadataCid);
}
function getEntrypoint() public view returns (string memory) {
return entrypoint;
}
function getPolicyCid() public view returns (string memory) {
return policyCid;
}
function getSchemaCid() public view returns (string memory) {
return schemaCid;
}
function getPolicyData() public view returns (address[] memory) {
return policyData;
}
function getPolicyConfig(
bytes32 policyId
) public view returns (INewtonPolicy.PolicyConfig memory) {
return _policyIdToConfig[policyId];
}
function isPolicyVerified() public view returns (bool) {
return NewtonPolicyFactory(factory).getPolicyVerificationInfo(address(this)).verified;
}
/// @notice Function to check if a contract implements an interface
/// @param interfaceId The interface identifier to check
/// @return True if the contract implements the interface, false otherwise
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) {
return
interfaceId == type(INewtonPolicy).interfaceId || super.supportsInterface(interfaceId);
}
}
"
},
"src/interfaces/INewtonPolicy.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @notice Interface for a NewtonPolicy
/// @dev For Rego policy grammar, refer to https://github.com/microsoft/regorus/blob/main/docs/grammar.md
interface INewtonPolicy is IERC165 {
struct PolicyConfig {
bytes policyParams;
uint32 expireAfter;
}
struct SetPolicyInfo {
bytes32 policyId;
address policyAddress;
address owner;
string policyCid;
string schemaCid;
string entrypoint;
PolicyConfig policyConfig;
address[] policyData;
}
struct PolicyInfo {
address policyAddress;
address owner;
string metadataCid;
string policyCid;
string schemaCid;
string entrypoint;
address[] policyData;
}
/* Events */
event PolicySet(address indexed client, bytes32 indexed policyId, SetPolicyInfo policy);
event policyMetadataCidUpdated(string metadataCid);
/**
* @notice Retrieves the metadata CID for the policy.
* @return The metadata CID for the policy.
*/
function getMetadataCid() external view returns (string memory);
/**
* @notice Sets the metadata CID for the policy.
* @param metadataCid The metadata CID to set for the policy.
*/
function setMetadataCid(
string calldata metadataCid
) external;
/**
* @notice Retrieves the policyID for the calling address.
* @return The policyID associated with the calling address.
*/
function getPolicyId(
address client
) external view returns (bytes32);
/**
* @notice Retrieves the policy evaluation entrypoint from the Rego policy
* @return The policy evaluation entrypoint from the Rego policy
* @dev Expected format is {package}.{output} for the Rego program
*/
function getEntrypoint() external view returns (string memory);
/**
* @notice Retrieves the policy params schema from the Rego policy
* @return The policy params schema from the Rego policy
* @dev https://docs.rs/regorus/latest/regorus/struct.Schema.html
*/
function getSchemaCid() external view returns (string memory);
/**
* @notice Retrieves the policy location for the policy.
* @return The policy location for the policy.
*/
function getPolicyCid() external view returns (string memory);
/**
* @notice Retrieves the policy configuration for the given policyID.
* @param policyId The policyID to retrieve the policy configuration for.
* @return The policy configuration for the given policyID.
*/
function getPolicyConfig(
bytes32 policyId
) external view returns (PolicyConfig memory);
/**
* @notice Retrieves the policy data contract addresses.
* @return The policy data contract addresses.
*/
function getPolicyData() external view returns (address[] memory);
/**
* @notice Retrieves the policy verified status.
* @return The policy verified status.
*/
function isPolicyVerified() external view returns (bool);
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"lib/eigenlayer-middleware/src/libraries/BN254.sol": {
"content": "// SPDX-License-Identifier: MIT
// several functions are taken or adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol (MIT license):
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license
pragma solidity ^0.8.27;
/**
* @title Library for operations on the BN254 elliptic curve.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contains BN254 parameters, common operations (addition, scalar mul, pairing), and BLS signature functionality.
*/
library BN254 {
// modulus for the underlying field F_p of the elliptic curve
uint256 internal constant FP_MODULUS =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
// modulus for the underlying field F_r of the elliptic curve
uint256 internal constant FR_MODULUS =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[1] * i + X[0]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/// @dev Thrown when the sum of two points of G1 fails
error ECAddFailed();
/// @dev Thrown when the scalar multiplication of a point of G1 fails
error ECMulFailed();
/// @dev Thrown when the scalar is too large.
error ScalarTooLarge();
/// @dev Thrown when the pairing check fails
error ECPairingFailed();
/// @dev Thrown when the exponentiation mod fails
error ExpModFailed();
function generatorG1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
// generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant G2x1 =
11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant G2x0 =
10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant G2y1 =
4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 internal constant G2y0 =
8495653923123431417604973247489272438418190587263600148770280649306958101930;
/// @notice returns the G2 generator
/// @dev mind the ordering of the 1s and 0s!
/// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract
/// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)."
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding
function generatorG2() internal pure returns (G2Point memory) {
return G2Point([G2x1, G2x0], [G2y1, G2y0]);
}
// negation of the generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant nG2x1 =
11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant nG2x0 =
10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant nG2y1 =
17805874995975841540914202342111839520379459829704422454583296818431106115052;
uint256 internal constant nG2y0 =
13392588948715843804641432497768002650278120570034223513918757245338268106653;
function negGeneratorG2() internal pure returns (G2Point memory) {
return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]);
}
bytes32 internal constant powersOfTauMerkleRoot =
0x22c998e49752bbb1918ba87d6d59dd0e83620a311ba91dd4b2cc84990b31b56f;
/**
* @param p Some point in G1.
* @return The negation of `p`, i.e. p.plus(p.negate()) should be zero.
*/
function negate(
G1Point memory p
) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, FP_MODULUS - (p.Y % FP_MODULUS));
}
}
/**
* @return r the sum of two points of G1
*/
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, ECAddFailed());
}
/**
* @notice an optimized ecMul implementation that takes O(log_2(s)) ecAdds
* @param p the point to multiply
* @param s the scalar to multiply by
* @dev this function is only safe to use if the scalar is 9 bits or less
*/
function scalar_mul_tiny(
BN254.G1Point memory p,
uint16 s
) internal view returns (BN254.G1Point memory) {
require(s < 2 ** 9, ScalarTooLarge());
// if s is 1 return p
if (s == 1) {
return p;
}
// the accumulated product to return
BN254.G1Point memory acc = BN254.G1Point(0, 0);
// the 2^n*p to add to the accumulated product in each iteration
BN254.G1Point memory p2n = p;
// value of most significant bit
uint16 m = 1;
// index of most significant bit
uint8 i = 0;
//loop until we reach the most significant bit
while (s >= m) {
unchecked {
// if the current bit is 1, add the 2^n*p to the accumulated product
if ((s >> i) & 1 == 1) {
acc = plus(acc, p2n);
}
// double the 2^n*p for the next iteration
p2n = plus(p2n, p2n);
// increment the index and double the value of the most significant bit
m <<= 1;
++i;
}
}
// return the accumulated product
return acc;
}
/**
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, ECMulFailed());
}
/**
* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, ECPairingFailed());
return out[0] != 0;
}
/**
* @notice This function is functionally the same as pairing(), however it specifies a gas limit
* the user can set, as a precompile may use the entire gas budget if it reverts.
*/
function safePairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
uint256 pairingGas
) internal view returns (bool, bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(pairingGas, 8, input, mul(12, 0x20), out, 0x20)
}
//Out is the output of the pairing precompile, either 0 or 1 based on whether the two pairings are equal.
//Success is true if the precompile actually goes through (aka all inputs are valid)
return (success, out[0] != 0);
}
/// @return hashedG1 the keccak256 hash of the G1 Point
/// @dev used for BLS signatures
function hashG1Point(
BN254.G1Point memory pk
) internal pure returns (bytes32 hashedG1) {
assembly {
mstore(0, mload(pk))
mstore(0x20, mload(add(0x20, pk)))
hashedG1 := keccak256(0, 0x40)
}
}
/// @return the keccak256 hash of the G2 Point
/// @dev used for BLS signatures
function hashG2Point(
BN254.G2Point memory pk
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1]));
}
/**
* @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
*/
function hashToG1(
bytes32 _x
) internal view returns (G1Point memory) {
uint256 beta = 0;
uint256 y = 0;
uint256 x = uint256(_x) % FP_MODULUS;
while (true) {
(beta, y) = findYFromX(x);
// y^2 == beta
if (beta == mulmod(y, y, FP_MODULUS)) {
return G1Point(x, y);
}
x = addmod(x, 1, FP_MODULUS);
}
return G1Point(0, 0);
}
/**
* Given X, find Y
*
* where y = sqrt(x^3 + b)
*
* Returns: (x^3 + b), y
*/
function findYFromX(
uint256 x
) internal view returns (uint256, uint256) {
// beta = (x^3 + b) % p
uint256 beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS);
// y^2 = x^3 + b
// this acts like: y = sqrt(beta) = beta^((p+1) / 4)
uint256 y = expMod(
beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS
);
return (beta, y);
}
function expMod(
uint256 _base,
uint256 _exponent,
uint256 _modulus
) internal view returns (uint256 retval) {
bool success;
uint256[1] memory output;
uint256[6] memory input;
input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32))
input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32))
input[3] = _base;
input[4] = _exponent;
input[5] = _modulus;
assembly {
success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, ExpModFailed());
return output[0];
}
}
"
},
"lib/eigenlayer-middleware/src/interfaces/IBLSSignatureChecker.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol";
import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
import {IStakeRegistry, IDelegationManager} from "./IStakeRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
interface IBLSSignatureCheckerErrors {
/// @notice Thrown when the caller is not the registry coordinator owner.
error OnlyRegistryCoordinatorOwner();
/// @notice Thrown when the quorum numbers input in is empty.
error InputEmptyQuorumNumbers();
/// @notice Thrown when two array parameters have mismatching lengths.
error InputArrayLengthMismatch();
/// @notice Thrown when the non-signer pubkey length does not match non-signer bitmap indices length.
error InputNonSignerLengthMismatch();
/// @notice Thrown when the reference block number is invalid.
error InvalidReferenceBlocknumber();
/// @notice Thrown when the non signer pubkeys are not sorted.
error NonSignerPubkeysNotSorted();
/// @notice Thrown when StakeRegistry updates have not been updated within withdrawalDelayBlocks window
error StaleStakesForbidden();
/// @notice Thrown when the quorum apk hash in storage does not match provided quorum apk.
error InvalidQuorumApkHash();
/// @notice Thrown when BLS pairing precompile call fails.
error InvalidBLSPairingKey();
/// @notice Thrown when BLS signature is invalid.
error InvalidBLSSignature();
}
interface IBLSSignatureCheckerTypes {
/// @notice Contains bitmap and pubkey hash information for non-signing operators.
/// @param quorumBitmaps Array of bitmaps indicating which quorums each non-signer was registered for.
/// @param pubkeyHashes Array of BLS public key hashes for each non-signer.
struct NonSignerInfo {
uint256[] quorumBitmaps;
bytes32[] pubkeyHashes;
}
/// @notice Contains non-signer information and aggregated signature data for BLS verification.
/// @param nonSignerQuorumBitmapIndices The indices of all non-signer quorum bitmaps.
/// @param nonSignerPubkeys The G1 public keys of all non-signers.
/// @param quorumApks The aggregate G1 public key of each quorum.
/// @param apkG2 The aggregate G2 public key of all signers.
/// @param sigma The aggregate G1 signature of all signers.
/// @param quorumApkIndices The indices of each quorum's aggregate public key in the APK registry.
/// @param totalStakeIndices The indices of each quorum's total stake in the stake registry.
/// @param nonSignerStakeIndices The indices of each non-signer's stake within each quorum.
/// @dev Used as input to checkSignatures() to verify BLS signatures.
struct NonSignerStakesAndSignature {
uint32[] nonSignerQuorumBitmapIndices;
BN254.G1Point[] nonSignerPubkeys;
BN254.G1Point[] quorumApks;
BN254.G2Point apkG2;
BN254.G1Point sigma;
uint32[] quorumApkIndices;
uint32[] totalStakeIndices;
uint32[][] nonSignerStakeIndices;
}
/// @notice Records the total stake amounts for operators in each quorum.
/// @param signedStakeForQuorum Array of total stake amounts from operators who signed, per quorum.
/// @param totalStakeForQuorum Array of total stake amounts from all operators, per quorum.
/// @dev Used to track stake distribution and calculate quorum thresholds. Array indices correspond to quorum numbers.
struct QuorumStakeTotals {
uint96[] signedStakeForQuorum;
uint96[] totalStakeForQuorum;
}
}
interface IBLSSignatureCheckerEvents is IBLSSignatureCheckerTypes {
/// @notice Emitted when `staleStakesForbiddenUpdate` is set.
event StaleStakesForbiddenUpdate(bool value);
}
interface IBLSSignatureChecker is IBLSSignatureCheckerErrors, IBLSSignatureCheckerEvents {
/* STATE */
/*
* @notice Returns the address of the registry coordinator contract.
* @return The address of the registry coordinator.
* @dev This value is immutable and set during contract construction.
*/
function registryCoordinator() external view returns (ISlashingRegistryCoordinator);
/*
* @notice Returns the address of the stake registry contract.
* @return The address of the stake registry.
* @dev This value is immutable and set during contract construction.
*/
function stakeRegistry() external view returns (IStakeRegistry);
/*
* @notice Returns the address of the BLS APK registry contract.
* @return The address of the BLS APK registry.
* @dev This value is immutable and set during contract construction.
*/
function blsApkRegistry() external view returns (IBLSApkRegistry);
/*
* @notice Returns the address of the delegation manager contract.
* @return The address of the delegation manager.
* @dev This value is immutable and set during contract construction.
*/
function delegation() external view returns (IDelegationManager);
/*
* @notice Returns whether stale stakes are forbidden in signature verification.
* @return True if stale stakes are forbidden, false otherwise.
*/
function staleStakesForbidden() external view returns (bool);
/* ACTIONS */
/*
* @notice Sets `value` as the new staleStakesForbidden flag.
* @param value True to forbid stale stakes, false to allow them.
* @dev Access restricted to the registry coordinator owner.
*/
function setStaleStakesForbidden(
bool value
) external;
/* VIEW */
/*
* @notice This function is called by disperser when it has aggregated all the signatures of the operators
* that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function
* checks that the claim for aggregated signatures are valid.
*
* The thesis of this procedure entails:
* 1. Getting the aggregated pubkey of all registered nodes at the time of pre-commit by the
* disperser (represented by apk in the parameters)
* 2. Subtracting the pubkeys of all non-signers (nonSignerPubkeys) and storing
* the output in apk to get aggregated pubkey of all operators that are part of quorum
* 3. Using this aggregated pubkey to verify the aggregated signature under BLS scheme
*
* @param msgHash The hash of the message that was signed. NOTE: Be careful to ensure msgHash is
* collision-resistant! This method does not hash msgHash in any way, so if an attacker is able
* to pass in an arbitrary value, they may be able to tamper with signature verification.
* @param quorumNumbers The quorum numbers to verify signatures for, where each byte is an 8-bit integer.
* @param referenceBlockNumber The block number at which the stake information is being verified
* @param nonSignerStakesAndSignature Contains non-signer information and aggregated signature data.
* @return quorumStakeTotals The struct containing the total and signed stake for each quorum
* @return signatoryRecordHash The hash of the signatory record, which is used for fraud proofs
* @dev Before signature verification, the function verifies operator stake information. T
Submitted on: 2025-10-14 18:41:02
Comments
Log in to comment.
No comments yet.