Commit a266b32d authored by John Doe's avatar John Doe
Browse files

monorepo.

parent 85ec022c

Too many changes to show.

To preserve performance only 360 of 360+ files are displayed.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IElasticERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
// Additional mint and burn functions
function burn(address account, uint256 amount) external;
function mint(address account, uint256 amount) external;
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
struct Payment {
bytes32 recipient;
uint256 withdrawalAmount;
uint256 swapAmount;
uint160 price_limit;
}
struct Deposit {
bytes signature;
bytes32 pi_hash;
}
struct DepositHash {
bytes32 pi_hash;
}
struct EncryptedUTXO {
bytes32 secret;
bytes32 amount;
bytes32 data;
}
struct Transaction {
bytes32 current_root;
bytes32 utxo_root;
bytes32 price_limit;
bytes32 timestamp;
bytes32 deadline;
bytes32 amount;
EncryptedUTXO[16] encrypted_utxo;
bytes32[16] withdrawals;
bytes32[16] commitments_in;
bytes32[16] commitments;
bytes32[16] recipients;
bytes32[16] nullifier_hashes;
bytes32[16] uids;
bytes32[16] swap_amounts;
Deposit deposit;
}
struct TransactionWithProof {
Transaction transaction;
bytes32[93] proof;
bytes proofU8;
}
struct Batch {
bytes32 tx_key_hash;
bytes32 recursive_key_hash;
bytes32 new_root;
bytes32 old_hist_root;
bytes32 new_hist_root;
bytes32 oracle;
bytes32[20] historic_path;
bytes32[16] aggregation_object;
Transaction[] transactions;
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract XFTMock is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor(string memory _vk) ERC20(_vk, _vk) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public {
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
_mint(to, amount);
}
function burn(address from, uint256 amount) public {
require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
_burn(from, amount);
}
}
\ No newline at end of file
// Verification Key Hash: 066e0b06b13ec4c45dbeb0b625dbfab874ecdc411cdb43bae4346e880b09e16c
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
library UltraVerificationKey {
function verificationKeyHash() internal pure returns(bytes32) {
return 0x066e0b06b13ec4c45dbeb0b625dbfab874ecdc411cdb43bae4346e880b09e16c;
}
function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure {
assembly {
mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000080000) // vk.circuit_size
mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000011) // vk.num_inputs
mstore(add(_vk, 0x40), 0x2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd) // vk.work_root
mstore(add(_vk, 0x60), 0x3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201) // vk.domain_inverse
mstore(add(_vk, 0x80), 0x174c5c19a31444028bb6368ff05acd095502e7d022af18c746a364dab4c7346e) // vk.Q1.x
mstore(add(_vk, 0xa0), 0x2e6d6b74f09bdcf37e2be6f0a05552f34840f8693d1c73edce05639d0698ea36) // vk.Q1.y
mstore(add(_vk, 0xc0), 0x243678b1fa4062f90887453567ef78c256421a5907513427b5c52aa3cb1e6a17) // vk.Q2.x
mstore(add(_vk, 0xe0), 0x10425e273dc8dd43dbb983a295f7f990b9475fca570f56f1d7f972166e5bf0d8) // vk.Q2.y
mstore(add(_vk, 0x100), 0x23b6836088c547e90ddfdb8146a5f7b3608aad75f4d0d5a050f119e6eea92c22) // vk.Q3.x
mstore(add(_vk, 0x120), 0x0cafbde5fed53b2d75343797c5718363f54c3b391730f7b49a8ae09cd79b8b20) // vk.Q3.y
mstore(add(_vk, 0x140), 0x06acf52ef347e83eae253c24631c08d66d76669d97c0cce87b0320363e0188bd) // vk.Q4.x
mstore(add(_vk, 0x160), 0x1f8257e9bfac704d9b0171c74f4f35ec94c608ae5ff55962db5bc4fd9e1aba89) // vk.Q4.y
mstore(add(_vk, 0x180), 0x11931c54c38fba5131f7a317e782a12a8e3e3726dc2eaa490049ce2ca8ca1cbb) // vk.Q_M.x
mstore(add(_vk, 0x1a0), 0x0565f2552174d90b7b0e9645969affd0015d483dffec9d93ede1b63be6dc3c16) // vk.Q_M.y
mstore(add(_vk, 0x1c0), 0x0bca92d0bac2e3d19be0265337ec7f4574f8195e313af04d651f342bb3159fb8) // vk.Q_C.x
mstore(add(_vk, 0x1e0), 0x1ecb4bbed3bfe4b4e91afe37b7871694eda583ff1d1111be20aa9eb8edf50a77) // vk.Q_C.y
mstore(add(_vk, 0x200), 0x0c1be01afc0073d9a1f1e23184a1d005a89d087265ea3a85f523ecd062212260) // vk.Q_ARITHMETIC.x
mstore(add(_vk, 0x220), 0x25a9e732ab54e2ebe8d031c166997a26a5d5dc143dc246b4b046b170406da4f1) // vk.Q_ARITHMETIC.y
mstore(add(_vk, 0x240), 0x2498b2e14394f700cac156f42ef63ba194ed99764e03f2e433cb0be94d0fc89f) // vk.QSORT.x
mstore(add(_vk, 0x260), 0x206103c2ff58564c9a4746efd3fff7637a46052eb129af96844c2d850d11d8f3) // vk.QSORT.y
mstore(add(_vk, 0x280), 0x212dfe13df5b9cfb846570ecf8269ed3648c24827e7dceafdeca6235e1dd5e38) // vk.Q_ELLIPTIC.x
mstore(add(_vk, 0x2a0), 0x284d15304c372e9c33d76628c66bd6c48ef526d08146e9b401dc7d1e373ccd3a) // vk.Q_ELLIPTIC.y
mstore(add(_vk, 0x2c0), 0x126a3a8e4c678d476c3c864cde50f9776d880443976f62a438b9db897b433ffa) // vk.Q_AUX.x
mstore(add(_vk, 0x2e0), 0x2866186a79faf6dde910250342628d6a7a1c6d32f88b1991b276852874bd73dc) // vk.Q_AUX.y
mstore(add(_vk, 0x300), 0x2bd3d50520f90ab39921a0f2ce6c8f5d5cc8a989720e939dbb4a8951d878f579) // vk.SIGMA1.x
mstore(add(_vk, 0x320), 0x0b5652d8de0be47d4a6496260931f15984d6cc0eb21e61c15991554eee8bfe91) // vk.SIGMA1.y
mstore(add(_vk, 0x340), 0x135cdf64147af6820b82eff62e1e62efb848f20c81dffc2ed36cc4d3ef9d6ee3) // vk.SIGMA2.x
mstore(add(_vk, 0x360), 0x16ce92a14e25ca8f353781ad6bf4c105139a2029c682b0e62606b721e9d8c848) // vk.SIGMA2.y
mstore(add(_vk, 0x380), 0x0c9177240c1a64d39bbdc9113347b71c8ad5899a9a617b70450e43ca0f08c0b9) // vk.SIGMA3.x
mstore(add(_vk, 0x3a0), 0x26486bfd9b1989300948793829dda7ebd3c783b85ef797363b15bdf9932d624f) // vk.SIGMA3.y
mstore(add(_vk, 0x3c0), 0x0933b476f487753e6b6a6c07579f099b3bd03e1d4fad080cdf864233e378e6f7) // vk.SIGMA4.x
mstore(add(_vk, 0x3e0), 0x19fa964521f482600ad8f48f2d74204aa35665cefec49c73255bcd7bb9cf1d2e) // vk.SIGMA4.y
mstore(add(_vk, 0x400), 0x1cadeb85ec1d913dbbebd04a2e9c216fffb782a908252de688cb3f87a9960239) // vk.TABLE1.x
mstore(add(_vk, 0x420), 0x140ce35846522600428e89cedfc30ef4f906e759e4b262f982f41b11e232bef0) // vk.TABLE1.y
mstore(add(_vk, 0x440), 0x186c47255bd1cac8572abe17f302a4a778dc042d05b5ad10ec72ccf87db72cbd) // vk.TABLE2.x
mstore(add(_vk, 0x460), 0x2696b8ea9abe2e21817826ff8ffdd70455306100f722aee5047d7b61d23be470) // vk.TABLE2.y
mstore(add(_vk, 0x480), 0x203c78601f28e6739d2031447af9f5015f00e4a92298c8e0af4906929ce4aa23) // vk.TABLE3.x
mstore(add(_vk, 0x4a0), 0x0b82d8af3657b8dacf80c03e267415e27c1b22c5b110041433f5f7a0077d6163) // vk.TABLE3.y
mstore(add(_vk, 0x4c0), 0x1ca5d2fc76408b3d82cd070612b1f00b2910e92b606943f0a66d4064ac05d6f3) // vk.TABLE4.x
mstore(add(_vk, 0x4e0), 0x12889680e44151655a6fb88176f0eb0e09e60039ec5cbc978bcb081ca4d41c1e) // vk.TABLE4.y
mstore(add(_vk, 0x500), 0x269c3c1e6be15df4cd6e18678103fb6a22fc4e79890d0e7d18259353640bf580) // vk.TABLE_TYPE.x
mstore(add(_vk, 0x520), 0x073ef16b8836c3c34ac6e02937c25aaf47728e8aaa50eea7a2a7e2d87b2a051c) // vk.TABLE_TYPE.y
mstore(add(_vk, 0x540), 0x0642a0979a2c84f7f2c1ae9c5285e8b54bc1a57e51ec50588ec003e5ccaa1ef0) // vk.ID1.x
mstore(add(_vk, 0x560), 0x10759f71ebeb64070c9bde2e55126711646725544ef4273f9a391b9b9b0af13d) // vk.ID1.y
mstore(add(_vk, 0x580), 0x05107532383658086d131205993b32158da27ddcf4a41708cf50e4e6ca986205) // vk.ID2.x
mstore(add(_vk, 0x5a0), 0x1c67ba6b417eddf6928c56e1fece6ba9c73c4579c446ff0bd86e794121b4957c) // vk.ID2.y
mstore(add(_vk, 0x5c0), 0x030c58a082fe2a8a256a3c7596ca585a024396e2377db7e854bf7f14514759b8) // vk.ID3.x
mstore(add(_vk, 0x5e0), 0x1606bd0cae6fbb354de28025f6deb12a003c43b7dafa272c94b35c752ceaa696) // vk.ID3.y
mstore(add(_vk, 0x600), 0x1e1a0bd62a0746fb76e5d6727d5db536662c58d163cf29375fff76b669856e5f) // vk.ID4.x
mstore(add(_vk, 0x620), 0x0f786242d4fd388fd0e83bc4074cef1a36162d3403af146af654e72acb72e4c6) // vk.ID4.y
mstore(add(_vk, 0x640), 0x01) // vk.contains_recursive_proof
mstore(add(_vk, 0x660), 1) // vk.recursive_proof_public_input_indices
mstore(add(_vk, 0x680), 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1) // vk.g2_x.X.c1
mstore(add(_vk, 0x6a0), 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0) // vk.g2_x.X.c0
mstore(add(_vk, 0x6c0), 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4) // vk.g2_x.Y.c1
mstore(add(_vk, 0x6e0), 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) // vk.g2_x.Y.c0
mstore(_omegaInverseLoc, 0x06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321) // vk.work_root_inverse
}
}
}
/**
* @title Ultra Plonk proof verification contract
* @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified
*/
abstract contract BaseUltraVerifier {
// VERIFICATION KEY MEMORY LOCATIONS
uint256 internal constant N_LOC = 0x380;
uint256 internal constant NUM_INPUTS_LOC = 0x3a0;
uint256 internal constant OMEGA_LOC = 0x3c0;
uint256 internal constant DOMAIN_INVERSE_LOC = 0x3e0;
uint256 internal constant Q1_X_LOC = 0x400;
uint256 internal constant Q1_Y_LOC = 0x420;
uint256 internal constant Q2_X_LOC = 0x440;
uint256 internal constant Q2_Y_LOC = 0x460;
uint256 internal constant Q3_X_LOC = 0x480;
uint256 internal constant Q3_Y_LOC = 0x4a0;
uint256 internal constant Q4_X_LOC = 0x4c0;
uint256 internal constant Q4_Y_LOC = 0x4e0;
uint256 internal constant QM_X_LOC = 0x500;
uint256 internal constant QM_Y_LOC = 0x520;
uint256 internal constant QC_X_LOC = 0x540;
uint256 internal constant QC_Y_LOC = 0x560;
uint256 internal constant QARITH_X_LOC = 0x580;
uint256 internal constant QARITH_Y_LOC = 0x5a0;
uint256 internal constant QSORT_X_LOC = 0x5c0;
uint256 internal constant QSORT_Y_LOC = 0x5e0;
uint256 internal constant QELLIPTIC_X_LOC = 0x600;
uint256 internal constant QELLIPTIC_Y_LOC = 0x620;
uint256 internal constant QAUX_X_LOC = 0x640;
uint256 internal constant QAUX_Y_LOC = 0x660;
uint256 internal constant SIGMA1_X_LOC = 0x680;
uint256 internal constant SIGMA1_Y_LOC = 0x6a0;
uint256 internal constant SIGMA2_X_LOC = 0x6c0;
uint256 internal constant SIGMA2_Y_LOC = 0x6e0;
uint256 internal constant SIGMA3_X_LOC = 0x700;
uint256 internal constant SIGMA3_Y_LOC = 0x720;
uint256 internal constant SIGMA4_X_LOC = 0x740;
uint256 internal constant SIGMA4_Y_LOC = 0x760;
uint256 internal constant TABLE1_X_LOC = 0x780;
uint256 internal constant TABLE1_Y_LOC = 0x7a0;
uint256 internal constant TABLE2_X_LOC = 0x7c0;
uint256 internal constant TABLE2_Y_LOC = 0x7e0;
uint256 internal constant TABLE3_X_LOC = 0x800;
uint256 internal constant TABLE3_Y_LOC = 0x820;
uint256 internal constant TABLE4_X_LOC = 0x840;
uint256 internal constant TABLE4_Y_LOC = 0x860;
uint256 internal constant TABLE_TYPE_X_LOC = 0x880;
uint256 internal constant TABLE_TYPE_Y_LOC = 0x8a0;
uint256 internal constant ID1_X_LOC = 0x8c0;
uint256 internal constant ID1_Y_LOC = 0x8e0;
uint256 internal constant ID2_X_LOC = 0x900;
uint256 internal constant ID2_Y_LOC = 0x920;
uint256 internal constant ID3_X_LOC = 0x940;
uint256 internal constant ID3_Y_LOC = 0x960;
uint256 internal constant ID4_X_LOC = 0x980;
uint256 internal constant ID4_Y_LOC = 0x9a0;
uint256 internal constant CONTAINS_RECURSIVE_PROOF_LOC = 0x9c0;
uint256 internal constant RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC = 0x9e0;
uint256 internal constant G2X_X0_LOC = 0xa00;
uint256 internal constant G2X_X1_LOC = 0xa20;
uint256 internal constant G2X_Y0_LOC = 0xa40;
uint256 internal constant G2X_Y1_LOC = 0xa60;
// ### PROOF DATA MEMORY LOCATIONS
uint256 internal constant W1_X_LOC = 0x1200;
uint256 internal constant W1_Y_LOC = 0x1220;
uint256 internal constant W2_X_LOC = 0x1240;
uint256 internal constant W2_Y_LOC = 0x1260;
uint256 internal constant W3_X_LOC = 0x1280;
uint256 internal constant W3_Y_LOC = 0x12a0;
uint256 internal constant W4_X_LOC = 0x12c0;
uint256 internal constant W4_Y_LOC = 0x12e0;
uint256 internal constant S_X_LOC = 0x1300;
uint256 internal constant S_Y_LOC = 0x1320;
uint256 internal constant Z_X_LOC = 0x1340;
uint256 internal constant Z_Y_LOC = 0x1360;
uint256 internal constant Z_LOOKUP_X_LOC = 0x1380;
uint256 internal constant Z_LOOKUP_Y_LOC = 0x13a0;
uint256 internal constant T1_X_LOC = 0x13c0;
uint256 internal constant T1_Y_LOC = 0x13e0;
uint256 internal constant T2_X_LOC = 0x1400;
uint256 internal constant T2_Y_LOC = 0x1420;
uint256 internal constant T3_X_LOC = 0x1440;
uint256 internal constant T3_Y_LOC = 0x1460;
uint256 internal constant T4_X_LOC = 0x1480;
uint256 internal constant T4_Y_LOC = 0x14a0;
uint256 internal constant W1_EVAL_LOC = 0x1600;
uint256 internal constant W2_EVAL_LOC = 0x1620;
uint256 internal constant W3_EVAL_LOC = 0x1640;
uint256 internal constant W4_EVAL_LOC = 0x1660;
uint256 internal constant S_EVAL_LOC = 0x1680;
uint256 internal constant Z_EVAL_LOC = 0x16a0;
uint256 internal constant Z_LOOKUP_EVAL_LOC = 0x16c0;
uint256 internal constant Q1_EVAL_LOC = 0x16e0;
uint256 internal constant Q2_EVAL_LOC = 0x1700;
uint256 internal constant Q3_EVAL_LOC = 0x1720;
uint256 internal constant Q4_EVAL_LOC = 0x1740;
uint256 internal constant QM_EVAL_LOC = 0x1760;
uint256 internal constant QC_EVAL_LOC = 0x1780;
uint256 internal constant QARITH_EVAL_LOC = 0x17a0;
uint256 internal constant QSORT_EVAL_LOC = 0x17c0;
uint256 internal constant QELLIPTIC_EVAL_LOC = 0x17e0;
uint256 internal constant QAUX_EVAL_LOC = 0x1800;
uint256 internal constant TABLE1_EVAL_LOC = 0x1840;
uint256 internal constant TABLE2_EVAL_LOC = 0x1860;
uint256 internal constant TABLE3_EVAL_LOC = 0x1880;
uint256 internal constant TABLE4_EVAL_LOC = 0x18a0;
uint256 internal constant TABLE_TYPE_EVAL_LOC = 0x18c0;
uint256 internal constant ID1_EVAL_LOC = 0x18e0;
uint256 internal constant ID2_EVAL_LOC = 0x1900;
uint256 internal constant ID3_EVAL_LOC = 0x1920;
uint256 internal constant ID4_EVAL_LOC = 0x1940;
uint256 internal constant SIGMA1_EVAL_LOC = 0x1960;
uint256 internal constant SIGMA2_EVAL_LOC = 0x1980;
uint256 internal constant SIGMA3_EVAL_LOC = 0x19a0;
uint256 internal constant SIGMA4_EVAL_LOC = 0x19c0;
uint256 internal constant W1_OMEGA_EVAL_LOC = 0x19e0;
uint256 internal constant W2_OMEGA_EVAL_LOC = 0x2000;
uint256 internal constant W3_OMEGA_EVAL_LOC = 0x2020;
uint256 internal constant W4_OMEGA_EVAL_LOC = 0x2040;
uint256 internal constant S_OMEGA_EVAL_LOC = 0x2060;
uint256 internal constant Z_OMEGA_EVAL_LOC = 0x2080;
uint256 internal constant Z_LOOKUP_OMEGA_EVAL_LOC = 0x20a0;
uint256 internal constant TABLE1_OMEGA_EVAL_LOC = 0x20c0;
uint256 internal constant TABLE2_OMEGA_EVAL_LOC = 0x20e0;
uint256 internal constant TABLE3_OMEGA_EVAL_LOC = 0x2100;
uint256 internal constant TABLE4_OMEGA_EVAL_LOC = 0x2120;
uint256 internal constant PI_Z_X_LOC = 0x2300;
uint256 internal constant PI_Z_Y_LOC = 0x2320;
uint256 internal constant PI_Z_OMEGA_X_LOC = 0x2340;
uint256 internal constant PI_Z_OMEGA_Y_LOC = 0x2360;
// Used for elliptic widget. These are alias names for wire + shifted wire evaluations
uint256 internal constant X1_EVAL_LOC = W2_EVAL_LOC;
uint256 internal constant X2_EVAL_LOC = W1_OMEGA_EVAL_LOC;
uint256 internal constant X3_EVAL_LOC = W2_OMEGA_EVAL_LOC;
uint256 internal constant Y1_EVAL_LOC = W3_EVAL_LOC;
uint256 internal constant Y2_EVAL_LOC = W4_OMEGA_EVAL_LOC;
uint256 internal constant Y3_EVAL_LOC = W3_OMEGA_EVAL_LOC;
uint256 internal constant QBETA_LOC = Q3_EVAL_LOC;
uint256 internal constant QBETA_SQR_LOC = Q4_EVAL_LOC;
uint256 internal constant QSIGN_LOC = Q1_EVAL_LOC;
// ### CHALLENGES MEMORY OFFSETS
uint256 internal constant C_BETA_LOC = 0x2600;
uint256 internal constant C_GAMMA_LOC = 0x2620;
uint256 internal constant C_ALPHA_LOC = 0x2640;
uint256 internal constant C_ETA_LOC = 0x2660;
uint256 internal constant C_ETA_SQR_LOC = 0x2680;
uint256 internal constant C_ETA_CUBE_LOC = 0x26a0;
uint256 internal constant C_ZETA_LOC = 0x26c0;
uint256 internal constant C_CURRENT_LOC = 0x26e0;
uint256 internal constant C_V0_LOC = 0x2700;
uint256 internal constant C_V1_LOC = 0x2720;
uint256 internal constant C_V2_LOC = 0x2740;
uint256 internal constant C_V3_LOC = 0x2760;
uint256 internal constant C_V4_LOC = 0x2780;
uint256 internal constant C_V5_LOC = 0x27a0;
uint256 internal constant C_V6_LOC = 0x27c0;
uint256 internal constant C_V7_LOC = 0x27e0;
uint256 internal constant C_V8_LOC = 0x2800;
uint256 internal constant C_V9_LOC = 0x2820;
uint256 internal constant C_V10_LOC = 0x2840;
uint256 internal constant C_V11_LOC = 0x2860;
uint256 internal constant C_V12_LOC = 0x2880;
uint256 internal constant C_V13_LOC = 0x28a0;
uint256 internal constant C_V14_LOC = 0x28c0;
uint256 internal constant C_V15_LOC = 0x28e0;
uint256 internal constant C_V16_LOC = 0x2900;
uint256 internal constant C_V17_LOC = 0x2920;
uint256 internal constant C_V18_LOC = 0x2940;
uint256 internal constant C_V19_LOC = 0x2960;
uint256 internal constant C_V20_LOC = 0x2980;
uint256 internal constant C_V21_LOC = 0x29a0;
uint256 internal constant C_V22_LOC = 0x29c0;
uint256 internal constant C_V23_LOC = 0x29e0;
uint256 internal constant C_V24_LOC = 0x2a00;
uint256 internal constant C_V25_LOC = 0x2a20;
uint256 internal constant C_V26_LOC = 0x2a40;
uint256 internal constant C_V27_LOC = 0x2a60;
uint256 internal constant C_V28_LOC = 0x2a80;
uint256 internal constant C_V29_LOC = 0x2aa0;
uint256 internal constant C_V30_LOC = 0x2ac0;
uint256 internal constant C_U_LOC = 0x2b00;
// ### LOCAL VARIABLES MEMORY OFFSETS
uint256 internal constant DELTA_NUMERATOR_LOC = 0x3000;
uint256 internal constant DELTA_DENOMINATOR_LOC = 0x3020;
uint256 internal constant ZETA_POW_N_LOC = 0x3040;
uint256 internal constant PUBLIC_INPUT_DELTA_LOC = 0x3060;
uint256 internal constant ZERO_POLY_LOC = 0x3080;
uint256 internal constant L_START_LOC = 0x30a0;
uint256 internal constant L_END_LOC = 0x30c0;
uint256 internal constant R_ZERO_EVAL_LOC = 0x30e0;
uint256 internal constant PLOOKUP_DELTA_NUMERATOR_LOC = 0x3100;
uint256 internal constant PLOOKUP_DELTA_DENOMINATOR_LOC = 0x3120;
uint256 internal constant PLOOKUP_DELTA_LOC = 0x3140;
uint256 internal constant ACCUMULATOR_X_LOC = 0x3160;
uint256 internal constant ACCUMULATOR_Y_LOC = 0x3180;
uint256 internal constant ACCUMULATOR2_X_LOC = 0x31a0;
uint256 internal constant ACCUMULATOR2_Y_LOC = 0x31c0;
uint256 internal constant PAIRING_LHS_X_LOC = 0x31e0;
uint256 internal constant PAIRING_LHS_Y_LOC = 0x3200;
uint256 internal constant PAIRING_RHS_X_LOC = 0x3220;
uint256 internal constant PAIRING_RHS_Y_LOC = 0x3240;
// misc stuff
uint256 internal constant OMEGA_INVERSE_LOC = 0x3300;
uint256 internal constant C_ALPHA_SQR_LOC = 0x3320;
uint256 internal constant C_ALPHA_CUBE_LOC = 0x3340;
uint256 internal constant C_ALPHA_QUAD_LOC = 0x3360;
uint256 internal constant C_ALPHA_BASE_LOC = 0x3380;
// ### RECURSION VARIABLE MEMORY LOCATIONS
uint256 internal constant RECURSIVE_P1_X_LOC = 0x3400;
uint256 internal constant RECURSIVE_P1_Y_LOC = 0x3420;
uint256 internal constant RECURSIVE_P2_X_LOC = 0x3440;
uint256 internal constant RECURSIVE_P2_Y_LOC = 0x3460;
uint256 internal constant PUBLIC_INPUTS_HASH_LOCATION = 0x3480;
// sub-identity storage
uint256 internal constant PERMUTATION_IDENTITY = 0x3500;
uint256 internal constant PLOOKUP_IDENTITY = 0x3520;
uint256 internal constant ARITHMETIC_IDENTITY = 0x3540;
uint256 internal constant SORT_IDENTITY = 0x3560;
uint256 internal constant ELLIPTIC_IDENTITY = 0x3580;
uint256 internal constant AUX_IDENTITY = 0x35a0;
uint256 internal constant AUX_NON_NATIVE_FIELD_EVALUATION = 0x35c0;
uint256 internal constant AUX_LIMB_ACCUMULATOR_EVALUATION = 0x35e0;
uint256 internal constant AUX_RAM_CONSISTENCY_EVALUATION = 0x3600;
uint256 internal constant AUX_ROM_CONSISTENCY_EVALUATION = 0x3620;
uint256 internal constant AUX_MEMORY_EVALUATION = 0x3640;
uint256 internal constant QUOTIENT_EVAL_LOC = 0x3660;
uint256 internal constant ZERO_POLY_INVERSE_LOC = 0x3680;
// when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time
uint256 internal constant NU_CHALLENGE_INPUT_LOC_A = 0x36a0;
uint256 internal constant NU_CHALLENGE_INPUT_LOC_B = 0x36c0;
uint256 internal constant NU_CHALLENGE_INPUT_LOC_C = 0x36e0;
bytes4 internal constant INVALID_VERIFICATION_KEY_SELECTOR = 0x7e5769bf;
bytes4 internal constant POINT_NOT_ON_CURVE_SELECTOR = 0xa3dad654;
bytes4 internal constant PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR = 0xeba9f4a6;
bytes4 internal constant PUBLIC_INPUT_GE_P_SELECTOR = 0x374a972f;
bytes4 internal constant MOD_EXP_FAILURE_SELECTOR = 0xf894a7bc;
bytes4 internal constant PAIRING_PREAMBLE_FAILED_SELECTOR = 0x01882d81;
bytes4 internal constant OPENING_COMMITMENT_FAILED_SELECTOR = 0x4e719763;
bytes4 internal constant PAIRING_FAILED_SELECTOR = 0xd71fd263;
uint256 internal constant ETA_INPUT_LENGTH = 0xc0; // W1, W2, W3 = 6 * 0x20 bytes
// We need to hash 41 field elements when generating the NU challenge
// w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14)
// qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7)
// table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9)
// w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7)
// table1_omega, table2_omega, table3_omega, table4_omega (4)
uint256 internal constant NU_INPUT_LENGTH = 0x520; // 0x520 = 41 * 0x20
// There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over
// W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4
uint256 internal constant NU_CALLDATA_SKIP_LENGTH = 0x2c0; // 11 * 0x40 = 0x2c0
uint256 internal constant NEGATIVE_INVERSE_OF_2_MODULO_P =
0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68
uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14
// y^2 = x^3 + ax + b
// for Grumpkin, a = 0 and b = -17. We use b in a custom gate relation that evaluates elliptic curve arithmetic
uint256 internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = 17;
error INVALID_VERIFICATION_KEY();
error POINT_NOT_ON_CURVE();
error PUBLIC_INPUT_COUNT_INVALID(uint256 expected, uint256 actual);
error PUBLIC_INPUT_INVALID_BN128_G1_POINT();
error PUBLIC_INPUT_GE_P();
error MOD_EXP_FAILURE();
error PAIRING_PREAMBLE_FAILED();
error OPENING_COMMITMENT_FAILED();
error PAIRING_FAILED();
function getVerificationKeyHash() public pure virtual returns (bytes32);
/**
* @dev We assume that the verification key loaded by this function is constant as we only verify it on deployment
*/
function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure virtual;
constructor() {
loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC);
// We verify that all of the EC points in the verification key lie on the bn128 curve.
assembly {
let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order
let success := 1
// VALIDATE Q1
{
let x := mload(Q1_X_LOC)
let y := mload(Q1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE Q2
{
let x := mload(Q2_X_LOC)
let y := mload(Q2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE Q3
{
let x := mload(Q3_X_LOC)
let y := mload(Q3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE Q4
{
let x := mload(Q4_X_LOC)
let y := mload(Q4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
mstore(0x00, x)
mstore(0x20, y)
}
// VALIDATE QM
{
let x := mload(QM_X_LOC)
let y := mload(QM_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE QC
{
let x := mload(QC_X_LOC)
let y := mload(QC_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE QARITH
{
let x := mload(QARITH_X_LOC)
let y := mload(QARITH_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE QSORT
{
let x := mload(QSORT_X_LOC)
let y := mload(QSORT_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE QELLIPTIC
{
let x := mload(QELLIPTIC_X_LOC)
let y := mload(QELLIPTIC_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE QAUX
{
let x := mload(QAUX_X_LOC)
let y := mload(QAUX_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE SIGMA1
{
let x := mload(SIGMA1_X_LOC)
let y := mload(SIGMA1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE SIGMA2
{
let x := mload(SIGMA2_X_LOC)
let y := mload(SIGMA2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE SIGMA3
{
let x := mload(SIGMA3_X_LOC)
let y := mload(SIGMA3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE SIGMA4
{
let x := mload(SIGMA4_X_LOC)
let y := mload(SIGMA4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE TABLE1
{
let x := mload(TABLE1_X_LOC)
let y := mload(TABLE1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE TABLE2
{
let x := mload(TABLE2_X_LOC)
let y := mload(TABLE2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE TABLE3
{
let x := mload(TABLE3_X_LOC)
let y := mload(TABLE3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE TABLE4
{
let x := mload(TABLE4_X_LOC)
let y := mload(TABLE4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE TABLE_TYPE
{
let x := mload(TABLE_TYPE_X_LOC)
let y := mload(TABLE_TYPE_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE ID1
{
let x := mload(ID1_X_LOC)
let y := mload(ID1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE ID2
{
let x := mload(ID2_X_LOC)
let y := mload(ID2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE ID3
{
let x := mload(ID3_X_LOC)
let y := mload(ID3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
// VALIDATE ID4
{
let x := mload(ID4_X_LOC)
let y := mload(ID4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
}
if iszero(success) {
mstore(0x0, INVALID_VERIFICATION_KEY_SELECTOR)
revert(0x00, 0x04)
}
}
}
/**
* @notice Verify a Ultra Plonk proof
* @param _proof - The serialized proof
* @param _publicInputs - An array of the public inputs
* @return True if proof is valid, reverts otherwise
*/
function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) {
loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC);
uint256 requiredPublicInputCount;
assembly {
requiredPublicInputCount := mload(NUM_INPUTS_LOC)
}
if (requiredPublicInputCount != _publicInputs.length) {
revert PUBLIC_INPUT_COUNT_INVALID(requiredPublicInputCount, _publicInputs.length);
}
assembly {
let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order
let p := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // Prime field order
/**
* LOAD PROOF FROM CALLDATA
*/
{
let data_ptr := add(calldataload(0x04), 0x24)
mstore(W1_Y_LOC, mod(calldataload(data_ptr), q))
mstore(W1_X_LOC, mod(calldataload(add(data_ptr, 0x20)), q))
mstore(W2_Y_LOC, mod(calldataload(add(data_ptr, 0x40)), q))
mstore(W2_X_LOC, mod(calldataload(add(data_ptr, 0x60)), q))
mstore(W3_Y_LOC, mod(calldataload(add(data_ptr, 0x80)), q))
mstore(W3_X_LOC, mod(calldataload(add(data_ptr, 0xa0)), q))
mstore(W4_Y_LOC, mod(calldataload(add(data_ptr, 0xc0)), q))
mstore(W4_X_LOC, mod(calldataload(add(data_ptr, 0xe0)), q))
mstore(S_Y_LOC, mod(calldataload(add(data_ptr, 0x100)), q))
mstore(S_X_LOC, mod(calldataload(add(data_ptr, 0x120)), q))
mstore(Z_Y_LOC, mod(calldataload(add(data_ptr, 0x140)), q))
mstore(Z_X_LOC, mod(calldataload(add(data_ptr, 0x160)), q))
mstore(Z_LOOKUP_Y_LOC, mod(calldataload(add(data_ptr, 0x180)), q))
mstore(Z_LOOKUP_X_LOC, mod(calldataload(add(data_ptr, 0x1a0)), q))
mstore(T1_Y_LOC, mod(calldataload(add(data_ptr, 0x1c0)), q))
mstore(T1_X_LOC, mod(calldataload(add(data_ptr, 0x1e0)), q))
mstore(T2_Y_LOC, mod(calldataload(add(data_ptr, 0x200)), q))
mstore(T2_X_LOC, mod(calldataload(add(data_ptr, 0x220)), q))
mstore(T3_Y_LOC, mod(calldataload(add(data_ptr, 0x240)), q))
mstore(T3_X_LOC, mod(calldataload(add(data_ptr, 0x260)), q))
mstore(T4_Y_LOC, mod(calldataload(add(data_ptr, 0x280)), q))
mstore(T4_X_LOC, mod(calldataload(add(data_ptr, 0x2a0)), q))
mstore(W1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2c0)), p))
mstore(W2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2e0)), p))
mstore(W3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x300)), p))
mstore(W4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x320)), p))
mstore(S_EVAL_LOC, mod(calldataload(add(data_ptr, 0x340)), p))
mstore(Z_EVAL_LOC, mod(calldataload(add(data_ptr, 0x360)), p))
mstore(Z_LOOKUP_EVAL_LOC, mod(calldataload(add(data_ptr, 0x380)), p))
mstore(Q1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3a0)), p))
mstore(Q2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3c0)), p))
mstore(Q3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3e0)), p))
mstore(Q4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x400)), p))
mstore(QM_EVAL_LOC, mod(calldataload(add(data_ptr, 0x420)), p))
mstore(QC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x440)), p))
mstore(QARITH_EVAL_LOC, mod(calldataload(add(data_ptr, 0x460)), p))
mstore(QSORT_EVAL_LOC, mod(calldataload(add(data_ptr, 0x480)), p))
mstore(QELLIPTIC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4a0)), p))
mstore(QAUX_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4c0)), p))
mstore(SIGMA1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4e0)), p))
mstore(SIGMA2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x500)), p))
mstore(SIGMA3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x520)), p))
mstore(SIGMA4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x540)), p))
mstore(TABLE1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x560)), p))
mstore(TABLE2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x580)), p))
mstore(TABLE3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5a0)), p))
mstore(TABLE4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5c0)), p))
mstore(TABLE_TYPE_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5e0)), p))
mstore(ID1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x600)), p))
mstore(ID2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x620)), p))
mstore(ID3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x640)), p))
mstore(ID4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x660)), p))
mstore(W1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x680)), p))
mstore(W2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6a0)), p))
mstore(W3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6c0)), p))
mstore(W4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6e0)), p))
mstore(S_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x700)), p))
mstore(Z_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x720)), p))
mstore(Z_LOOKUP_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x740)), p))
mstore(TABLE1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x760)), p))
mstore(TABLE2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x780)), p))
mstore(TABLE3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7a0)), p))
mstore(TABLE4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7c0)), p))
mstore(PI_Z_Y_LOC, mod(calldataload(add(data_ptr, 0x7e0)), q))
mstore(PI_Z_X_LOC, mod(calldataload(add(data_ptr, 0x800)), q))
mstore(PI_Z_OMEGA_Y_LOC, mod(calldataload(add(data_ptr, 0x820)), q))
mstore(PI_Z_OMEGA_X_LOC, mod(calldataload(add(data_ptr, 0x840)), q))
}
/**
* LOAD RECURSIVE PROOF INTO MEMORY
*/
{
if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
let public_inputs_ptr := add(calldataload(0x24), 0x24)
let index_counter := add(shl(5, mload(RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC)), public_inputs_ptr)
let x0 := calldataload(index_counter)
x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20))))
x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40))))
x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60))))
let y0 := calldataload(add(index_counter, 0x80))
y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0))))
y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0))))
y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0))))
let x1 := calldataload(add(index_counter, 0x100))
x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120))))
x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140))))
x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160))))
let y1 := calldataload(add(index_counter, 0x180))
y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0))))
y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0))))
y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0))))
mstore(RECURSIVE_P1_X_LOC, x0)
mstore(RECURSIVE_P1_Y_LOC, y0)
mstore(RECURSIVE_P2_X_LOC, x1)
mstore(RECURSIVE_P2_Y_LOC, y1)
// validate these are valid bn128 G1 points
if iszero(and(and(lt(x0, q), lt(x1, q)), and(lt(y0, q), lt(y1, q)))) {
mstore(0x00, PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR)
revert(0x00, 0x04)
}
}
}
{
/**
* Generate initial challenge
*/
mstore(0x00, shl(224, mload(N_LOC)))
mstore(0x04, shl(224, mload(NUM_INPUTS_LOC)))
let challenge := keccak256(0x00, 0x08)
/**
* Generate eta challenge
*/
mstore(PUBLIC_INPUTS_HASH_LOCATION, challenge)
// The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs
let public_inputs_start := add(calldataload(0x24), 0x24)
// copy the public inputs over
let public_input_size := mul(mload(NUM_INPUTS_LOC), 0x20)
calldatacopy(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_inputs_start, public_input_size)
// copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length)
let w_start := add(calldataload(0x04), 0x24)
calldatacopy(add(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_input_size), w_start, ETA_INPUT_LENGTH)
// Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0)
let challenge_bytes_size := add(0x20, add(public_input_size, ETA_INPUT_LENGTH))
challenge := keccak256(PUBLIC_INPUTS_HASH_LOCATION, challenge_bytes_size)
{
let eta := mod(challenge, p)
mstore(C_ETA_LOC, eta)
mstore(C_ETA_SQR_LOC, mulmod(eta, eta, p))
mstore(C_ETA_CUBE_LOC, mulmod(mload(C_ETA_SQR_LOC), eta, p))
}
/**
* Generate beta challenge
*/
mstore(0x00, challenge)
mstore(0x20, mload(W4_Y_LOC))
mstore(0x40, mload(W4_X_LOC))
mstore(0x60, mload(S_Y_LOC))
mstore(0x80, mload(S_X_LOC))
challenge := keccak256(0x00, 0xa0)
mstore(C_BETA_LOC, mod(challenge, p))
/**
* Generate gamma challenge
*/
mstore(0x00, challenge)
mstore8(0x20, 0x01)
challenge := keccak256(0x00, 0x21)
mstore(C_GAMMA_LOC, mod(challenge, p))
/**
* Generate alpha challenge
*/
mstore(0x00, challenge)
mstore(0x20, mload(Z_Y_LOC))
mstore(0x40, mload(Z_X_LOC))
mstore(0x60, mload(Z_LOOKUP_Y_LOC))
mstore(0x80, mload(Z_LOOKUP_X_LOC))
challenge := keccak256(0x00, 0xa0)
mstore(C_ALPHA_LOC, mod(challenge, p))
/**
* Compute and store some powers of alpha for future computations
*/
let alpha := mload(C_ALPHA_LOC)
mstore(C_ALPHA_SQR_LOC, mulmod(alpha, alpha, p))
mstore(C_ALPHA_CUBE_LOC, mulmod(mload(C_ALPHA_SQR_LOC), alpha, p))
mstore(C_ALPHA_QUAD_LOC, mulmod(mload(C_ALPHA_CUBE_LOC), alpha, p))
mstore(C_ALPHA_BASE_LOC, alpha)
/**
* Generate zeta challenge
*/
mstore(0x00, challenge)
mstore(0x20, mload(T1_Y_LOC))
mstore(0x40, mload(T1_X_LOC))
mstore(0x60, mload(T2_Y_LOC))
mstore(0x80, mload(T2_X_LOC))
mstore(0xa0, mload(T3_Y_LOC))
mstore(0xc0, mload(T3_X_LOC))
mstore(0xe0, mload(T4_Y_LOC))
mstore(0x100, mload(T4_X_LOC))
challenge := keccak256(0x00, 0x120)
mstore(C_ZETA_LOC, mod(challenge, p))
mstore(C_CURRENT_LOC, challenge)
}
/**
* EVALUATE FIELD OPERATIONS
*/
/**
* COMPUTE PUBLIC INPUT DELTA
* ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ)
*/
{
let beta := mload(C_BETA_LOC) // β
let gamma := mload(C_GAMMA_LOC) // γ
let work_root := mload(OMEGA_LOC) // ω
let numerator_value := 1
let denominator_value := 1
let p_clone := p // move p to the front of the stack
let valid_inputs := true
// Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
let public_inputs_ptr := add(calldataload(0x24), 0x24)
// endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
let endpoint_ptr := add(public_inputs_ptr, mul(mload(NUM_INPUTS_LOC), 0x20))
// root_1 = β * 0x05
let root_1 := mulmod(beta, 0x05, p_clone) // k1.β
// root_2 = β * 0x0c
let root_2 := mulmod(beta, 0x0c, p_clone)
// @note 0x05 + 0x07 == 0x0c == external coset generator
for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } {
/**
* input = public_input[i]
* valid_inputs &= input < p
* temp = input + gamma
* numerator_value *= (β.σ(i) + wᵢ + γ) // σ(i) = 0x05.ωⁱ
* denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ
* root_1 *= ω
* root_2 *= ω
*/
let input := calldataload(public_inputs_ptr)
valid_inputs := and(valid_inputs, lt(input, p_clone))
let temp := addmod(input, gamma, p_clone)
numerator_value := mulmod(numerator_value, add(root_1, temp), p_clone)
denominator_value := mulmod(denominator_value, add(root_2, temp), p_clone)
root_1 := mulmod(root_1, work_root, p_clone)
root_2 := mulmod(root_2, work_root, p_clone)
}
// Revert if not all public inputs are field elements (i.e. < p)
if iszero(valid_inputs) {
mstore(0x00, PUBLIC_INPUT_GE_P_SELECTOR)
revert(0x00, 0x04)
}
mstore(DELTA_NUMERATOR_LOC, numerator_value)
mstore(DELTA_DENOMINATOR_LOC, denominator_value)
}
/**
* Compute Plookup delta factor [γ(1 + β)]^{n-k}
* k = num roots cut out of Z_H = 4
*/
{
let delta_base := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
let delta_numerator := delta_base
{
let exponent := mload(N_LOC)
let count := 1
for {} lt(count, exponent) { count := add(count, count) } {
delta_numerator := mulmod(delta_numerator, delta_numerator, p)
}
}
mstore(PLOOKUP_DELTA_NUMERATOR_LOC, delta_numerator)
let delta_denominator := mulmod(delta_base, delta_base, p)
delta_denominator := mulmod(delta_denominator, delta_denominator, p)
mstore(PLOOKUP_DELTA_DENOMINATOR_LOC, delta_denominator)
}
/**
* Compute lagrange poly and vanishing poly fractions
*/
{
/**
* vanishing_numerator = zeta
* ZETA_POW_N = zeta^n
* vanishing_numerator -= 1
* accumulating_root = omega_inverse
* work_root = p - accumulating_root
* domain_inverse = domain_inverse
* vanishing_denominator = zeta + work_root
* work_root *= accumulating_root
* vanishing_denominator *= (zeta + work_root)
* work_root *= accumulating_root
* vanishing_denominator *= (zeta + work_root)
* vanishing_denominator *= (zeta + (zeta + accumulating_root))
* work_root = omega
* lagrange_numerator = vanishing_numerator * domain_inverse
* l_start_denominator = zeta - 1
* accumulating_root = work_root^2
* l_end_denominator = accumulating_root^2 * work_root * zeta - 1
* Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly
*/
let zeta := mload(C_ZETA_LOC)
// compute zeta^n, where n is a power of 2
let vanishing_numerator := zeta
{
// pow_small
let exponent := mload(N_LOC)
let count := 1
for {} lt(count, exponent) { count := add(count, count) } {
vanishing_numerator := mulmod(vanishing_numerator, vanishing_numerator, p)
}
}
mstore(ZETA_POW_N_LOC, vanishing_numerator)
vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p)
let accumulating_root := mload(OMEGA_INVERSE_LOC)
let work_root := sub(p, accumulating_root)
let domain_inverse := mload(DOMAIN_INVERSE_LOC)
let vanishing_denominator := addmod(zeta, work_root, p)
work_root := mulmod(work_root, accumulating_root, p)
vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
work_root := mulmod(work_root, accumulating_root, p)
vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
vanishing_denominator :=
mulmod(vanishing_denominator, addmod(zeta, mulmod(work_root, accumulating_root, p), p), p)
work_root := mload(OMEGA_LOC)
let lagrange_numerator := mulmod(vanishing_numerator, domain_inverse, p)
let l_start_denominator := addmod(zeta, sub(p, 1), p)
accumulating_root := mulmod(work_root, work_root, p)
let l_end_denominator :=
addmod(
mulmod(mulmod(mulmod(accumulating_root, accumulating_root, p), work_root, p), zeta, p), sub(p, 1), p
)
/**
* Compute inversions using Montgomery's batch inversion trick
*/
let accumulator := mload(DELTA_DENOMINATOR_LOC)
let t0 := accumulator
accumulator := mulmod(accumulator, vanishing_denominator, p)
let t1 := accumulator
accumulator := mulmod(accumulator, vanishing_numerator, p)
let t2 := accumulator
accumulator := mulmod(accumulator, l_start_denominator, p)
let t3 := accumulator
accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
let t4 := accumulator
{
mstore(0, 0x20)
mstore(0x20, 0x20)
mstore(0x40, 0x20)
mstore(0x60, mulmod(accumulator, l_end_denominator, p))
mstore(0x80, sub(p, 2))
mstore(0xa0, p)
if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
mstore(0x0, MOD_EXP_FAILURE_SELECTOR)
revert(0x00, 0x04)
}
accumulator := mload(0x00)
}
t4 := mulmod(accumulator, t4, p)
accumulator := mulmod(accumulator, l_end_denominator, p)
t3 := mulmod(accumulator, t3, p)
accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
t2 := mulmod(accumulator, t2, p)
accumulator := mulmod(accumulator, l_start_denominator, p)
t1 := mulmod(accumulator, t1, p)
accumulator := mulmod(accumulator, vanishing_numerator, p)
t0 := mulmod(accumulator, t0, p)
accumulator := mulmod(accumulator, vanishing_denominator, p)
accumulator := mulmod(mulmod(accumulator, accumulator, p), mload(DELTA_DENOMINATOR_LOC), p)
mstore(PUBLIC_INPUT_DELTA_LOC, mulmod(mload(DELTA_NUMERATOR_LOC), accumulator, p))
mstore(ZERO_POLY_LOC, mulmod(vanishing_numerator, t0, p))
mstore(ZERO_POLY_INVERSE_LOC, mulmod(vanishing_denominator, t1, p))
mstore(L_START_LOC, mulmod(lagrange_numerator, t2, p))
mstore(PLOOKUP_DELTA_LOC, mulmod(mload(PLOOKUP_DELTA_NUMERATOR_LOC), t3, p))
mstore(L_END_LOC, mulmod(lagrange_numerator, t4, p))
}
/**
* UltraPlonk Widget Ordering:
*
* 1. Permutation widget
* 2. Plookup widget
* 3. Arithmetic widget
* 4. Fixed base widget (?)
* 5. GenPermSort widget
* 6. Elliptic widget
* 7. Auxiliary widget
*/
/**
* COMPUTE PERMUTATION WIDGET EVALUATION
*/
{
let alpha := mload(C_ALPHA_LOC)
let beta := mload(C_BETA_LOC)
let gamma := mload(C_GAMMA_LOC)
/**
* t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2)
* t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4)
* result = alpha_base * z_eval * t1 * t2
* t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval)
* t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval)
* result -= (alpha_base * z_omega_eval * t1 * t2)
*/
let t1 :=
mulmod(
add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)),
add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)),
p
)
let t2 :=
mulmod(
add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)),
add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)),
p
)
let result := mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_EVAL_LOC), mulmod(t1, t2, p), p), p)
t1 :=
mulmod(
add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)),
add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)),
p
)
t2 :=
mulmod(
add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)),
add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)),
p
)
result :=
addmod(
result,
sub(p, mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_OMEGA_EVAL_LOC), mulmod(t1, t2, p), p), p)),
p
)
/**
* alpha_base *= alpha
* result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI}))
* alpha_base *= alpha
* result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1))
* alpha_Base *= alpha
*/
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
result :=
addmod(
result,
mulmod(
mload(C_ALPHA_BASE_LOC),
mulmod(
mload(L_END_LOC),
addmod(mload(Z_OMEGA_EVAL_LOC), sub(p, mload(PUBLIC_INPUT_DELTA_LOC)), p),
p
),
p
),
p
)
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
mstore(
PERMUTATION_IDENTITY,
addmod(
result,
mulmod(
mload(C_ALPHA_BASE_LOC),
mulmod(mload(L_START_LOC), addmod(mload(Z_EVAL_LOC), sub(p, 1), p), p),
p
),
p
)
)
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
}
/**
* COMPUTE PLOOKUP WIDGET EVALUATION
*/
{
/**
* Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³
* f = η.q3(z)
* f += (w3(z) + qc.w_3(zω))
* f *= η
* f += (w2(z) + qm.w2(zω))
* f *= η
* f += (w1(z) + q2.w1(zω))
*/
let f := mulmod(mload(C_ETA_LOC), mload(Q3_EVAL_LOC), p)
f :=
addmod(f, addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_OMEGA_EVAL_LOC), p), p), p)
f := mulmod(f, mload(C_ETA_LOC), p)
f :=
addmod(f, addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p), p)
f := mulmod(f, mload(C_ETA_LOC), p)
f :=
addmod(f, addmod(mload(W1_EVAL_LOC), mulmod(mload(Q2_EVAL_LOC), mload(W1_OMEGA_EVAL_LOC), p), p), p)
// t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z)
let t :=
addmod(
addmod(
addmod(
mulmod(mload(TABLE4_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
mulmod(mload(TABLE3_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
p
),
mulmod(mload(TABLE2_EVAL_LOC), mload(C_ETA_LOC), p),
p
),
mload(TABLE1_EVAL_LOC),
p
)
// t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw)
let t_omega :=
addmod(
addmod(
addmod(
mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
p
),
mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p),
p
),
mload(TABLE1_OMEGA_EVAL_LOC),
p
)
/**
* Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1)
* gamma_beta_constant = γ(β + 1)
* numerator = f * TABLE_TYPE_EVAL + gamma
* temp0 = t(z) + t(zω) * β + gamma_beta_constant
* numerator *= temp0
* numerator *= (β + 1)
* temp0 = alpha * l_1
* numerator += temp0
* numerator *= z_lookup(z)
* numerator -= temp0
*/
let gamma_beta_constant := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
let numerator := addmod(mulmod(f, mload(TABLE_TYPE_EVAL_LOC), p), mload(C_GAMMA_LOC), p)
let temp0 := addmod(addmod(t, mulmod(t_omega, mload(C_BETA_LOC), p), p), gamma_beta_constant, p)
numerator := mulmod(numerator, temp0, p)
numerator := mulmod(numerator, addmod(mload(C_BETA_LOC), 1, p), p)
temp0 := mulmod(mload(C_ALPHA_LOC), mload(L_START_LOC), p)
numerator := addmod(numerator, temp0, p)
numerator := mulmod(numerator, mload(Z_LOOKUP_EVAL_LOC), p)
numerator := addmod(numerator, sub(p, temp0), p)
/**
* Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z)
* note: delta_factor = [γ(1 + β)]^{n-k}
* denominator = s(z) + βs(zω) + γ(β + 1)
* temp1 = α²L_end(z)
* denominator -= temp1
* denominator *= z_lookup(zω)
* denominator += temp1 * delta_factor
* PLOOKUP_IDENTITY = (numerator - denominator).alpha_base
* alpha_base *= alpha^3
*/
let denominator :=
addmod(
addmod(mload(S_EVAL_LOC), mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_BETA_LOC), p), p),
gamma_beta_constant,
p
)
let temp1 := mulmod(mload(C_ALPHA_SQR_LOC), mload(L_END_LOC), p)
denominator := addmod(denominator, sub(p, temp1), p)
denominator := mulmod(denominator, mload(Z_LOOKUP_OMEGA_EVAL_LOC), p)
denominator := addmod(denominator, mulmod(temp1, mload(PLOOKUP_DELTA_LOC), p), p)
mstore(PLOOKUP_IDENTITY, mulmod(addmod(numerator, sub(p, denominator), p), mload(C_ALPHA_BASE_LOC), p))
// update alpha
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
}
/**
* COMPUTE ARITHMETIC WIDGET EVALUATION
*/
{
/**
* The basic arithmetic gate identity in standard plonk is as follows.
* (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0
* However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below):
* q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) +
* (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0
*
* This formula results in several cases depending on q_arith:
* 1. q_arith == 0: Arithmetic gate is completely disabled
*
* 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation
* with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0
*
* 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is:
* (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0
* It allows defining w_4 at next index (w_4_omega) in terms of current wire values
*
* 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split
* the equation into two:
*
* q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
* and
* w_1 + w_4 - w_1_omega + q_m = 0 (we are reusing q_m here)
*
* 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1).
* The equation can be split into two:
*
* (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0
* and
* w_1 + w_4 - w_1_omega + q_m = 0
*
* The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at
* the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at
* product.
*/
let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(Q1_EVAL_LOC), p)
let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(Q2_EVAL_LOC), p)
let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(Q3_EVAL_LOC), p)
let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
// @todo - Add a explicit test that hits QARITH == 3
// w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
let w1w2qm :=
mulmod(
mulmod(
mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p),
addmod(mload(QARITH_EVAL_LOC), sub(p, 3), p),
p
),
NEGATIVE_INVERSE_OF_2_MODULO_P,
p
)
// (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c
let identity :=
addmod(
mload(QC_EVAL_LOC), addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p), p
)
// if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
// w_1 + w_4 - w_1_omega + q_m = 0
// we use this gate to save an addition gate when adding or subtracting non-native field elements
// α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
let extra_small_addition_gate_identity :=
mulmod(
mload(C_ALPHA_LOC),
mulmod(
addmod(mload(QARITH_EVAL_LOC), sub(p, 2), p),
addmod(
mload(QM_EVAL_LOC),
addmod(
sub(p, mload(W1_OMEGA_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p
),
p
),
p
),
p
)
// if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity
// N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires!
// alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity))
mstore(
ARITHMETIC_IDENTITY,
mulmod(
mload(C_ALPHA_BASE_LOC),
mulmod(
mload(QARITH_EVAL_LOC),
addmod(
identity,
mulmod(
addmod(mload(QARITH_EVAL_LOC), sub(p, 1), p),
addmod(mload(W4_OMEGA_EVAL_LOC), extra_small_addition_gate_identity, p),
p
),
p
),
p
),
p
)
)
// update alpha
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p))
}
/**
* COMPUTE GENPERMSORT WIDGET EVALUATION
*/
{
/**
* D1 = (w2 - w1)
* D2 = (w3 - w2)
* D3 = (w4 - w3)
* D4 = (w1_omega - w4)
*
* α_a = alpha_base
* α_b = alpha_base * α
* α_c = alpha_base * α^2
* α_d = alpha_base * α^3
*
* range_accumulator = (
* D1(D1 - 1)(D1 - 2)(D1 - 3).α_a +
* D2(D2 - 1)(D2 - 2)(D2 - 3).α_b +
* D3(D3 - 1)(D3 - 2)(D3 - 3).α_c +
* D4(D4 - 1)(D4 - 2)(D4 - 3).α_d +
* ) . q_sort
*/
let minus_two := sub(p, 2)
let minus_three := sub(p, 3)
let d1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
let d2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
let d3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
let d4 := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
let range_accumulator :=
mulmod(
mulmod(
mulmod(addmod(mulmod(d1, d1, p), sub(p, d1), p), addmod(d1, minus_two, p), p),
addmod(d1, minus_three, p),
p
),
mload(C_ALPHA_BASE_LOC),
p
)
range_accumulator :=
addmod(
range_accumulator,
mulmod(
mulmod(
mulmod(addmod(mulmod(d2, d2, p), sub(p, d2), p), addmod(d2, minus_two, p), p),
addmod(d2, minus_three, p),
p
),
mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
p
),
p
)
range_accumulator :=
addmod(
range_accumulator,
mulmod(
mulmod(
mulmod(addmod(mulmod(d3, d3, p), sub(p, d3), p), addmod(d3, minus_two, p), p),
addmod(d3, minus_three, p),
p
),
mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p),
p
),
p
)
range_accumulator :=
addmod(
range_accumulator,
mulmod(
mulmod(
mulmod(addmod(mulmod(d4, d4, p), sub(p, d4), p), addmod(d4, minus_two, p), p),
addmod(d4, minus_three, p),
p
),
mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p),
p
),
p
)
range_accumulator := mulmod(range_accumulator, mload(QSORT_EVAL_LOC), p)
mstore(SORT_IDENTITY, range_accumulator)
// update alpha
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
}
/**
* COMPUTE ELLIPTIC WIDGET EVALUATION
*/
{
/**
* endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta
* endo_sqr_term = x_2^2
* endo_sqr_term *= (x_3 - x_1)
* endo_sqr_term *= q_beta^2
* leftovers = x_2^2
* leftovers *= x_2
* leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget
* leftovers -= (y_2^2 + y_1^2)
* sign_term = y_2 * y_1
* sign_term += sign_term
* sign_term *= q_sign
*/
// q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
let x_diff := addmod(mload(X2_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p)
let y2_sqr := mulmod(mload(Y2_EVAL_LOC), mload(Y2_EVAL_LOC), p)
let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p)
let y1y2 := mulmod(mulmod(mload(Y1_EVAL_LOC), mload(Y2_EVAL_LOC), p), mload(QSIGN_LOC), p)
let x_add_identity :=
addmod(
mulmod(
addmod(mload(X3_EVAL_LOC), addmod(mload(X2_EVAL_LOC), mload(X1_EVAL_LOC), p), p),
mulmod(x_diff, x_diff, p),
p
),
addmod(
sub(
p,
addmod(y2_sqr, y1_sqr, p)
),
addmod(y1y2, y1y2, p),
p
),
p
)
x_add_identity :=
mulmod(
mulmod(
x_add_identity,
addmod(
1,
sub(p, mload(QM_EVAL_LOC)),
p
),
p
),
mload(C_ALPHA_BASE_LOC),
p
)
// q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
let y1_plus_y3 := addmod(
mload(Y1_EVAL_LOC),
mload(Y3_EVAL_LOC),
p
)
let y_diff := addmod(mulmod(mload(Y2_EVAL_LOC), mload(QSIGN_LOC), p), sub(p, mload(Y1_EVAL_LOC)), p)
let y_add_identity :=
addmod(
mulmod(y1_plus_y3, x_diff, p),
mulmod(addmod(mload(X3_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p), y_diff, p),
p
)
y_add_identity :=
mulmod(
mulmod(y_add_identity, addmod(1, sub(p, mload(QM_EVAL_LOC)), p), p),
mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
p
)
// ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL
mstore(
ELLIPTIC_IDENTITY, mulmod(addmod(x_add_identity, y_add_identity, p), mload(QELLIPTIC_EVAL_LOC), p)
)
}
{
/**
* x_pow_4 = (y_1_sqr - curve_b) * x_1;
* y_1_sqr_mul_4 = y_1_sqr + y_1_sqr;
* y_1_sqr_mul_4 += y_1_sqr_mul_4;
* x_1_pow_4_mul_9 = x_pow_4;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_pow_4;
* x_1_sqr_mul_3 = x_1_sqr + x_1_sqr + x_1_sqr;
* x_double_identity = (x_3 + x_1 + x_1) * y_1_sqr_mul_4 - x_1_pow_4_mul_9;
* y_double_identity = x_1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y_1 + y_3);
*/
// (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
let x1_sqr := mulmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p)
let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p)
let x_pow_4 := mulmod(addmod(y1_sqr, GRUMPKIN_CURVE_B_PARAMETER_NEGATED, p), mload(X1_EVAL_LOC), p)
let y1_sqr_mul_4 := mulmod(y1_sqr, 4, p)
let x1_pow_4_mul_9 := mulmod(x_pow_4, 9, p)
let x1_sqr_mul_3 := mulmod(x1_sqr, 3, p)
let x_double_identity :=
addmod(
mulmod(
addmod(mload(X3_EVAL_LOC), addmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p), p),
y1_sqr_mul_4,
p
),
sub(p, x1_pow_4_mul_9),
p
)
// (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
let y_double_identity :=
addmod(
mulmod(x1_sqr_mul_3, addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), p),
sub(
p,
mulmod(
addmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p),
addmod(mload(Y1_EVAL_LOC), mload(Y3_EVAL_LOC), p),
p
)
),
p
)
x_double_identity := mulmod(x_double_identity, mload(C_ALPHA_BASE_LOC), p)
y_double_identity :=
mulmod(y_double_identity, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), p)
x_double_identity := mulmod(x_double_identity, mload(QM_EVAL_LOC), p)
y_double_identity := mulmod(y_double_identity, mload(QM_EVAL_LOC), p)
// ELLIPTIC_IDENTITY += (x_double_identity + y_double_identity) * Q_DOUBLE_EVAL
mstore(
ELLIPTIC_IDENTITY,
addmod(
mload(ELLIPTIC_IDENTITY),
mulmod(addmod(x_double_identity, y_double_identity, p), mload(QELLIPTIC_EVAL_LOC), p),
p
)
)
// update alpha
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
}
/**
* COMPUTE AUXILIARY WIDGET EVALUATION
*/
{
{
/**
* Non native field arithmetic gate 2
* _ _
* / _ _ _ 14 \
* q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 |
* \_ _/
*
* limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2
* non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega
* non_native_field_gate_2 = non_native_field_gate_2 * limb_size
* non_native_field_gate_2 -= w_4_omega
* non_native_field_gate_2 += limb_subproduct
* non_native_field_gate_2 *= q_4
* limb_subproduct *= limb_size
* limb_subproduct += w_1_omega * w_2_omega
* non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3
* non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m
* non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2
*/
let limb_subproduct :=
addmod(
mulmod(mload(W1_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p),
mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_EVAL_LOC), p),
p
)
let non_native_field_gate_2 :=
addmod(
addmod(
mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p),
p
),
sub(p, mload(W3_OMEGA_EVAL_LOC)),
p
)
non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p)
non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p)
non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p)
limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p)
limb_subproduct :=
addmod(limb_subproduct, mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p)
let non_native_field_gate_1 :=
mulmod(
addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p),
mload(Q3_EVAL_LOC),
p
)
let non_native_field_gate_3 :=
mulmod(
addmod(
addmod(limb_subproduct, mload(W4_EVAL_LOC), p),
sub(p, addmod(mload(W3_OMEGA_EVAL_LOC), mload(W4_OMEGA_EVAL_LOC), p)),
p
),
mload(QM_EVAL_LOC),
p
)
let non_native_field_identity :=
mulmod(
addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p),
mload(Q2_EVAL_LOC),
p
)
mstore(AUX_NON_NATIVE_FIELD_EVALUATION, non_native_field_identity)
}
{
/**
* limb_accumulator_1 = w_2_omega;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_1_omega;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_3;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_2;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_1;
* limb_accumulator_1 -= w_4;
* limb_accumulator_1 *= q_4;
*/
let limb_accumulator_1 := mulmod(mload(W2_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_OMEGA_EVAL_LOC), p)
limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p)
limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p)
limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p)
limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p)
limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p)
/**
* limb_accumulator_2 = w_3_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_2_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_1_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_4;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_3;
* limb_accumulator_2 -= w_4_omega;
* limb_accumulator_2 *= q_m;
*/
let limb_accumulator_2 := mulmod(mload(W3_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_OMEGA_EVAL_LOC), p)
limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_OMEGA_EVAL_LOC), p)
limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p)
limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p)
limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p)
mstore(
AUX_LIMB_ACCUMULATOR_EVALUATION,
mulmod(addmod(limb_accumulator_1, limb_accumulator_2, p), mload(Q3_EVAL_LOC), p)
)
}
{
/**
* memory_record_check = w_3;
* memory_record_check *= eta;
* memory_record_check += w_2;
* memory_record_check *= eta;
* memory_record_check += w_1;
* memory_record_check *= eta;
* memory_record_check += q_c;
*
* partial_record_check = memory_record_check;
*
* memory_record_check -= w_4;
*/
let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(C_ETA_LOC), p)
memory_record_check := addmod(memory_record_check, mload(W2_EVAL_LOC), p)
memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
memory_record_check := addmod(memory_record_check, mload(W1_EVAL_LOC), p)
memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p)
let partial_record_check := memory_record_check
memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p)
mstore(AUX_MEMORY_EVALUATION, memory_record_check)
// index_delta = w_1_omega - w_1
let index_delta := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
// record_delta = w_4_omega - w_4
let record_delta := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
// index_is_monotonically_increasing = index_delta * (index_delta - 1)
let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, sub(p, 1), p), p)
// adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
let adjacent_values_match_if_adjacent_indices_match :=
mulmod(record_delta, addmod(1, sub(p, index_delta), p), p)
// AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check
mstore(
AUX_ROM_CONSISTENCY_EVALUATION,
addmod(
mulmod(
addmod(
mulmod(adjacent_values_match_if_adjacent_indices_match, mload(C_ALPHA_LOC), p),
index_is_monotonically_increasing,
p
),
mload(C_ALPHA_LOC),
p
),
memory_record_check,
p
)
)
{
/**
* next_gate_access_type = w_3_omega;
* next_gate_access_type *= eta;
* next_gate_access_type += w_2_omega;
* next_gate_access_type *= eta;
* next_gate_access_type += w_1_omega;
* next_gate_access_type *= eta;
* next_gate_access_type = w_4_omega - next_gate_access_type;
*/
let next_gate_access_type := mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p)
next_gate_access_type := addmod(next_gate_access_type, mload(W2_OMEGA_EVAL_LOC), p)
next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
next_gate_access_type := addmod(next_gate_access_type, mload(W1_OMEGA_EVAL_LOC), p)
next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
next_gate_access_type := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, next_gate_access_type), p)
// value_delta = w_3_omega - w_3
let value_delta := addmod(mload(W3_OMEGA_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
// adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation :=
mulmod(
addmod(1, sub(p, index_delta), p),
mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p),
p
)
// AUX_RAM_CONSISTENCY_EVALUATION
/**
* access_type = w_4 - partial_record_check
* access_check = access_type^2 - access_type
* next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type
* RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += index_is_monotonically_increasing;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += next_gate_access_type_is_boolean;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += access_check;
*/
let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p)
let access_check := mulmod(access_type, addmod(access_type, sub(p, 1), p), p)
let next_gate_access_type_is_boolean :=
mulmod(next_gate_access_type, addmod(next_gate_access_type, sub(p, 1), p), p)
let RAM_cci :=
mulmod(
adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation,
mload(C_ALPHA_LOC),
p
)
RAM_cci := addmod(RAM_cci, index_is_monotonically_increasing, p)
RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
RAM_cci := addmod(RAM_cci, next_gate_access_type_is_boolean, p)
RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
RAM_cci := addmod(RAM_cci, access_check, p)
mstore(AUX_RAM_CONSISTENCY_EVALUATION, RAM_cci)
}
{
// timestamp_delta = w_2_omega - w_2
let timestamp_delta := addmod(mload(W2_OMEGA_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
// RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
let RAM_timestamp_check_identity :=
addmod(
mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p), sub(p, mload(W3_EVAL_LOC)), p
)
/**
* memory_identity = ROM_consistency_check_identity * q_2;
* memory_identity += RAM_timestamp_check_identity * q_4;
* memory_identity += memory_record_check * q_m;
* memory_identity *= q_1;
* memory_identity += (RAM_consistency_check_identity * q_arith);
*
* auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity;
* auxiliary_identity *= q_aux;
* auxiliary_identity *= alpha_base;
*/
let memory_identity := mulmod(mload(AUX_ROM_CONSISTENCY_EVALUATION), mload(Q2_EVAL_LOC), p)
memory_identity :=
addmod(memory_identity, mulmod(RAM_timestamp_check_identity, mload(Q4_EVAL_LOC), p), p)
memory_identity :=
addmod(memory_identity, mulmod(mload(AUX_MEMORY_EVALUATION), mload(QM_EVAL_LOC), p), p)
memory_identity := mulmod(memory_identity, mload(Q1_EVAL_LOC), p)
memory_identity :=
addmod(
memory_identity, mulmod(mload(AUX_RAM_CONSISTENCY_EVALUATION), mload(QARITH_EVAL_LOC), p), p
)
let auxiliary_identity := addmod(memory_identity, mload(AUX_NON_NATIVE_FIELD_EVALUATION), p)
auxiliary_identity := addmod(auxiliary_identity, mload(AUX_LIMB_ACCUMULATOR_EVALUATION), p)
auxiliary_identity := mulmod(auxiliary_identity, mload(QAUX_EVAL_LOC), p)
auxiliary_identity := mulmod(auxiliary_identity, mload(C_ALPHA_BASE_LOC), p)
mstore(AUX_IDENTITY, auxiliary_identity)
// update alpha
mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
}
}
}
{
/**
* quotient = ARITHMETIC_IDENTITY
* quotient += PERMUTATION_IDENTITY
* quotient += PLOOKUP_IDENTITY
* quotient += SORT_IDENTITY
* quotient += ELLIPTIC_IDENTITY
* quotient += AUX_IDENTITY
* quotient *= ZERO_POLY_INVERSE
*/
mstore(
QUOTIENT_EVAL_LOC,
mulmod(
addmod(
addmod(
addmod(
addmod(
addmod(mload(PERMUTATION_IDENTITY), mload(PLOOKUP_IDENTITY), p),
mload(ARITHMETIC_IDENTITY),
p
),
mload(SORT_IDENTITY),
p
),
mload(ELLIPTIC_IDENTITY),
p
),
mload(AUX_IDENTITY),
p
),
mload(ZERO_POLY_INVERSE_LOC),
p
)
)
}
/**
* GENERATE NU AND SEPARATOR CHALLENGES
*/
{
let current_challenge := mload(C_CURRENT_LOC)
// get a calldata pointer that points to the start of the data we want to copy
let calldata_ptr := add(calldataload(0x04), 0x24)
calldata_ptr := add(calldata_ptr, NU_CALLDATA_SKIP_LENGTH)
mstore(NU_CHALLENGE_INPUT_LOC_A, current_challenge)
mstore(NU_CHALLENGE_INPUT_LOC_B, mload(QUOTIENT_EVAL_LOC))
calldatacopy(NU_CHALLENGE_INPUT_LOC_C, calldata_ptr, NU_INPUT_LENGTH)
// hash length = (0x20 + num field elements), we include the previous challenge in the hash
let challenge := keccak256(NU_CHALLENGE_INPUT_LOC_A, add(NU_INPUT_LENGTH, 0x40))
mstore(C_V0_LOC, mod(challenge, p))
// We need THIRTY-ONE independent nu challenges!
mstore(0x00, challenge)
mstore8(0x20, 0x01)
mstore(C_V1_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x02)
mstore(C_V2_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x03)
mstore(C_V3_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x04)
mstore(C_V4_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x05)
mstore(C_V5_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x06)
mstore(C_V6_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x07)
mstore(C_V7_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x08)
mstore(C_V8_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x09)
mstore(C_V9_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0a)
mstore(C_V10_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0b)
mstore(C_V11_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0c)
mstore(C_V12_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0d)
mstore(C_V13_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0e)
mstore(C_V14_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x0f)
mstore(C_V15_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x10)
mstore(C_V16_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x11)
mstore(C_V17_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x12)
mstore(C_V18_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x13)
mstore(C_V19_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x14)
mstore(C_V20_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x15)
mstore(C_V21_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x16)
mstore(C_V22_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x17)
mstore(C_V23_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x18)
mstore(C_V24_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x19)
mstore(C_V25_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x1a)
mstore(C_V26_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x1b)
mstore(C_V27_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x1c)
mstore(C_V28_LOC, mod(keccak256(0x00, 0x21), p))
mstore8(0x20, 0x1d)
mstore(C_V29_LOC, mod(keccak256(0x00, 0x21), p))
// @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change?
mstore8(0x20, 0x1d)
challenge := keccak256(0x00, 0x21)
mstore(C_V30_LOC, mod(challenge, p))
// separator
mstore(0x00, challenge)
mstore(0x20, mload(PI_Z_Y_LOC))
mstore(0x40, mload(PI_Z_X_LOC))
mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
mstore(0x80, mload(PI_Z_OMEGA_X_LOC))
mstore(C_U_LOC, mod(keccak256(0x00, 0xa0), p))
}
let success := 0
// VALIDATE T1
{
let x := mload(T1_X_LOC)
let y := mload(T1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(ACCUMULATOR_X_LOC, x)
mstore(add(ACCUMULATOR_X_LOC, 0x20), y)
}
// VALIDATE T2
{
let x := mload(T2_X_LOC) // 0x1400
let y := mload(T2_Y_LOC) // 0x1420
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mload(ZETA_POW_N_LOC))
// accumulator_2 = [T2].zeta^n
success := staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)
// accumulator = [T1] + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE T3
{
let x := mload(T3_X_LOC)
let y := mload(T3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p))
// accumulator_2 = [T3].zeta^{2n}
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE T4
{
let x := mload(T4_X_LOC)
let y := mload(T4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p), mload(ZETA_POW_N_LOC), p))
// accumulator_2 = [T4].zeta^{3n}
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE W1
{
let x := mload(W1_X_LOC)
let y := mload(W1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V0_LOC), p))
// accumulator_2 = v0.(u + 1).[W1]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE W2
{
let x := mload(W2_X_LOC)
let y := mload(W2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V1_LOC), p))
// accumulator_2 = v1.(u + 1).[W2]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE W3
{
let x := mload(W3_X_LOC)
let y := mload(W3_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V2_LOC), p))
// accumulator_2 = v2.(u + 1).[W3]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE W4
{
let x := mload(W4_X_LOC)
let y := mload(W4_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V3_LOC), p))
// accumulator_2 = v3.(u + 1).[W4]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE S
{
let x := mload(S_X_LOC)
let y := mload(S_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V4_LOC), p))
// accumulator_2 = v4.(u + 1).[S]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE Z
{
let x := mload(Z_X_LOC)
let y := mload(Z_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V5_LOC), p))
// accumulator_2 = v5.(u + 1).[Z]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE Z_LOOKUP
{
let x := mload(Z_LOOKUP_X_LOC)
let y := mload(Z_LOOKUP_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V6_LOC), p))
// accumulator_2 = v6.(u + 1).[Z_LOOKUP]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE Q1
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(Q1_X_LOC))
mstore(0x20, mload(Q1_Y_LOC))
mstore(0x40, mload(C_V7_LOC))
// accumulator_2 = v7.[Q1]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE Q2
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(Q2_X_LOC))
mstore(0x20, mload(Q2_Y_LOC))
mstore(0x40, mload(C_V8_LOC))
// accumulator_2 = v8.[Q2]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE Q3
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(Q3_X_LOC))
mstore(0x20, mload(Q3_Y_LOC))
mstore(0x40, mload(C_V9_LOC))
// accumulator_2 = v9.[Q3]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE Q4
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(Q4_X_LOC))
mstore(0x20, mload(Q4_Y_LOC))
mstore(0x40, mload(C_V10_LOC))
// accumulator_2 = v10.[Q4]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QM
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QM_X_LOC))
mstore(0x20, mload(QM_Y_LOC))
mstore(0x40, mload(C_V11_LOC))
// accumulator_2 = v11.[Q;]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QC
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QC_X_LOC))
mstore(0x20, mload(QC_Y_LOC))
mstore(0x40, mload(C_V12_LOC))
// accumulator_2 = v12.[QC]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QARITH
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QARITH_X_LOC))
mstore(0x20, mload(QARITH_Y_LOC))
mstore(0x40, mload(C_V13_LOC))
// accumulator_2 = v13.[QARITH]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QSORT
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QSORT_X_LOC))
mstore(0x20, mload(QSORT_Y_LOC))
mstore(0x40, mload(C_V14_LOC))
// accumulator_2 = v14.[QSORT]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QELLIPTIC
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QELLIPTIC_X_LOC))
mstore(0x20, mload(QELLIPTIC_Y_LOC))
mstore(0x40, mload(C_V15_LOC))
// accumulator_2 = v15.[QELLIPTIC]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE QAUX
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(QAUX_X_LOC))
mstore(0x20, mload(QAUX_Y_LOC))
mstore(0x40, mload(C_V16_LOC))
// accumulator_2 = v15.[Q_AUX]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE SIGMA1
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(SIGMA1_X_LOC))
mstore(0x20, mload(SIGMA1_Y_LOC))
mstore(0x40, mload(C_V17_LOC))
// accumulator_2 = v17.[sigma1]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE SIGMA2
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(SIGMA2_X_LOC))
mstore(0x20, mload(SIGMA2_Y_LOC))
mstore(0x40, mload(C_V18_LOC))
// accumulator_2 = v18.[sigma2]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE SIGMA3
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(SIGMA3_X_LOC))
mstore(0x20, mload(SIGMA3_Y_LOC))
mstore(0x40, mload(C_V19_LOC))
// accumulator_2 = v19.[sigma3]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE SIGMA4
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(SIGMA4_X_LOC))
mstore(0x20, mload(SIGMA4_Y_LOC))
mstore(0x40, mload(C_V20_LOC))
// accumulator_2 = v20.[sigma4]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE TABLE1
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(TABLE1_X_LOC))
mstore(0x20, mload(TABLE1_Y_LOC))
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V21_LOC), p))
// accumulator_2 = u.[table1]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE TABLE2
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(TABLE2_X_LOC))
mstore(0x20, mload(TABLE2_Y_LOC))
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V22_LOC), p))
// accumulator_2 = u.[table2]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE TABLE3
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(TABLE3_X_LOC))
mstore(0x20, mload(TABLE3_Y_LOC))
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V23_LOC), p))
// accumulator_2 = u.[table3]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE TABLE4
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(TABLE4_X_LOC))
mstore(0x20, mload(TABLE4_Y_LOC))
mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V24_LOC), p))
// accumulator_2 = u.[table4]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE TABLE_TYPE
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(TABLE_TYPE_X_LOC))
mstore(0x20, mload(TABLE_TYPE_Y_LOC))
mstore(0x40, mload(C_V25_LOC))
// accumulator_2 = v25.[TableType]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE ID1
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(ID1_X_LOC))
mstore(0x20, mload(ID1_Y_LOC))
mstore(0x40, mload(C_V26_LOC))
// accumulator_2 = v26.[ID1]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE ID2
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(ID2_X_LOC))
mstore(0x20, mload(ID2_Y_LOC))
mstore(0x40, mload(C_V27_LOC))
// accumulator_2 = v27.[ID2]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE ID3
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(ID3_X_LOC))
mstore(0x20, mload(ID3_Y_LOC))
mstore(0x40, mload(C_V28_LOC))
// accumulator_2 = v28.[ID3]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// ACCUMULATE ID4
// Verification key fields verified to be on curve at contract deployment
mstore(0x00, mload(ID4_X_LOC))
mstore(0x20, mload(ID4_Y_LOC))
mstore(0x40, mload(C_V29_LOC))
// accumulator_2 = v29.[ID4]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
/**
* COMPUTE BATCH EVALUATION SCALAR MULTIPLIER
*/
{
/**
* batch_evaluation = v0 * (w_1_omega * u + w_1_eval)
* batch_evaluation += v1 * (w_2_omega * u + w_2_eval)
* batch_evaluation += v2 * (w_3_omega * u + w_3_eval)
* batch_evaluation += v3 * (w_4_omega * u + w_4_eval)
* batch_evaluation += v4 * (s_omega_eval * u + s_eval)
* batch_evaluation += v5 * (z_omega_eval * u + z_eval)
* batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval)
*/
let batch_evaluation :=
mulmod(
mload(C_V0_LOC),
addmod(mulmod(mload(W1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W1_EVAL_LOC), p),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V1_LOC),
addmod(mulmod(mload(W2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W2_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V2_LOC),
addmod(mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W3_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V3_LOC),
addmod(mulmod(mload(W4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W4_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V4_LOC),
addmod(mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(S_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V5_LOC),
addmod(mulmod(mload(Z_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V6_LOC),
addmod(mulmod(mload(Z_LOOKUP_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_LOOKUP_EVAL_LOC), p),
p
),
p
)
/**
* batch_evaluation += v7 * Q1_EVAL
* batch_evaluation += v8 * Q2_EVAL
* batch_evaluation += v9 * Q3_EVAL
* batch_evaluation += v10 * Q4_EVAL
* batch_evaluation += v11 * QM_EVAL
* batch_evaluation += v12 * QC_EVAL
* batch_evaluation += v13 * QARITH_EVAL
* batch_evaluation += v14 * QSORT_EVAL_LOC
* batch_evaluation += v15 * QELLIPTIC_EVAL_LOC
* batch_evaluation += v16 * QAUX_EVAL_LOC
* batch_evaluation += v17 * SIGMA1_EVAL_LOC
* batch_evaluation += v18 * SIGMA2_EVAL_LOC
* batch_evaluation += v19 * SIGMA3_EVAL_LOC
* batch_evaluation += v20 * SIGMA4_EVAL_LOC
*/
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V7_LOC), mload(Q1_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V8_LOC), mload(Q2_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V9_LOC), mload(Q3_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V10_LOC), mload(Q4_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V11_LOC), mload(QM_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V12_LOC), mload(QC_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V13_LOC), mload(QARITH_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V14_LOC), mload(QSORT_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V15_LOC), mload(QELLIPTIC_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V16_LOC), mload(QAUX_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V17_LOC), mload(SIGMA1_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V18_LOC), mload(SIGMA2_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V19_LOC), mload(SIGMA3_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V20_LOC), mload(SIGMA4_EVAL_LOC), p), p)
/**
* batch_evaluation += v21 * (table1(zw) * u + table1(z))
* batch_evaluation += v22 * (table2(zw) * u + table2(z))
* batch_evaluation += v23 * (table3(zw) * u + table3(z))
* batch_evaluation += v24 * (table4(zw) * u + table4(z))
* batch_evaluation += v25 * table_type_eval
* batch_evaluation += v26 * id1_eval
* batch_evaluation += v27 * id2_eval
* batch_evaluation += v28 * id3_eval
* batch_evaluation += v29 * id4_eval
* batch_evaluation += quotient_eval
*/
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V21_LOC),
addmod(mulmod(mload(TABLE1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE1_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V22_LOC),
addmod(mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE2_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V23_LOC),
addmod(mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE3_EVAL_LOC), p),
p
),
p
)
batch_evaluation :=
addmod(
batch_evaluation,
mulmod(
mload(C_V24_LOC),
addmod(mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE4_EVAL_LOC), p),
p
),
p
)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V25_LOC), mload(TABLE_TYPE_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V26_LOC), mload(ID1_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V27_LOC), mload(ID2_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V28_LOC), mload(ID3_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V29_LOC), mload(ID4_EVAL_LOC), p), p)
batch_evaluation := addmod(batch_evaluation, mload(QUOTIENT_EVAL_LOC), p)
mstore(0x00, 0x01) // [1].x
mstore(0x20, 0x02) // [1].y
mstore(0x40, sub(p, batch_evaluation))
// accumulator_2 = -[1].(batch_evaluation)
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
if iszero(success) {
mstore(0x0, OPENING_COMMITMENT_FAILED_SELECTOR)
revert(0x00, 0x04)
}
}
/**
* PERFORM PAIRING PREAMBLE
*/
{
let u := mload(C_U_LOC)
let zeta := mload(C_ZETA_LOC)
// VALIDATE PI_Z
{
let x := mload(PI_Z_X_LOC)
let y := mload(PI_Z_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
// compute zeta.[PI_Z] and add into accumulator
mstore(0x40, zeta)
success := staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)
// accumulator = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
// VALIDATE PI_Z_OMEGA
{
let x := mload(PI_Z_OMEGA_X_LOC)
let y := mload(PI_Z_OMEGA_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
mstore(0x40, mulmod(mulmod(u, zeta, p), mload(OMEGA_LOC), p))
// accumulator_2 = u.zeta.omega.[PI_Z_OMEGA]
success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
// PAIRING_RHS = accumulator + accumulator_2
success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, PAIRING_RHS_X_LOC, 0x40))
mstore(0x00, mload(PI_Z_X_LOC))
mstore(0x20, mload(PI_Z_Y_LOC))
mstore(0x40, mload(PI_Z_OMEGA_X_LOC))
mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
mstore(0x80, u)
success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40))
// PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u
success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
// negate lhs y-coordinate
mstore(PAIRING_LHS_Y_LOC, sub(q, mload(PAIRING_LHS_Y_LOC)))
if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
// VALIDATE RECURSIVE P1
{
let x := mload(RECURSIVE_P1_X_LOC)
let y := mload(RECURSIVE_P1_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
// compute u.u.[recursive_p1] and write into 0x60
mstore(0x40, mulmod(u, u, p))
success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x60, 0x40))
// VALIDATE RECURSIVE P2
{
let x := mload(RECURSIVE_P2_X_LOC)
let y := mload(RECURSIVE_P2_Y_LOC)
let xx := mulmod(x, x, q)
// validate on curve
if iszero(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) {
mstore(0x0, POINT_NOT_ON_CURVE_SELECTOR)
revert(0x00, 0x04)
}
mstore(0x00, x)
mstore(0x20, y)
}
// compute u.u.[recursive_p2] and write into 0x00
// 0x40 still contains u*u
success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x00, 0x40))
// compute u.u.[recursiveP1] + rhs and write into rhs
mstore(0xa0, mload(PAIRING_RHS_X_LOC))
mstore(0xc0, mload(PAIRING_RHS_Y_LOC))
success := and(success, staticcall(gas(), 6, 0x60, 0x80, PAIRING_RHS_X_LOC, 0x40))
// compute u.u.[recursiveP2] + lhs and write into lhs
mstore(0x40, mload(PAIRING_LHS_X_LOC))
mstore(0x60, mload(PAIRING_LHS_Y_LOC))
success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
}
if iszero(success) {
mstore(0x0, PAIRING_PREAMBLE_FAILED_SELECTOR)
revert(0x00, 0x04)
}
}
/**
* PERFORM PAIRING
*/
{
// rhs paired with [1]_2
// lhs paired with [x]_2
mstore(0x00, mload(PAIRING_RHS_X_LOC))
mstore(0x20, mload(PAIRING_RHS_Y_LOC))
mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) // this is [1]_2
mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)
mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)
mstore(0xc0, mload(PAIRING_LHS_X_LOC))
mstore(0xe0, mload(PAIRING_LHS_Y_LOC))
mstore(0x100, mload(G2X_X0_LOC))
mstore(0x120, mload(G2X_X1_LOC))
mstore(0x140, mload(G2X_Y0_LOC))
mstore(0x160, mload(G2X_Y1_LOC))
success := staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20)
if iszero(and(success, mload(0x00))) {
mstore(0x0, PAIRING_FAILED_SELECTOR)
revert(0x00, 0x04)
}
}
{
mstore(0x00, 0x01)
return(0x00, 0x20) // Proof succeeded!
}
}
}
}
contract UltraVerifier is BaseUltraVerifier {
function getVerificationKeyHash() public pure override(BaseUltraVerifier) returns (bytes32) {
return UltraVerificationKey.verificationKeyHash();
}
function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BaseUltraVerifier) {
UltraVerificationKey.loadVerificationKey(vk, _omegaInverseLoc);
}
}
pragma solidity ^0.8.26;
pragma experimental ABIEncoderV2;
import "./Interfaces/IERC20.sol";
import "./Interfaces/IElasticERC20.sol";
import "./Libraries/structs.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
// Upgradeable
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
interface IVerifier {
function verify(bytes calldata, bytes32[] calldata) external view returns (bool);
function getVerificationKeyHash() external pure returns (bytes32);
}
interface IOracle {
function getCost(
uint256 _amount,
address _chainlinkFeed,
address _xftPool
) external view returns (uint256);
function chainlinkPrice(address _chainlinkFeed) external view returns (uint256);
}
interface IWETH9 is IERC20{
function deposit() external payable;
function withdraw(uint256 _amount) external;
}
interface IMomiji {
function publish(bytes calldata _proof, Batch calldata _batch) external;
}
contract Momiji is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable, EIP712Upgradeable {
address constant _weth9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 constant MAX_FIELD_VALUE = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000;
uint256 constant FIELD_MODULUS = MAX_FIELD_VALUE + 1;
bytes32 constant ZERO_VALUE = 0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720;
bytes32 constant ZERO_UTXO_ROOT = 0x11d25ff6aa8a431fbce8e8d9a87a2d7986adf38e724fbe47f15752d0931f14d8;
bytes32 constant INITIAL_STATE = 0x06f93f503e77fcdcacfe622e66adc639b63e8c0083f5cab5d71d461aa4562c92;
bytes32 constant COINBASE_PAYMENT = 0x0000000000000000000000000000000000000000000000000000000000000003;
bytes32 constant BROADCASTER_PAYMENT = 0x0000000000000000000000000000000000000000000000000000000000000002;
bytes32 constant PROVER_PAYMENT = 0x0000000000000000000000000000000000000000000000000000000000000001;
bytes32 constant NO_PAYMENT = 0x0000000000000000000000000000000000000000000000000000000000000000;
IWETH9 constant weth9 = IWETH9(_weth9);
IVerifier public verifier;
IElasticERC20 public token;
IOracle public oracle;
ISwapRouter public swapRouter;
bytes32[] public validRoots;
bytes32 public txKeyHash;
bytes32 public txWrapperKeyHash;
bytes32 public recursiveKeyHash;
uint256 public STATE_DEPTH;
uint256 public MAX_ITEMS;
uint256 public MAX_UTXOS;
bytes32 public merkleRoot;
bytes32 public histRoot;
// Training Wheels
uint256 public dailyCap;
uint256 public dailyMint;
uint256 public lastCapReset;
uint256 public burnPercentageSwap;
event EncryptedUTXOBroadcast(bytes32 indexed _oldRoot, bytes32 indexed _utxoId, EncryptedUTXO _encryptedUTXO);
event TransactionBroadcast(TransactionWithProof _tx, bytes32 indexed _merkleRoot, bytes32 indexed _txId);
event TransactionPublish(Transaction _tx, bytes32 indexed _merkleRoot, bytes32 indexed _txId);
event BatchPublish(uint256 indexed _batchNumber, bytes32 _oldRoot, bytes32 indexed _newRoot, bytes32 indexed _oracle, bytes32[20] _historicPath);
event BroadcastAddress(bytes32 indexed _merkleRoot, string indexed _address); // _address is a libp2p address
// Training Wheels
event DailyCapChanged(uint256 indexed _oldCap, uint256 indexed _newCap);
event BurnPercentageChanged(uint256 indexed _oldPercentage, uint256 indexed _newPercentage);
mapping(bytes32 => bool) public nullifierHashes;
mapping(bytes32 => bytes32) public utxoPrevRoots;
mapping(bytes32 => bytes32) public relay;
uint256 public batchNumber;
address public xftPool;
address public xft;
uint24 public fee;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
IVerifier _verifier,
address _token,
bytes32 _txKeyHash,
bytes32 _txWrapperKeyHash,
bytes32 _recursiveKeyHash,
address _xftPool
) public initializer {
__Pausable_init();
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
verifier = _verifier;
token = IElasticERC20(_token);
xft = _token;
txKeyHash = _txKeyHash;
txWrapperKeyHash = _txWrapperKeyHash;
recursiveKeyHash = _recursiveKeyHash;
xftPool = _xftPool;
swapRouter = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
STATE_DEPTH = 20;
MAX_ITEMS = 16;
MAX_UTXOS = 16;
merkleRoot = ZERO_VALUE;
validRoots.push(merkleRoot);
histRoot = INITIAL_STATE;
fee = 3000;
burnPercentageSwap = 5;
dailyCap = 1;
__EIP712_init("Momiji", "1");
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
modifier nonreentrant() {
assembly {
if tload(0) { revert(0, 0) }
tstore(0, 1)
}
_;
assembly {
tstore(0, 0)
}
}
receive() external payable {
if (msg.sender != _weth9) {
msg.sender.call{value: msg.value}("");
}
}
fallback() external payable {
if (msg.sender != _weth9) {
msg.sender.call{value: msg.value}("");
}
}
// Training Wheels
function updateCircuits(bytes32[] calldata _circuits) public onlyOwner {
txKeyHash = _circuits[0];
txWrapperKeyHash = _circuits[1];
recursiveKeyHash = _circuits[2];
verifier = IVerifier(_toAddress(_circuits[3]));
}
// Training Wheels
function emergencyPause() public onlyOwner {
_pause();
}
// Training Wheels
function emergencyUnpause() public onlyOwner {
_unpause();
}
// Training Wheels
function changeCap(uint256 _cap) public onlyOwner {
require(_cap < 1000, "momiji.changeCap: Cannot exceed 1000 bps");
emit DailyCapChanged(dailyCap, _cap);
dailyCap = _cap;
}
// Training Wheels
function changeBurnPercentage(uint256 _percentage) public onlyOwner {
require(_percentage < 100, "momiji.changeBurnPercentage: Cannot exceed 100%");
emit BurnPercentageChanged(burnPercentageSwap, _percentage);
burnPercentageSwap = _percentage;
}
// Training Wheels
function changePools(address[] calldata _addrs) public onlyOwner {
swapRouter = ISwapRouter(_addrs[0]);
xftPool = _addrs[1];
xft = _addrs[2];
token = IElasticERC20(_addrs[2]);
}
function getSpentNullifiers(bytes32[] calldata _nullifierHashes) external view returns (bool[] memory spent) {
uint256 nullifierHashesLength = _nullifierHashes.length;
spent = new bool[](nullifierHashesLength);
for (uint256 i; i < nullifierHashesLength; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}
function getValidRoots() public view returns (bytes32[] memory) {
return validRoots;
}
function getValidRootAtIndex(uint256 _index) public view returns (bytes32) {
return validRoots[_index];
}
function getValidRootsPaginated(uint256 _start, uint256 _end) public view returns (bytes32[] memory) {
require(_start < _end, "Invalid range");
require(_end < validRoots.length, "Invalid end index");
bytes32[] memory paginatedRoots = new bytes32[](_end - _start);
uint256 paginatedIndex = 0;
for (uint256 i = _start; i <= _end; i++) {
paginatedRoots[paginatedIndex] = validRoots[i];
paginatedIndex++;
}
return paginatedRoots;
}
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
return nullifierHashes[_nullifierHash];
}
function ecRecover(bytes32 _hash, bytes calldata _signature) public pure returns (address) {
return ECDSA.recover(_hash, _signature);
}
function _toAddress(bytes32 _address) internal pure returns (address) {
return address(uint160(uint256(_address)));
}
function _fromAddress(address _address) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_address)));
}
function hashTypedDataV4(Deposit calldata _deposit) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(
keccak256("DepositHash(bytes32 pi_hash)"),
_deposit.pi_hash
)));
}
function broadcastTransaction(TransactionWithProof calldata _tx, bytes32 _recipient) public nonreentrant {
bytes32 _signatureHash;
if (_tx.transaction.amount > 0) {
_signatureHash = _hashTypedDataV4(keccak256(abi.encode(
keccak256("DepositHash(bytes32 pi_hash)"),
_tx.transaction.deposit.pi_hash
)));
} else {
_signatureHash = 0;
}
bytes32 _piHash = hashCircuitInputsForTx(_tx.transaction, _signatureHash);
require(relay[_piHash] == 0, "momiji.broadcast: already broadcasted");
for (uint256 i = 0; i < 16; i++) {
if (_tx.transaction.recipients[i] == BROADCASTER_PAYMENT)
relay[_piHash] = _recipient;
}
emit TransactionBroadcast(_tx, merkleRoot, _piHash);
}
function broadcastAddress(string calldata _address) public nonreentrant {
emit BroadcastAddress(merkleRoot, _address);
}
function _swapForETH(uint256 _swapAmount, uint160 _priceLimit) internal returns (uint256 _amountOut) {
token.approve(address(swapRouter), _swapAmount);
_amountOut = swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: xft,
tokenOut: _weth9,
fee: fee,
recipient: address(this),
deadline: block.timestamp,
amountIn: _swapAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: _priceLimit
})
);
weth9.withdraw(_amountOut);
return _amountOut;
}
function verifyProof(
bytes calldata _proof,
Batch calldata _batch,
bytes32 _accumulator
) public view returns (bool) {
bytes32[] memory publicInputs = new bytes32[](17);
publicInputs[0] = prepareBatchPublicInputs(_batch, _accumulator);
for (uint256 i = 0; i < 16; i++) publicInputs[i + 1] = _batch.aggregation_object[i];
return verifier.verify(_proof, publicInputs);
}
function _publishDeposit(
Transaction calldata _tx,
bytes32 _signatureHash
) internal {
address depositor = ecRecover(_signatureHash, _tx.deposit.signature);
require(token.balanceOf(depositor) >= uint256(_tx.amount), "momiji._publishDeposit: insufficient balance");
token.burn(depositor, uint256(_tx.amount));
}
function _handleUTXOs(Transaction calldata _tx) internal {
for (uint256 j = 0; j < 16; j++) {
if (_tx.nullifier_hashes[j] != ZERO_VALUE) {
require(!nullifierHashes[_tx.nullifier_hashes[j]], 'momiji._publishWithdraw: nullifier spent');
nullifierHashes[_tx.nullifier_hashes[j]] = true;
}
if (_tx.commitments[j] != ZERO_VALUE) {
require(utxoPrevRoots[_tx.commitments[j]] == 0, 'momiji._publishWithdraw: utxo exists');
utxoPrevRoots[_tx.commitments[j]] = merkleRoot;
emit EncryptedUTXOBroadcast(
merkleRoot,
_tx.uids[j],
_tx.encrypted_utxo[j]
);
}
if (_tx.commitments_in[j] != ZERO_VALUE) require(utxoPrevRoots[_tx.commitments_in[j]] != 0, "momiji.publish: commitment doesn't exist");
}
}
function _createPayments(
Transaction calldata _tx,
Payment[] memory _payments,
bytes32 _piHash,
uint256 _txIndex
) internal {
for (uint256 j = 0; j < MAX_UTXOS; j++) {
bytes32 _recipient = ZERO_VALUE;
if (_tx.recipients[j] != ZERO_VALUE) {
if (_tx.recipients[j] == BROADCASTER_PAYMENT) {
_recipient = relay[_piHash];
} else {
_recipient = _tx.recipients[j];
}
uint256 _swapAmount = uint256(_tx.swap_amounts[j]);
uint256 _withdrawalAmount = uint256(_tx.withdrawals[j]) - _swapAmount;
_swapAmount = _swapAmount * (100 - burnPercentageSwap) / 100;
uint160 _priceLimit = (_tx.price_limit == ZERO_VALUE) ? 0 : uint160(uint256(_tx.price_limit));
_payments[(_txIndex * MAX_UTXOS) + j] = Payment(_recipient, _withdrawalAmount, _swapAmount, _priceLimit);
}
}
}
function _handlePayments(Payment[] memory _payments, uint256 _txCount) internal {
uint256 _paymentsCount = 1;
uint256 _xftForSwap;
uint160 _priceLimit;
bytes32[] memory _recipients = new bytes32[](_txCount * MAX_UTXOS + 1);
Payment memory _payment;
for (uint256 i = 0; i < _txCount; i++) {
for (uint256 j = 0; j < MAX_UTXOS; j++) {
_payment = _payments[i * MAX_UTXOS + j];
if (_payment.recipient != ZERO_VALUE && _payment.recipient != NO_PAYMENT) {
if (_payment.recipient == COINBASE_PAYMENT) _payment.recipient = _fromAddress(block.coinbase);
if (_payment.recipient == PROVER_PAYMENT) _payment.recipient = _fromAddress(msg.sender);
if (_priceLimit < _payment.price_limit) _priceLimit = _payment.price_limit;
uint256 _recipientIndex;
bytes32 _thisRecipient = _payment.recipient;
assembly {
_recipientIndex := tload(_thisRecipient)
}
if (_recipientIndex == 0) {
_recipients[_paymentsCount] = _payment.recipient;
_recipientIndex = i * MAX_UTXOS + j;
assembly {
tstore(_thisRecipient, _recipientIndex)
}
_paymentsCount++;
} else {
if (_payment.withdrawalAmount > 0) _payments[_recipientIndex].withdrawalAmount += _payment.withdrawalAmount;
if (_payment.swapAmount > 0) _payments[_recipientIndex].swapAmount += _payment.swapAmount;
_payments[i * MAX_UTXOS + j] = Payment(NO_PAYMENT, 0, 0, 0);
}
_xftForSwap += _payment.swapAmount;
}
}
}
// Training Wheels
uint256 _now = block.timestamp;
if (_now - lastCapReset > 86400) {
lastCapReset = _now;
dailyMint = 0;
}
uint256 _amountOut = 0;
if (_xftForSwap > 0) {
// Training Wheels
require(_xftForSwap + dailyMint <= (dailyCap * token.totalSupply() / 1000), "momiji._handlePayments: daily cap exceeded");
dailyMint += _xftForSwap;
token.mint(address(this), _xftForSwap);
_amountOut = _swapForETH(_xftForSwap, _priceLimit);
for (uint256 i = 1; i < _paymentsCount; i++) {
if (_recipients[i] != NO_PAYMENT) {
uint256 _thisPaymentIndex;
bytes32 _thisRecipient = _recipients[i];
assembly {
_thisPaymentIndex := tload(_thisRecipient)
}
_payment = _payments[uint256(_thisPaymentIndex)];
uint256 _amountOutShare;
if (_payment.swapAmount > 0) {
_amountOutShare = _payment.swapAmount * _amountOut / _xftForSwap;
payable(_toAddress(_recipients[i])).transfer(_amountOutShare);
}
uint256 _xftAmountOut = _payment.withdrawalAmount;
if (_xftAmountOut > 0) {
// Training Wheels
require(_xftAmountOut + dailyMint <= (dailyCap * token.totalSupply() / 1000), "momiji._handlePayments: daily cap exceeded");
dailyMint += _xftAmountOut;
token.mint(_toAddress(_recipients[i]), _xftAmountOut);
}
}
}
}
}
function _getSignatureHash(Transaction calldata _transaction) public view returns (bytes32 _signatureHash) {
if (_transaction.amount > 0) {
_signatureHash = _hashTypedDataV4(keccak256(abi.encode(
keccak256("DepositHash(bytes32 pi_hash)"),
hashCircuitInputsForTxWithoutDeposit(_transaction)
)));
} else {
_signatureHash = 0;
}
return _signatureHash;
}
function _accumulatePublicInputs(
bytes32 _previousAccumulator,
bytes32 _publicInputsHash
) public view returns (bytes32) {
bytes32[] memory _hash = new bytes32[](4);
_hash[0] = _previousAccumulator;
_hash[1] = _publicInputsHash;
_hash[2] = txKeyHash;
_hash[3] = (_previousAccumulator == ZERO_VALUE) ? txWrapperKeyHash : recursiveKeyHash;
return _hashAndMod(_hash);
}
function _updateState(Batch memory _batch) internal {
validRoots.push(_batch.new_root);
merkleRoot = _batch.new_root;
histRoot = _batch.new_hist_root;
batchNumber++;
}
function simulatePublish(Batch calldata _batch) public returns (bytes memory returnData) {
(bool success, returnData) = address(this).call(abi.encodeWithSelector(IMomiji.publish.selector, "0x", _batch));
}
function publish(bytes calldata _proof, Batch calldata _batch) public nonreentrant whenNotPaused {
require(batchNumber < 2 ** STATE_DEPTH, "momiji.publish: state depth reached");
require(_batch.tx_key_hash == txKeyHash, 'momiji.publish: invalid tx keyHash');
require(_batch.recursive_key_hash == recursiveKeyHash, 'momiji.publish: invalid recursive keyHash');
require(_batch.old_hist_root == histRoot, 'momiji.publish: invalid historic root');
uint256 _txCount = _batch.transactions.length;
bytes32 _accumulator = ZERO_VALUE;
Payment[] memory _payments = new Payment[](MAX_ITEMS * MAX_UTXOS);
for (uint256 i = 0; i < _txCount; i++) {
require(_batch.transactions[i].utxo_root != ZERO_VALUE, 'momiji.publish: tx must not be empty');
require(_batch.transactions[i].utxo_root != ZERO_UTXO_ROOT, 'momiji.publish: tx utxos must not be empty');
require(uint256(_batch.transactions[i].timestamp) % 60 == 0, 'momiji.publish: timestamp must be divisible by 60');
require(_batch.transactions[i].timestamp < bytes32(block.timestamp), 'momiji.publish: tx not yet valid');
require(uint256(_batch.transactions[i].deadline) % 60 == 0, 'momiji.publish: deadline must be divisible by 60');
require(_batch.transactions[i].deadline > bytes32(block.timestamp), 'momiji.publish: tx has expired');
bytes32 _signatureHash = _getSignatureHash(_batch.transactions[i]);
bytes32 _publicInputsHash = hashCircuitInputsForTx(_batch.transactions[i], _signatureHash);
_accumulator = _accumulatePublicInputs(_accumulator, _publicInputsHash);
if (_signatureHash > 0) _publishDeposit(_batch.transactions[i], _signatureHash);
_handleUTXOs(_batch.transactions[i]);
_createPayments(_batch.transactions[i], _payments, _publicInputsHash, i);
emit TransactionPublish(_batch.transactions[i], merkleRoot, _publicInputsHash);
}
_handlePayments(_payments, _txCount);
_updateState(_batch);
emit BatchPublish(batchNumber, merkleRoot, _batch.new_root, _batch.oracle, _batch.historic_path);
require(_proof.length > 0, 'momiji.publish: no proof');
require(verifyProof(_proof, _batch, _accumulator), 'momiji.publish: invalid proof');
}
function prepareBatchPublicInputs(
Batch calldata _batch,
bytes32 _accumulator
) public view returns (bytes32) {
bytes32[] memory _hash = new bytes32[](26);
_hash[0] = _batch.new_root;
_hash[1] = _batch.old_hist_root;
_hash[2] = _batch.new_hist_root;
_hash[3] = _accumulator;
_hash[4] = _batch.tx_key_hash;
_hash[5] = _batch.recursive_key_hash;
for (uint256 i = 0; i < 20; i++) {
_hash[6 + i] = _batch.historic_path[i];
}
return _hashAndMod(_hash);
}
function hashCircuitInputsForTx(Transaction calldata _tx, bytes32 _signatureHash) public view returns (bytes32) {
bytes32[] memory _hashInputs = new bytes32[](53);
_hashInputs[0] = _tx.current_root;
_hashInputs[1] = _tx.utxo_root;
_hashInputs[2] = _tx.amount;
_hashInputs[3] = hashContractOnlyInputsForTx(_tx, _signatureHash);
for (uint256 i = 0; i < 16; i++) {
_hashInputs[4] = bytes32(uint256(_hashInputs[4]) + uint256(_tx.withdrawals[i]));
_hashInputs[5 + i] = _tx.commitments_in[i];
_hashInputs[21 + i] = _tx.commitments[i];
_hashInputs[37 + i] = _tx.nullifier_hashes[i];
}
return _hashAndMod(_hashInputs);
}
function hashContractOnlyInputsForTx(Transaction calldata _tx, bytes32 _signatureHash) public view returns (bytes32) {
bytes32[] memory _hashInputs = new bytes32[](100);
_hashInputs[0] = _tx.timestamp;
_hashInputs[1] = _tx.deadline;
_hashInputs[2] = bytes32(uint256(_signatureHash) % FIELD_MODULUS);
_hashInputs[3] = _tx.price_limit;
for (uint256 i = 0; i < 16; i++) {
_hashInputs[4 + i] = _tx.recipients[i];
_hashInputs[20 + i] = _tx.swap_amounts[i];
_hashInputs[36 + i] = _tx.uids[i];
_hashInputs[52 + (i * 3)] = _tx.encrypted_utxo[i].secret;
_hashInputs[53 + (i * 3)] = _tx.encrypted_utxo[i].amount;
_hashInputs[54 + (i * 3)] = _tx.encrypted_utxo[i].data;
}
return _hashAndMod(_hashInputs);
}
function hashCircuitInputsForTxWithoutDeposit(Transaction calldata _tx) public view returns (bytes32) {
bytes32[] memory _hashInputs = new bytes32[](53);
_hashInputs[0] = _tx.current_root;
_hashInputs[1] = _tx.utxo_root;
_hashInputs[2] = _tx.amount;
_hashInputs[3] = hashContractOnlyInputsForTxWithoutDeposit(_tx);
for (uint256 i = 0; i < 16; i++) {
_hashInputs[4] = bytes32(uint256(_hashInputs[4]) + uint256(_tx.withdrawals[i]));
_hashInputs[5 + i] = _tx.commitments_in[i];
_hashInputs[21 + i] = _tx.commitments[i];
_hashInputs[37 + i] = _tx.nullifier_hashes[i];
}
return _hashAndMod(_hashInputs);
}
function hashContractOnlyInputsForTxWithoutDeposit(Transaction calldata _tx) public view returns (bytes32) {
bytes32[] memory _hashInputs = new bytes32[](99);
_hashInputs[0] = _tx.timestamp;
_hashInputs[1] = _tx.deadline;
_hashInputs[2] = _tx.price_limit;
for (uint256 i = 0; i < 16; i++) {
_hashInputs[3 + i] = _tx.recipients[i];
_hashInputs[19 + i] = _tx.swap_amounts[i];
_hashInputs[35 + i] = _tx.uids[i];
_hashInputs[51 + (i * 3)] = _tx.encrypted_utxo[i].secret;
_hashInputs[52 + (i * 3)] = _tx.encrypted_utxo[i].amount;
_hashInputs[53 + (i * 3)] = _tx.encrypted_utxo[i].data;
}
return _hashAndMod(_hashInputs);
}
function _hashAndMod(bytes32[] memory _hashes) internal view returns (bytes32) {
bytes32 _hash = keccak256(abi.encodePacked(_hashes));
return bytes32(uint256(_hash) % FIELD_MODULUS);
}
}
\ No newline at end of file
File added
~#쓈YD;2Nո7N0Qz5NX&QDžJ^U?΢VG[Ba^"c*A<j_FAQVrci
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment