Contract Name:
VerifierProxy
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol";
import {IVerifier} from "./interfaces/IVerifier.sol";
import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol";
import {AccessControllerInterface} from "../../shared/interfaces/AccessControllerInterface.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";
import {Common} from "../libraries/Common.sol";
/**
* The verifier proxy contract is the gateway for all report verification requests
* on a chain. It is responsible for taking in a verification request and routing
* it to the correct verifier contract.
*/
contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterface {
/// @notice This event is emitted whenever a new verifier contract is set
/// @param oldConfigDigest The config digest that was previously the latest config
/// digest of the verifier contract at the verifier address.
/// @param oldConfigDigest The latest config digest of the verifier contract
/// at the verifier address.
/// @param verifierAddress The address of the verifier contract that verifies reports for
/// a given digest
event VerifierSet(bytes32 oldConfigDigest, bytes32 newConfigDigest, address verifierAddress);
/// @notice This event is emitted whenever a new verifier contract is initialized
/// @param verifierAddress The address of the verifier contract that verifies reports
event VerifierInitialized(address verifierAddress);
/// @notice This event is emitted whenever a verifier is unset
/// @param configDigest The config digest that was unset
/// @param verifierAddress The Verifier contract address unset
event VerifierUnset(bytes32 configDigest, address verifierAddress);
/// @notice This event is emitted when a new access controller is set
/// @param oldAccessController The old access controller address
/// @param newAccessController The new access controller address
event AccessControllerSet(address oldAccessController, address newAccessController);
/// @notice This event is emitted when a new fee manager is set
/// @param oldFeeManager The old fee manager address
/// @param newFeeManager The new fee manager address
event FeeManagerSet(address oldFeeManager, address newFeeManager);
/// @notice This error is thrown whenever an address tries
/// to exeecute a transaction that it is not authorized to do so
error AccessForbidden();
/// @notice This error is thrown whenever a zero address is passed
error ZeroAddress();
/// @notice This error is thrown when trying to set a verifier address
/// for a digest that has already been initialized
/// @param configDigest The digest for the verifier that has
/// already been set
/// @param verifier The address of the verifier the digest was set for
error ConfigDigestAlreadySet(bytes32 configDigest, address verifier);
/// @notice This error is thrown when trying to set a verifier address that has already been initialized
error VerifierAlreadyInitialized(address verifier);
/// @notice This error is thrown when the verifier at an address does
/// not conform to the verifier interface
error VerifierInvalid();
/// @notice This error is thrown when the fee manager at an address does
/// not conform to the fee manager interface
error FeeManagerInvalid();
/// @notice This error is thrown whenever a verifier is not found
/// @param configDigest The digest for which a verifier is not found
error VerifierNotFound(bytes32 configDigest);
/// @notice This error is thrown whenever billing fails.
error BadVerification();
/// @notice Mapping of authorized verifiers
mapping(address => bool) private s_initializedVerifiers;
/// @notice Mapping between config digests and verifiers
mapping(bytes32 => address) private s_verifiersByConfig;
/// @notice The contract to control addresses that are allowed to verify reports
AccessControllerInterface public s_accessController;
/// @notice The contract to control fees for report verification
IVerifierFeeManager public s_feeManager;
constructor(AccessControllerInterface accessController) ConfirmedOwner(msg.sender) {
s_accessController = accessController;
}
modifier checkAccess() {
AccessControllerInterface ac = s_accessController;
if (address(ac) != address(0) && !ac.hasAccess(msg.sender, msg.data)) revert AccessForbidden();
_;
}
modifier onlyInitializedVerifier() {
if (!s_initializedVerifiers[msg.sender]) revert AccessForbidden();
_;
}
modifier onlyValidVerifier(address verifierAddress) {
if (verifierAddress == address(0)) revert ZeroAddress();
if (!IERC165(verifierAddress).supportsInterface(IVerifier.verify.selector)) revert VerifierInvalid();
_;
}
modifier onlyUnsetConfigDigest(bytes32 configDigest) {
address configDigestVerifier = s_verifiersByConfig[configDigest];
if (configDigestVerifier != address(0)) revert ConfigDigestAlreadySet(configDigest, configDigestVerifier);
_;
}
/// @inheritdoc TypeAndVersionInterface
function typeAndVersion() external pure override returns (string memory) {
return "VerifierProxy 2.0.0";
}
/// @inheritdoc IVerifierProxy
function verify(
bytes calldata payload,
bytes calldata parameterPayload
) external payable checkAccess returns (bytes memory) {
IVerifierFeeManager feeManager = s_feeManager;
// Bill the verifier
if (address(feeManager) != address(0)) {
feeManager.processFee{value: msg.value}(payload, parameterPayload, msg.sender);
}
return _verify(payload);
}
/// @inheritdoc IVerifierProxy
function verifyBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload
) external payable checkAccess returns (bytes[] memory verifiedReports) {
IVerifierFeeManager feeManager = s_feeManager;
// Bill the verifier
if (address(feeManager) != address(0)) {
feeManager.processFeeBulk{value: msg.value}(payloads, parameterPayload, msg.sender);
}
//verify the reports
verifiedReports = new bytes[](payloads.length);
for (uint256 i; i < payloads.length; ++i) {
verifiedReports[i] = _verify(payloads[i]);
}
return verifiedReports;
}
function _verify(bytes calldata payload) internal returns (bytes memory verifiedReport) {
// First 32 bytes of the signed report is the config digest
bytes32 configDigest = bytes32(payload);
address verifierAddress = s_verifiersByConfig[configDigest];
if (verifierAddress == address(0)) revert VerifierNotFound(configDigest);
return IVerifier(verifierAddress).verify(payload, msg.sender);
}
/// @inheritdoc IVerifierProxy
function initializeVerifier(address verifierAddress) external override onlyOwner onlyValidVerifier(verifierAddress) {
if (s_initializedVerifiers[verifierAddress]) revert VerifierAlreadyInitialized(verifierAddress);
s_initializedVerifiers[verifierAddress] = true;
emit VerifierInitialized(verifierAddress);
}
/// @inheritdoc IVerifierProxy
function setVerifier(
bytes32 currentConfigDigest,
bytes32 newConfigDigest,
Common.AddressAndWeight[] calldata addressesAndWeights
) external override onlyUnsetConfigDigest(newConfigDigest) onlyInitializedVerifier {
s_verifiersByConfig[newConfigDigest] = msg.sender;
// Empty recipients array will be ignored and must be set off chain
if (addressesAndWeights.length > 0) {
if (address(s_feeManager) == address(0)) {
revert ZeroAddress();
}
s_feeManager.setFeeRecipients(newConfigDigest, addressesAndWeights);
}
emit VerifierSet(currentConfigDigest, newConfigDigest, msg.sender);
}
/// @inheritdoc IVerifierProxy
function unsetVerifier(bytes32 configDigest) external override onlyOwner {
address verifierAddress = s_verifiersByConfig[configDigest];
if (verifierAddress == address(0)) revert VerifierNotFound(configDigest);
delete s_verifiersByConfig[configDigest];
emit VerifierUnset(configDigest, verifierAddress);
}
/// @inheritdoc IVerifierProxy
function getVerifier(bytes32 configDigest) external view override returns (address) {
return s_verifiersByConfig[configDigest];
}
/// @inheritdoc IVerifierProxy
function setAccessController(AccessControllerInterface accessController) external onlyOwner {
address oldAccessController = address(s_accessController);
s_accessController = accessController;
emit AccessControllerSet(oldAccessController, address(accessController));
}
/// @inheritdoc IVerifierProxy
function setFeeManager(IVerifierFeeManager feeManager) external onlyOwner {
if (address(feeManager) == address(0)) revert ZeroAddress();
if (
!IERC165(feeManager).supportsInterface(IVerifierFeeManager.processFee.selector) ||
!IERC165(feeManager).supportsInterface(IVerifierFeeManager.processFeeBulk.selector)
) revert FeeManagerInvalid();
address oldFeeManager = address(s_feeManager);
s_feeManager = IVerifierFeeManager(feeManager);
emit FeeManagerSet(oldFeeManager, address(feeManager));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Common} from "../../libraries/Common.sol";
import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol";
import {IVerifierFeeManager} from "./IVerifierFeeManager.sol";
interface IVerifierProxy {
/**
* @notice Verifies that the data encoded has been signed
* correctly by routing to the correct verifier, and bills the user if applicable.
* @param payload The encoded data to be verified, including the signed
* report.
* @param parameterPayload fee metadata for billing
* @return verifierResponse The encoded report from the verifier.
*/
function verify(
bytes calldata payload,
bytes calldata parameterPayload
) external payable returns (bytes memory verifierResponse);
/**
* @notice Bulk verifies that the data encoded has been signed
* correctly by routing to the correct verifier, and bills the user if applicable.
* @param payloads The encoded payloads to be verified, including the signed
* report.
* @param parameterPayload fee metadata for billing
* @return verifiedReports The encoded reports from the verifier.
*/
function verifyBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload
) external payable returns (bytes[] memory verifiedReports);
/**
* @notice Sets the verifier address initially, allowing `setVerifier` to be set by this Verifier in the future
* @param verifierAddress The address of the verifier contract to initialize
*/
function initializeVerifier(address verifierAddress) external;
/**
* @notice Sets a new verifier for a config digest
* @param currentConfigDigest The current config digest
* @param newConfigDigest The config digest to set
* @param addressesAndWeights The addresses and weights of reward recipients
* reports for a given config digest.
*/
function setVerifier(
bytes32 currentConfigDigest,
bytes32 newConfigDigest,
Common.AddressAndWeight[] memory addressesAndWeights
) external;
/**
* @notice Removes a verifier for a given config digest
* @param configDigest The config digest of the verifier to remove
*/
function unsetVerifier(bytes32 configDigest) external;
/**
* @notice Retrieves the verifier address that verifies reports
* for a config digest.
* @param configDigest The config digest to query for
* @return verifierAddress The address of the verifier contract that verifies
* reports for a given config digest.
*/
function getVerifier(bytes32 configDigest) external view returns (address verifierAddress);
/**
* @notice Called by the admin to set an access controller contract
* @param accessController The new access controller to set
*/
function setAccessController(AccessControllerInterface accessController) external;
/**
* @notice Updates the fee manager
* @param feeManager The new fee manager
*/
function setFeeManager(IVerifierFeeManager feeManager) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {Common} from "../../libraries/Common.sol";
interface IVerifier is IERC165 {
/**
* @notice Verifies that the data encoded has been signed
* correctly by routing to the correct verifier.
* @param signedReport The encoded data to be verified.
* @param sender The address that requested to verify the contract.
* This is only used for logging purposes.
* @dev Verification is typically only done through the proxy contract so
* we can't just use msg.sender to log the requester as the msg.sender
* contract will always be the proxy.
* @return verifierResponse The encoded verified response.
*/
function verify(bytes calldata signedReport, address sender) external returns (bytes memory verifierResponse);
/**
* @notice sets a configuration and its associated keys and f
* @param configDigest The digest of the configuration we're setting
* @param signers addresses with which oracles sign the reports
* @param f number of faulty oracles the system can tolerate
* @param recipientAddressesAndWeights the addresses and weights of all the recipients to receive rewards
*/
function setConfig(
bytes32 configDigest,
address[] calldata signers,
uint8 f,
Common.AddressAndWeight[] memory recipientAddressesAndWeights
) external;
/**
* @notice updates a configuration that has been set
* @param configDigest The digest of the configuration we're updating
* @param prevSigners the existing signers that need to be removed
* @param newSigners the signers to be added
* @param f the newnumber of faulty oracles the system can tolerate
*/
function updateConfig(
bytes32 configDigest,
address[] calldata prevSigners,
address[] calldata newSigners,
uint8 f
) external;
/**
* @notice Activates the configuration for a config digest
* @param configDigest The config digest to activate
* @dev This function can be called by the contract admin to activate a configuration.
*/
function activateConfig(bytes32 configDigest) external;
/**
* @notice Deactivates the configuration for a config digest
* @param configDigest The config digest to deactivate
* @dev This function can be called by the contract admin to deactivate an incorrect configuration.
*/
function deactivateConfig(bytes32 configDigest) external;
/**
* @notice information about current offchain reporting protocol configuration
* @param configDigest Config Digest to fetch data for
* @return blockNumber block at which this config was set
*/
function latestConfigDetails(
bytes32 configDigest
) external view returns (uint32 blockNumber);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract TypeAndVersionInterface {
function typeAndVersion() external pure virtual returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AccessControllerInterface {
function hasAccess(address user, bytes calldata data) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {Common} from "../../libraries/Common.sol";
interface IVerifierFeeManager is IERC165 {
/**
* @notice Handles fees for a report from the subscriber and manages rewards
* @param payload report to process the fee for
* @param parameterPayload fee payload
* @param subscriber address of the fee will be applied
*/
function processFee(bytes calldata payload, bytes calldata parameterPayload, address subscriber) external payable;
/**
* @notice Processes the fees for each report in the payload, billing the subscriber and paying the reward manager
* @param payloads reports to process
* @param parameterPayload fee payload
* @param subscriber address of the user to process fee for
*/
function processFeeBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload,
address subscriber
) external payable;
/**
* @notice Sets the fee recipients according to the fee manager
* @param configDigest digest of the configuration
* @param rewardRecipientAndWeights the address and weights of all the recipients to receive rewards
*/
function setFeeRecipients(
bytes32 configDigest,
Common.AddressAndWeight[] calldata rewardRecipientAndWeights
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/*
* @title Common
* @author Michael Fletcher
* @notice Common functions and structs
*/
library Common {
// @notice The asset struct to hold the address of an asset and amount
struct Asset {
address assetAddress;
uint256 amount;
}
// @notice Struct to hold the address and its associated weight
struct AddressAndWeight {
address addr;
uint64 weight;
}
/**
* @notice Checks if an array of AddressAndWeight has duplicate addresses
* @param recipients The array of AddressAndWeight to check
* @return bool True if there are duplicates, false otherwise
*/
function _hasDuplicateAddresses(address[] memory recipients) internal pure returns (bool) {
for (uint256 i = 0; i < recipients.length; ) {
for (uint256 j = i + 1; j < recipients.length; ) {
if (recipients[i] == recipients[j]) {
return true;
}
unchecked {
++j;
}
}
unchecked {
++i;
}
}
return false;
}
/**
* @notice Checks if an array of AddressAndWeight has duplicate addresses
* @param recipients The array of AddressAndWeight to check
* @return bool True if there are duplicates, false otherwise
*/
function _hasDuplicateAddresses(Common.AddressAndWeight[] memory recipients) internal pure returns (bool) {
for (uint256 i = 0; i < recipients.length; ) {
for (uint256 j = i + 1; j < recipients.length; ) {
if (recipients[i].addr == recipients[j].addr) {
return true;
}
unchecked {
++j;
}
}
unchecked {
++i;
}
}
return false;
}
/**
* @notice sorts a list of addresses numerically
* @param arr The array of addresses to sort
* @param left the start index
* @param right the end index
*/
function _quickSort(address[] memory arr, int256 left, int256 right) internal pure {
int256 i = left;
int256 j = right;
if (i == j) return;
address pivot = arr[uint256(left + (right - left) / 2)];
while (i <= j) {
while (uint160(arr[uint256(i)]) < uint160(pivot)) i++;
while (uint160(pivot) < uint160(arr[uint256(j)])) j--;
if (i <= j) {
(arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]);
i++;
j--;
}
}
if (left < j) _quickSort(arr, left, j);
if (i < right) _quickSort(arr, i, right);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}