// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // Cache the end of the memory to calculate the length later.
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(str, length) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) return toString(uint256(value));
unchecked {
str = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // Cache the end to calculate the length later.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
str := add(str, 2)
mstore(str, 40) // Store the length.
let o := add(str, 0x20)
mstore(add(o, 40), 0) // Zeroize the slot after the string.
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Store the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
mstore(last, 0) // Zeroize the slot after the string.
mstore(result, totalLength) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n) // Store the length.
let o := add(result, 0x20)
mstore(o, s) // Store the bytes of the string.
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(result, 0x40)) // Allocate the memory.
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
mstore(result, 0) // Zeroize the length slot.
mstore(add(result, 0x1f), packed) // Store the length and bytes.
mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
or( // Load the length and the bytes of `a` and `b`.
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
resultA := mload(0x40) // Grab the free memory pointer.
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Ownable} from "./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `user`'s roles is updated to `roles`.
/// Each bit of `roles` represents whether the role is set.
event RolesUpdated(address indexed user, uint256 indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The role slot of `user` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
/// let roleSlot := keccak256(0x00, 0x20)
/// ```
/// This automatically ignores the upper bits of the `user` in case
/// they are not clean, as well as keep the `keccak256` under 32-bytes.
///
/// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Overwrite the roles directly without authorization guard.
function _setRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Store the new value.
sstore(keccak256(0x0c, 0x20), roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Updates the roles directly without authorization guard.
/// If `on` is true, each set bit of `roles` will be turned on,
/// otherwise, each set bit of `roles` will be turned off.
function _updateRoles(address user, uint256 roles, bool on) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value.
let current := sload(roleSlot)
// Compute the updated roles if `on` is true.
let updated := or(current, roles)
// Compute the updated roles if `on` is false.
// Use `and` to compute the intersection of `current` and `roles`,
// `xor` it with `current` to flip the bits in the intersection.
if iszero(on) { updated := xor(current, and(current, roles)) }
// Then, store the new value.
sstore(roleSlot, updated)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
/// @dev Grants the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn on.
function _grantRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, true);
}
/// @dev Removes the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn off.
function _removeRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.
function _checkRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,
/// and does not have any of the `roles`.
/// Checks for ownership first, then lazily checks for roles.
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,
/// and is not the owner.
/// Checks for roles first, then lazily checks for ownership.
function _checkRolesOrOwner(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity
// cleans dirty upper bits when storing variables into memory.
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
/// @solidity memory-safe-assembly
assembly {
// Grab the pointer to the free memory.
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
// The absence of lookup tables, De Bruijn, etc., here is intentional for
// smaller bytecode, as this function is not meant to be called on-chain.
for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.
// Push back into the ordinals array if the bit is set.
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
// Store the length of `ordinals`.
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.
mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be an no-op for the role.
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be an no-op for the role.
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be an no-op for the role.
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the roles of `user`.
function rolesOf(address user) public view virtual returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles != 0;
}
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by an account with `roles`.
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account
/// with `roles`. Checks for ownership first, then lazily checks for roles.
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`
/// or the owner. Checks for roles first, then lazily checks for ownership.
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ROLE CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// IYKYK
uint256 internal constant _ROLE_0 = 1 << 0;
uint256 internal constant _ROLE_1 = 1 << 1;
uint256 internal constant _ROLE_2 = 1 << 2;
uint256 internal constant _ROLE_3 = 1 << 3;
uint256 internal constant _ROLE_4 = 1 << 4;
uint256 internal constant _ROLE_5 = 1 << 5;
uint256 internal constant _ROLE_6 = 1 << 6;
uint256 internal constant _ROLE_7 = 1 << 7;
uint256 internal constant _ROLE_8 = 1 << 8;
uint256 internal constant _ROLE_9 = 1 << 9;
uint256 internal constant _ROLE_10 = 1 << 10;
uint256 internal constant _ROLE_11 = 1 << 11;
uint256 internal constant _ROLE_12 = 1 << 12;
uint256 internal constant _ROLE_13 = 1 << 13;
uint256 internal constant _ROLE_14 = 1 << 14;
uint256 internal constant _ROLE_15 = 1 << 15;
uint256 internal constant _ROLE_16 = 1 << 16;
uint256 internal constant _ROLE_17 = 1 << 17;
uint256 internal constant _ROLE_18 = 1 << 18;
uint256 internal constant _ROLE_19 = 1 << 19;
uint256 internal constant _ROLE_20 = 1 << 20;
uint256 internal constant _ROLE_21 = 1 << 21;
uint256 internal constant _ROLE_22 = 1 << 22;
uint256 internal constant _ROLE_23 = 1 << 23;
uint256 internal constant _ROLE_24 = 1 << 24;
uint256 internal constant _ROLE_25 = 1 << 25;
uint256 internal constant _ROLE_26 = 1 << 26;
uint256 internal constant _ROLE_27 = 1 << 27;
uint256 internal constant _ROLE_28 = 1 << 28;
uint256 internal constant _ROLE_29 = 1 << 29;
uint256 internal constant _ROLE_30 = 1 << 30;
uint256 internal constant _ROLE_31 = 1 << 31;
uint256 internal constant _ROLE_32 = 1 << 32;
uint256 internal constant _ROLE_33 = 1 << 33;
uint256 internal constant _ROLE_34 = 1 << 34;
uint256 internal constant _ROLE_35 = 1 << 35;
uint256 internal constant _ROLE_36 = 1 << 36;
uint256 internal constant _ROLE_37 = 1 << 37;
uint256 internal constant _ROLE_38 = 1 << 38;
uint256 internal constant _ROLE_39 = 1 << 39;
uint256 internal constant _ROLE_40 = 1 << 40;
uint256 internal constant _ROLE_41 = 1 << 41;
uint256 internal constant _ROLE_42 = 1 << 42;
uint256 internal constant _ROLE_43 = 1 << 43;
uint256 internal constant _ROLE_44 = 1 << 44;
uint256 internal constant _ROLE_45 = 1 << 45;
uint256 internal constant _ROLE_46 = 1 << 46;
uint256 internal constant _ROLE_47 = 1 << 47;
uint256 internal constant _ROLE_48 = 1 << 48;
uint256 internal constant _ROLE_49 = 1 << 49;
uint256 internal constant _ROLE_50 = 1 << 50;
uint256 internal constant _ROLE_51 = 1 << 51;
uint256 internal constant _ROLE_52 = 1 << 52;
uint256 internal constant _ROLE_53 = 1 << 53;
uint256 internal constant _ROLE_54 = 1 << 54;
uint256 internal constant _ROLE_55 = 1 << 55;
uint256 internal constant _ROLE_56 = 1 << 56;
uint256 internal constant _ROLE_57 = 1 << 57;
uint256 internal constant _ROLE_58 = 1 << 58;
uint256 internal constant _ROLE_59 = 1 << 59;
uint256 internal constant _ROLE_60 = 1 << 60;
uint256 internal constant _ROLE_61 = 1 << 61;
uint256 internal constant _ROLE_62 = 1 << 62;
uint256 internal constant _ROLE_63 = 1 << 63;
uint256 internal constant _ROLE_64 = 1 << 64;
uint256 internal constant _ROLE_65 = 1 << 65;
uint256 internal constant _ROLE_66 = 1 << 66;
uint256 internal constant _ROLE_67 = 1 << 67;
uint256 internal constant _ROLE_68 = 1 << 68;
uint256 internal constant _ROLE_69 = 1 << 69;
uint256 internal constant _ROLE_70 = 1 << 70;
uint256 internal constant _ROLE_71 = 1 << 71;
uint256 internal constant _ROLE_72 = 1 << 72;
uint256 internal constant _ROLE_73 = 1 << 73;
uint256 internal constant _ROLE_74 = 1 << 74;
uint256 internal constant _ROLE_75 = 1 << 75;
uint256 internal constant _ROLE_76 = 1 << 76;
uint256 internal constant _ROLE_77 = 1 << 77;
uint256 internal constant _ROLE_78 = 1 << 78;
uint256 internal constant _ROLE_79 = 1 << 79;
uint256 internal constant _ROLE_80 = 1 << 80;
uint256 internal constant _ROLE_81 = 1 << 81;
uint256 internal constant _ROLE_82 = 1 << 82;
uint256 internal constant _ROLE_83 = 1 << 83;
uint256 internal constant _ROLE_84 = 1 << 84;
uint256 internal constant _ROLE_85 = 1 << 85;
uint256 internal constant _ROLE_86 = 1 << 86;
uint256 internal constant _ROLE_87 = 1 << 87;
uint256 internal constant _ROLE_88 = 1 << 88;
uint256 internal constant _ROLE_89 = 1 << 89;
uint256 internal constant _ROLE_90 = 1 << 90;
uint256 internal constant _ROLE_91 = 1 << 91;
uint256 internal constant _ROLE_92 = 1 << 92;
uint256 internal constant _ROLE_93 = 1 << 93;
uint256 internal constant _ROLE_94 = 1 << 94;
uint256 internal constant _ROLE_95 = 1 << 95;
uint256 internal constant _ROLE_96 = 1 << 96;
uint256 internal constant _ROLE_97 = 1 << 97;
uint256 internal constant _ROLE_98 = 1 << 98;
uint256 internal constant _ROLE_99 = 1 << 99;
uint256 internal constant _ROLE_100 = 1 << 100;
uint256 internal constant _ROLE_101 = 1 << 101;
uint256 internal constant _ROLE_102 = 1 << 102;
uint256 internal constant _ROLE_103 = 1 << 103;
uint256 internal constant _ROLE_104 = 1 << 104;
uint256 internal constant _ROLE_105 = 1 << 105;
uint256 internal constant _ROLE_106 = 1 << 106;
uint256 internal constant _ROLE_107 = 1 << 107;
uint256 internal constant _ROLE_108 = 1 << 108;
uint256 internal constant _ROLE_109 = 1 << 109;
uint256 internal constant _ROLE_110 = 1 << 110;
uint256 internal constant _ROLE_111 = 1 << 111;
uint256 internal constant _ROLE_112 = 1 << 112;
uint256 internal constant _ROLE_113 = 1 << 113;
uint256 internal constant _ROLE_114 = 1 << 114;
uint256 internal constant _ROLE_115 = 1 << 115;
uint256 internal constant _ROLE_116 = 1 << 116;
uint256 internal constant _ROLE_117 = 1 << 117;
uint256 internal constant _ROLE_118 = 1 << 118;
uint256 internal constant _ROLE_119 = 1 << 119;
uint256 internal constant _ROLE_120 = 1 << 120;
uint256 internal constant _ROLE_121 = 1 << 121;
uint256 internal constant _ROLE_122 = 1 << 122;
uint256 internal constant _ROLE_123 = 1 << 123;
uint256 internal constant _ROLE_124 = 1 << 124;
uint256 internal constant _ROLE_125 = 1 << 125;
uint256 internal constant _ROLE_126 = 1 << 126;
uint256 internal constant _ROLE_127 = 1 << 127;
uint256 internal constant _ROLE_128 = 1 << 128;
uint256 internal constant _ROLE_129 = 1 << 129;
uint256 internal constant _ROLE_130 = 1 << 130;
uint256 internal constant _ROLE_131 = 1 << 131;
uint256 internal constant _ROLE_132 = 1 << 132;
uint256 internal constant _ROLE_133 = 1 << 133;
uint256 internal constant _ROLE_134 = 1 << 134;
uint256 internal constant _ROLE_135 = 1 << 135;
uint256 internal constant _ROLE_136 = 1 << 136;
uint256 internal constant _ROLE_137 = 1 << 137;
uint256 internal constant _ROLE_138 = 1 << 138;
uint256 internal constant _ROLE_139 = 1 << 139;
uint256 internal constant _ROLE_140 = 1 << 140;
uint256 internal constant _ROLE_141 = 1 << 141;
uint256 internal constant _ROLE_142 = 1 << 142;
uint256 internal constant _ROLE_143 = 1 << 143;
uint256 internal constant _ROLE_144 = 1 << 144;
uint256 internal constant _ROLE_145 = 1 << 145;
uint256 internal constant _ROLE_146 = 1 << 146;
uint256 internal constant _ROLE_147 = 1 << 147;
uint256 internal constant _ROLE_148 = 1 << 148;
uint256 internal constant _ROLE_149 = 1 << 149;
uint256 internal constant _ROLE_150 = 1 << 150;
uint256 internal constant _ROLE_151 = 1 << 151;
uint256 internal constant _ROLE_152 = 1 << 152;
uint256 internal constant _ROLE_153 = 1 << 153;
uint256 internal constant _ROLE_154 = 1 << 154;
uint256 internal constant _ROLE_155 = 1 << 155;
uint256 internal constant _ROLE_156 = 1 << 156;
uint256 internal constant _ROLE_157 = 1 << 157;
uint256 internal constant _ROLE_158 = 1 << 158;
uint256 internal constant _ROLE_159 = 1 << 159;
uint256 internal constant _ROLE_160 = 1 << 160;
uint256 internal constant _ROLE_161 = 1 << 161;
uint256 internal constant _ROLE_162 = 1 << 162;
uint256 internal constant _ROLE_163 = 1 << 163;
uint256 internal constant _ROLE_164 = 1 << 164;
uint256 internal constant _ROLE_165 = 1 << 165;
uint256 internal constant _ROLE_166 = 1 << 166;
uint256 internal constant _ROLE_167 = 1 << 167;
uint256 internal constant _ROLE_168 = 1 << 168;
uint256 internal constant _ROLE_169 = 1 << 169;
uint256 internal constant _ROLE_170 = 1 << 170;
uint256 internal constant _ROLE_171 = 1 << 171;
uint256 internal constant _ROLE_172 = 1 << 172;
uint256 internal constant _ROLE_173 = 1 << 173;
uint256 internal constant _ROLE_174 = 1 << 174;
uint256 internal constant _ROLE_175 = 1 << 175;
uint256 internal constant _ROLE_176 = 1 << 176;
uint256 internal constant _ROLE_177 = 1 << 177;
uint256 internal constant _ROLE_178 = 1 << 178;
uint256 internal constant _ROLE_179 = 1 << 179;
uint256 internal constant _ROLE_180 = 1 << 180;
uint256 internal constant _ROLE_181 = 1 << 181;
uint256 internal constant _ROLE_182 = 1 << 182;
uint256 internal constant _ROLE_183 = 1 << 183;
uint256 internal constant _ROLE_184 = 1 << 184;
uint256 internal constant _ROLE_185 = 1 << 185;
uint256 internal constant _ROLE_186 = 1 << 186;
uint256 internal constant _ROLE_187 = 1 << 187;
uint256 internal constant _ROLE_188 = 1 << 188;
uint256 internal constant _ROLE_189 = 1 << 189;
uint256 internal constant _ROLE_190 = 1 << 190;
uint256 internal constant _ROLE_191 = 1 << 191;
uint256 internal constant _ROLE_192 = 1 << 192;
uint256 internal constant _ROLE_193 = 1 << 193;
uint256 internal constant _ROLE_194 = 1 << 194;
uint256 internal constant _ROLE_195 = 1 << 195;
uint256 internal constant _ROLE_196 = 1 << 196;
uint256 internal constant _ROLE_197 = 1 << 197;
uint256 internal constant _ROLE_198 = 1 << 198;
uint256 internal constant _ROLE_199 = 1 << 199;
uint256 internal constant _ROLE_200 = 1 << 200;
uint256 internal constant _ROLE_201 = 1 << 201;
uint256 internal constant _ROLE_202 = 1 << 202;
uint256 internal constant _ROLE_203 = 1 << 203;
uint256 internal constant _ROLE_204 = 1 << 204;
uint256 internal constant _ROLE_205 = 1 << 205;
uint256 internal constant _ROLE_206 = 1 << 206;
uint256 internal constant _ROLE_207 = 1 << 207;
uint256 internal constant _ROLE_208 = 1 << 208;
uint256 internal constant _ROLE_209 = 1 << 209;
uint256 internal constant _ROLE_210 = 1 << 210;
uint256 internal constant _ROLE_211 = 1 << 211;
uint256 internal constant _ROLE_212 = 1 << 212;
uint256 internal constant _ROLE_213 = 1 << 213;
uint256 internal constant _ROLE_214 = 1 << 214;
uint256 internal constant _ROLE_215 = 1 << 215;
uint256 internal constant _ROLE_216 = 1 << 216;
uint256 internal constant _ROLE_217 = 1 << 217;
uint256 internal constant _ROLE_218 = 1 << 218;
uint256 internal constant _ROLE_219 = 1 << 219;
uint256 internal constant _ROLE_220 = 1 << 220;
uint256 internal constant _ROLE_221 = 1 << 221;
uint256 internal constant _ROLE_222 = 1 << 222;
uint256 internal constant _ROLE_223 = 1 << 223;
uint256 internal constant _ROLE_224 = 1 << 224;
uint256 internal constant _ROLE_225 = 1 << 225;
uint256 internal constant _ROLE_226 = 1 << 226;
uint256 internal constant _ROLE_227 = 1 << 227;
uint256 internal constant _ROLE_228 = 1 << 228;
uint256 internal constant _ROLE_229 = 1 << 229;
uint256 internal constant _ROLE_230 = 1 << 230;
uint256 internal constant _ROLE_231 = 1 << 231;
uint256 internal constant _ROLE_232 = 1 << 232;
uint256 internal constant _ROLE_233 = 1 << 233;
uint256 internal constant _ROLE_234 = 1 << 234;
uint256 internal constant _ROLE_235 = 1 << 235;
uint256 internal constant _ROLE_236 = 1 << 236;
uint256 internal constant _ROLE_237 = 1 << 237;
uint256 internal constant _ROLE_238 = 1 << 238;
uint256 internal constant _ROLE_239 = 1 << 239;
uint256 internal constant _ROLE_240 = 1 << 240;
uint256 internal constant _ROLE_241 = 1 << 241;
uint256 internal constant _ROLE_242 = 1 << 242;
uint256 internal constant _ROLE_243 = 1 << 243;
uint256 internal constant _ROLE_244 = 1 << 244;
uint256 internal constant _ROLE_245 = 1 << 245;
uint256 internal constant _ROLE_246 = 1 << 246;
uint256 internal constant _ROLE_247 = 1 << 247;
uint256 internal constant _ROLE_248 = 1 << 248;
uint256 internal constant _ROLE_249 = 1 << 249;
uint256 internal constant _ROLE_250 = 1 << 250;
uint256 internal constant _ROLE_251 = 1 << 251;
uint256 internal constant _ROLE_252 = 1 << 252;
uint256 internal constant _ROLE_253 = 1 << 253;
uint256 internal constant _ROLE_254 = 1 << 254;
uint256 internal constant _ROLE_255 = 1 << 255;
}
// SPDX-License-Identifier: AGPL-3.0-only
//▗▄▄▖ ▗▞▀▚▖▗▞▀▜▌█ ▗▄▖ ▗▖ ▄▄▄ ■ ▄▄▄ ▗▞▀▜▌▗▞▀▘ ■ ▄ ▄▄▄ ▄▄▄▄
//▐▌ ▐▌▐▛▀▀▘▝▚▄▟▌█ ▐▌ ▐▌▐▌ ▀▄▄▗▄▟▙▄▖█ ▝▚▄▟▌▝▚▄▖▗▄▟▙▄▖▄ █ █ █ █
//▐▛▀▚▖▝▚▄▄▖ █ ▐▛▀▜▌▐▛▀▚▖▄▄▄▀ ▐▌ █ ▐▌ █ ▀▄▄▄▀ █ █
//▐▌ ▐▌ █ ▐▌ ▐▌▐▙▄▞▘ ▐▌ ▐▌ █
// ▐▌ ▐▌
import {UltraVerifier} from "../src/verifier.sol";
import {SSTORE2} from "lib/solady/src/utils/SSTORE2.sol";
import {LibString} from "lib/solady/src/utils/LibString.sol";
import {Ownable} from "lib/solady/src/auth/OwnableRoles.sol";
pragma solidity ^0.8.13;
contract RealAbstraction is Ownable {
uint256 public lastPart;
address public verifier;
string[] public projectUrls;
mapping(uint => line) public linePart;
//sstore2 seems worst in cost here
struct line {
address creator;
uint256 field1;
uint256 field2;
uint256 field3;
uint256 field4;
}
UltraVerifier public ultraVerifier;
address[4] public html;
constructor(address _verifier, address owner) {
verifier = _verifier;
ultraVerifier = UltraVerifier(verifier);
_initializeOwner(owner);
projectUrls = new string[](4);
}
function title() external view returns (string memory) {
return "Real Abstraction (A Line Made by Proofs)";
}
function authors() external view returns (string[] memory) {
string[] memory a = new string[](1);
a[0] = "Paul Seidler";
return a;
}
function addresses() external view returns (address[] memory) {
address[] memory b = new address[](2);
b[0] = address(this);
b[1] = verifier;
return b;
}
function urls() external view returns (string[] memory) {
return projectUrls;
}
function addUrls(string memory url, uint256 pos) external onlyOwner {
require(pos < projectUrls.length, "Invalid position");
projectUrls[pos] = url;
}
function addShowHtml(string memory a, string memory b) external onlyOwner {
html[0] = SSTORE2.write(bytes(a));
html[1] = SSTORE2.write(bytes(b));
}
function addProofGenerationHTML(
string memory c,
string memory d
) external onlyOwner {
html[2] = SSTORE2.write(bytes(c));
html[3] = SSTORE2.write(bytes(d));
}
// this is a test function
// function ForceAddLineONLYTEST(
// uint256 a,
// uint256 b,
// uint256 c,
// uint256 d
// ) external onlyOwner {
// linePart[lastPart] = line(
// msg.sender,
// uint256(a),
// uint256(b),
// uint256(c),
// uint256(d)
// );
// lastPart = lastPart + 1;
// }
function createLinePart(
bytes calldata _proof,
bytes32[] calldata _publicInputs
) external {
require(verifyProof(_proof, _publicInputs) == true);
linePart[lastPart] = line(
msg.sender,
uint256(_publicInputs[0]),
uint256(_publicInputs[1]),
uint256(_publicInputs[2]),
uint256(_publicInputs[3])
);
lastPart = lastPart + 1;
}
function verifyProof(
bytes calldata _proof,
bytes32[] calldata _publicInputs
) public view returns (bool) {
return ultraVerifier.verify(_proof, _publicInputs);
}
///from here obscure render part
function generatePathArray() internal view returns (string memory) {
string memory allArrays = "[";
for (uint i = 0; i < lastPart; i++) {
string memory oneArray = string.concat(
"[",
LibString.toString(linePart[i].field1),
"n,",
LibString.toString(linePart[i].field2),
"n,",
LibString.toString(linePart[i].field3),
"n,",
LibString.toString(linePart[i].field4),
"n],"
);
allArrays = string.concat(allArrays, oneArray);
}
return string.concat(allArrays, "]");
}
function text() external view returns (string memory) {
return
string.concat(
string(SSTORE2.read(html[0])),
generatePathArray(),
string(SSTORE2.read(html[1]))
);
}
function returnPathes(
uint256 start,
uint256 end
) external view returns (uint[] memory, address[] memory) {
require(start < end, "END > START");
require(end < lastPart + 1, "END > LAST PATH");
uint[] memory allPathes = new uint256[]((end - start) * 4);
address[] memory allAdresses = new address[](end - start);
uint256 counter = 0;
uint256 counterAddress = 0;
for (start; start < end; start = start + 1) {
allPathes[counter + 0] = linePart[start].field1;
allPathes[counter + 1] = linePart[start].field2;
allPathes[counter + 2] = linePart[start].field3;
allPathes[counter + 3] = linePart[start].field4;
counter = counter + 4;
allAdresses[start] = linePart[start].creator;
counterAddress = counterAddress + 1;
}
return (allPathes, allAdresses);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)
library SSTORE2 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The proxy initialization code.
uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;
/// @dev Hash of the `_CREATE3_PROXY_INITCODE`.
/// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the storage contract.
error DeploymentFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Writes `data` into the bytecode of a storage contract and returns its address.
function write(bytes memory data) internal returns (address pointer) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.
/**
* ---------------------------------------------------+
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------|
* 61 l | PUSH2 l | l | |
* 80 | DUP1 | l l | |
* 60 0xa | PUSH1 0xa | 0xa l l | |
* 3D | RETURNDATASIZE | 0 0xa l l | |
* 39 | CODECOPY | l | [0..l): code |
* 3D | RETURNDATASIZE | 0 l | [0..l): code |
* F3 | RETURN | | [0..l): code |
* 00 | STOP | | |
* ---------------------------------------------------+
* @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.
* Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
*/
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
// Deploy a new contract with the generated creation code.
pointer := create(0, add(data, 0x15), add(n, 0xb))
if iszero(pointer) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Writes `data` into the bytecode of a storage contract with `salt`
/// and returns its normal CREATE2 deterministic address.
function writeCounterfactual(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
// Deploy a new contract with the generated creation code.
pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
if iszero(pointer) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Writes `data` into the bytecode of a storage contract and returns its address.
/// This uses the so-called "CREATE3" workflow,
/// which means that `pointer` is agnostic to `data, and only depends on `salt`.
function writeDeterministic(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.
let proxy := create2(0, 0x10, 0x10, salt)
if iszero(proxy) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, proxy) // Store the proxy's address.
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
pointer := keccak256(0x1e, 0x17)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
if iszero(
mul( // The arguments of `mul` are evaluated last to first.
extcodesize(pointer),
call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
)
) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADDRESS CALCULATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the initialization code hash of the storage contract for `data`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
hash := keccak256(add(data, 0x15), add(n, 0xb))
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`
function predictCounterfactualAddress(bytes memory data, bytes32 salt)
internal
view
returns (address pointer)
{
pointer = predictCounterfactualAddress(data, salt, address(this));
}
/// @dev Returns the CREATE2 address of the storage contract for `data`
/// deployed with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictCounterfactualAddress(bytes memory data, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(data);
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
// Restore the part of the free memory pointer that has been overwritten.
mstore(0x35, 0)
}
}
/// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.
function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
pointer = predictDeterministicAddress(salt, address(this));
}
/// @dev Returns the "CREATE3" deterministic address for `salt` with `deployer`.
function predictDeterministicAddress(bytes32 salt, address deployer)
internal
pure
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, deployer) // Store `deployer`.
mstore8(0x0b, 0xff) // Store the prefix.
mstore(0x20, salt) // Store the salt.
mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.
mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.
mstore(0x40, m) // Restore the free memory pointer.
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
pointer := keccak256(0x1e, 0x17)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* READ LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.
function read(address pointer) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
mstore(data, n) // Store the length.
mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.
function read(address pointer, uint256 start) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
extcodecopy(pointer, add(data, 0x1f), start, add(n, 0x21))
mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.
}
}
/// @dev Returns a slice of the data on `pointer` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `pointer` MUST be deployed via the SSTORE2 write functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `pointer` does not have any code.
function read(address pointer, uint256 start, uint256 end)
internal
view
returns (bytes memory data)
{
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
if iszero(and(0xff, mload(add(data, d)))) {
let n := sub(extcodesize(pointer), 0x01)
returndatacopy(returndatasize(), returndatasize(), shr(64, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(data, d) // Store the length.
mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.
}
}
}
// Verification Key Hash: 1186bda0e757f19045fa23c64bb1eb2e42e8debb7a22a6f505398d8b0d5ec9f6
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
library UltraVerificationKey {
function verificationKeyHash() internal pure returns(bytes32) {
return 0x1186bda0e757f19045fa23c64bb1eb2e42e8debb7a22a6f505398d8b0d5ec9f6;
}
function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure {
assembly {
mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000040000) // vk.circuit_size
mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000004) // vk.num_inputs
mstore(add(_vk, 0x40), 0x19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e0) // vk.work_root
mstore(add(_vk, 0x60), 0x30644259cd94e7dd5045d7a27013b7fcd21c9e3b7fa75222e7bda49b729b0401) // vk.domain_inverse
mstore(add(_vk, 0x80), 0x23e03c8dd6c06e1fcd404be83dec0b632f86c204399381f31ba5ef1b41041f43) // vk.Q1.x
mstore(add(_vk, 0xa0), 0x30204846ef63716ad7bdf8f35ad8ed3e7da6f1ebd540c485fe0589dfb51e1de4) // vk.Q1.y
mstore(add(_vk, 0xc0), 0x1972c268cb3dbb1d5fca4ec17f5ef711fbe24cee554c6be8474c20149c89f930) // vk.Q2.x
mstore(add(_vk, 0xe0), 0x1a4a60843aef61a949b434df40b19c6735dc3ff7e72f058dd777774ea0420c41) // vk.Q2.y
mstore(add(_vk, 0x100), 0x127e39e7a752c79d1fa89ae670ff3bf8a972c46edcf42ba66de29c8b99f68fb1) // vk.Q3.x
mstore(add(_vk, 0x120), 0x23624ba7d20ace9a7c834a85a924b31f6e5e9cb492bfae435814fd4dc89b8d2a) // vk.Q3.y
mstore(add(_vk, 0x140), 0x0ae54d7d9ddc4a98f774593b47b6d53a7e16f507e1d5966c86060468821eb231) // vk.Q4.x
mstore(add(_vk, 0x160), 0x20a4982279d4ce299cec27d5b5430142970b82f7d81bb34ad1b3894d6bd43c6a) // vk.Q4.y
mstore(add(_vk, 0x180), 0x088f015bfbb7724235781922213693a55672b3368dbf00af54dd0222457201fc) // vk.Q_M.x
mstore(add(_vk, 0x1a0), 0x151bfe6cdd667fbc9d67dda8f67d3841387732f7b43fbc374e6d1c2968906a60) // vk.Q_M.y
mstore(add(_vk, 0x1c0), 0x01c92c31e4f3df8501873f327cee5a4d9665aa744f6696fd6d4f7963a8c60279) // vk.Q_C.x
mstore(add(_vk, 0x1e0), 0x16185a2b636930236e8e7c570ee2307aa368045d682728084e16deeb7a649a23) // vk.Q_C.y
mstore(add(_vk, 0x200), 0x092125afff75a4269a4e31a8a511e4485ca41430bc69d29bca1f6eab7e17e012) // vk.Q_ARITHMETIC.x
mstore(add(_vk, 0x220), 0x01631f37f3dd4304500e0c364344751fec84c7402204d79237dbbe98651ae9f3) // vk.Q_ARITHMETIC.y
mstore(add(_vk, 0x240), 0x10f01e624e52530f7ccd4f16d6c456efa2e0c9ffa06b66a47c0b0846c735cc91) // vk.QSORT.x
mstore(add(_vk, 0x260), 0x064b0b2186f82172f568efe5dae799ef55183ed1d96d823d41f6bd7d9dab493b) // vk.QSORT.y
mstore(add(_vk, 0x280), 0x2d5247c138f521f72a738d5497ad48d7fac4e2369d9a8e99a9b9f8070d931963) // vk.Q_ELLIPTIC.x
mstore(add(_vk, 0x2a0), 0x055c8ea419d61ab8dcc3533ad944e83262d0648c1864002d48a5f4907aa301a4) // vk.Q_ELLIPTIC.y
mstore(add(_vk, 0x2c0), 0x11dd1c3fb5ca5bf61c6099e6336be813cc2f6892e0798c621072eecdecd1bb37) // vk.Q_AUX.x
mstore(add(_vk, 0x2e0), 0x303c0aa23939940b56093dcc2319a32409ee1ce4ce16b2f3ca9334df71053914) // vk.Q_AUX.y
mstore(add(_vk, 0x300), 0x01da7bb5df5aa0a5d7749c7fdd28de9c761dbb62f5c24f9ead1225b3799c0579) // vk.SIGMA1.x
mstore(add(_vk, 0x320), 0x00286be19d5dafd3416e5eaee030536f8202b0a9871c70b01d44a39e21db4a3c) // vk.SIGMA1.y
mstore(add(_vk, 0x340), 0x1f2f4ea075e5a630fe17d9fba0afca88f5e0bee9114a07f355a0e5b505aabe2b) // vk.SIGMA2.x
mstore(add(_vk, 0x360), 0x2021e701cb04bc02832b2b373e7ad0fd0191cfb97378b7e56ac54376adf27713) // vk.SIGMA2.y
mstore(add(_vk, 0x380), 0x2a7df824a1675a1e92311f6a824768688d899ee70ce0df36fe1275cf5a1fe1d9) // vk.SIGMA3.x
mstore(add(_vk, 0x3a0), 0x15178ce5eba1d2da8d9b2b7362a32158b7ac05f9166b91ddf19afc175d3dbda2) // vk.SIGMA3.y
mstore(add(_vk, 0x3c0), 0x2def69211b7750f47a314fbd4814328f9f2a867909a37b2a3fa006d132cf6de2) // vk.SIGMA4.x
mstore(add(_vk, 0x3e0), 0x02931c280f134a6584b261de75efab5e6c76514990442ea1f15a18d9ad3f01c6) // vk.SIGMA4.y
mstore(add(_vk, 0x400), 0x192711fca2f5b36970aae483f46068b91f52060c02f8325294db54bb0a11933e) // vk.TABLE1.x
mstore(add(_vk, 0x420), 0x1bff24689c6227448a09dbed17ef39405aa88aedd9f9384dfe3f3d0a0480cbac) // vk.TABLE1.y
mstore(add(_vk, 0x440), 0x194b5ba84c106e0146f0952b5454de8274ff80a9f83cf14c4f6f0742eeb19864) // vk.TABLE2.x
mstore(add(_vk, 0x460), 0x1ec2dc36740cd8f027ac2c850a042d4482e3b79c8105eef51df522056ae7605a) // vk.TABLE2.y
mstore(add(_vk, 0x480), 0x2263709bd57b4b6419d0e9ddb56ac9c9419a18e11184552647e1788e62f3daf3) // vk.TABLE3.x
mstore(add(_vk, 0x4a0), 0x28473c39c8ad63fc67af038fa2594679efec0bf7da69050d9ddee43a5837f731) // vk.TABLE3.y
mstore(add(_vk, 0x4c0), 0x0c55dfdec4ea5722f75304d9d07d41349451120f02efda3f0355fc1c953256c6) // vk.TABLE4.x
mstore(add(_vk, 0x4e0), 0x2018b108aefa236db9e8d68ca15fd206e209683379f8cb36fe3a8a7d0456bf08) // vk.TABLE4.y
mstore(add(_vk, 0x500), 0x044840c394aea0c66af2b47aacc5c67b7f95b1b817e8215ff706e6adf02370fd) // vk.TABLE_TYPE.x
mstore(add(_vk, 0x520), 0x1b231e208d637332ff2182fe11f11404be27e4c2486620025dbaefe957144cbe) // vk.TABLE_TYPE.y
mstore(add(_vk, 0x540), 0x0984ac0b2047371908b53eb0f372d8fe0cd86f0612acbcf8876c5a3c8049c3fd) // vk.ID1.x
mstore(add(_vk, 0x560), 0x2c948e5fdd9cb29b71691edec1d2eaebd92ce00f23c31fb626a4c9cc053e6032) // vk.ID1.y
mstore(add(_vk, 0x580), 0x1d461349d3482341f0ed26762917440ae21816b1f6c61657626383f14a1242a7) // vk.ID2.x
mstore(add(_vk, 0x5a0), 0x0dd76f790e85266fc6662844762a7bfb6c23e2e6e25e507d70c75663c739ee5b) // vk.ID2.y
mstore(add(_vk, 0x5c0), 0x0378399cf18cf78e9ac6060550a16e3e4ccdb1009770c877c36ba432a79280da) // vk.ID3.x
mstore(add(_vk, 0x5e0), 0x28bf671e4e5eeebfa956dc041218174883633d6b2998e947725dcfe32670017d) // vk.ID3.y
mstore(add(_vk, 0x600), 0x2073db7e73ead65e8ce37f1b11f567eb5b1acbfa73231e0a8a97bf96ba71838f) // vk.ID4.x
mstore(add(_vk, 0x620), 0x1f5185260a80cc0a4d06e442ff040deda34a675437fa6cbae7b00f75ec3d0fe7) // vk.ID4.y
mstore(add(_vk, 0x640), 0x00) // vk.contains_recursive_proof
mstore(add(_vk, 0x660), 0) // 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, 0x036853f083780e87f8d7c71d111119c57dbe118c22d5ad707a82317466c5174c) // 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);
}
}
{
"compilationTarget": {
"src/RealAbstraction.sol": "RealAbstraction"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":forge-std/=lib/forge-std/src/",
":solady/=lib/solady/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_verifier","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"string","name":"c","type":"string"},{"internalType":"string","name":"d","type":"string"}],"name":"addProofGenerationHTML","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"a","type":"string"},{"internalType":"string","name":"b","type":"string"}],"name":"addShowHtml","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"url","type":"string"},{"internalType":"uint256","name":"pos","type":"uint256"}],"name":"addUrls","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authors","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_proof","type":"bytes"},{"internalType":"bytes32[]","name":"_publicInputs","type":"bytes32[]"}],"name":"createLinePart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"html","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"linePart","outputs":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"field1","type":"uint256"},{"internalType":"uint256","name":"field2","type":"uint256"},{"internalType":"uint256","name":"field3","type":"uint256"},{"internalType":"uint256","name":"field4","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"projectUrls","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"returnPathes","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"text","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"title","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"ultraVerifier","outputs":[{"internalType":"contract UltraVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"urls","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_proof","type":"bytes"},{"internalType":"bytes32[]","name":"_publicInputs","type":"bytes32[]"}],"name":"verifyProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]