diff --git a/contracts/.solhint.json b/contracts/.solhint.json index 66ef4e72a..727fc79f0 100644 --- a/contracts/.solhint.json +++ b/contracts/.solhint.json @@ -3,11 +3,13 @@ "rules": { "code-complexity": ["error", 18], "compiler-version": ["error", ">=0.8.0"], - "max-line-length": ["warn", 100], - "no-console": "warn", + "max-line-length": ["off", 100], + "no-console": "off", "var-name-mixedcase": "off", "func-name-mixedcase": "off", "avoid-low-level-calls": "off", - "no-inline-assembly": "off" + "no-inline-assembly": "off", + "no-global-import": "off", + "no-empty-blocks": "off" } } \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index a2d58d9c9..d3d6201eb 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -4,4 +4,11 @@ out = "out" libs = ["lib"] fs_permissions = [{ access = "read-write", path = "./"}] +# https://book.getfoundry.sh/reference/config/formatter#line_length +[fmt] +line_length = 100 +tab_width = 4 +func_attrs_with_params_multiline = true +ignore = ["lib/**"] + # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/contracts/script/deploy/FunctionGateway.s.sol b/contracts/script/deploy/FunctionGateway.s.sol index ea81fcf17..542f74853 100644 --- a/contracts/script/deploy/FunctionGateway.s.sol +++ b/contracts/script/deploy/FunctionGateway.s.sol @@ -2,18 +2,17 @@ pragma solidity ^0.8.16; import "forge-std/console.sol"; -import {BaseScript} from "script/misc/Base.s.sol"; -import {FunctionGateway} from "src/FunctionGateway.sol"; -import {Proxy} from "src/upgrades/Proxy.sol"; +import {BaseScript} from "../misc/Base.s.sol"; +import {FunctionGateway} from "../../src/FunctionGateway.sol"; +import {Proxy} from "../../src/upgrades/Proxy.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; contract DeployFunctionGateway is BaseScript { function run() external broadcaster { - console.log("Deploying FunctionGateway contract on chain %s", Strings.toString(block.chainid)); + console.log( + "Deploying FunctionGateway contract on chain %s", Strings.toString(block.chainid) + ); - // Check inputs - uint256 SCALAR = envUint256("SCALAR"); - address SUCCINCT_FEE_VAULT = envAddress("SUCCINCT_FEE_VAULT", block.chainid); address TIMELOCK = envAddress("TIMELOCK", block.chainid); address GUARDIAN = envAddress("GUARDIAN", block.chainid); bytes32 CREATE2_SALT = envBytes32("CREATE2_SALT"); @@ -23,8 +22,9 @@ contract DeployFunctionGateway is BaseScript { FunctionGateway gatewayImpl = new FunctionGateway{salt: CREATE2_SALT}(); FunctionGateway gateway; if (!UPGRADE) { - gateway = FunctionGateway(address(new Proxy{salt: CREATE2_SALT}(address(gatewayImpl), ""))); - gateway.initialize(SCALAR, SUCCINCT_FEE_VAULT, TIMELOCK, GUARDIAN); + gateway = + FunctionGateway(address(new Proxy{salt: CREATE2_SALT}(address(gatewayImpl), ""))); + gateway.initialize(TIMELOCK, GUARDIAN); } else { gateway = FunctionGateway(envAddress("FUNCTION_GATEWAY", block.chainid)); gateway.upgradeTo(address(gatewayImpl)); diff --git a/contracts/script/deploy/Guardian.s.sol b/contracts/script/deploy/Guardian.s.sol index 749d7f305..916bc34bf 100644 --- a/contracts/script/deploy/Guardian.s.sol +++ b/contracts/script/deploy/Guardian.s.sol @@ -10,7 +10,9 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; // "Guardian" refers to a Gnosis Safe proxy. contract DeployGuardian is BaseScript { function run() external broadcaster { - console.log("Deploying Guardian (Safe) contract on chain %s", Strings.toString(block.chainid)); + console.log( + "Deploying Guardian (Safe) contract on chain %s", Strings.toString(block.chainid) + ); // Check inputs @@ -48,6 +50,12 @@ contract DeployGuardian is BaseScript { address(0) ); - return Safe(payable(_safeFactory.createProxyWithNonce(address(_safeSingleton), initializer, uint256(_salt)))); + return Safe( + payable( + _safeFactory.createProxyWithNonce( + address(_safeSingleton), initializer, uint256(_salt) + ) + ) + ); } } diff --git a/contracts/script/deploy/StorageOracle.s.sol b/contracts/script/deploy/StorageOracle.s.sol deleted file mode 100644 index 7eac391ee..000000000 --- a/contracts/script/deploy/StorageOracle.s.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import "forge-std/console.sol"; -import {BaseScript} from "script/misc/Base.s.sol"; -import {StorageOracle} from "src/examples/storage/StorageOracle.sol"; -import {Proxy} from "src/upgrades/Proxy.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployStorageOracle is BaseScript { - function run() external broadcaster { - console.log("Deploying StorageOracle contract on chain %s", Strings.toString(block.chainid)); - - // Check inputs - address FUNCTION_GATEWAY = envAddress("FUNCTION_GATEWAY", block.chainid); - bytes32 FUNCTION_ID = envBytes32("FUNCTION_ID"); - address TIMELOCK = envAddress("TIMELOCK", block.chainid); - address GUARDIAN = envAddress("GUARDIAN", block.chainid); - bytes32 CREATE2_SALT = envBytes32("CREATE2_SALT"); - bool UPGRADE = envBool("UPGRADE_VIA_EOA", false); - - // Deploy contract - StorageOracle gatewayImpl = new StorageOracle{salt: CREATE2_SALT}(); - StorageOracle gateway; - if (!UPGRADE) { - gateway = StorageOracle(address(new Proxy{salt: CREATE2_SALT}(address(gatewayImpl), ""))); - gateway.initialize(FUNCTION_GATEWAY, FUNCTION_ID, TIMELOCK, GUARDIAN); - } else { - gateway = StorageOracle(envAddress("STORAGE_ORACLE", block.chainid)); - gateway.upgradeTo(address(gatewayImpl)); - } - - // Write address - writeEnvAddress(DEPLOYMENT_FILE, "STORAGE_ORACLE", address(gateway)); - writeEnvAddress(DEPLOYMENT_FILE, "STORAGE_ORACLE_IMPL", address(gatewayImpl)); - } -} diff --git a/contracts/script/deploy/StorageVerifier.sol b/contracts/script/deploy/StorageVerifier.sol deleted file mode 100644 index d08bd066a..000000000 --- a/contracts/script/deploy/StorageVerifier.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import "forge-std/console.sol"; -import {BaseScript} from "script/misc/Base.s.sol"; -import {StorageVerifier} from "src/examples/storage/StorageVerifier.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {IFunctionRegistry} from "src/interfaces/IFunctionRegistry.sol"; - -contract DeployStorageVerifier is BaseScript { - function run() external broadcaster { - console.log("Deploying StorageVerifier contract on chain %s", Strings.toString(block.chainid)); - - // Check inputs - address FUNCTION_GATEWAY = envAddress("FUNCTION_GATEWAY", block.chainid); - bytes32 CREATE2_SALT = envBytes32("CREATE2_SALT"); - - // Deploy contract - StorageVerifier verifier = new StorageVerifier{salt: CREATE2_SALT}(); - bytes32 functionId = IFunctionRegistry(FUNCTION_GATEWAY).registerFunction(address(verifier), "storage"); - console.log("FunctionId:"); - console.logBytes32(functionId); - - // Write address - writeEnvAddress(DEPLOYMENT_FILE, "STORAGE_VERIFIER", address(verifier)); - } -} diff --git a/contracts/script/deploy/SuccinctFeeVault.s.sol b/contracts/script/deploy/SuccinctFeeVault.s.sol index 37a175e30..f09657fd9 100644 --- a/contracts/script/deploy/SuccinctFeeVault.s.sol +++ b/contracts/script/deploy/SuccinctFeeVault.s.sol @@ -8,7 +8,9 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; contract DeploySuccinctFeeVault is BaseScript { function run() external broadcaster { - console.log("Deploying SuccinctFeeVault contract on chain %s", Strings.toString(block.chainid)); + console.log( + "Deploying SuccinctFeeVault contract on chain %s", Strings.toString(block.chainid) + ); // Check inputs bytes32 CREATE2_SALT = envBytes32("CREATE2_SALT"); diff --git a/contracts/script/misc/Base.s.sol b/contracts/script/misc/Base.s.sol index 89f6a4ecd..56f810144 100644 --- a/contracts/script/misc/Base.s.sol +++ b/contracts/script/misc/Base.s.sol @@ -76,7 +76,10 @@ abstract contract BaseScript is Script { return value; } - function envUint32s(string memory name, string memory delimiter) internal returns (uint32[] memory) { + function envUint32s(string memory name, string memory delimiter) + internal + returns (uint32[] memory) + { uint256[] memory values = new uint256[](0); values = vm.envOr(name, delimiter, values); if (values.length == 0) { @@ -167,9 +170,12 @@ abstract contract BaseScript is Script { console.log(string.concat(string.concat(addrVar, "="), Strings.toHexString(value))); } - function writeEnvAddresses(string memory file, string memory name, address[] memory values, string memory delimiter) - internal - { + function writeEnvAddresses( + string memory file, + string memory name, + address[] memory values, + string memory delimiter + ) internal { string memory addrVar = string.concat(name, "_", Strings.toString(block.chainid)); string memory line = string.concat(addrVar, "="); string memory addrs; @@ -243,7 +249,11 @@ abstract contract BaseScript is Script { return _b; } - function buildSignaturesFromArray(bytes[] memory _signatures) internal pure returns (bytes memory) { + function buildSignaturesFromArray(bytes[] memory _signatures) + internal + pure + returns (bytes memory) + { bytes memory signatures; for (uint256 i = 0; i < _signatures.length; i++) { signatures = bytes.concat(signatures, bytes(_signatures[i])); diff --git a/contracts/script/misc/Upgrade.s.sol b/contracts/script/misc/Upgrade.s.sol index 8d223127e..9ac4af967 100644 --- a/contracts/script/misc/Upgrade.s.sol +++ b/contracts/script/misc/Upgrade.s.sol @@ -15,7 +15,10 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; // ); // contract UpgradeSignSchedule is BaseScript { - function run(address PROXY, address IMPL) external returns (address signer, bytes memory signature) { + function run(address PROXY, address IMPL) + external + returns (address signer, bytes memory signature) + { // Check inputs address TIMELOCK = envAddress("TIMELOCK", block.chainid); address GUARDIAN = envAddress("GUARDIAN", block.chainid); @@ -36,9 +39,12 @@ contract UpgradeSignSchedule is BaseScript { bytes[] memory payloads = new bytes[](1); payloads[0] = abi.encodeWithSelector(IProxy.upgradeTo.selector, IMPL); - bytes32 id = ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); + bytes32 id = + ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); if (ITimelock(TIMELOCK).isOperation(id)) { - revert("operation already exists in Timelock, change CREATE2_SALT to schedule a new one"); + revert( + "operation already exists in Timelock, change CREATE2_SALT to schedule a new one" + ); } scheduleBatchData = abi.encodeWithSelector( @@ -82,7 +88,11 @@ contract UpgradeSignSchedule is BaseScript { // After enough signatures have been collected, a call to Safe.execTransaction(..., signatures) is // made which schedules the call on the Timelock. contract UpgradeSendSchedule is BaseScript { - function run(address PROXY, address IMPL, bytes memory _signatures) external broadcaster returns (bool success) { + function run(address PROXY, address IMPL, bytes memory _signatures) + external + broadcaster + returns (bool success) + { // Check inputs address TIMELOCK = envAddress("TIMELOCK", block.chainid); address GUARDIAN = envAddress("GUARDIAN", block.chainid); @@ -103,9 +113,12 @@ contract UpgradeSendSchedule is BaseScript { bytes[] memory payloads = new bytes[](1); payloads[0] = abi.encodeWithSelector(IProxy.upgradeTo.selector, IMPL); - bytes32 id = ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); + bytes32 id = + ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); if (ITimelock(TIMELOCK).isOperation(id)) { - revert("operation already exists in Timelock, change CREATE2_SALT to schedule a new one"); + revert( + "operation already exists in Timelock, change CREATE2_SALT to schedule a new one" + ); } scheduleBatchData = abi.encodeWithSelector( @@ -122,7 +135,9 @@ contract UpgradeSendSchedule is BaseScript { { if (ISafe(GUARDIAN).getThreshold() * 65 > _signatures.length) { console.log( - "not enough signatures, need %d have %d", ISafe(GUARDIAN).getThreshold(), _signatures.length / 65 + "not enough signatures, need %d have %d", + ISafe(GUARDIAN).getThreshold(), + _signatures.length / 65 ); return false; } @@ -147,7 +162,10 @@ contract UpgradeSendSchedule is BaseScript { // After MINIMUM_DELAY has passed, the call to Timelock.execute() can be made. contract UpgradeSignExecute is BaseScript { - function run(address PROXY, address IMPL) external returns (address signer, bytes memory signature) { + function run(address PROXY, address IMPL) + external + returns (address signer, bytes memory signature) + { // Check inputs address TIMELOCK = envAddress("TIMELOCK", block.chainid); address GUARDIAN = envAddress("GUARDIAN", block.chainid); @@ -168,7 +186,8 @@ contract UpgradeSignExecute is BaseScript { bytes[] memory payloads = new bytes[](1); payloads[0] = abi.encodeWithSelector(IProxy.upgradeTo.selector, IMPL); - bytes32 id = ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); + bytes32 id = + ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); if (ITimelock(TIMELOCK).isOperationDone(id)) { console.log("operation already executed in Timelock"); return (address(0), ""); @@ -212,7 +231,11 @@ contract UpgradeSignExecute is BaseScript { } contract UpgradeSendExecute is BaseScript { - function run(address PROXY, address IMPL, bytes memory _signatures) external broadcaster returns (bool success) { + function run(address PROXY, address IMPL, bytes memory _signatures) + external + broadcaster + returns (bool success) + { // Check inputs address TIMELOCK = envAddress("TIMELOCK", block.chainid); address GUARDIAN = envAddress("GUARDIAN", block.chainid); @@ -233,7 +256,8 @@ contract UpgradeSendExecute is BaseScript { bytes[] memory payloads = new bytes[](1); payloads[0] = abi.encodeWithSelector(IProxy.upgradeTo.selector, IMPL); - bytes32 id = ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); + bytes32 id = + ITimelock(TIMELOCK).hashOperationBatch(targets, values, payloads, 0, CREATE2_SALT); if (ITimelock(TIMELOCK).isOperationDone(id)) { console.log("operation already executed in Timelock"); return true; @@ -253,7 +277,9 @@ contract UpgradeSendExecute is BaseScript { { if (ISafe(GUARDIAN).getThreshold() * 65 > _signatures.length) { console.log( - "not enough signatures, need %d have %d", ISafe(GUARDIAN).getThreshold(), _signatures.length / 65 + "not enough signatures, need %d have %d", + ISafe(GUARDIAN).getThreshold(), + _signatures.length / 65 ); return false; } @@ -338,7 +364,11 @@ interface ISafe { event RemovedOwner(address owner); event SafeReceived(address indexed sender, uint256 value); event SafeSetup( - address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler + address indexed initiator, + address[] owners, + uint256 threshold, + address initializer, + address fallbackHandler ); event SignMsg(bytes32 indexed msgHash); @@ -347,10 +377,15 @@ interface ISafe { function approveHash(bytes32 hashToApprove) external; function approvedHashes(address, bytes32) external view returns (uint256); function changeThreshold(uint256 _threshold) external; - function checkNSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures, uint256 requiredSignatures) + function checkNSignatures( + bytes32 dataHash, + bytes memory data, + bytes memory signatures, + uint256 requiredSignatures + ) external view; + function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures) external view; - function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures) external view; function disableModule(address prevModule, address module) external; function domainSeparator() external view returns (bytes32); function enableModule(address module) external; @@ -378,12 +413,18 @@ interface ISafe { address refundReceiver, bytes memory signatures ) external payable returns (bool success); - function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) - external - returns (bool success); - function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) - external - returns (bool success, bytes memory returnData); + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success); + function execTransactionFromModuleReturnData( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success, bytes memory returnData); function getChainId() external view returns (uint256); function getModulesPaginated(address start, uint256 pageSize) external diff --git a/contracts/src/FunctionGateway.sol b/contracts/src/FunctionGateway.sol index cb8163980..b07bd3a40 100644 --- a/contracts/src/FunctionGateway.sol +++ b/contracts/src/FunctionGateway.sol @@ -2,246 +2,274 @@ pragma solidity ^0.8.16; -import {IFunctionGateway, FunctionRequest} from "./interfaces/IFunctionGateway.sol"; +import {IFunctionGateway} from "./interfaces/IFunctionGateway.sol"; import {IFunctionVerifier} from "./interfaces/IFunctionVerifier.sol"; import {FunctionRegistry} from "./FunctionRegistry.sol"; import {TimelockedUpgradeable} from "./upgrades/TimelockedUpgradeable.sol"; -import {IFeeVault} from "src/payments/interfaces/IFeeVault.sol"; +import {IFeeVault} from "./payments/interfaces/IFeeVault.sol"; contract FunctionGateway is IFunctionGateway, FunctionRegistry, TimelockedUpgradeable { - /// @dev The proof id for an aggregate proof. - bytes32 public constant AGGREGATION_FUNCTION_ID = keccak256("AGGREGATION_FUNCTION_ID"); + /// @dev The address of the fee vault. + address public feeVault; - /// @dev The default gas limit for requests. - uint256 public constant DEFAULT_GAS_LIMIT = 1000000; + /// @dev A nonce for keeping track of requests. + uint32 public nonce; - /// @dev Keeps track of the nonce for generating request ids. - uint256 public nonce; + /// @dev A mapping from request nonces to request hashes. + mapping(uint32 => bytes32) public requests; - /// @dev Maps request ids to their corresponding requests. - mapping(bytes32 => FunctionRequest) public requests; + /// @dev The currently verified function identifier. + bytes32 public verifiedFunctionId; - /// @notice The dynamic scalar for requests. - uint256 public scalar; + /// @dev The currently verified function input hash. + bytes32 public verifiedInputHash; - /// @notice A reference to the contract where fees are sent. - /// @dev During the request functions, this is used to add msg.value to the sender's balance. - address public feeVault; + /// @dev The currently verified function output. + bytes public verifiedOutput; - function initialize(uint256 _scalar, address _feeVault, address _timelock, address _guardian) - external - initializer - { - scalar = _scalar; - feeVault = _feeVault; - __TimelockedUpgradeable_init(_timelock, _guardian); - } + /// @dev A flag that indicates whether the contract is currently making a callback. + bool public isCallback; - function request(bytes32 _functionId, bytes memory _input, bytes4 _callbackSelector, bytes memory _context) - external - payable - returns (bytes32) - { - return request(_functionId, _input, _callbackSelector, _context, DEFAULT_GAS_LIMIT, tx.origin); + /// @dev Initializes the contract. + /// @param _timelock The address of the timelock contract. + /// @param _guardian The address of the guardian. + function initialize(address _timelock, address _guardian) external initializer { + isCallback = false; + __TimelockedUpgradeable_init(_timelock, _guardian); } - /// @dev Requests for a proof to be generated by the marketplace. - /// @param _functionId The id of the proof to be generated. - /// @param _input The input to the proof. - /// @param _context The context of the runtime. + /// @dev Creates a onchain request for a proof. The output and proof is fulfilled asynchronously + /// by the provided callback. + /// @param _functionId The function identifier. + /// @param _input The function input. + /// @param _context The function context. /// @param _callbackSelector The selector of the callback function. - function request( + /// @param _callbackGasLimit The gas limit for the callback function. + function requestCallback( bytes32 _functionId, bytes memory _input, - bytes4 _callbackSelector, bytes memory _context, - uint256 _gasLimit, - address _refundAccount - ) public payable returns (bytes32) { + bytes4 _callbackSelector, + uint32 _callbackGasLimit + ) external payable returns (bytes32) { + // Compute the callback hash uniquely associated with this request. bytes32 inputHash = sha256(_input); bytes32 contextHash = keccak256(_context); - FunctionRequest memory r = FunctionRequest({ - functionId: _functionId, - inputHash: inputHash, - outputHash: bytes32(0), - contextHash: contextHash, - callbackAddress: msg.sender, - callbackSelector: _callbackSelector, - proofFulfilled: false, - callbackFulfilled: false - }); - - uint256 feeAmount = _handlePayment(_gasLimit, _refundAccount, msg.sender, msg.value); - - bytes32 requestId = keccak256(abi.encode(nonce, r)); - requests[requestId] = r; - - emit ProofRequested(nonce, _functionId, requestId, _input, _context, _gasLimit, feeAmount); + address callbackAddress = msg.sender; + bytes32 requestHash = _requestHash( + nonce, + _functionId, + inputHash, + contextHash, + callbackAddress, + _callbackSelector, + _callbackGasLimit + ); + + // Increment the nonce. nonce++; - return requestId; - } - /// @dev The entrypoint for fulfilling proofs which are not in batches. - /// @param _requestId The id of the request to be fulfilled. - /// @param _outputHash The output hash of the proof. - /// @param _proof The proof. - function fulfill(bytes32 _requestId, bytes32 _outputHash, bytes memory _proof) external { - // Do some sanity checks. - FunctionRequest storage r = requests[_requestId]; - if (r.callbackAddress == address(0)) { - revert RequestNotFound(_requestId); - } else if (r.proofFulfilled) { - revert ProofAlreadyFulfilled(_requestId); - } + // Store the callback hash. + requests[nonce] = requestHash; + emit RequestCallback( + nonce, + _functionId, + _input, + _context, + callbackAddress, + _callbackSelector, + _callbackGasLimit + ); - // Update the request. - r.proofFulfilled = true; - r.outputHash = _outputHash; + // Send the fee to the vault. + IFeeVault(feeVault).depositNative{value: msg.value}(callbackAddress); - // Verify the proof. - address verifier = verifiers[r.functionId]; - if (!IFunctionVerifier(verifier).verify(r.inputHash, _outputHash, _proof)) { - revert InvalidProof(address(verifier), r.inputHash, _outputHash, _proof); - } + return requestHash; + } - emit ProofFulfilled(_requestId, _outputHash, _proof); + /// @dev Creates a proof request for a call. This function is equivalent to an off-chain request + /// through an API. + /// @param _functionId The function identifier. + /// @param _input The function input. + /// @param _address The address of the callback contract. + /// @param _data The data for the callback function. + function requestCall( + bytes32 _functionId, + bytes memory _input, + address _address, + bytes memory _data + ) external payable { + // Emit event. + emit RequestCall(_functionId, _input, _address, _data); + + // Send the fee to the vault. + IFeeVault(feeVault).depositNative{value: msg.value}(_address); } - /// @dev The entrypoint for fulfilling proofs which are in batches. - /// @param _requestIds The ids of the requests to be fulfilled. - /// @param _aggregateProof The aggregate proof. - /// @param _inputsRoot The root of the inputs. - /// @param _outputHashes The output hashes of the proofs. - /// @param _outputsRoot The root of the outputs. - /// @param _verificationKeyRoot The root of the verification keys. - function fulfillBatch( - bytes32[] memory _requestIds, - bytes memory _aggregateProof, - bytes32 _inputsRoot, - bytes32[] memory _outputHashes, - bytes32 _outputsRoot, - bytes32 _verificationKeyRoot - ) external { - // Collect the input hashes and verification key hashes. - bytes32[] memory inputHashes = new bytes32[](_requestIds.length); - bytes32[] memory verificationKeyHashes = new bytes32[](_requestIds.length); - for (uint256 i = 0; i < _requestIds.length; i++) { - bytes32 requestId = _requestIds[i]; - FunctionRequest storage r = requests[requestId]; - if (r.callbackAddress == address(0)) { - revert RequestNotFound(requestId); - } else if (r.proofFulfilled) { - revert ProofAlreadyFulfilled(requestId); - } - inputHashes[i] = r.inputHash; - address verifier = verifiers[r.functionId]; - verificationKeyHashes[i] = IFunctionVerifier(verifier).verificationKeyHash(); + /// @dev If the call matches the currently verified function, returns the output. Otherwise, + /// this function reverts. + /// @param _functionId The function identifier. + /// @param _input The function input. + function verifiedCall(bytes32 _functionId, bytes memory _input) + external + view + returns (bytes memory) + { + bytes32 inputHash = sha256(_input); + if (verifiedFunctionId == _functionId && verifiedInputHash == inputHash) { + return verifiedOutput; + } else { + revert InvalidCall(_functionId, _input); } + } - // Do some sanity checks. - if (_requestIds.length != _outputHashes.length) { - revert LengthMismatch(_requestIds.length, _outputHashes.length); - } else if (_inputsRoot != keccak256(abi.encode(inputHashes))) { - revert InputsRootMismatch(_inputsRoot, inputHashes); - } else if (_outputsRoot != keccak256(abi.encode(_outputHashes))) { - revert OutputsRootMismatch(_outputsRoot, _outputHashes); - } else if (_verificationKeyRoot != keccak256(abi.encode(verificationKeyHashes))) { - revert VerificationKeysRootMismatch(_verificationKeyRoot, verificationKeyHashes); - } + /// @dev Fulfills a request by providing the output and proof. + /// @param _nonce The nonce of the request. + /// @param _functionId The function identifier. + /// @param _inputHash The hash of the function input. + /// @param _callbackAddress The address of the callback contract. + /// @param _callbackSelector The selector of the callback function. + /// @param _callbackGasLimit The gas limit for the callback function. + /// @param _context The function context. + /// @param _output The function output. + /// @param _proof The function proof. + function fulfillCallback( + uint32 _nonce, + bytes32 _functionId, + bytes32 _inputHash, + address _callbackAddress, + bytes4 _callbackSelector, + uint32 _callbackGasLimit, + bytes memory _context, + bytes memory _output, + bytes memory _proof + ) external { + // Reconstruct the callback hash. + bytes32 contextHash = keccak256(_context); + bytes32 requestHash = _requestHash( + _nonce, + _functionId, + _inputHash, + contextHash, + _callbackAddress, + _callbackSelector, + _callbackGasLimit + ); - // Update the requests. - for (uint256 i = 0; i < _requestIds.length; i++) { - bytes32 requestId = _requestIds[i]; - requests[requestId].proofFulfilled = true; - requests[requestId].outputHash = _outputHashes[i]; + // Assert that the callback hash is unfilfilled. + if (requests[_nonce] != requestHash) { + revert InvalidRequest(_nonce, requests[_nonce], requestHash); } - // Verify the aggregate proof. - address aggregationVerifier = verifiers[AGGREGATION_FUNCTION_ID]; - if (!IFunctionVerifier(aggregationVerifier).verify(_inputsRoot, _outputsRoot, _aggregateProof)) { - revert InvalidProof(address(aggregationVerifier), _inputsRoot, _outputsRoot, _aggregateProof); - } + // Delete the callback hash for a gas refund. + delete requests[_nonce]; - emit ProofBatchFulfilled( - _requestIds, _aggregateProof, _inputsRoot, _outputHashes, _outputsRoot, _verificationKeyRoot - ); - } + // Compute the output hash. + bytes32 outputHash = sha256(_output); - /// @dev Fulfills the callback for a request. - /// @param _requestId The id of the request to be fulfilled. - /// @param _output The output of the proof. - /// @param _context The context of the runtime. - function callback(bytes32 _requestId, bytes memory _output, bytes memory _context) external { - // Do some sanity checks. - FunctionRequest storage r = requests[_requestId]; - if (r.callbackFulfilled) { - revert CallbackAlreadyFulfilled(_requestId); - } else if (r.callbackAddress == address(0)) { - revert RequestNotFound(_requestId); - } else if (r.contextHash != keccak256(_context)) { - revert ContextMismatch(_requestId, _context); - } else if (r.outputHash != sha256(_output)) { - revert OutputMismatch(_requestId, _output); - } else if (!r.proofFulfilled) { - revert ProofNotFulfilled(_requestId); - } + // Verify the proof. + _verify(_functionId, _inputHash, outputHash, _proof); - // Update the request. - r.callbackFulfilled = true; + // Execute the callback. + isCallback = true; + (bool status,) = + _callbackAddress.call(abi.encodeWithSelector(_callbackSelector, _output, _context)); + isCallback = false; - // Call the callback. - (bool status,) = r.callbackAddress.call(abi.encodeWithSelector(r.callbackSelector, _output, _context)); + // If the callback failed, revert. if (!status) { - revert CallbackFailed(r.callbackAddress, r.callbackSelector); + revert CallbackFailed(_callbackSelector, _output, _context); } - emit CallbackFulfilled(_requestId, _output, _context); + // Emit event. + emit RequestFulfilled(_nonce, _functionId, _inputHash, outputHash); } - /// @notice Update the scalar. - function updateScalar(uint256 _scalar) external onlyGuardian { - scalar = _scalar; + /// @dev The entrypoint for fulfilling a call. + /// @param _functionId The function identifier. + /// @param _input The function input. + /// @param _output The function output. + /// @param _proof The function proof. + /// @param _callbackAddress The address of the callback contract. + /// @param _callbackData The data for the callback function. + function fulfillCall( + bytes32 _functionId, + bytes memory _input, + bytes memory _output, + bytes memory _proof, + address _callbackAddress, + bytes memory _callbackData + ) external { + // Compute the input and output hashes. + bytes32 inputHash = sha256(_input); + bytes32 outputHash = sha256(_output); - emit ScalarUpdated(_scalar); - } + // Verify the proof. + _verify(_functionId, inputHash, outputHash, _proof); - /// @notice Calculates the feeAmount for the default gasLimit. - function calculateFeeAmount() external view returns (uint256 feeAmount) { - return calculateFeeAmount(DEFAULT_GAS_LIMIT); - } + // Set the current verified call. + verifiedFunctionId = _functionId; + verifiedInputHash = inputHash; + verifiedOutput = _output; - /// @notice Calculates the feeAmount for a given gasLimit. - function calculateFeeAmount(uint256 _gasLimit) public view returns (uint256 feeAmount) { - if (scalar == 0) { - feeAmount = tx.gasprice * _gasLimit; - } else { - feeAmount = tx.gasprice * _gasLimit * scalar; + // Execute the callback. + (bool status,) = _callbackAddress.call(_callbackData); + if (!status) { + revert CallFailed(_callbackAddress, _callbackData); } - } - /// @dev Calculates the feeAmount for the request, sends the feeAmount to the FeeVault, and - /// sends the excess amount as a refund to the refundAccount. - function _handlePayment(uint256 _gasLimit, address _refundAccount, address _senderAccount, uint256 _value) - private - returns (uint256 feeAmount) - { - feeAmount = calculateFeeAmount(_gasLimit); - if (_value < feeAmount) { - revert InsufficientFeeAmount(feeAmount, _value); - } + // Delete the current verified call. + delete verifiedFunctionId; + delete verifiedInputHash; + delete verifiedOutput; - // Send the feeAmount amount to the fee vault. - if (feeAmount > 0 && feeVault != address(0)) { - IFeeVault(feeVault).depositNative{value: feeAmount}(_senderAccount); - } + // Emit event. + emit Call(_functionId, inputHash, outputHash); + } - // Send the excess amount to the refund account. - uint256 refundAmount = _value - feeAmount; - if (refundAmount > 0) { - (bool success,) = _refundAccount.call{value: refundAmount}(""); - if (!success) { - revert RefundFailed(_refundAccount, refundAmount); - } + /// @dev Computes a unique identifier for a request. + /// @param _functionId The function identifier. + /// @param _inputHash The hash of the function input. + /// @param _contextHash The hash of the function context. + /// @param _callbackAddress The address of the callback contract. + /// @param _callbackSelector The selector of the callback function. + /// @param _callbackGasLimit The gas limit for the callback function. + function _requestHash( + uint32 _nonce, + bytes32 _functionId, + bytes32 _inputHash, + bytes32 _contextHash, + address _callbackAddress, + bytes4 _callbackSelector, + uint32 _callbackGasLimit + ) internal pure returns (bytes32) { + return keccak256( + abi.encodePacked( + _nonce, + _functionId, + _inputHash, + _contextHash, + _callbackAddress, + _callbackSelector, + _callbackGasLimit + ) + ); + } + + /// @dev Verifies a proof with respect to a function identifier, input hash, and output hash. + /// @param _functionId The function identifier. + /// @param _inputHash The hash of the function input. + /// @param _outputHash The hash of the function output. + /// @param _proof The function proof. + function _verify( + bytes32 _functionId, + bytes32 _inputHash, + bytes32 _outputHash, + bytes memory _proof + ) internal { + address verifier = verifiers[_functionId]; + if (!IFunctionVerifier(verifier).verify(_inputHash, _outputHash, _proof)) { + revert InvalidProof(address(verifier), _inputHash, _outputHash, _proof); } } diff --git a/contracts/src/FunctionRegistry.sol b/contracts/src/FunctionRegistry.sol index 17d0221b9..3ace65193 100644 --- a/contracts/src/FunctionRegistry.sol +++ b/contracts/src/FunctionRegistry.sol @@ -4,16 +4,19 @@ pragma solidity ^0.8.16; import {IFunctionRegistry} from "./interfaces/IFunctionRegistry.sol"; contract FunctionRegistry is IFunctionRegistry { - /// @dev Maps functionId's to their corresponding verifiers. + /// @dev Maps function identifiers to their corresponding verifiers. mapping(bytes32 => address) public verifiers; - /// @dev Maps functionId's to their corresponding owners. + /// @dev Maps function identifiers to their corresponding owners. mapping(bytes32 => address) public verifierOwners; /// @notice Registers a function, using a pre-deployed verifier. /// @param _verifier The address of the verifier. /// @param _name The name of the function to be registered. - function registerFunction(address _verifier, string memory _name) external returns (bytes32 functionId) { + function registerFunction(address _verifier, string memory _name) + external + returns (bytes32 functionId) + { functionId = getFunctionId(msg.sender, _name); if (address(verifiers[functionId]) != address(0)) { revert FunctionAlreadyRegistered(functionId); // should call update instead @@ -49,7 +52,10 @@ contract FunctionRegistry is IFunctionRegistry { /// @notice Updates the function, using a pre-deployed verifier. /// @param _verifier The address of the verifier. /// @param _name The name of the function to be updated. - function updateFunction(address _verifier, string memory _name) external returns (bytes32 functionId) { + function updateFunction(address _verifier, string memory _name) + external + returns (bytes32 functionId) + { functionId = getFunctionId(msg.sender, _name); if (msg.sender != verifierOwners[functionId]) { revert NotFunctionOwner(msg.sender, verifierOwners[functionId]); @@ -82,11 +88,18 @@ contract FunctionRegistry is IFunctionRegistry { /// @notice Returns the functionId for a given owner and function name. /// @param _owner The owner of the function (sender of registerFunction). /// @param _name The name of the function. - function getFunctionId(address _owner, string memory _name) public pure returns (bytes32 functionId) { + function getFunctionId(address _owner, string memory _name) + public + pure + returns (bytes32 functionId) + { functionId = keccak256(abi.encode(_owner, _name)); } - function _deploy(bytes memory _bytecode, bytes32 _salt) internal returns (address deployedAddr) { + function _deploy(bytes memory _bytecode, bytes32 _salt) + internal + returns (address deployedAddr) + { if (_bytecode.length == 0) revert EmptyBytecode(); assembly { diff --git a/contracts/src/examples/secp256r1/Example.sol b/contracts/src/examples/secp256r1/Example.sol deleted file mode 100644 index 6467e19a7..000000000 --- a/contracts/src/examples/secp256r1/Example.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.16; - -import {Secp256r1} from "./Secp256r1.sol"; - -contract Example { - function a() external { - address gateway = address(0); - bytes32 functionId = bytes32(0); - uint256 r = 0; - uint256 s = 0; - bytes4 callbackSelector = Example.b.selector; - bytes memory context = abi.encode(r, s); - Secp256r1.request(gateway, functionId, r, s, callbackSelector, context); - } - - function b(bytes memory _output, bytes memory _context) external pure returns (uint256) { - bool verified = Secp256r1.decode(_output); - (uint256 r, uint256 s) = abi.decode(_context, (uint256, uint256)); - if (verified) { - return r + s; - } - return 0; - } -} diff --git a/contracts/src/examples/secp256r1/Secp256r1.sol b/contracts/src/examples/secp256r1/Secp256r1.sol deleted file mode 100644 index fd095c1fe..000000000 --- a/contracts/src/examples/secp256r1/Secp256r1.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.16; - -import {IFunctionGateway} from "../../interfaces/IFunctionGateway.sol"; - -library Secp256r1 { - /// @dev Requests for a Secp256r1 signature to be verified. - /// @param _gateway The gateway to the function. - /// @param _functionId The id of the function. - /// @param _r The r value of the signature. - /// @param _s The s value of the signature. - /// @param _callbackSelector The selector of the callback function. - /// @param _context The context of the runtime. - function request( - address _gateway, - bytes32 _functionId, - uint256 _r, - uint256 _s, - bytes4 _callbackSelector, - bytes memory _context - ) internal returns (bytes32) { - bytes memory input = abi.encode(_r, _s); - return IFunctionGateway(_gateway).request(_functionId, input, _callbackSelector, _context); - } - - /// @dev Decodes the output of the function. - /// @param _output The output of the function. - function decode(bytes memory _output) internal pure returns (bool) { - return abi.decode(_output, (bool)); - } -} diff --git a/contracts/src/examples/storage/NounsOwnership.sol b/contracts/src/examples/storage/NounsOwnership.sol deleted file mode 100644 index ac22511bd..000000000 --- a/contracts/src/examples/storage/NounsOwnership.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import {StorageOracle} from "./StorageOracle.sol"; - -/// @notice Projects Nouns ownership from L1. -contract NounsOwnership { - /// @dev L1 Nouns NFT contract: https://etherscan.io/address/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03 - address internal constant NOUNS_ADDRESS = 0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03; - - /// @dev Storage Slot in the contract for `mapping(uint256 => address) private _owners;` - uint256 internal constant OWNERS_SLOT = 3; - - address public immutable STORAGE_ORACLE; - - constructor(address _storageOracle) { - STORAGE_ORACLE = _storageOracle; - } - - /// @notice Requests that the ownership of a given noun be accouted for in voting. - /// @dev This can be called for an existing noun to update the owner. - /// @param _tokenId The token Id of the noun to claim. For example, to claim noun #100, this should be - /// 100. - function claimOwnership(uint256 _tokenId) external returns (bytes32 requestId) { - uint256 slot = uint256(keccak256(abi.encode(_tokenId, OWNERS_SLOT))); - requestId = StorageOracle(STORAGE_ORACLE).requestStorageSlot(NOUNS_ADDRESS, slot); - } - - /// @param _tokenId The token Id of the noun to check ownership of. - function ownerOf(uint256 _tokenId) external view returns (address owner) { - (bytes32 value,) = - StorageOracle(STORAGE_ORACLE).slots(NOUNS_ADDRESS, uint256(keccak256(abi.encode(_tokenId, OWNERS_SLOT)))); - return address(uint160(uint256(value))); - } - - /// @param _tokenId The token Id of the noun to last updated block of. - function lastUpdatedBlock(uint256 _tokenId) external view returns (uint256 blockNumber) { - (, blockNumber) = - StorageOracle(STORAGE_ORACLE).slots(NOUNS_ADDRESS, uint256(keccak256(abi.encode(_tokenId, OWNERS_SLOT)))); - } -} diff --git a/contracts/src/examples/storage/StorageOracle.sol b/contracts/src/examples/storage/StorageOracle.sol deleted file mode 100644 index 4855e12ff..000000000 --- a/contracts/src/examples/storage/StorageOracle.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import {IFunctionGateway} from "../../interfaces/IFunctionGateway.sol"; -import {TimelockedUpgradeable} from "../../upgrades/TimelockedUpgradeable.sol"; - -/// @dev https://community.optimism.io/docs/protocol/protocol-2.0/#l1block -interface L1BlockPrecompile { - function number() external view returns (uint64); - function timestamp() external view returns (uint64); - function hash() external view returns (bytes32); -} - -/// @title StorageOracle -/// @notice Stores slot values of L1 accounts. -contract StorageOracle is TimelockedUpgradeable { - /// @notice Represents a storage slot value on L1, with the block number it was retrieved from. - struct StorageSlot { - bytes32 value; - uint256 blockNumber; - } - - /// @dev https://community.optimism.io/docs/developers/build/differences/#opcode-differences - L1BlockPrecompile public constant L1_BLOCK = L1BlockPrecompile(0x4200000000000000000000000000000000000015); - address public gateway; - bytes32 public functionId; - /// @notice Mapping for L1 account -> storage slot number -> storage slot value. - mapping(address => mapping(uint256 => StorageSlot)) public slots; - - event SlotRequested(uint256 indexed blockNumber, bytes32 indexed blockHash, address indexed account, uint256 slot); - event SlotUpdated(uint256 indexed blockNumber, address indexed account, uint256 slot, bytes32 value); - - error InvalidL1BlockHash(); - error InvalidL1BlockNumber(); - error NotFromFunctionGateway(address sender); - error OutdatedBlockNumber(uint256 blockNumber, uint256 storedBlockNumber); - - /// @param _gateway The FunctionGateway address. - /// @param _functionId The functionId for the storage verifier. - function initialize(address _gateway, bytes32 _functionId, address _timelock, address _guardian) - external - initializer - { - gateway = _gateway; - functionId = _functionId; - __TimelockedUpgradeable_init(_timelock, _guardian); - } - - /// @notice Request a storage slot value for a given account on L1. - function requestStorageSlot(address _account, uint256 _slot) external payable returns (bytes32 requestId) { - bytes32 blockHash = L1_BLOCK.hash(); - if (blockHash == bytes32(0)) { - revert InvalidL1BlockHash(); - } - uint256 blockNumber = L1_BLOCK.number(); - if (blockNumber == 0) { - revert InvalidL1BlockNumber(); - } - - bytes memory input = abi.encode(blockHash, _account, _slot); - bytes memory context = abi.encode(blockNumber, _account, _slot); - requestId = IFunctionGateway(gateway).request{value: msg.value}( - functionId, input, StorageOracle.handleStorageSlot.selector, context - ); - - emit SlotRequested(blockNumber, blockHash, _account, _slot); - } - - /// @dev Callback function to recieve the storage slot value from the FunctionGateway. If for existing slot, MUST update - /// for a more recent blockNumber. - function handleStorageSlot(bytes memory _output, bytes memory _context) external { - if (msg.sender != gateway) { - revert NotFromFunctionGateway(msg.sender); - } - - bytes32 slotValue = abi.decode(_output, (bytes32)); - (uint256 blockNumber, address account, uint256 slot) = abi.decode(_context, (uint256, address, uint256)); - if (blockNumber <= slots[account][slot].blockNumber) { - revert OutdatedBlockNumber(blockNumber, slots[account][slot].blockNumber); - } - - slots[account][slot] = StorageSlot(slotValue, blockNumber); - - emit SlotUpdated(blockNumber, account, slot, slotValue); - } -} diff --git a/contracts/src/examples/storage/StorageVerifier.sol b/contracts/src/examples/storage/StorageVerifier.sol deleted file mode 100644 index a7d126e07..000000000 --- a/contracts/src/examples/storage/StorageVerifier.sol +++ /dev/null @@ -1,576 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -library Pairing { - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct G1Point { - uint256 X; - uint256 Y; - } - - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } - - /* - * @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, PRIME_Q - (p.Y % PRIME_Q)); - } - } - - /* - * @return 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, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { invalid() } - } - - require(success, "pairing-add-failed"); - } - - /* - * Same as plus but accepts raw input instead of struct - * @return The sum of two points of G1, one is represented as array - */ - function plus_raw(uint256[4] memory input, G1Point memory r) internal view { - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { invalid() } - } - - require(success, "pairing-add-failed"); - } - - /* - * @return 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, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { invalid() } - } - require(success, "pairing-mul-failed"); - } - - /* - * Same as scalar_mul but accepts raw input instead of struct, - * Which avoid extra allocation. provided input can be allocated outside and re-used multiple times - */ - function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view { - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { invalid() } - } - require(success, "pairing-mul-failed"); - } - - /* @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, - G1Point memory c1, - G2Point memory c2, - G1Point memory d1, - G2Point memory d2 - ) internal view returns (bool) { - G1Point[4] memory p1 = [a1, b1, c1, d1]; - G2Point[4] memory p2 = [a2, b2, c2, d2]; - uint256 inputSize = 24; - uint256[] memory input = new uint256[](inputSize); - - for (uint256 i = 0; i < 4; 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, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) - // Use "invalid" to make gas estimation work - switch success - case 0 { invalid() } - } - - require(success, "pairing-opcode-failed"); - - return out[0] != 0; - } -} - -contract Verifier { - using Pairing for *; - - uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct VerifyingKey { - Pairing.G1Point alfa1; - Pairing.G2Point beta2; - Pairing.G2Point gamma2; - Pairing.G2Point delta2; - } - // []G1Point IC (K in gnark) appears directly in verifyProof - - struct Proof { - Pairing.G1Point A; - Pairing.G2Point B; - Pairing.G1Point C; - } - - function verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.alfa1 = Pairing.G1Point( - uint256(15065378376707638670416117646191938546464823528318298434911407668648410017312), - uint256(14328565780794488154843107537696396004559344718567701918290331837021769695183) - ); - vk.beta2 = Pairing.G2Point( - [ - uint256(21608662413593678936351578876605429104372744341332808284734169211800668886021), - uint256(14272630991227820574186833459180341377383838929920263355368708236729516236510) - ], - [ - uint256(16641043772513434533950209102074465229233476042166934559766744767810330184564), - uint256(6162122694319393529287233670241265074511780247270252331117359314308552821119) - ] - ); - vk.gamma2 = Pairing.G2Point( - [ - uint256(13132182365077749935258621721022525042673781526193917683715236922498527090287), - uint256(15561074370462920423820148502023399216464240426394708700564724325660560082718) - ], - [ - uint256(10082726381035042218296813991627285842615800319245653642200496444791944486189), - uint256(19936166073830122769051017946410845224800149361868042760143437401219984978115) - ] - ); - vk.delta2 = Pairing.G2Point( - [ - uint256(18835698337094098366375289269375058880930688910548530995640100337968257621142), - uint256(2538885030664125768153917843471118992747126086079950113951128163711148595911) - ], - [ - uint256(3381368293223064763369350882484383507924963445101730404619590717560533944931), - uint256(20044872545096760477413396293539672831863209972531251378473847402381937985325) - ] - ); - } - - // accumulate scalarMul(mul_input) into q - // that is computes sets q = (mul_input[0:2] * mul_input[3]) + q - function accumulate( - uint256[3] memory mul_input, - Pairing.G1Point memory p, - uint256[4] memory buffer, - Pairing.G1Point memory q - ) internal view { - // computes p = mul_input[0:2] * mul_input[3] - Pairing.scalar_mul_raw(mul_input, p); - - // point addition inputs - buffer[0] = q.X; - buffer[1] = q.Y; - buffer[2] = p.X; - buffer[3] = p.Y; - - // q = p + q - Pairing.plus_raw(buffer, q); - } - - /* - * @returns Whether the proof is valid given the hardcoded verifying key - * above and the public inputs - */ - function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[65] memory input) - public - view - returns (bool r) - { - Proof memory proof; - proof.A = Pairing.G1Point(a[0], a[1]); - proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); - proof.C = Pairing.G1Point(c[0], c[1]); - - // Make sure that proof.A, B, and C are each less than the prime q - require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); - require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); - - require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); - require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); - - require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); - require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); - - require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); - require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); - - // Make sure that every input is less than the snark scalar field - for (uint256 i = 0; i < input.length; i++) { - require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field"); - } - - VerifyingKey memory vk = verifyingKey(); - - // Compute the linear combination vk_x - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - - // Buffer reused for addition p1 + p2 to avoid memory allocations - // [0:2] -> p1.X, p1.Y ; [2:4] -> p2.X, p2.Y - uint256[4] memory add_input; - - // Buffer reused for multiplication p1 * s - // [0:2] -> p1.X, p1.Y ; [3] -> s - uint256[3] memory mul_input; - - // temporary point to avoid extra allocations in accumulate - Pairing.G1Point memory q = Pairing.G1Point(0, 0); - - vk_x.X = uint256(7654151173307285122173887557943556592926627323803440265411428835933171698030); // vk.K[0].X - vk_x.Y = uint256(1267569108851906122418933906145913022663962383604792269197553633453069852082); // vk.K[0].Y - mul_input[0] = uint256(0); // vk.K[1].X - mul_input[1] = uint256(0); // vk.K[1].Y - mul_input[2] = input[0]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[1] * input[0] - mul_input[0] = uint256(10855663286981623084541363489660102193206622537607772833968317321581433303751); // vk.K[2].X - mul_input[1] = uint256(12156643609098206979852635788280459246023978138921482912702542075055217047953); // vk.K[2].Y - mul_input[2] = input[1]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[2] * input[1] - mul_input[0] = uint256(379076147614366186237852555128877932752707022181195558773951575810360280983); // vk.K[3].X - mul_input[1] = uint256(20275768584033756898412813982387176725473685447816316945827890314022804466668); // vk.K[3].Y - mul_input[2] = input[2]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[3] * input[2] - mul_input[0] = uint256(2030788471383854807393572254897579868967448362247501984749898450508158620589); // vk.K[4].X - mul_input[1] = uint256(16007079715104718539427533809679786172137912244729461946995485300671697674877); // vk.K[4].Y - mul_input[2] = input[3]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[4] * input[3] - mul_input[0] = uint256(3659729532935328078600286048545654261948161883356705951517335433023302604447); // vk.K[5].X - mul_input[1] = uint256(6719541045159043658238314026173320015187154961043383047031507241065433979987); // vk.K[5].Y - mul_input[2] = input[4]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[5] * input[4] - mul_input[0] = uint256(9023351031242744707107208505085543026451788790872710445827418724631815295840); // vk.K[6].X - mul_input[1] = uint256(2498974708525416644646883774124922731192136194723500432208844468584751225497); // vk.K[6].Y - mul_input[2] = input[5]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[6] * input[5] - mul_input[0] = uint256(11760246946898200849775229670108235157754487907263669813035215235872743186597); // vk.K[7].X - mul_input[1] = uint256(5382428339030687870768870582444025050484374185231261099126636032475175446148); // vk.K[7].Y - mul_input[2] = input[6]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[7] * input[6] - mul_input[0] = uint256(332103316032981202977921170491728275515984536882295042122024740487031669015); // vk.K[8].X - mul_input[1] = uint256(484223038059012831998672398465994235268759953393977542517196949254233028660); // vk.K[8].Y - mul_input[2] = input[7]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[8] * input[7] - mul_input[0] = uint256(5344906529970141689021459145557374163117422738842071706750631467728871290394); // vk.K[9].X - mul_input[1] = uint256(7731978403516757842874921595990408683343135883653940410950541995815224341199); // vk.K[9].Y - mul_input[2] = input[8]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[9] * input[8] - mul_input[0] = uint256(10362237886978658404784044741070389359500890596626548033780244659029433510363); // vk.K[10].X - mul_input[1] = uint256(6285629694982155865291087094189363562595967452963101197020929967022416193184); // vk.K[10].Y - mul_input[2] = input[9]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[10] * input[9] - mul_input[0] = uint256(1936826942872850266674041622347967683160440267203145838962497651901744863132); // vk.K[11].X - mul_input[1] = uint256(2489418929250071077827953016163896767277738430716133365272418875838627300251); // vk.K[11].Y - mul_input[2] = input[10]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[11] * input[10] - mul_input[0] = uint256(12779474367520445587507333272472534286610205070395166091355451386189619020759); // vk.K[12].X - mul_input[1] = uint256(16098399295897998668449640624417961627391582297976508801357763570695142130577); // vk.K[12].Y - mul_input[2] = input[11]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[12] * input[11] - mul_input[0] = uint256(443172960809137696796573525061835890990476988281640266727036593096513946387); // vk.K[13].X - mul_input[1] = uint256(20448435027686077373503601710307109999095366634908587134616976911597013705326); // vk.K[13].Y - mul_input[2] = input[12]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[13] * input[12] - mul_input[0] = uint256(4420010271881666237419507746577855992446973907995876983937642148718830707448); // vk.K[14].X - mul_input[1] = uint256(14540506650087850032683132977210992280987068520451687156325445797696065869484); // vk.K[14].Y - mul_input[2] = input[13]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[14] * input[13] - mul_input[0] = uint256(19641835005247180332480402296715294035949028476829642551362696889499513656838); // vk.K[15].X - mul_input[1] = uint256(4812315591576788861668368040515410531600872313469076363556584755378733696050); // vk.K[15].Y - mul_input[2] = input[14]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[15] * input[14] - mul_input[0] = uint256(12640577748972357982630117727971303258087585863699713717297881582503076631360); // vk.K[16].X - mul_input[1] = uint256(13065923353823232570476651439967308146274765135151428476341066795511706991767); // vk.K[16].Y - mul_input[2] = input[15]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[16] * input[15] - mul_input[0] = uint256(13572128760084308415140083506753360673600577607804820189016855697861704739224); // vk.K[17].X - mul_input[1] = uint256(8442833044896943902431485896013816816589701482716364853929712353957738745885); // vk.K[17].Y - mul_input[2] = input[16]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[17] * input[16] - mul_input[0] = uint256(12782767107221283109044396047775873882831103174902550450883267206197174805868); // vk.K[18].X - mul_input[1] = uint256(21725909149821822419863341739266801796457530684451541722014143306092290491812); // vk.K[18].Y - mul_input[2] = input[17]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[18] * input[17] - mul_input[0] = uint256(11078573045918050069962548128864339547899985435179862834648073403084209835710); // vk.K[19].X - mul_input[1] = uint256(12399059488837415085805609005448464807782897587253414030053853866684873097988); // vk.K[19].Y - mul_input[2] = input[18]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[19] * input[18] - mul_input[0] = uint256(10315226201173711467954752250803493769599555861590514433803248756358327104145); // vk.K[20].X - mul_input[1] = uint256(2856544295114544565653429320946926740804556743379163983013096592795845303539); // vk.K[20].Y - mul_input[2] = input[19]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[20] * input[19] - mul_input[0] = uint256(9354895592827270566590945984304896833859741287021261458869270672256004059535); // vk.K[21].X - mul_input[1] = uint256(15011309793775328188185572246572377707679615478035837762242785740323839285319); // vk.K[21].Y - mul_input[2] = input[20]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[21] * input[20] - mul_input[0] = uint256(13313466138074340718734302586098076097388950008677954748589938155741454888448); // vk.K[22].X - mul_input[1] = uint256(12353745256729266438311891904289828780910344800526689870927542307621168813347); // vk.K[22].Y - mul_input[2] = input[21]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[22] * input[21] - mul_input[0] = uint256(15190018048332196524754887079224540675648717818816895400470708784186351975840); // vk.K[23].X - mul_input[1] = uint256(10147458378909811594952055908189722911538701320475523176144463017748535907490); // vk.K[23].Y - mul_input[2] = input[22]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[23] * input[22] - mul_input[0] = uint256(234614104392190853693722220259832416753154027782029485053737056100888951760); // vk.K[24].X - mul_input[1] = uint256(4128702334020980420987327656543100690673142825029758802716705558844651759585); // vk.K[24].Y - mul_input[2] = input[23]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[24] * input[23] - mul_input[0] = uint256(6931986590268588882910380631925893379786131874131718051994417017802935487518); // vk.K[25].X - mul_input[1] = uint256(6553771184053803320674257601817296516132168924473495362665037590748879926835); // vk.K[25].Y - mul_input[2] = input[24]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[25] * input[24] - mul_input[0] = uint256(8007641449250544351042554455733868623978825618215306263257441983965247015306); // vk.K[26].X - mul_input[1] = uint256(6989427805525814667453773033842061928861369548889935848958642430130423311990); // vk.K[26].Y - mul_input[2] = input[25]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[26] * input[25] - mul_input[0] = uint256(14741810725607764328249036718716826447801414725797362707614991969593535198061); // vk.K[27].X - mul_input[1] = uint256(16924891809278267680585602860764437386367597260415854373705866086335127810942); // vk.K[27].Y - mul_input[2] = input[26]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[27] * input[26] - mul_input[0] = uint256(19606675076775461784511771066572876373775296642178524644407593031446474991396); // vk.K[28].X - mul_input[1] = uint256(8606522840804950948717310490003731381536956837776527018324136881608861581571); // vk.K[28].Y - mul_input[2] = input[27]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[28] * input[27] - mul_input[0] = uint256(3378778428452374523256569312957451002365689609349537175414837663286905789890); // vk.K[29].X - mul_input[1] = uint256(3398091315042692109385568436973712260471414367803233969505626729524803304405); // vk.K[29].Y - mul_input[2] = input[28]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[29] * input[28] - mul_input[0] = uint256(8552650451497299813794466582330968619836679761737303659681933318167757098631); // vk.K[30].X - mul_input[1] = uint256(4217931479579097251137035220978681356363760563375323313336646382986482561900); // vk.K[30].Y - mul_input[2] = input[29]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[30] * input[29] - mul_input[0] = uint256(17389761499517884028227459747393901628845657128749005600031578978917606598569); // vk.K[31].X - mul_input[1] = uint256(10555185012236734284382729900325079069840722015806083080596709578503001010042); // vk.K[31].Y - mul_input[2] = input[30]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[31] * input[30] - mul_input[0] = uint256(20522064923903915435529181641985382408182131990229432495749925727116732333618); // vk.K[32].X - mul_input[1] = uint256(9262127328114327567334564923775981849508792353488856435505031509658876768785); // vk.K[32].Y - mul_input[2] = input[31]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[32] * input[31] - mul_input[0] = uint256(0); // vk.K[33].X - mul_input[1] = uint256(0); // vk.K[33].Y - mul_input[2] = input[32]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[33] * input[32] - mul_input[0] = uint256(19805515723576526151865962480176526559600946331940416602919435733744559178939); // vk.K[34].X - mul_input[1] = uint256(14436323825307532673910662135739037295632234829812967405189138584329725932226); // vk.K[34].Y - mul_input[2] = input[33]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[34] * input[33] - mul_input[0] = uint256(19769937692070903973717952756328400818768446873200827647850618096003799853020); // vk.K[35].X - mul_input[1] = uint256(21577770339510253918286877921192503157062550953375939162858739967204290523211); // vk.K[35].Y - mul_input[2] = input[34]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[35] * input[34] - mul_input[0] = uint256(21463274337361306731827127358497242847186037166889108461214045419815817218996); // vk.K[36].X - mul_input[1] = uint256(19499490089855815631318730286338300201490444832730925605965665171488963647858); // vk.K[36].Y - mul_input[2] = input[35]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[36] * input[35] - mul_input[0] = uint256(12057739572080112380123927889780603267198763016607327963671783717329310186493); // vk.K[37].X - mul_input[1] = uint256(7303684643153206320562424737389940301747294453769658376891530120300287091977); // vk.K[37].Y - mul_input[2] = input[36]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[37] * input[36] - mul_input[0] = uint256(17217188987453618497424883038450136632316646488983452367886954124919864036693); // vk.K[38].X - mul_input[1] = uint256(21375339730127974465467984229113021398656189521351737743163893047555883800246); // vk.K[38].Y - mul_input[2] = input[37]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[38] * input[37] - mul_input[0] = uint256(18787439887787093780339026828453522129057912122177599159715079795672616250109); // vk.K[39].X - mul_input[1] = uint256(17851621594125688853191944994514545725947854063365545119007748240805316325955); // vk.K[39].Y - mul_input[2] = input[38]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[39] * input[38] - mul_input[0] = uint256(15022035693900431687276953568986992848817580330708047655879922986997313472221); // vk.K[40].X - mul_input[1] = uint256(20284983706070006505302341041678459987145987163750240764970385624811896664406); // vk.K[40].Y - mul_input[2] = input[39]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[40] * input[39] - mul_input[0] = uint256(17682250282074192986606512022644465366834162256038110695035322304798987194845); // vk.K[41].X - mul_input[1] = uint256(17735039760536327883984565482595346927072649564075439600588336978864313757679); // vk.K[41].Y - mul_input[2] = input[40]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[41] * input[40] - mul_input[0] = uint256(20899706779328863341678425647248526006380374041381805254108969921590054971843); // vk.K[42].X - mul_input[1] = uint256(9208903989072050237486846027857559959173926655646571064322647318415126307423); // vk.K[42].Y - mul_input[2] = input[41]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[42] * input[41] - mul_input[0] = uint256(8094119844233180842261868250217351476703160601947508218059277542084472590858); // vk.K[43].X - mul_input[1] = uint256(3931401739387971212950794367903322610453707604654345792724304050982429234275); // vk.K[43].Y - mul_input[2] = input[42]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[43] * input[42] - mul_input[0] = uint256(1316264156504633102498105591347442946770583029642110683067801761935109545631); // vk.K[44].X - mul_input[1] = uint256(6410737200033529651526204670194859866494250065115817937275936426766906855619); // vk.K[44].Y - mul_input[2] = input[43]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[44] * input[43] - mul_input[0] = uint256(8804621950111225410125455844142464146394958064668792060504877361795418468316); // vk.K[45].X - mul_input[1] = uint256(8580620024820260438211068784935002090479952452161813928224108672526860866015); // vk.K[45].Y - mul_input[2] = input[44]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[45] * input[44] - mul_input[0] = uint256(7112295527738970285127563356966652173102917507489227391418611658263599307482); // vk.K[46].X - mul_input[1] = uint256(17275281338238142402668320571083598275071996182405634500711023887464421538127); // vk.K[46].Y - mul_input[2] = input[45]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[46] * input[45] - mul_input[0] = uint256(5240121977703409724976930147474666123582523807199050824430926013248898323413); // vk.K[47].X - mul_input[1] = uint256(9235777244560018425574571644168716210830462792585067951593424650639702291943); // vk.K[47].Y - mul_input[2] = input[46]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[47] * input[46] - mul_input[0] = uint256(16709515988692486378095964497727607992954421154155836143184835325120234108282); // vk.K[48].X - mul_input[1] = uint256(4318821262168551260213596218763477445736619275692986821452072174624316039747); // vk.K[48].Y - mul_input[2] = input[47]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[48] * input[47] - mul_input[0] = uint256(4635653803757786734812939730130448826232706399998169438201812455241736781365); // vk.K[49].X - mul_input[1] = uint256(10620534372183167886846599595246470641725726870973800884133180230395135737336); // vk.K[49].Y - mul_input[2] = input[48]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[49] * input[48] - mul_input[0] = uint256(21882091146466759727378676055635488784699217830186438786659067805274266463217); // vk.K[50].X - mul_input[1] = uint256(18598242203333244012781528775935406341731159877256787836648474120945984728668); // vk.K[50].Y - mul_input[2] = input[49]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[50] * input[49] - mul_input[0] = uint256(4981598058944746539451200624832095463060756413653505740624778630043872771559); // vk.K[51].X - mul_input[1] = uint256(15417437381372953121848338347341631007478788246258161716837437944605430967710); // vk.K[51].Y - mul_input[2] = input[50]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[51] * input[50] - mul_input[0] = uint256(16449857651479430087361783294716085780818735843608843357258053633847274952841); // vk.K[52].X - mul_input[1] = uint256(15723174467364037738901289426375812656268714827433964282590968293223190370878); // vk.K[52].Y - mul_input[2] = input[51]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[52] * input[51] - mul_input[0] = uint256(20648916574105536651189253176665249822733862047163598918925707075866654074662); // vk.K[53].X - mul_input[1] = uint256(21287059011152424060114653424447126287308325233923052797964482922876748269568); // vk.K[53].Y - mul_input[2] = input[52]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[53] * input[52] - mul_input[0] = uint256(18585476542396457205150744199897004875593607482225637447845157197513269605578); // vk.K[54].X - mul_input[1] = uint256(3417167586405724706465908229613681922657557710181148039128574753784005202656); // vk.K[54].Y - mul_input[2] = input[53]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[54] * input[53] - mul_input[0] = uint256(4511397141251924031887085372482825080786918659993462057314369239464754481694); // vk.K[55].X - mul_input[1] = uint256(21717095464432043238756681501430279756357661473638833021102155654431510563342); // vk.K[55].Y - mul_input[2] = input[54]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[55] * input[54] - mul_input[0] = uint256(9212519189230801322001536903365645225220584836288401322046467666908189548807); // vk.K[56].X - mul_input[1] = uint256(19744538517056645677142028979018732961431154170994932788904230767809445689768); // vk.K[56].Y - mul_input[2] = input[55]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[56] * input[55] - mul_input[0] = uint256(12289453430414039567836724210064005227666479474586979621733298761776796474516); // vk.K[57].X - mul_input[1] = uint256(12989757651650597082755681430685414608745195391709803644101621833221841663099); // vk.K[57].Y - mul_input[2] = input[56]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[57] * input[56] - mul_input[0] = uint256(2369478084041109780531328278734355652837607008951232860940114157378030098765); // vk.K[58].X - mul_input[1] = uint256(6119868655329830957213547984204983011004726394242293527463828569451316899897); // vk.K[58].Y - mul_input[2] = input[57]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[58] * input[57] - mul_input[0] = uint256(17989837724212112366551871898291137740340429905303785911117451851352882896345); // vk.K[59].X - mul_input[1] = uint256(17949950156855941131881511270898739855917513493341349952799783132793577741533); // vk.K[59].Y - mul_input[2] = input[58]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[59] * input[58] - mul_input[0] = uint256(7262439193785268583033241221853354235600977055409379173808433414317405448832); // vk.K[60].X - mul_input[1] = uint256(12203906070920291015993347316731095119713139475700145895368333405554307230470); // vk.K[60].Y - mul_input[2] = input[59]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[60] * input[59] - mul_input[0] = uint256(1162813742019157110963043810774081780095578126075902578179759093147701028966); // vk.K[61].X - mul_input[1] = uint256(6196461946796674808960015791266782945323722628464646545853640633136894953642); // vk.K[61].Y - mul_input[2] = input[60]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[61] * input[60] - mul_input[0] = uint256(8734960838152226512446036029393702945097778032815532218825582662229148105809); // vk.K[62].X - mul_input[1] = uint256(14156597841489472902105242066512520003665698393998522446948180722237464995747); // vk.K[62].Y - mul_input[2] = input[61]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[62] * input[61] - mul_input[0] = uint256(4584525156462878436907385844466134332162203762915420480339839395820190826598); // vk.K[63].X - mul_input[1] = uint256(20896772351738173920864818723101902251047201025669140610634794223821078661262); // vk.K[63].Y - mul_input[2] = input[62]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[63] * input[62] - mul_input[0] = uint256(15548856620589266889065140246191848830841596843403455464590867915077143726220); // vk.K[64].X - mul_input[1] = uint256(19703685940179675295187023238588296384286868782044376229887047852708735602340); // vk.K[64].Y - mul_input[2] = input[63]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[64] * input[63] - mul_input[0] = uint256(6512240350257283986195670322307359040229223793918266787712417443520969192740); // vk.K[65].X - mul_input[1] = uint256(17883424236536710893435059391856918208546300448278439854180388920157605103236); // vk.K[65].Y - mul_input[2] = input[64]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[65] * input[64] - - return - Pairing.pairing(Pairing.negate(proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2); - } -} - -interface IFunctionVerifier { - function verify(bytes32 _inputHash, bytes32 _outputHash, bytes memory _proof) external view returns (bool); - - function verificationKeyHash() external pure returns (bytes32); -} - -contract StorageVerifier is IFunctionVerifier, Verifier { - function verify(bytes32 _inputHash, bytes32 _outputHash, bytes memory _proof) external pure returns (bool) { - (uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) = - abi.decode(_proof, (uint256[2], uint256[2][2], uint256[2])); - - uint256[65] memory input; - input[0] = uint256(_inputHash); - input[1] = uint256(_outputHash); - - // remove unused warnings - a = a; - b = b; - c = c; - // temporary, should do: - // return verifyProof(a, b, c, input); - return true; - } - - function verificationKeyHash() external pure returns (bytes32) { - return keccak256(abi.encode(verifyingKey())); - } -} diff --git a/contracts/src/interfaces/IFunctionGateway.sol b/contracts/src/interfaces/IFunctionGateway.sol index 81209fe0f..e969f7176 100644 --- a/contracts/src/interfaces/IFunctionGateway.sol +++ b/contracts/src/interfaces/IFunctionGateway.sol @@ -1,91 +1,51 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.5.0; - -struct FunctionRequest { - bytes32 functionId; - bytes32 inputHash; - bytes32 outputHash; - bytes32 contextHash; - address callbackAddress; - bytes4 callbackSelector; - bool proofFulfilled; - bool callbackFulfilled; -} +pragma solidity >=0.8.0; interface IFunctionGatewayEvents { - event ProofRequested( - uint256 indexed nonce, + event RequestCallback( + uint32 indexed nonce, bytes32 indexed functionId, - bytes32 requestId, - bytes inputs, + bytes input, bytes context, - uint256 gasLimit, - uint256 feeAmount + address callbackAddress, + bytes4 callbackSelector, + uint32 callbackGasLimit + ); + event RequestCall( + bytes32 indexed functionId, bytes input, address callbackAddress, bytes callbackData ); - event ProofFulfilled(bytes32 requestId, bytes32 outputHash, bytes proof); - event ProofBatchFulfilled( - bytes32[] requestIds, - bytes aggregateProof, - bytes32 inputsRoot, - bytes32[] outputHashes, - bytes32 outputsRoot, - bytes32 verificationKeyRoot + event RequestFulfilled( + uint32 indexed nonce, bytes32 indexed functionId, bytes32 inputHash, bytes32 outputHash ); - event CallbackFulfilled(bytes32 requestId, bytes output, bytes context); - event ScalarUpdated(uint256 scalar); + event Call(bytes32 indexed functionId, bytes32 inputHash, bytes32 outputHash); } interface IFunctionGatewayErrors { - error RequestNotFound(bytes32 requestId); - error ContextMismatch(bytes32 contextHash, bytes context); - error OutputMismatch(bytes32 outputHash, bytes context); - error InputsRootMismatch(bytes32 inputsRoot, bytes32[] inputHashes); - error OutputsRootMismatch(bytes32 outputsRoot, bytes32[] outputHashes); - error VerificationKeysRootMismatch(bytes32 outputsRoot, bytes32[] outputHashes); - error ProofNotFulfilled(bytes32 requestId); - error ProofAlreadyFulfilled(bytes32 requestId); + error InvalidRequest(uint32 nonce, bytes32 expectedRequestHash, bytes32 requestHash); + error CallbackFailed(bytes4 callbackSelector, bytes output, bytes context); + error InvalidCall(bytes32 functionId, bytes input); + error CallFailed(address callbackAddress, bytes callbackData); error InvalidProof(address verifier, bytes32 inputHash, bytes32 outputHash, bytes proof); - error CallbackFailed(address callbackAddress, bytes4 callbackSelector); - error CallbackAlreadyFulfilled(bytes32 requestId); - error LengthMismatch(uint256 expected, uint256 actual); - error InsufficientFeeAmount(uint256 expected, uint256 actual); - error RefundFailed(address refundAccount, uint256 refundAmount); } interface IFunctionGateway is IFunctionGatewayEvents, IFunctionGatewayErrors { - function requests(bytes32 requestId) - external - view - returns (bytes32, bytes32, bytes32, bytes32, address, bytes4, bool, bool); - - function request(bytes32 functionId, bytes memory input, bytes4 callbackSelector, bytes memory context) - external - payable - returns (bytes32); - - function request( - bytes32 functionId, - bytes memory input, - bytes4 callbackSelector, - bytes memory context, - uint256 gasLimit, - address refundAccount + function requestCallback( + bytes32 _functionId, + bytes memory _input, + bytes memory _context, + bytes4 _callbackSelector, + uint32 _callbackGasLimit ) external payable returns (bytes32); - function fulfill(bytes32 requestId, bytes32 outputHash, bytes memory proof) external; - - function fulfillBatch( - bytes32[] memory requestIds, - bytes memory aggregateProof, - bytes32 inputsRoot, - bytes32[] memory outputHashes, - bytes32 outputsRoot, - bytes32 verificationKeyRoot - ) external; - - function callback(bytes32 requestId, bytes memory output, bytes memory context) external; + function requestCall( + bytes32 _functionId, + bytes memory _input, + address _callbackAddress, + bytes memory _callbackData + ) external payable; - function calculateFeeAmount() external view returns (uint256); - - function calculateFeeAmount(uint256 gasLimit) external view returns (uint256); + function verifiedCall(bytes32 _functionId, bytes memory _input) + external + view + returns (bytes memory); } diff --git a/contracts/src/interfaces/IFunctionRegistry.sol b/contracts/src/interfaces/IFunctionRegistry.sol index 1797f7261..697886040 100644 --- a/contracts/src/interfaces/IFunctionRegistry.sol +++ b/contracts/src/interfaces/IFunctionRegistry.sol @@ -2,10 +2,14 @@ pragma solidity >=0.5.0; interface IFunctionRegistryEvents { - event FunctionRegistered(bytes32 indexed functionId, address verifier, string name, address owner); + event FunctionRegistered( + bytes32 indexed functionId, address verifier, string name, address owner + ); event FunctionVerifierUpdated(bytes32 indexed functionId, address verifier); event FunctionOwnerUpdated(bytes32 indexed functionId, address owner); - event Deployed(bytes32 indexed bytecodeHash, bytes32 indexed salt, address indexed deployedAddress); + event Deployed( + bytes32 indexed bytecodeHash, bytes32 indexed salt, address indexed deployedAddress + ); } interface IFunctionRegistryErrors { @@ -19,13 +23,20 @@ interface IFunctionRegistryErrors { interface IFunctionRegistry is IFunctionRegistryEvents, IFunctionRegistryErrors { function verifiers(bytes32 functionId) external view returns (address verifier); function verifierOwners(bytes32 functionId) external view returns (address owner); - function registerFunction(address verifier, string memory name) external returns (bytes32 functionId); + function registerFunction(address verifier, string memory name) + external + returns (bytes32 functionId); function deployAndRegisterFunction(bytes memory bytecode, string memory name) external returns (bytes32 functionId, address verifier); - function updateFunction(address verifier, string memory name) external returns (bytes32 functionId); + function updateFunction(address verifier, string memory name) + external + returns (bytes32 functionId); function deployAndUpdateFunction(bytes memory bytecode, string memory _name) external returns (bytes32 functionId, address verifier); - function getFunctionId(address owner, string memory name) external pure returns (bytes32 functionId); + function getFunctionId(address owner, string memory name) + external + pure + returns (bytes32 functionId); } diff --git a/contracts/src/interfaces/IFunctionVerifier.sol b/contracts/src/interfaces/IFunctionVerifier.sol index 1c5817c9d..7b8652557 100644 --- a/contracts/src/interfaces/IFunctionVerifier.sol +++ b/contracts/src/interfaces/IFunctionVerifier.sol @@ -2,7 +2,9 @@ pragma solidity >=0.5.0; interface IFunctionVerifier { - function verify(bytes32 inputHash, bytes32 outputHash, bytes memory proof) external returns (bool); + function verify(bytes32 inputHash, bytes32 outputHash, bytes memory proof) + external + returns (bool); function verificationKeyHash() external view returns (bytes32); } diff --git a/contracts/src/libraries/OutputReader.sol b/contracts/src/libraries/OutputReader.sol index 48cc7efe6..f52c57bc6 100644 --- a/contracts/src/libraries/OutputReader.sol +++ b/contracts/src/libraries/OutputReader.sol @@ -1,7 +1,11 @@ pragma solidity ^0.8.16; library OutputReader { - function readUint256(bytes memory _output, uint256 _offset) internal pure returns (uint256, uint256) { + function readUint256(bytes memory _output, uint256 _offset) + internal + pure + returns (uint256, uint256) + { uint256 value; assembly { value := mload(add(add(_output, 0x20), _offset)) @@ -9,7 +13,11 @@ library OutputReader { return (_offset + 32, value); } - function readUint128(bytes memory _output, uint256 _offset) internal pure returns (uint256, uint128) { + function readUint128(bytes memory _output, uint256 _offset) + internal + pure + returns (uint256, uint128) + { uint128 value; assembly { value := mload(add(add(_output, 0x10), _offset)) @@ -17,7 +25,11 @@ library OutputReader { return (_offset + 16, value); } - function readUint64(bytes memory _output, uint256 _offset) internal pure returns (uint256, uint64) { + function readUint64(bytes memory _output, uint256 _offset) + internal + pure + returns (uint256, uint64) + { uint64 value; assembly { value := mload(add(add(_output, 0x08), _offset)) @@ -25,7 +37,11 @@ library OutputReader { return (_offset + 8, value); } - function readUint32(bytes memory _output, uint256 _offset) internal pure returns (uint256, uint32) { + function readUint32(bytes memory _output, uint256 _offset) + internal + pure + returns (uint256, uint32) + { uint32 value; assembly { value := mload(add(add(_output, 0x04), _offset)) diff --git a/contracts/src/mocks/MockFunctionGateway.sol b/contracts/src/mocks/MockFunctionGateway.sol index f79098190..90df14ff6 100644 --- a/contracts/src/mocks/MockFunctionGateway.sol +++ b/contracts/src/mocks/MockFunctionGateway.sol @@ -1,140 +1,4 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.16; -import {VmSafe} from "forge-std/Vm.sol"; -import {stdJson} from "forge-std/StdJson.sol"; -import {IFunctionGateway, FunctionRequest} from "../interfaces/IFunctionGateway.sol"; - -contract MockFunctionGateway is IFunctionGateway { - VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); - uint256 public DEFAULT_GAS_LIMIT = 1000000; - uint256 public nonce; - uint256 public scalar = 1; - mapping(bytes32 => FunctionRequest) public requests; - mapping(bytes32 => bytes) public outputs; - - function loadFixture(string memory _path) external { - string memory json; - try vm.readFile(_path) returns (string memory data) { - json = data; - } catch { - revert("MockFunctionGateway: fixture not found"); - } - bytes memory input = stdJson.readBytes(json, "$.input"); - bytes memory output = stdJson.readBytes(json, "$.output"); - outputs[sha256(input)] = output; - } - - function setRequest(bytes32 _requestId, FunctionRequest memory _request) public { - requests[_requestId] = _request; - } - - function request(bytes32 _functionId, bytes memory _input, bytes4 _callbackSelector, bytes memory _context) - external - payable - returns (bytes32) - { - return request(_functionId, _input, _callbackSelector, _context, DEFAULT_GAS_LIMIT, tx.origin); - } - - function request( - bytes32 _functionId, - bytes memory _input, - bytes4 _callbackSelector, - bytes memory _context, - uint256 _gasLimit, - address - ) public payable returns (bytes32) { - bytes32 inputHash = sha256(_input); - bytes32 contextHash = keccak256(_context); - FunctionRequest memory r = FunctionRequest({ - functionId: _functionId, - inputHash: inputHash, - outputHash: bytes32(0), - contextHash: contextHash, - callbackAddress: msg.sender, - callbackSelector: _callbackSelector, - proofFulfilled: false, - callbackFulfilled: false - }); - - bytes32 requestId = keccak256(abi.encode(nonce, r)); - requests[requestId] = r; - - emit ProofRequested(nonce, _functionId, requestId, _input, _context, _gasLimit, calculateFeeAmount(_gasLimit)); - nonce++; - - // If fixture has been pre-loaded, automatically fulfill and callback. - bytes memory output = outputs[inputHash]; - if (output.length > 0) { - r.outputHash = keccak256(output); - r.proofFulfilled = true; - r.callbackFulfilled = true; - - (bool status,) = msg.sender.call(abi.encodeWithSelector(_callbackSelector, output, _context)); - if (!status) { - revert CallbackFailed(msg.sender, _callbackSelector); - } - } - - return requestId; - } - - function fulfill(bytes32 _requestId, bytes32 _outputHash, bytes memory _proof) external { - FunctionRequest storage r = requests[_requestId]; - r.proofFulfilled = true; - r.outputHash = _outputHash; - - emit ProofFulfilled(_requestId, _outputHash, _proof); - } - - function fulfillBatch( - bytes32[] memory _requestIds, - bytes memory _aggregateProof, - bytes32 _inputsRoot, - bytes32[] memory _outputHashes, - bytes32 _outputsRoot, - bytes32 _verificationKeyRoot - ) external { - for (uint256 i = 0; i < _requestIds.length; i++) { - bytes32 requestId = _requestIds[i]; - FunctionRequest storage r = requests[requestId]; - r.proofFulfilled = true; - r.outputHash = _outputHashes[i]; - } - - emit ProofBatchFulfilled( - _requestIds, _aggregateProof, _inputsRoot, _outputHashes, _outputsRoot, _verificationKeyRoot - ); - } - - function callback(bytes32 _requestId, bytes memory _output, bytes memory _context) external { - FunctionRequest storage r = requests[_requestId]; - r.callbackFulfilled = true; - - (bool status,) = r.callbackAddress.call(abi.encodeWithSelector(r.callbackSelector, _output, _context)); - if (!status) { - revert CallbackFailed(r.callbackAddress, r.callbackSelector); - } - - emit CallbackFulfilled(_requestId, _output, _context); - } - - function calculateFeeAmount() external view returns (uint256 feeAmount) { - return calculateFeeAmount(DEFAULT_GAS_LIMIT); - } - - function calculateFeeAmount(uint256 _gasLimit) public view returns (uint256 feeAmount) { - if (scalar == 0) { - feeAmount = tx.gasprice * _gasLimit; - } else { - feeAmount = tx.gasprice * _gasLimit * scalar; - } - } - - function setScalar(uint256 _scalar) external { - scalar = _scalar; - - emit ScalarUpdated(scalar); - } -} +contract MockFunctionGateway {} diff --git a/contracts/src/upgrades/Timelock.sol b/contracts/src/upgrades/Timelock.sol index 817c733ad..a505b37c6 100644 --- a/contracts/src/upgrades/Timelock.sol +++ b/contracts/src/upgrades/Timelock.sol @@ -10,7 +10,10 @@ contract Timelock is TimelockController { /// @param _proposers accounts to be granted proposer and canceller roles /// @param _executors accounts to be granted executor role /// @param _admin optional account to be granted admin role; disable with zero address - constructor(uint256 _minDelay, address[] memory _proposers, address[] memory _executors, address _admin) - TimelockController(_minDelay, _proposers, _executors, _admin) - {} + constructor( + uint256 _minDelay, + address[] memory _proposers, + address[] memory _executors, + address _admin + ) TimelockController(_minDelay, _proposers, _executors, _admin) {} } diff --git a/contracts/src/upgrades/TimelockedUpgradeable.sol b/contracts/src/upgrades/TimelockedUpgradeable.sol index db7e1dfef..9dacdcf9a 100644 --- a/contracts/src/upgrades/TimelockedUpgradeable.sol +++ b/contracts/src/upgrades/TimelockedUpgradeable.sol @@ -1,15 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.16; -import {Versioned} from "src/upgrades/Versioned.sol"; +import {Versioned} from "./Versioned.sol"; import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; -import {AccessControlUpgradeable} from "@openzeppelin-upgradeable/contracts/access/AccessControlUpgradeable.sol"; +import {AccessControlUpgradeable} from + "@openzeppelin-upgradeable/contracts/access/AccessControlUpgradeable.sol"; /// @title TimelockedUpgradeable /// @notice A base contract that has modifiers to specify that certain functions are only callable /// by a sender with a timelock or guardian role. -abstract contract TimelockedUpgradeable is Versioned, Initializable, UUPSUpgradeable, AccessControlUpgradeable { +abstract contract TimelockedUpgradeable is + Versioned, + Initializable, + UUPSUpgradeable, + AccessControlUpgradeable +{ /// @notice A random constant used to identify addresses with the permission of a 'timelock'. /// @dev Should be set to a 'timelock' contract, which may only execute calls after being /// for a certain amount of time. @@ -43,7 +49,10 @@ abstract contract TimelockedUpgradeable is Versioned, Initializable, UUPSUpgrade /// @notice Initializes the contract. /// @dev The DEFAULT_ADMIN_ROLE needs to be set but should be unused. - function __TimelockedUpgradeable_init(address _timelock, address _guardian) internal onlyInitializing { + function __TimelockedUpgradeable_init(address _timelock, address _guardian) + internal + onlyInitializing + { __AccessControl_init(); __UUPSUpgradeable_init(); _grantRole(DEFAULT_ADMIN_ROLE, _timelock); diff --git a/contracts/test/FunctionGateway.t.sol b/contracts/test/FunctionGateway.t.sol index ff8cc8363..471551cf3 100644 --- a/contracts/test/FunctionGateway.t.sol +++ b/contracts/test/FunctionGateway.t.sol @@ -5,284 +5,4 @@ import "forge-std/Vm.sol"; import "forge-std/console.sol"; import "forge-std/Test.sol"; -import {FunctionGateway} from "src/FunctionGateway.sol"; -import { - FunctionRequest, - IFunctionGateway, - IFunctionGatewayEvents, - IFunctionGatewayErrors -} from "src/interfaces/IFunctionGateway.sol"; -import {IFunctionRegistry} from "src/interfaces/IFunctionRegistry.sol"; -import {TestConsumer, AttackConsumer, TestFunctionVerifier} from "test/TestUtils.sol"; -import {Proxy} from "src/upgrades/Proxy.sol"; -import {SuccinctFeeVault} from "src/payments/SuccinctFeeVault.sol"; - -contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayErrors { - // Example Function Request and expected values - string internal constant FUNCTION_NAME = "test-verifier"; - bytes32 internal FUNCTION_ID; - bytes internal constant REQUEST = bytes("functionInput"); - bytes4 internal constant CALLBACK_SELECTOR = TestConsumer.handleRequest.selector; - bytes internal constant CALLBACK_CONTEXT = abi.encode(0); - bytes internal constant REQUEST_OUTPUT = abi.encode(true); - bytes32 internal constant REQUEST_OUTPUT_HASH = sha256(REQUEST_OUTPUT); - bytes internal constant REQUEST_PROOF = hex""; - bytes32 internal EXPECTED_REQUEST_ID; - uint256 internal constant DEFAULT_FEE = 0.1 ether; - uint256 internal constant DEFAULT_SCALAR = 1; - - address internal timelock; - address internal guardian; - address payable internal sender; - address internal feeVault; - address internal gateway; - address internal verifier; - address payable internal consumer; - FunctionRequest internal expectedRequest; - - function setUp() public { - timelock = makeAddr("timelock"); - guardian = makeAddr("guardian"); - sender = payable(makeAddr("sender")); - feeVault = address(new SuccinctFeeVault(guardian)); - consumer = payable(address(new TestConsumer())); - - // Deploy FunctionGateway - address gatewayImpl = address(new FunctionGateway()); - gateway = address(new Proxy(gatewayImpl, "")); - FunctionGateway(gateway).initialize(DEFAULT_SCALAR, feeVault, timelock, guardian); - - vm.prank(sender); - (FUNCTION_ID, verifier) = - IFunctionRegistry(gateway).deployAndRegisterFunction(type(TestFunctionVerifier).creationCode, FUNCTION_NAME); - - expectedRequest = FunctionRequest({ - functionId: FUNCTION_ID, - inputHash: sha256(REQUEST), - outputHash: bytes32(0), - contextHash: keccak256(CALLBACK_CONTEXT), - callbackAddress: consumer, - callbackSelector: CALLBACK_SELECTOR, - proofFulfilled: false, - callbackFulfilled: false - }); - EXPECTED_REQUEST_ID = keccak256(abi.encode(FunctionGateway(gateway).nonce(), expectedRequest)); - - vm.deal(sender, DEFAULT_FEE); - vm.deal(consumer, DEFAULT_FEE); - } - - function test_Request_WhenFromCall() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Request - vm.expectEmit(true, true, true, true, gateway); - emit ProofRequested( - prevNonce, - FUNCTION_ID, - EXPECTED_REQUEST_ID, - REQUEST, - CALLBACK_CONTEXT, - FunctionGateway(gateway).DEFAULT_GAS_LIMIT(), - 0 - ); - vm.prank(consumer); - bytes32 requestId = FunctionGateway(gateway).request{value: DEFAULT_FEE}( - FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT - ); - assertEq(EXPECTED_REQUEST_ID, requestId); - - ( - bytes32 functionId, - bytes32 inputHash, - bytes32 outputHash, - bytes32 contextHash, - address callbackAddress, - bytes4 callbackSelector, - bool proofFulfilled, - bool callbackFulfilled - ) = FunctionGateway(gateway).requests(requestId); - assertEq(prevNonce + 1, FunctionGateway(gateway).nonce()); - assertEq(FUNCTION_ID, functionId); - assertEq(sha256(REQUEST), inputHash); - assertEq(bytes32(0), outputHash); - assertEq(keccak256(CALLBACK_CONTEXT), contextHash); - assertEq(address(consumer), callbackAddress); - assertEq(CALLBACK_SELECTOR, callbackSelector); - assertEq(false, proofFulfilled); - assertEq(false, callbackFulfilled); - } - - function test_Request_WhenFromContract() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Request - vm.prank(sender); - bytes32 requestId = TestConsumer(payable(address(consumer))).sendRequest{value: DEFAULT_FEE}( - address(gateway), FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT - ); - assertEq(EXPECTED_REQUEST_ID, requestId); - - ( - bytes32 functionId, - bytes32 inputHash, - bytes32 outputHash, - bytes32 contextHash, - address callbackAddress, - bytes4 callbackSelector, - bool proofFulfilled, - bool callbackFulfilled - ) = FunctionGateway(gateway).requests(requestId); - assertEq(prevNonce + 1, FunctionGateway(gateway).nonce()); - assertEq(FUNCTION_ID, functionId); - assertEq(sha256(REQUEST), inputHash); - assertEq(bytes32(0), outputHash); - assertEq(keccak256(CALLBACK_CONTEXT), contextHash); - assertEq(address(consumer), callbackAddress); - assertEq(CALLBACK_SELECTOR, callbackSelector); - assertEq(false, proofFulfilled); - assertEq(false, callbackFulfilled); - } - - function test_Request_WhenNoFee() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Request - vm.expectEmit(true, true, true, true, gateway); - emit ProofRequested( - prevNonce, - FUNCTION_ID, - EXPECTED_REQUEST_ID, - REQUEST, - CALLBACK_CONTEXT, - FunctionGateway(gateway).DEFAULT_GAS_LIMIT(), - 0 - ); - vm.prank(consumer); - bytes32 requestId = FunctionGateway(gateway).request(FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT); - assertEq(EXPECTED_REQUEST_ID, requestId); - - ( - bytes32 functionId, - bytes32 inputHash, - bytes32 outputHash, - bytes32 contextHash, - address callbackAddress, - bytes4 callbackSelector, - bool proofFulfilled, - bool callbackFulfilled - ) = FunctionGateway(gateway).requests(requestId); - assertEq(prevNonce + 1, FunctionGateway(gateway).nonce()); - assertEq(FUNCTION_ID, functionId); - assertEq(sha256(REQUEST), inputHash); - assertEq(bytes32(0), outputHash); - assertEq(keccak256(CALLBACK_CONTEXT), contextHash); - assertEq(address(consumer), callbackAddress); - assertEq(CALLBACK_SELECTOR, callbackSelector); - assertEq(false, proofFulfilled); - assertEq(false, callbackFulfilled); - } - - function test_Fulfill_WhenFromContract() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Request - vm.prank(sender); - bytes32 requestId = TestConsumer(payable(address(consumer))).sendRequest{value: DEFAULT_FEE}( - address(gateway), FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT - ); - - // Fulfill - vm.expectEmit(true, true, true, true, gateway); - emit ProofFulfilled(requestId, REQUEST_OUTPUT_HASH, REQUEST_PROOF); - FunctionGateway(gateway).fulfill(requestId, REQUEST_OUTPUT_HASH, REQUEST_PROOF); - - ( - bytes32 functionId, - bytes32 inputHash, - bytes32 outputHash, - bytes32 contextHash, - address callbackAddress, - bytes4 callbackSelector, - bool proofFulfilled, - bool callbackFulfilled - ) = FunctionGateway(gateway).requests(requestId); - assertEq(prevNonce + 1, FunctionGateway(gateway).nonce()); - assertEq(FUNCTION_ID, functionId); - assertEq(sha256(REQUEST), inputHash); - assertEq(REQUEST_OUTPUT_HASH, outputHash); - assertEq(keccak256(CALLBACK_CONTEXT), contextHash); - assertEq(address(consumer), callbackAddress); - assertEq(CALLBACK_SELECTOR, callbackSelector); - assertEq(true, proofFulfilled); - assertEq(false, callbackFulfilled); - } - - function test_Callback_WhenFromContract() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Request - vm.prank(sender); - bytes32 requestId = TestConsumer(payable(address(consumer))).sendRequest{value: DEFAULT_FEE}( - address(gateway), FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT - ); - - // Fulfill - FunctionGateway(gateway).fulfill(requestId, REQUEST_OUTPUT_HASH, REQUEST_PROOF); - - // Callback - vm.expectEmit(true, true, true, true, gateway); - emit CallbackFulfilled(requestId, REQUEST_OUTPUT, CALLBACK_CONTEXT); - FunctionGateway(gateway).callback(requestId, REQUEST_OUTPUT, CALLBACK_CONTEXT); - ( - bytes32 functionId, - bytes32 inputHash, - bytes32 outputHash, - bytes32 contextHash, - address callbackAddress, - bytes4 callbackSelector, - bool proofFulfilled, - bool callbackFulfilled - ) = FunctionGateway(gateway).requests(requestId); - assertEq(prevNonce + 1, FunctionGateway(gateway).nonce()); - assertEq(FUNCTION_ID, functionId); - assertEq(sha256(REQUEST), inputHash); - assertEq(REQUEST_OUTPUT_HASH, outputHash); - assertEq(keccak256(CALLBACK_CONTEXT), contextHash); - assertEq(address(consumer), callbackAddress); - assertEq(CALLBACK_SELECTOR, callbackSelector); - assertEq(true, proofFulfilled); - assertEq(true, callbackFulfilled); - } - - function test_RevertCallback_WhenReentered() public { - uint256 prevNonce = FunctionGateway(gateway).nonce(); - assertEq(prevNonce, 0); - - // Setup attack consumer - consumer = payable(address(new AttackConsumer())); - - // Request - vm.prank(sender); - bytes32 requestId = TestConsumer(payable(address(consumer))).sendRequest( - address(gateway), FUNCTION_ID, REQUEST, CALLBACK_SELECTOR, CALLBACK_CONTEXT - ); - - AttackConsumer(consumer).setCallbackParams(requestId, REQUEST_OUTPUT, CALLBACK_CONTEXT); - - // Fulfill - FunctionGateway(gateway).fulfill(requestId, REQUEST_OUTPUT_HASH, REQUEST_PROOF); - - // Callback - // inner error: vm.expectRevert(abi.encodeWithSelector(CallbackAlreadyFulfilled.selector, requestId)); - vm.expectRevert( - abi.encodeWithSelector(CallbackFailed.selector, consumer, AttackConsumer.handleRequest.selector) - ); - FunctionGateway(gateway).callback(requestId, REQUEST_OUTPUT, CALLBACK_CONTEXT); - } -} +contract FunctionGatewayTest is Test {} diff --git a/contracts/test/NounsOwnership.t.sol b/contracts/test/NounsOwnership.t.sol deleted file mode 100644 index 2c730609f..000000000 --- a/contracts/test/NounsOwnership.t.sol +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import "forge-std/Vm.sol"; -import "forge-std/console.sol"; -import "forge-std/Test.sol"; - -import {StorageOracle} from "../src/examples/storage/StorageOracle.sol"; -import {NounsOwnership} from "../src/examples/storage/NounsOwnership.sol"; -import {FunctionGateway} from "../src/FunctionGateway.sol"; - -import {IFunctionGatewayEvents, IFunctionGatewayErrors} from "../src/interfaces/IFunctionGateway.sol"; -import {MockFunctionGateway} from "../src/mocks/MockFunctionGateway.sol"; -import {Proxy} from "src/upgrades/Proxy.sol"; - -contract TestError is IFunctionGatewayErrors { - error InvalidL1BlockHash(); - error InvalidL1BlockNumber(); - error NotFromFunctionGateway(address sender); - error OutdatedBlockNumber(uint256 blockNumber, uint256 storedBlockNumber); -} - -contract TestEvents is IFunctionGatewayEvents { - event SlotRequested(uint256 indexed blockNumber, bytes32 indexed blockHash, address indexed account, uint256 slot); - event SlotUpdated(uint256 indexed blockNumber, address indexed account, uint256 slot, bytes32 value); -} - -contract NounsOwnershipTest is Test, TestError, TestEvents { - /// @dev Fork a specific block so that L1_Block.hash() returns a consistent value, and thus, proof input is the same. - uint256 internal constant BLOCK_NUMBER = 12345678; - /// @dev Nouns NFT contract: https://etherscan.io/address/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03 - address internal constant NOUNS_ACCOUNT = 0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03; - /// @dev Get slot key for noun40.eth's (0xa555d1Ee16780B2d414eD97f4f169c0740099615) NFT - // 1. `mapping(uint256 => address) private _owners` slot is #3 - // 2. tokenId is to check 40 - uint256 internal constant OWNERS_SLOT = 3; - uint256 internal constant NOUN_NUMBER = 40; - address internal constant NOUN_OWNER = 0xa555d1Ee16780B2d414eD97f4f169c0740099615; - uint256 internal constant SLOT = uint256(keccak256(abi.encode(NOUN_NUMBER, OWNERS_SLOT))); - bytes32 internal constant FUNCTION_ID = keccak256("STORAGE"); - - address internal timelock; - address internal guardian; - address internal gateway; - address internal storageOracle; - address internal nounsOwnership; - - bool internal skipTest; - - modifier onlyWithFork() { - if (skipTest) return; - _; - } - - function setUp() public { - try vm.envString("RPC_420") returns (string memory RPC_420) { - vm.createSelectFork(RPC_420, BLOCK_NUMBER); - } catch { - console.log("RPC_420 not set, skipping test"); - skipTest = true; - } - - timelock = makeAddr("timelock"); - guardian = makeAddr("guardian"); - gateway = address(new MockFunctionGateway()); - - // Deploy StorageOracle - address storageOracleImpl = address(new StorageOracle()); - storageOracle = address(new Proxy(storageOracleImpl, "")); - StorageOracle(storageOracle).initialize(address(gateway), FUNCTION_ID, timelock, guardian); - - nounsOwnership = address(new NounsOwnership(storageOracle)); - } - - function test_ClaimOwnership() public onlyWithFork { - bytes32 requestId = NounsOwnership(nounsOwnership).claimOwnership(NOUN_NUMBER); - - (bytes32 functionId,,,, address callbackAddress,, bool proofFulfilled, bool callbackFulfilled) = - FunctionGateway(gateway).requests(requestId); - assertEq(FUNCTION_ID, functionId); - assertEq(storageOracle, callbackAddress); - assertEq(false, proofFulfilled); - assertEq(false, callbackFulfilled); - } - - function test_OwnerOf() public onlyWithFork { - bytes32 requestId = NounsOwnership(nounsOwnership).claimOwnership(NOUN_NUMBER); - - bytes memory output = abi.encode(NOUN_OWNER); - bytes memory context = abi.encode(BLOCK_NUMBER, NOUNS_ACCOUNT, SLOT); - MockFunctionGateway(gateway).callback(requestId, output, context); - - address owner = NounsOwnership(nounsOwnership).ownerOf(NOUN_NUMBER); - assertEq(NOUN_OWNER, owner); - } - - function test_LastUpdateBlock() public onlyWithFork { - bytes32 requestId = NounsOwnership(nounsOwnership).claimOwnership(NOUN_NUMBER); - - bytes memory output = abi.encode(NOUN_OWNER); - bytes memory context = abi.encode(BLOCK_NUMBER, NOUNS_ACCOUNT, SLOT); - MockFunctionGateway(gateway).callback(requestId, output, context); - - uint256 blockNumber = NounsOwnership(nounsOwnership).lastUpdatedBlock(NOUN_NUMBER); - assertEq(BLOCK_NUMBER, blockNumber); - } - - function test_OwnerOf_WithFixture() public onlyWithFork { - bytes memory context = abi.encode(BLOCK_NUMBER, NOUNS_ACCOUNT, SLOT); - - // Use input and output from fixture - string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/test/fixtures/nouns-fixture.json"); - MockFunctionGateway(gateway).loadFixture(path); - NounsOwnership(nounsOwnership).claimOwnership(NOUN_NUMBER); - - address owner = NounsOwnership(nounsOwnership).ownerOf(NOUN_NUMBER); - assertEq(NOUN_OWNER, owner); - } -} - -// Nouns NFT contract: https://etherscan.io/address/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03 -// Storage Layout: -// | Name | Type | Slot | Offset | Bytes | Contract | -// |--------------------|-------------------------------------------------------------------------------|------|--------|-------|-------------------------------------| -// | _owner | address | 0 | 0 | 20 | contracts/NounsToken.sol:NounsToken | -// | _name | string | 1 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _symbol | string | 2 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _owners | mapping(uint256 => address) | 3 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _balances | mapping(address => uint256) | 4 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _tokenApprovals | mapping(uint256 => address) | 5 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _operatorApprovals | mapping(address => mapping(address => bool)) | 6 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _ownedTokens | mapping(address => mapping(uint256 => uint256)) | 7 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _ownedTokensIndex | mapping(uint256 => uint256) | 8 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _allTokens | uint256[] | 9 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _allTokensIndex | mapping(uint256 => uint256) | 10 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _delegates | mapping(address => address) | 11 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | checkpoints | mapping(address => mapping(uint32 => struct ERC721Checkpointable.Checkpoint)) | 12 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | numCheckpoints | mapping(address => uint32) | 13 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | nonces | mapping(address => uint256) | 14 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | noundersDAO | address | 15 | 0 | 20 | contracts/NounsToken.sol:NounsToken | -// | minter | address | 16 | 0 | 20 | contracts/NounsToken.sol:NounsToken | -// | descriptor | contract INounsDescriptorMinimal | 17 | 0 | 20 | contracts/NounsToken.sol:NounsToken | -// | seeder | contract INounsSeeder | 18 | 0 | 20 | contracts/NounsToken.sol:NounsToken | -// | isMinterLocked | bool | 18 | 20 | 1 | contracts/NounsToken.sol:NounsToken | -// | isDescriptorLocked | bool | 18 | 21 | 1 | contracts/NounsToken.sol:NounsToken | -// | isSeederLocked | bool | 18 | 22 | 1 | contracts/NounsToken.sol:NounsToken | -// | seeds | mapping(uint256 => struct INounsSeeder.Seed) | 19 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _currentNounId | uint256 | 20 | 0 | 32 | contracts/NounsToken.sol:NounsToken | -// | _contractURIHash | string | 21 | 0 | 32 | contracts/NounsToken.sol:NounsToken | diff --git a/contracts/test/TestUtils.sol b/contracts/test/TestUtils.sol deleted file mode 100644 index ee9e5a223..000000000 --- a/contracts/test/TestUtils.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import {FunctionGateway} from "src/FunctionGateway.sol"; -import {IFunctionVerifier} from "src/interfaces/IFunctionVerifier.sol"; - -library TestFuncLib { - function request( - address _gateway, - bytes32 _functionId, - bytes memory _input, - bytes4 _callbackSelector, - bytes memory _context - ) internal returns (bytes32) { - return FunctionGateway(_gateway).request{value: msg.value}(_functionId, _input, _callbackSelector, _context); - } - - function decode(bytes memory _output) internal pure returns (bool) { - return abi.decode(_output, (bool)); - } -} - -contract TestConsumer { - uint256 public nonce; - - error NotVerified(); - error InvalidRequestNonce(uint256 expectedNonce, uint256 actualNonce); - - function sendRequest( - address _gateway, - bytes32 _functionId, - bytes memory _input, - bytes4 _callbackSelector, - bytes memory _context - ) external payable returns (bytes32) { - return TestFuncLib.request(_gateway, _functionId, _input, _callbackSelector, _context); - } - - function handleRequest(bytes memory _output, bytes memory _context) external { - bool verified = TestFuncLib.decode(_output); - if (!verified) { - revert NotVerified(); - } - - uint256 requestNonce = abi.decode(_context, (uint256)); - if (requestNonce != nonce) { - revert InvalidRequestNonce(nonce, requestNonce); - } - - ++nonce; - } - - receive() external payable {} -} - -// Attempts re-entry into the gateway contract. -contract AttackConsumer { - uint256 public nonce; - - bytes32 public requestId; - bytes public output; - bytes public context; - - error NotVerified(); - error InvalidRequestNonce(uint256 expectedNonce, uint256 actualNonce); - - function sendRequest( - address _gateway, - bytes32 _functionId, - bytes memory _input, - bytes4 _callbackSelector, - bytes memory _context - ) external payable returns (bytes32) { - return TestFuncLib.request(_gateway, _functionId, _input, _callbackSelector, _context); - } - - function setCallbackParams(bytes32 _requestId, bytes memory _output, bytes memory _proof) external { - requestId = _requestId; - output = _output; - context = _proof; - } - - function handleRequest(bytes memory, bytes memory) external { - FunctionGateway(msg.sender).callback(requestId, output, context); - } - - receive() external payable {} -} - -contract TestFunctionVerifier is IFunctionVerifier { - function verificationKeyHash() external pure returns (bytes32) { - return keccak256("verificationKeyHash"); - } - - function verify(bytes32, bytes32, bytes memory) external pure returns (bool) { - return true; - } -} diff --git a/contracts/test/fixtures/nouns-fixture.json b/contracts/test/fixtures/nouns-fixture.json deleted file mode 100644 index d596de1c9..000000000 --- a/contracts/test/fixtures/nouns-fixture.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "input": "0xb99c7a251e3880d3560ff23412f4b880c196c252a791a4667694447892c051a70000000000000000000000009c8ff314c9bc7f6e59a9d9225fb22946427edc03f787d5ff306ee7ea1d7b35b5cacd5a837646921c113945dbc3a3b6329ce40033", - "output": "0x000000000000000000000000a555d1Ee16780B2d414eD97f4f169c0740099615" -} \ No newline at end of file diff --git a/contracts/test/libraries/OutputReader.t.sol b/contracts/test/libraries/OutputReader.t.sol index ffe328fa8..d784588bd 100644 --- a/contracts/test/libraries/OutputReader.t.sol +++ b/contracts/test/libraries/OutputReader.t.sol @@ -5,7 +5,7 @@ import "forge-std/Vm.sol"; import "forge-std/console.sol"; import "forge-std/Test.sol"; -import {OutputReader} from "src/libraries/OutputReader.sol"; +import {OutputReader} from "../../src/libraries/OutputReader.sol"; contract OutputReaderTest is Test { function setUp() public {}