From b41bed180f3b2fa009a2bca3a29dfb8c1670e309 Mon Sep 17 00:00:00 2001 From: matias-gonz Date: Thu, 6 Feb 2025 10:54:36 -0300 Subject: [PATCH 01/10] Add OidcKeyRegistry --- src/OidcKeyRegistry.sol | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/OidcKeyRegistry.sol diff --git a/src/OidcKeyRegistry.sol b/src/OidcKeyRegistry.sol new file mode 100644 index 00000000..ca1eca99 --- /dev/null +++ b/src/OidcKeyRegistry.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract OidcKeyRegistry is Initializable, OwnableUpgradeable { + uint8 public constant MAX_KEYS = 5; + + struct Key { + bytes32 kid; // Key ID + bytes n; // RSA modulus + bytes e; // RSA exponent + } + + // Mapping uses keccak256(iss) as the key + mapping(bytes32 => Key[MAX_KEYS]) public OIDCKeys; // Stores up to MAX_KEYS per issuer + mapping(bytes32 => uint8) public keyIndexes; // Tracks the latest key index for each issuer + + constructor() { + initialize(); + } + + function initialize() public initializer { + __Ownable_init(); + } + + function hashIssuer(string memory iss) public pure returns (bytes32) { + return keccak256(abi.encodePacked(iss)); + } + + function setKey(bytes32 issHash, Key memory key) public onlyOwner { + uint8 index = keyIndexes[issHash]; + uint8 nextIndex = (index + 1) % MAX_KEYS; // Circular buffer + OIDCKeys[issHash][nextIndex] = key; + keyIndexes[issHash] = nextIndex; + } + + function setKeys(bytes32 issHash, Key[] memory keys) public onlyOwner { + for (uint8 i = 0; i < keys.length; i++) { + setKey(issHash, keys[i]); + } + } + + function getKey(bytes32 issHash, bytes32 kid) public view returns (Key memory) { + require(kid != 0, "Invalid kid"); + Key[MAX_KEYS] storage keys = OIDCKeys[issHash]; + for (uint8 i = 0; i < MAX_KEYS; i++) { + if (keys[i].kid == kid) { + return keys[i]; + } + } + revert("Key not found"); + } +} From ecde01c18a37c0d2c2371f912b43ff059ff98eec Mon Sep 17 00:00:00 2001 From: matias-gonz Date: Thu, 6 Feb 2025 10:58:41 -0300 Subject: [PATCH 02/10] Update deploy script --- scripts/deploy.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 760507b3..85c3f922 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -10,8 +10,9 @@ const ACCOUNT_IMPL_NAME = "SsoAccount"; const FACTORY_NAME = "AAFactory"; const PAYMASTER_NAME = "ExampleAuthServerPaymaster"; const BEACON_NAME = "SsoBeacon"; +const OIDC_KEY_REGISTRY_NAME = "OidcKeyRegistry"; -async function deploy(name: string, deployer: Wallet, proxy: boolean, args?: any[]): Promise { +async function deploy(name: string, deployer: Wallet, proxy: boolean, args?: any[], initArgs?: any): Promise { // eslint-disable-next-line @typescript-eslint/no-require-imports const { deployFactory, create2, ethersStaticSalt } = require("../test/utils"); console.log("Deploying", name, "contract..."); @@ -26,7 +27,7 @@ async function deploy(name: string, deployer: Wallet, proxy: boolean, args?: any console.log(name, "contract deployed at:", implAddress, "\n"); return implAddress; } - const proxyContract = await create2("TransparentProxy", deployer, ethersStaticSalt, [implAddress]); + const proxyContract = await create2("TransparentProxy", deployer, ethersStaticSalt, [implAddress, initArgs ?? "0x"]); const proxyAddress = await proxyContract.getAddress(); console.log(name, "proxy contract deployed at:", proxyAddress, "\n"); return proxyAddress; @@ -82,6 +83,8 @@ task("deploy", "Deploys ZKsync SSO contracts") const beacon = await deploy(BEACON_NAME, deployer, false, [implementation]); const factory = await deploy(FACTORY_NAME, deployer, !cmd.noProxy, [beacon]); const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions]); + const oidcKeyRegistryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_KEY_REGISTRY_NAME)).abi); + await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); await fundPaymaster(paymaster, cmd.fund); } else { @@ -105,6 +108,9 @@ task("deploy", "Deploys ZKsync SSO contracts") } args = [cmd.factory, cmd.sessions]; } + if (cmd.only == OIDC_KEY_REGISTRY_NAME) { + args = []; + } const deployedContract = await deploy(cmd.only, deployer, false, args); if (cmd.only == PAYMASTER_NAME) { From a63697580042fad7e66e2b5ad41a294e6c18b042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Fri, 14 Feb 2025 10:19:56 -0300 Subject: [PATCH 03/10] Merge Guardian recovery (#296) * feat: empty GuardianRecoveryValidator Created GuardianRecoveryValidator structure to start development * feat: methods to add a guardian implemented proposeRecoveryKey and addRecoveryKey with tests. * fix: reverting when guardian not found When a user tries to remove a guardian that does not exist we revert the tx. * fix: uint to uint256 Using uint256 instead of uint everywhere. * feat: add validateTransaction implementation to GuardianRecoveryValidatior * chore: refactor tests * chore: clean up code * feat: improve init method * feat: simplify initRecovery method * chore: resolve build issues * chore: resolve build issues * chore: resolve pr comments * feat: restore guardiansFor method * chore: remove unused access to accountGuardians * feat: make guardian recovery validator contract proxy-able * chore: simplify initializer function name * feat: add function to retrieve guarded accounts * fix: improve recovery validator logic * feat: allow paymaster calls to GuardianRecoveryValidator * feat: fix guardian recovery validator compilation * fix: add compiler version and remove unwanted comments * fix: bugs and jsdoc format to match rest of package * fix: test that included guardian contract * feat: add passkey to account relation * feat: prevent account overlap * feat: improve registered accounts logic * fix: tests * fix: unknown accounts * fix: discard recovery bug * fix: move account verifications * feat: add guardian added time to guardian information * fix: deployment * fix: address to account id is not empty when initiating recovery * fix: remove double save on guardedAccounts * Fix/paymaster-recovery-validator (#291) * fix: add missing functions from recovery validator to paymaster * feat: add guardian not self check --------- Co-authored-by: calvo.generico Co-authored-by: Lukasz Romanowski <5160687+MiniRoman@users.noreply.github.com> Co-authored-by: Agustin Aon <21188659+aon@users.noreply.github.com> --- package.json | 1 + pnpm-lock.yaml | 3 + scripts/deploy.ts | 8 +- src/AAFactory.sol | 120 ++++++- src/TransparentProxy.sol | 5 +- src/interfaces/IGuardianRecoveryValidator.sol | 27 ++ src/test/ExampleAuthServerPaymaster.sol | 23 +- src/validators/GuardianRecoveryValidator.sol | 296 ++++++++++++++++ test/GuardianRecoveryValidatorTest.ts | 330 ++++++++++++++++++ test/PasskeyModule.ts | 6 +- test/SessionKeyTest.ts | 3 + test/utils.ts | 49 ++- 12 files changed, 857 insertions(+), 14 deletions(-) create mode 100644 src/interfaces/IGuardianRecoveryValidator.sol create mode 100644 src/validators/GuardianRecoveryValidator.sol create mode 100644 test/GuardianRecoveryValidatorTest.ts diff --git a/package.json b/package.json index 54fcb7ed..24652569 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@nomad-xyz/excessively-safe-call": "0.0.1-rc.1", "@nomicfoundation/hardhat-chai-matchers": "2.0.8", "@nomicfoundation/hardhat-ethers": "3.0.8", + "@nomicfoundation/hardhat-network-helpers": "^1.0.12", "@nomicfoundation/hardhat-toolbox": "^5.0.0", "@nomicfoundation/hardhat-verify": "2.0.11", "@openzeppelin/contracts": "4.9.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8ea79d9..abdf6fce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@nomicfoundation/hardhat-ethers': specifier: 3.0.8 version: 3.0.8(ethers@6.13.2)(hardhat@2.22.17(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.15))(@types/node@20.16.10)(typescript@5.6.2))(typescript@5.6.2)) + '@nomicfoundation/hardhat-network-helpers': + specifier: ^1.0.12 + version: 1.0.12(hardhat@2.22.17(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.15))(@types/node@20.16.10)(typescript@5.6.2))(typescript@5.6.2)) '@nomicfoundation/hardhat-toolbox': specifier: ^5.0.0 version: 5.0.0(g4q45fufb3cpd4wckvbqanslk4) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 85c3f922..f35c2d02 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -6,6 +6,7 @@ import { Wallet } from "zksync-ethers"; const WEBAUTH_NAME = "WebAuthValidator"; const SESSIONS_NAME = "SessionKeyValidator"; +const GUARDIAN_RECOVERY_NAME = "GuardianRecoveryValidator"; const ACCOUNT_IMPL_NAME = "SsoAccount"; const FACTORY_NAME = "AAFactory"; const PAYMASTER_NAME = "ExampleAuthServerPaymaster"; @@ -33,7 +34,6 @@ async function deploy(name: string, deployer: Wallet, proxy: boolean, args?: any return proxyAddress; } - task("deploy", "Deploys ZKsync SSO contracts") .addOptionalParam("only", "name of a specific contract to deploy") .addFlag("noProxy", "do not deploy transparent proxies for factory and modules") @@ -77,12 +77,14 @@ task("deploy", "Deploys ZKsync SSO contracts") } if (!cmd.only) { - await deploy(WEBAUTH_NAME, deployer, !cmd.noProxy); + const webauth = await deploy(WEBAUTH_NAME, deployer, !cmd.noProxy); const sessions = await deploy(SESSIONS_NAME, deployer, !cmd.noProxy); const implementation = await deploy(ACCOUNT_IMPL_NAME, deployer, false); const beacon = await deploy(BEACON_NAME, deployer, false, [implementation]); const factory = await deploy(FACTORY_NAME, deployer, !cmd.noProxy, [beacon]); - const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions]); + const guardianInterface = new ethers.Interface((await hre.artifacts.readArtifact(GUARDIAN_RECOVERY_NAME)).abi); + const recovery = await deploy(GUARDIAN_RECOVERY_NAME, deployer, !cmd.noProxy, [webauth, factory], guardianInterface.encodeFunctionData("initialize", [webauth, factory])); + const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); const oidcKeyRegistryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_KEY_REGISTRY_NAME)).abi); await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); diff --git a/src/AAFactory.sol b/src/AAFactory.sol index bf944e83..c46c31e7 100644 --- a/src/AAFactory.sol +++ b/src/AAFactory.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.24; import { DEPLOYER_SYSTEM_CONTRACT } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { IContractDeployer } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IContractDeployer.sol"; import { SystemContractsCaller } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { ISsoAccount } from "./interfaces/ISsoAccount.sol"; @@ -12,6 +13,8 @@ import { ISsoAccount } from "./interfaces/ISsoAccount.sol"; /// @custom:security-contact security@matterlabs.dev /// @dev This contract is used to deploy SSO accounts as beacon proxies. contract AAFactory { + using Strings for string; + /// @notice Emitted when a new account is successfully created. /// @param accountAddress The address of the newly created account. /// @param uniqueAccountId A unique identifier for the account. @@ -24,6 +27,17 @@ contract AAFactory { /// @notice A mapping from unique account IDs to their corresponding deployed account addresses. mapping(string => address) public accountMappings; + /// @notice A mapping from account addresses to their corresponding unique account IDs. + mapping(address => string) public accountIds; + + /// @notice A mapping that marks account IDs as being used for recovery. + /// @dev This is used to prevent the same account ID from being used for recovery, deployment and future uses. + mapping(string => address) public recoveryAccountIds; + + error AccountAlreadyRegistered(string uniqueAccountId, address accountAddress); + error AccountNotRegistered(string uniqueAccountId, address accountAddress); + error AccountUsedForRecovery(string uniqueAccountId, address accountAddress); + /// @notice Constructor that initializes the factory with a beacon proxy bytecode hash and implementation contract address. /// @param _beaconProxyBytecodeHash The bytecode hash of the beacon proxy. /// @param _beacon The address of the UpgradeableBeacon contract used for the SSO accounts' beacon proxies. @@ -49,8 +63,6 @@ contract AAFactory { bytes[] calldata _initialValidators, address[] calldata _initialK1Owners ) external returns (address accountAddress) { - require(accountMappings[_uniqueAccountId] == address(0), "Account already exists"); - (bool success, bytes memory returnData) = SystemContractsCaller.systemCallWithReturndata( uint32(gasleft()), address(DEPLOYER_SYSTEM_CONTRACT), @@ -63,11 +75,113 @@ contract AAFactory { require(success, "Deployment failed"); (accountAddress) = abi.decode(returnData, (address)); - accountMappings[_uniqueAccountId] = accountAddress; + // Check if the account is already registered + // Note: this check is done at this point, to use `accountAddress` to process the error message. + require( + accountMappings[_uniqueAccountId] == address(0), + AccountAlreadyRegistered(_uniqueAccountId, accountAddress) + ); + require(accountIds[accountAddress].equal(""), AccountAlreadyRegistered(_uniqueAccountId, accountAddress)); + require( + recoveryAccountIds[_uniqueAccountId] == address(0), + AccountUsedForRecovery(_uniqueAccountId, accountAddress) + ); // Initialize the newly deployed account with validators, hooks and K1 owners. ISsoAccount(accountAddress).initialize(_initialValidators, _initialK1Owners); + _registerAccount(_uniqueAccountId, accountAddress); + emit AccountCreated(accountAddress, _uniqueAccountId); } + + /// @notice Registers an account with a given account ID. + /// @dev Can only be called by the account's validators. + /// @param _uniqueAccountId The unique identifier for the account. + /// @param _accountAddress The address of the account to register. + function registerAccount( + string calldata _uniqueAccountId, + address _accountAddress + ) external onlyAccountValidator(_accountAddress) { + require( + accountMappings[_uniqueAccountId] == address(0), + AccountAlreadyRegistered(_uniqueAccountId, _accountAddress) + ); + require(accountIds[_accountAddress].equal(""), AccountAlreadyRegistered(_uniqueAccountId, _accountAddress)); + require( + recoveryAccountIds[_uniqueAccountId] == address(0), + AccountUsedForRecovery(_uniqueAccountId, _accountAddress) + ); + + _registerAccount(_uniqueAccountId, _accountAddress); + } + + function _registerAccount(string calldata _uniqueAccountId, address _accountAddress) internal { + accountMappings[_uniqueAccountId] = _accountAddress; + accountIds[_accountAddress] = _uniqueAccountId; + } + + /// @notice Unregisters an account from the factory. + /// @dev Can only be called by the account's validators. + /// @param _uniqueAccountId The unique identifier for the account. + /// @param _accountAddress The address of the account to unregister. + function unregisterAccount( + string memory _uniqueAccountId, + address _accountAddress + ) external onlyAccountValidator(_accountAddress) { + require( + accountMappings[_uniqueAccountId] == _accountAddress, + AccountNotRegistered(_uniqueAccountId, _accountAddress) + ); + require( + accountIds[_accountAddress].equal(_uniqueAccountId), + AccountNotRegistered(_uniqueAccountId, _accountAddress) + ); + + accountMappings[_uniqueAccountId] = address(0); + accountIds[_accountAddress] = ""; + } + + /// @notice Updates the account mapping for a given account ID during recovery. + /// @dev Can only be called by the account's validators. + /// @param _uniqueAccountId The unique identifier for the account. + /// @param _accountAddress The address of the account to update the mapping for. + function registerRecoveryBlockedAccount( + string calldata _uniqueAccountId, + address _accountAddress + ) external onlyAccountValidator(_accountAddress) { + require( + accountMappings[_uniqueAccountId] == address(0), + AccountAlreadyRegistered(_uniqueAccountId, _accountAddress) + ); + require( + recoveryAccountIds[_uniqueAccountId] == address(0), + AccountUsedForRecovery(_uniqueAccountId, _accountAddress) + ); + + recoveryAccountIds[_uniqueAccountId] = _accountAddress; + } + + /// @notice Unregisters a recovery blocked account from the factory. + /// @dev Can only be called by the account's validators. + /// @param _uniqueAccountId The unique identifier for the account. + /// @param _accountAddress The address of the account to unregister. + function unregisterRecoveryBlockedAccount( + string calldata _uniqueAccountId, + address _accountAddress + ) external onlyAccountValidator(_accountAddress) { + require( + recoveryAccountIds[_uniqueAccountId] == _accountAddress, + AccountNotRegistered(_uniqueAccountId, _accountAddress) + ); + + recoveryAccountIds[_uniqueAccountId] = address(0); + } + + /// @notice Modifier that checks if the caller is a validator for the given account. + /// @param _accountAddress The address of the account to check the validator for. + modifier onlyAccountValidator(address _accountAddress) { + require(ISsoAccount(_accountAddress).isModuleValidator(msg.sender), "Unauthorized validator"); + _; + } } diff --git a/src/TransparentProxy.sol b/src/TransparentProxy.sol index 678ad40e..7d5c66c1 100644 --- a/src/TransparentProxy.sol +++ b/src/TransparentProxy.sol @@ -12,7 +12,10 @@ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/trans /// cheap delegate calls on ZKsync. /// @dev This proxy is placed in front of `AAFactory` and all modules (`WebAuthValidator`, `SessionKeyValidator`). contract TransparentProxy is TransparentUpgradeableProxy, EfficientProxy { - constructor(address implementation) TransparentUpgradeableProxy(implementation, msg.sender, bytes("")) {} + constructor( + address implementation, + bytes memory data + ) TransparentUpgradeableProxy(implementation, msg.sender, data) {} function _delegate(address implementation) internal override(EfficientProxy, Proxy) { EfficientProxy._delegate(implementation); diff --git a/src/interfaces/IGuardianRecoveryValidator.sol b/src/interfaces/IGuardianRecoveryValidator.sol new file mode 100644 index 00000000..8a54f5a7 --- /dev/null +++ b/src/interfaces/IGuardianRecoveryValidator.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { IModuleValidator } from "./IModuleValidator.sol"; +import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; + +interface IGuardianRecoveryValidator is IModuleValidator { + struct GuardianConfirmation { + address ssoAccount; + } + + function proposeValidationKey(address externalAccount) external; + + function removeValidationKey(address externalAccount) external; + + function initRecovery(address accountToRecover, bytes memory passkey, string memory accountId) external; + + function addValidationKey(bytes memory key) external returns (bool); + + function validateTransaction( + bytes32 signedHash, + bytes memory signature, + Transaction calldata transaction + ) external returns (bool); + + function validateSignature(bytes32 signedHash, bytes memory signature) external view returns (bool); +} diff --git a/src/test/ExampleAuthServerPaymaster.sol b/src/test/ExampleAuthServerPaymaster.sol index 4c03897e..7f7a1865 100644 --- a/src/test/ExampleAuthServerPaymaster.sol +++ b/src/test/ExampleAuthServerPaymaster.sol @@ -10,16 +10,22 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { AAFactory } from "../AAFactory.sol"; import { SessionKeyValidator } from "../validators/SessionKeyValidator.sol"; +import { GuardianRecoveryValidator } from "../validators/GuardianRecoveryValidator.sol"; /// @author Matter Labs /// @notice This contract does not include any validations other than using the paymaster general flow. contract ExampleAuthServerPaymaster is IPaymaster, Ownable { address public immutable AA_FACTORY_CONTRACT_ADDRESS; address public immutable SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS; + address public immutable ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS; bytes4 constant DEPLOY_ACCOUNT_SELECTOR = AAFactory.deployProxySsoAccount.selector; bytes4 constant SESSION_CREATE_SELECTOR = SessionKeyValidator.createSession.selector; bytes4 constant SESSION_REVOKE_KEY_SELECTOR = SessionKeyValidator.revokeKey.selector; bytes4 constant SESSION_REVOKE_KEYS_SELECTOR = SessionKeyValidator.revokeKeys.selector; + bytes4 constant GUARDIAN_RECOVERY_ADD_KEY_SELECTOR = GuardianRecoveryValidator.addValidationKey.selector; + bytes4 constant GUARDIAN_RECOVERY_PROPOSE_KEY_SELECTOR = GuardianRecoveryValidator.proposeValidationKey.selector; + bytes4 constant GUARDIAN_RECOVERY_DISCARD_RECOVERY_SELECTOR = GuardianRecoveryValidator.discardRecovery.selector; + bytes4 constant GUARDIAN_RECOVERY_REMOVE_KEY_SELECTOR = GuardianRecoveryValidator.removeValidationKey.selector; modifier onlyBootloader() { require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Only bootloader can call this method"); @@ -27,9 +33,10 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { _; } - constructor(address aaFactoryAddress, address sessionKeyValidatorAddress) { + constructor(address aaFactoryAddress, address sessionKeyValidatorAddress, address accountRecoveryValidatorAddress) { AA_FACTORY_CONTRACT_ADDRESS = aaFactoryAddress; SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS = sessionKeyValidatorAddress; + ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS = accountRecoveryValidatorAddress; } function validateAndPayForPaymasterTransaction( @@ -44,7 +51,9 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { // Ensure the transaction is calling one of our allowed contracts address to = address(uint160(_transaction.to)); require( - to == AA_FACTORY_CONTRACT_ADDRESS || to == SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS, + to == AA_FACTORY_CONTRACT_ADDRESS || + to == SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS || + to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS, "Unsupported contract address" ); @@ -63,6 +72,16 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { ); } + if (to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS) { + require( + methodSelector == GUARDIAN_RECOVERY_ADD_KEY_SELECTOR || + methodSelector == GUARDIAN_RECOVERY_PROPOSE_KEY_SELECTOR || + methodSelector == GUARDIAN_RECOVERY_DISCARD_RECOVERY_SELECTOR || + methodSelector == GUARDIAN_RECOVERY_REMOVE_KEY_SELECTOR, + "Unsupported method" + ); + } + bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); require(paymasterInputSelector == IPaymasterFlow.general.selector, "Unsupported paymaster flow"); diff --git a/src/validators/GuardianRecoveryValidator.sol b/src/validators/GuardianRecoveryValidator.sol new file mode 100644 index 00000000..f3476458 --- /dev/null +++ b/src/validators/GuardianRecoveryValidator.sol @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { IGuardianRecoveryValidator } from "../interfaces/IGuardianRecoveryValidator.sol"; +import { WebAuthValidator } from "./WebAuthValidator.sol"; +import { AAFactory } from "../AAFactory.sol"; +import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; +import { IModule } from "../interfaces/IModule.sol"; +import { SignatureDecoder } from "../libraries/SignatureDecoder.sol"; + +contract GuardianRecoveryValidator is Initializable, IGuardianRecoveryValidator { + struct Guardian { + address addr; + bool isReady; + uint64 addedAt; + } + struct RecoveryRequest { + bytes passkey; + uint256 timestamp; + string accountId; + } + + error GuardianCannotBeSelf(); + error GuardianNotFound(address guardian); + error GuardianNotProposed(address guardian); + error PasskeyNotMatched(); + error CooldownPeriodNotPassed(); + error ExpiredRequest(); + + /// @dev Event indicating new recovery process being initiated + event RecoveryInitiated(address account, address guardian); + + uint256 constant REQUEST_VALIDITY_TIME = 72 * 60 * 60; // 72 hours + uint256 constant REQUEST_DELAY_TIME = 24 * 60 * 60; // 24 hours + + mapping(address account => Guardian[]) public accountGuardians; + mapping(address guardian => address[]) public guardedAccounts; + mapping(address account => RecoveryRequest) public pendingRecoveryData; + + WebAuthValidator public webAuthValidator; + AAFactory public aaFactory; + + /// @notice The constructor sets the web authn validator for which recovery process can be initiated. Used only for non proxied deployment + constructor(WebAuthValidator _webAuthValidator, AAFactory _aaFactory) { + initialize(_webAuthValidator, _aaFactory); + } + + /// @notice Initializer function that sets validator initial configuration. Expected to be used in the proxy. + /// @dev Sets webAuthValidator address + /// @param _webAuthValidator Address of WebAuthnValidator contracts + function initialize(WebAuthValidator _webAuthValidator, AAFactory _aaFactory) public initializer { + webAuthValidator = _webAuthValidator; + aaFactory = _aaFactory; + } + + /// @notice Validator initiator for given sso account. This module does not support initialization on creation + function onInstall(bytes calldata) external {} + + /// @notice Removes all past guardians when this module is disabled in a account + function onUninstall(bytes calldata) external { + Guardian[] storage guardians = accountGuardians[msg.sender]; + for (uint256 i = 0; i < guardians.length; i++) { + address guardian = guardians[i].addr; + + address[] storage accounts = guardedAccounts[guardian]; + for (uint256 j = 0; j < accounts.length; j++) { + if (accounts[j] == msg.sender) { + // If found last account is moved to current position, and then + // last element is removed from array. + accounts[j] = accounts[accounts.length - 1]; + accounts.pop(); + break; + } + } + } + + delete accountGuardians[msg.sender]; + } + + /// @notice The `proposeValidationKey` method handles the initial registration of guardians by: + /// 1. Taking an external account address and store it as pending guardian + /// 2. Enable `addValidationKey` to confirm this account + /// @param newGuardian New Guardian's address + function proposeValidationKey(address newGuardian) external { + if (msg.sender == newGuardian) revert GuardianCannotBeSelf(); + + Guardian[] storage guardians = accountGuardians[msg.sender]; + + // If the guardian exist this method stops + for (uint256 i = 0; i < guardians.length; i++) { + if (guardians[i].addr == newGuardian) { + return; + } + } + + guardians.push(Guardian(newGuardian, false, uint64(block.timestamp))); + guardedAccounts[newGuardian].push(msg.sender); + } + + /// @notice This method handles the removal of guardians by: + /// 1. Accepting an address as input + /// 2. Removing the account from the list of guardians + /// @param guardianToRemove Guardian's address to remove + function removeValidationKey(address guardianToRemove) external { + Guardian[] storage guardians = accountGuardians[msg.sender]; + + // Searchs guardian with given address + for (uint256 i = 0; i < guardians.length; i++) { + if (guardians[i].addr == guardianToRemove) { + // If found last guardian is moved to current position, and then + // last element is removed from array. + guardians[i] = guardians[guardians.length - 1]; + guardians.pop(); + break; + } + } + + address[] storage accounts = guardedAccounts[guardianToRemove]; + for (uint256 i = 0; i < accounts.length; i++) { + if (accounts[i] == msg.sender) { + // If found last account is moved to current position, and then + // last element is removed from array. + accounts[i] = accounts[accounts.length - 1]; + accounts.pop(); + return; + } + } + + revert GuardianNotFound(guardianToRemove); + } + + /// @notice This method allows to accept being a guardian of given account + /// @param key Encoded address of account which msg.sender is becoming guardian of + /// @return Flag indicating whether guardian was already valid or not + function addValidationKey(bytes memory key) external returns (bool) { + address accountToGuard = abi.decode(key, (address)); + Guardian[] storage guardians = accountGuardians[accountToGuard]; + + // Searches if the caller is in the list of guardians. + // If guardian found is set to true. + for (uint256 i = 0; i < guardians.length; i++) { + if (guardians[i].addr == msg.sender) { + // We return true if the guardian was not confirmed before. + if (guardians[i].isReady) return false; + + guardians[i].isReady = true; + return true; + } + } + + revert GuardianNotProposed(msg.sender); + } + + /// @notice This modifier allows execution only by active guardian of account + /// @param account Address of account for which we verify guardian existence + modifier onlyGuardianOf(address account) { + bool isGuardian = false; + for (uint256 i = 0; i < accountGuardians[account].length; i++) { + if (accountGuardians[account][i].addr == msg.sender && accountGuardians[account][i].isReady) { + isGuardian = true; + break; + } + } + if (!isGuardian) revert GuardianNotFound(msg.sender); + // Continue execution if called by guardian + _; + } + + /// @notice This method initializes a recovery process for a given account + /// @param accountToRecover Address of account for which given recovery is initiated + /// @param passkey Encoded new passkey, that will be passed to WebAuthnModule + function initRecovery( + address accountToRecover, + bytes memory passkey, + string memory accountId + ) external onlyGuardianOf(accountToRecover) { + pendingRecoveryData[accountToRecover] = RecoveryRequest(passkey, block.timestamp, accountId); + aaFactory.registerRecoveryBlockedAccount(accountId, accountToRecover); + emit RecoveryInitiated(accountToRecover, msg.sender); + } + + /// @notice This method allows to discard currently pending recovery + function discardRecovery() external { + aaFactory.unregisterRecoveryBlockedAccount(pendingRecoveryData[msg.sender].accountId, msg.sender); + delete pendingRecoveryData[msg.sender]; + } + + /// @notice Checks if a given passkey matches a pending recovery request and is ready to be used + /// @param accountId The account ID to check + /// @return account The account being recovered (zero address if no match) + /// @return ready True if the passkey matches and is ready to be used + /// @return remainingTime Time in seconds until the passkey can be used (0 if ready or no match) + function checkRecoveryRequest( + string memory accountId + ) external view returns (address account, bool ready, uint256 remainingTime) { + account = aaFactory.recoveryAccountIds(accountId); + + if (account == address(0)) { + return (account, false, 0); + } + + RecoveryRequest storage request = pendingRecoveryData[account]; + uint256 timePassedSinceRequest = block.timestamp - request.timestamp; + + // If request expired + if (timePassedSinceRequest > REQUEST_VALIDITY_TIME) { + return (account, false, 0); + } + + // If still in cooldown period + if (timePassedSinceRequest < REQUEST_DELAY_TIME) { + return (account, false, REQUEST_DELAY_TIME - timePassedSinceRequest); + } + + // Passkey matches and is ready to be used + return (account, true, 0); + } + + /// @inheritdoc IModuleValidator + function validateTransaction(bytes32, bytes memory, Transaction calldata transaction) external returns (bool) { + // If the user has a recovery in progress then: + // 1. The method will verify calls to `WebAuthnModule` + // 2. Checks if the transaction is attempting to modify passkeys + // 3. Verify the new passkey is the one stored in `initRecovery` + // 4. Allows anyone to call this method, as the recovery was already verified in `initRecovery` + // 5. Verifies that the required timelock period has passed since `initRecovery` was called + require(transaction.data.length >= 4, "Only function calls are supported"); + bytes4 selector = bytes4(transaction.data[:4]); + + require(transaction.to <= type(uint160).max, "Overflow"); + address target = address(uint160(transaction.to)); + + if (target != address(webAuthValidator)) { + return false; + } + + // Check for calling "addValidationKey" method by anyone on WebAuthValidator contract + require(selector == WebAuthValidator.addValidationKey.selector, "Unauthorized function call"); + bytes memory validationKeyData = abi.decode(transaction.data[4:], (bytes)); + + // Verify that current request matches pending one + if ( + pendingRecoveryData[msg.sender].passkey.length != validationKeyData.length || + keccak256(pendingRecoveryData[msg.sender].passkey) != keccak256(validationKeyData) + ) revert PasskeyNotMatched(); + + // Ensure time constraints + uint256 timePassedSinceRequest = block.timestamp - pendingRecoveryData[msg.sender].timestamp; + if (timePassedSinceRequest < REQUEST_DELAY_TIME) revert CooldownPeriodNotPassed(); + if (timePassedSinceRequest > REQUEST_VALIDITY_TIME) revert ExpiredRequest(); + + string memory accountId = pendingRecoveryData[msg.sender].accountId; + + // Update account mapping in AAFactory + string memory previousAccountId = aaFactory.accountIds(msg.sender); + aaFactory.unregisterAccount(previousAccountId, msg.sender); + aaFactory.unregisterRecoveryBlockedAccount(accountId, msg.sender); + aaFactory.registerAccount(accountId, msg.sender); + + // Cleanup currently processed recovery data + delete pendingRecoveryData[msg.sender]; + + return true; + } + + /// @inheritdoc IModuleValidator + function validateSignature(bytes32, bytes memory) external pure returns (bool) { + return false; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return + interfaceId == type(IERC165).interfaceId || + interfaceId == type(IModuleValidator).interfaceId || + interfaceId == type(IModule).interfaceId; + } + + /// @notice Returns all guardians for an account + /// @param addr Address of account to get guardians for + /// @return Array of guardians for the account + function guardiansFor(address addr) public view returns (Guardian[] memory) { + return accountGuardians[addr]; + } + + /// @notice Returns all accounts guarded by a guardian + /// @param guardian Address of guardian to get guarded accounts for + /// @return Array of accounts guarded by the guardian + function guardianOf(address guardian) public view returns (address[] memory) { + return guardedAccounts[guardian]; + } +} diff --git a/test/GuardianRecoveryValidatorTest.ts b/test/GuardianRecoveryValidatorTest.ts new file mode 100644 index 00000000..0f483c89 --- /dev/null +++ b/test/GuardianRecoveryValidatorTest.ts @@ -0,0 +1,330 @@ +import * as helpers from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { randomBytes } from "crypto"; +import { ethers, HDNodeWallet } from "ethers"; +import { Address, encodeAbiParameters, Hex, parseEther } from "viem"; +import { Provider, SmartAccount, utils, Wallet } from "zksync-ethers"; + +import { AAFactory, GuardianRecoveryValidator, GuardianRecoveryValidator__factory, SsoAccount, SsoAccount__factory, WebAuthValidator } from "../typechain-types"; +import { encodeKeyFromBytes, generateES256R1Key, getRawPublicKeyFromCrpyto } from "./PasskeyModule"; +import { cacheBeforeEach, ContractFixtures, getProvider } from "./utils"; + +describe("GuardianRecoveryValidator", function () { + const fixtures = new ContractFixtures(); + const abiCoder = new ethers.AbiCoder(); + const provider = getProvider(); + let guardiansValidatorAddr: Address; + let factory: AAFactory; + let ssoAccountInstance: SsoAccount; + let newGuardianConnectedSsoAccount: SmartAccount; + let ownerConnectedSsoAccount: SmartAccount; + let guardianWallet: Wallet; + let ownerWallet: Wallet; + let externalUserWallet: Wallet; + let webauthn: WebAuthValidator; + let guardianValidator: GuardianRecoveryValidator; + + cacheBeforeEach(async () => { + guardianWallet = new Wallet(Wallet.createRandom().privateKey, provider); + ownerWallet = new Wallet(Wallet.createRandom().privateKey, provider); + externalUserWallet = new Wallet(Wallet.createRandom().privateKey, provider); + + const generatedKey = await generatePassKey(); + + guardianValidator = await fixtures.getGuardianRecoveryValidator(); + webauthn = await fixtures.getWebAuthnVerifierContract(); + guardiansValidatorAddr = await guardianValidator.getAddress() as Address; + factory = await fixtures.getAaFactory(); + const randomSalt = randomBytes(32); + const accountId = "recovery-key-test-id" + randomBytes(32).toString(); + const initialValidators = [ + ethers.AbiCoder.defaultAbiCoder().encode(["address", "bytes"], [await webauthn.getAddress(), generatedKey]), + ethers.AbiCoder.defaultAbiCoder().encode(["address", "bytes"], [await guardianValidator.getAddress(), ethers.AbiCoder.defaultAbiCoder().encode( + ["address[]"], + [[]], + )]), + ]; + ssoAccountInstance = SsoAccount__factory.connect(await factory.deployProxySsoAccount.staticCall( + randomSalt, + accountId, + initialValidators, + [ownerWallet], + ), fixtures.wallet); + await factory.deployProxySsoAccount( + randomSalt, + accountId, + initialValidators, + [ownerWallet], + ); + const ssoAccountInstanceAddress = await ssoAccountInstance.getAddress(); + await (await fixtures.wallet.sendTransaction({ value: parseEther("0.2"), to: ssoAccountInstanceAddress })).wait(); ; + await (await fixtures.wallet.sendTransaction({ value: parseEther("0.2"), to: guardianWallet.address })).wait(); + newGuardianConnectedSsoAccount = new SmartAccount({ + payloadSigner: async (hash) => { + const data = abiCoder.encode( + ["bytes", "address", "bytes"], + [ + guardianWallet.signingKey.sign(hash).serialized, + guardiansValidatorAddr, + abiCoder.encode( + ["uint256"], + [123], + ), + ], + ); + return data; + }, + address: await ssoAccountInstance.getAddress(), + secret: guardianWallet.privateKey, + }, provider); + ownerConnectedSsoAccount = new SmartAccount({ + address: await ssoAccountInstance.getAddress(), + secret: ownerWallet.privateKey, + }, provider); + }); + + async function randomWallet(): Promise<[HDNodeWallet, GuardianRecoveryValidator]> { + const wallet = Wallet.createRandom(getProvider()); + const connected = GuardianRecoveryValidator__factory.connect(guardiansValidatorAddr, wallet); + await fixtures.wallet.sendTransaction({ value: parseEther("0.2"), to: wallet.address }); + + return [wallet, connected]; + } + + function callAddValidationKey(contract: GuardianRecoveryValidator, account: string): Promise { + const encoded = encodeAbiParameters( + [{ type: "address" }], + [account as Hex], + ); + return contract.addValidationKey(encoded); + } + + it("can propose a guardian", async function () { + const [user1, connectedUser1] = await randomWallet(); + const [guardian] = await randomWallet(); + + const tx = await connectedUser1.proposeValidationKey(guardian.address); + await tx.wait(); + + const res = await connectedUser1.getFunction("guardiansFor").staticCall(user1.address); + expect(res.length).to.equal(1); + expect(res[0][0]).to.equal(guardian.address); + expect(res[0][1]).to.equal(false); + }); + + it("fails when tries to confirm a guardian that was not proposed.", async function () { + const [user1] = await randomWallet(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, guardianConnection] = await randomWallet(); + + await expect(callAddValidationKey(guardianConnection, user1.address)) + .to.revertedWithCustomError(guardianConnection, "GuardianNotProposed"); + }); + + it("fails when tries to confirm a was proposed for a different account.", async function () { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, user1Connection] = await randomWallet(); + const [user2] = await randomWallet(); + const [guardian, guardianConnection] = await randomWallet(); + + const tx1 = await user1Connection.proposeValidationKey(guardian.address); + await tx1.wait(); + + await expect(callAddValidationKey(guardianConnection, user2.address)) + .to.revertedWithCustomError(guardianConnection, "GuardianNotProposed"); + }); + + it("works to confirm a proposed account.", async function () { + const [user1, user1Connected] = await randomWallet(); + const [guardian, guardianConnected] = await randomWallet(); + + const tx = await user1Connected.proposeValidationKey(guardian.address); + await tx.wait(); + + await callAddValidationKey(guardianConnected, user1.address); + + const res = await user1Connected.getFunction("guardiansFor").staticCall(user1.address); + expect(res.length).to.equal(1); + expect(res[0][0]).to.equal(guardian.address); + expect(res[0][1]).to.equal(true); + }); + + describe("When attached to SsoAccount", () => { + describe("When initiating new guardian addition operation", () => { + it("it adds guardian as non ready one.", async function () { + const [newGuardianWallet] = await randomWallet(); + const functionData = guardianValidator.interface.encodeFunctionData( + "proposeValidationKey", + [newGuardianWallet.address], + ); + const txToSign = { + ...(await aaTxTemplate(await ssoAccountInstance.getAddress(), provider)), + type: 1, + to: guardiansValidatorAddr, + data: functionData, + }; + txToSign.gasLimit = await provider.estimateGas(txToSign); + const txData = await ownerConnectedSsoAccount.signTransaction(txToSign); + const tx = await provider.broadcastTransaction(txData); + await tx.wait(); + + const [newGuardian] = (await guardianValidator.guardiansFor(newGuardianConnectedSsoAccount.address)).slice(-1); + expect(newGuardian.addr).to.eq(newGuardianWallet.address); + expect(newGuardian.isReady).to.eq(false); + }); + }); + describe("When approving existing guardian addition operation", () => { + cacheBeforeEach(async () => { + const functionData = guardianValidator.interface.encodeFunctionData( + "proposeValidationKey", + [guardianWallet.address], + ); + const txToSign = { + ...(await aaTxTemplate(await ssoAccountInstance.getAddress(), provider)), + to: guardiansValidatorAddr, + data: functionData, + }; + txToSign.gasLimit = await provider.estimateGas(txToSign); + const txData = await ownerConnectedSsoAccount.signTransaction(txToSign); + const tx = await provider.broadcastTransaction(txData); + await tx.wait(); + }); + const sut = async () => { + return guardianValidator.connect(guardianWallet) + .addValidationKey(abiCoder.encode(["address"], [newGuardianConnectedSsoAccount.address])); + }; + it("it makes guardian active one.", async function () { + await sut(); + + const [newGuardian] = (await guardianValidator.guardiansFor(newGuardianConnectedSsoAccount.address)).slice(-1); + expect(newGuardian.addr).to.eq(guardianWallet.address); + expect(newGuardian.isReady).to.eq(true); + }); + }); + describe("When having active guardian", () => { + cacheBeforeEach(async () => { + const functionData = guardianValidator.interface.encodeFunctionData( + "proposeValidationKey", + [guardianWallet.address], + ); + const txToSign = { + ...(await aaTxTemplate(await ssoAccountInstance.getAddress(), provider)), + to: guardiansValidatorAddr, + data: functionData, + }; + txToSign.gasLimit = await provider.estimateGas(txToSign); + const txData = await ownerConnectedSsoAccount.signTransaction(txToSign); + const tx = await provider.broadcastTransaction(txData); + await tx.wait(); + await guardianValidator.connect(guardianWallet).addValidationKey(abiCoder.encode(["address"], [newGuardianConnectedSsoAccount.address])); + }); + + describe("And initiating recovery process", () => { + let newKey: string; + let refTimestamp: number; + let accountId: string; + + cacheBeforeEach(async () => { + newKey = await generatePassKey(); + refTimestamp = (await provider.getBlock("latest")).timestamp; + accountId = `id-${randomBytes(32).toString()}`; + }); + const sut = async (signer: ethers.Signer = guardianWallet) => { + const tx = await guardianValidator.connect(signer).initRecovery( + ssoAccountInstance.getAddress(), newKey, accountId, + ); + return tx; + }; + // FIXME + it.skip("it creates new recovery process.", async function () { + await sut(); + + const request = (await guardianValidator.pendingRecoveryData( + newGuardianConnectedSsoAccount.address, + )); + expect(request.passkey).to.eq(newKey); + expect(Math.abs(Number(request.timestamp) - refTimestamp)).to.lt(10); + }); + it("it prohibits non guardian from starting recovery process", async function () { + await expect(sut(externalUserWallet)).to.be.reverted; + }); + }); + //FIXME + describe.skip("And has active recovery process and trying to execute", () => { + let newKey: string; + let accountId: string; + + cacheBeforeEach(async () => { + newKey = await generatePassKey(); + accountId = `id-${randomBytes(32).toString()}`; + + await guardianValidator.connect(guardianWallet) + .initRecovery(newGuardianConnectedSsoAccount.address, newKey, accountId); + }); + const sut = async (keyToAdd: string, ssoAccount: SmartAccount = newGuardianConnectedSsoAccount) => { + const functionData = webauthn.interface.encodeFunctionData( + "addValidationKey", + [keyToAdd], + ); + const txToSign = { + ...(await aaTxTemplate(await ssoAccountInstance.getAddress(), provider)), + to: await webauthn.getAddress(), + data: functionData, + }; + txToSign.gasLimit = await provider.estimateGas(txToSign); + return await ssoAccount.sendTransaction(txToSign); + }; + describe("but not enough time has passed", () => { + it.skip("it should not accept transaction.", async function () { + await helpers.time.increase(12 * 60 * 60); + await expect(sut(newKey)).to.be.reverted; + }); + }); + describe("but passing wrong new key", () => { + it("it should revert.", async function () { + const wrongKey = await generatePassKey(); + await helpers.time.increase(1 * 24 * 60 * 60 + 60); + await expect(sut(wrongKey)).to.be.reverted; + }); + }); + describe("and passing correct new key", () => { + it("it should clean up pending request.", async function () { + await helpers.time.increase(2 * 24 * 60 * 60); + await sut(newKey); + + const request = (await guardianValidator.pendingRecoveryData( + newGuardianConnectedSsoAccount.address, + )); + expect(request.passkey).to.eq("0x"); + expect(request.timestamp).to.eq(0); + }); + }); + }); + }); + }); +}); + +async function generatePassKey() { + const keyDomain = randomBytes(32).toString("hex"); + const generatedR1Key = await generateES256R1Key(); + const [generatedX, generatedY] = await getRawPublicKeyFromCrpyto(generatedR1Key); + const generatedKey = encodeKeyFromBytes([generatedX, generatedY], keyDomain); + return generatedKey; +} + +async function aaTxTemplate(proxyAccountAddress: string, provider: Provider) { + return { + type: 113, + from: proxyAccountAddress, + data: "0x", + value: 0, + chainId: (await provider.getNetwork()).chainId, + nonce: await provider.getTransactionCount(proxyAccountAddress), + gasPrice: await provider.getGasPrice(), + customData: { + gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, + customSignature: undefined, + }, + gasLimit: 0n, + }; +} diff --git a/test/PasskeyModule.ts b/test/PasskeyModule.ts index ef5263bf..601cfaad 100644 --- a/test/PasskeyModule.ts +++ b/test/PasskeyModule.ts @@ -157,7 +157,7 @@ async function getPublicKey(publicPasskey: Uint8Array): Promise<[Hex, Hex]> { return [`0x${Buffer.from(x).toString("hex")}`, `0x${Buffer.from(y).toString("hex")}`]; } -async function getRawPublicKeyFromCrpyto(cryptoKeyPair: CryptoKeyPair) { +export async function getRawPublicKeyFromCrpyto(cryptoKeyPair: CryptoKeyPair) { const keyMaterial = await crypto.subtle.exportKey("raw", cryptoKeyPair.publicKey); return [new Uint8Array(keyMaterial.slice(1, 33)), new Uint8Array(keyMaterial.slice(33, 65))]; } @@ -274,7 +274,7 @@ export async function toHash(data: Uint8Array | string): Promise { } // Generate an ECDSA key pair with the P-256 curve (secp256r1) -async function generateES256R1Key() { +export async function generateES256R1Key() { return await crypto.subtle.generateKey(r1KeygenParams, false, ["sign", "verify"]); } @@ -397,7 +397,7 @@ function encodeKeyFromHex(hexStrings: [Hex, Hex], domain: string) { ) } -function encodeKeyFromBytes(bytes: [Uint8Array, Uint8Array], domain: string) { +export function encodeKeyFromBytes(bytes: [Uint8Array, Uint8Array], domain: string) { return encodeKeyFromHex([toHex(bytes[0]), toHex(bytes[1])], domain); } diff --git a/test/SessionKeyTest.ts b/test/SessionKeyTest.ts index b8331055..e729b1a4 100644 --- a/test/SessionKeyTest.ts +++ b/test/SessionKeyTest.ts @@ -310,9 +310,12 @@ describe("SessionKeyModule tests", function () { assert(beaconContract != null, "No Beacon deployed"); const factoryContract = await fixtures.getAaFactory(); assert(factoryContract != null, "No AA Factory deployed"); + const guardianRecoveryContract = await fixtures.getGuardianRecoveryValidator(); + assert(guardianRecoveryContract != null, "No Guardian Recovery deployed"); const authServerPaymaster = await fixtures.deployExampleAuthServerPaymaster( await factoryContract.getAddress(), await sessionModuleContract.getAddress(), + await guardianRecoveryContract.getAddress(), ); assert(authServerPaymaster != null, "No Auth Server Paymaster deployed"); diff --git a/test/utils.ts b/test/utils.ts index 479d2b85..a466627e 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,9 +6,14 @@ import { ethers, parseEther, randomBytes } from "ethers"; import { readFileSync } from "fs"; import { promises } from "fs"; import * as hre from "hardhat"; +import { + SnapshotRestorer, + takeSnapshot, +} from "@nomicfoundation/hardhat-network-helpers"; import { ContractFactory, Provider, utils, Wallet } from "zksync-ethers"; import { base64UrlToUint8Array, getPublicKeyBytesFromPasskeySignature, unwrapEC2Signature } from "zksync-sso/utils"; import { Address, isHex, toHex } from "viem"; +import { AsyncFunc } from "mocha"; import type { AAFactory, @@ -18,7 +23,8 @@ import type { SsoAccount, WebAuthValidator, SsoBeacon, - AccountProxy + AccountProxy, + GuardianRecoveryValidator } from "../typechain-types"; import { AAFactory__factory, @@ -29,7 +35,8 @@ import { SsoAccount__factory, WebAuthValidator__factory, SsoBeacon__factory, - TestPaymaster__factory + TestPaymaster__factory, + GuardianRecoveryValidator__factory } from "../typechain-types"; export const ethersStaticSalt = new Uint8Array([ @@ -99,6 +106,17 @@ export class ContractFixtures { return isHex(contractAddress) ? contractAddress : toHex(contractAddress); } + private _guardianRecoveryValidator: GuardianRecoveryValidator + async getGuardianRecoveryValidator () { + if (this._guardianRecoveryValidator === undefined) { + const webAuthVerifier = await this.getWebAuthnVerifierContract(); + const aaFactoryAddress = await this.getAaFactoryAddress() + const contract = await create2("GuardianRecoveryValidator", this.wallet, ethersStaticSalt, [await webAuthVerifier.getAddress(), aaFactoryAddress]); + this._guardianRecoveryValidator = GuardianRecoveryValidator__factory.connect(await contract.getAddress(), this.wallet); + } + return this._guardianRecoveryValidator + } + private _accountImplContract: SsoAccount; async getAccountImplContract(salt?: ethers.BytesLike) { if (!this._accountImplContract) { @@ -134,6 +152,7 @@ export class ContractFixtures { async deployExampleAuthServerPaymaster( aaFactoryAddress: string, sessionKeyValidatorAddress: string, + guardianRecoveryValidatorAddress: string, ): Promise { const contract = await create2( "ExampleAuthServerPaymaster", @@ -142,6 +161,7 @@ export class ContractFixtures { [ aaFactoryAddress, sessionKeyValidatorAddress, + guardianRecoveryValidatorAddress, ], ); const paymasterAddress = ExampleAuthServerPaymaster__factory.connect(await contract.getAddress(), this.wallet); @@ -405,3 +425,28 @@ export class RecordedResponse { // the domain linked the passkey that needs to be validated readonly expectedOrigin: string; } + +const SNAPSHOTS: SnapshotRestorer[] = []; + +export function cacheBeforeEach(initializer: AsyncFunc): void { + let initialized = false; + + beforeEach(async function () { + if (!initialized) { + await initializer.call(this); + SNAPSHOTS.push(await takeSnapshot()); + initialized = true; + } else { + const snapshotId = SNAPSHOTS.pop()!; + await snapshotId.restore(); + SNAPSHOTS.push(await takeSnapshot()); + } + }); + + after(async function () { + if (initialized) { + const snapshotId = SNAPSHOTS.pop()!; + await snapshotId.restore(); + } + }); +} From f806176c096f8c1fd26f8c243d22c95620405a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Fri, 14 Feb 2025 10:42:56 -0300 Subject: [PATCH 04/10] test: OidcKeyRegistry (#284) * Add getOidcKeyRegistryContract * Add 'helloworld' test * Test should set one key * Test should revert when a non-owner tries to set a key * Test should correctly implement circular key storage * Update test/OidcKeyRegistryTest.ts * Add Oidc to dictionary --- cspell-config/cspell-misc.txt | 4 ++ test/OidcKeyRegistryTest.ts | 98 +++++++++++++++++++++++++++++++++++ test/utils.ts | 11 ++++ 3 files changed, 113 insertions(+) create mode 100644 test/OidcKeyRegistryTest.ts diff --git a/cspell-config/cspell-misc.txt b/cspell-config/cspell-misc.txt index 3c8d0a4e..625ecfe2 100644 --- a/cspell-config/cspell-misc.txt +++ b/cspell-config/cspell-misc.txt @@ -28,3 +28,7 @@ usdc // examples/nft-quest Fren fren + +// src/OidcKeyRegistry +Oidc +oidc diff --git a/test/OidcKeyRegistryTest.ts b/test/OidcKeyRegistryTest.ts new file mode 100644 index 00000000..5ec63cdd --- /dev/null +++ b/test/OidcKeyRegistryTest.ts @@ -0,0 +1,98 @@ +import { Address } from "viem"; +import { ContractFixtures, getProvider } from "./utils"; +import { Wallet } from "zksync-ethers"; +import { expect } from "chai"; +import { OidcKeyRegistry, OidcKeyRegistry__factory } from "../typechain-types"; +import { ethers } from "ethers"; + +describe("OidcKeyRegistry", function () { + let fixtures: ContractFixtures; + let oidcKeyRegistry: OidcKeyRegistry; + + this.beforeEach(async () => { + fixtures = new ContractFixtures(); + oidcKeyRegistry = await fixtures.deployOidcKeyRegistryContract(); + }); + + it("should return empty key when fetching a non-existent key", async () => { + const issuer = "https://example.com"; + const issHash = await oidcKeyRegistry.hashIssuer(issuer); + const nonExistentKid = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + + await expect(oidcKeyRegistry.getKey(issHash, nonExistentKid)).to.be.revertedWith("Key not found"); + }); + + it("should set one key", async () => { + const issuer = "https://example.com"; + const issHash = await oidcKeyRegistry.hashIssuer(issuer); + const key = { + kid: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + n: "0xabcdef", + e: "0x010001", + }; + + await oidcKeyRegistry.setKey(issHash, key); + + const storedKey = await oidcKeyRegistry.getKey(issHash, key.kid); + expect(storedKey.kid).to.equal(key.kid); + expect(storedKey.n).to.equal(key.n); + expect(storedKey.e).to.equal(key.e); + }); + + it("should revert when a non-owner tries to set a key", async () => { + const issuer = "https://example.com"; + const issHash = await oidcKeyRegistry.hashIssuer(issuer); + const key = { + kid: "0x3333333333333333333333333333333333333333333333333333333333333333", + n: "0xcccc", + e: "0x010001", + }; + + const nonOwner = Wallet.createRandom(getProvider()); + const nonOwnerRegistry = OidcKeyRegistry__factory.connect(await oidcKeyRegistry.getAddress(), nonOwner); + + await expect(nonOwnerRegistry.setKey(issHash, key)).to.be.revertedWith("Ownable: caller is not the owner"); + }); + + it("should correctly implement circular key storage", async () => { + const issuer = "https://example.com"; + const issHash = await oidcKeyRegistry.hashIssuer(issuer); + + const keys = Array.from({ length: 5 }, (_, i) => ({ + kid: ethers.keccak256(ethers.toUtf8Bytes(`key${i + 1}`)), + n: "0xabcdef", + e: "0x010001", + })); + + for (const key of keys) { + await oidcKeyRegistry.setKey(issHash, key); + } + + // Check that the keys are stored correctly + for (let i = 0; i < 5; i++) { + const storedKey = await oidcKeyRegistry.getKey(issHash, keys[i].kid); + expect(storedKey.kid).to.equal(keys[i].kid); + } + + const moreKeys = Array.from({ length: 5 }, (_, i) => ({ + kid: ethers.keccak256(ethers.toUtf8Bytes(`key${i + 6}`)), + n: "0xabcdef", + e: "0x010001", + })); + + for (const key of moreKeys) { + await oidcKeyRegistry.setKey(issHash, key); + } + + // Check that the new keys are stored correctly + for (let i = 0; i < 5; i++) { + const storedKey = await oidcKeyRegistry.getKey(issHash, moreKeys[i].kid); + expect(storedKey.kid).to.equal(moreKeys[i].kid); + } + + // Check that the old keys are not stored anymore + for (let i = 0; i < 5; i++) { + await expect(oidcKeyRegistry.getKey(issHash, keys[i].kid)).to.be.revertedWith("Key not found"); + } + }); +}); diff --git a/test/utils.ts b/test/utils.ts index a466627e..9e7dec8b 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -24,6 +24,7 @@ import type { WebAuthValidator, SsoBeacon, AccountProxy, + OidcKeyRegistry, GuardianRecoveryValidator } from "../typechain-types"; import { @@ -36,6 +37,7 @@ import { WebAuthValidator__factory, SsoBeacon__factory, TestPaymaster__factory, + OidcKeyRegistry__factory, GuardianRecoveryValidator__factory } from "../typechain-types"; @@ -135,6 +137,15 @@ export class ContractFixtures { return this._accountProxyContract; } + private _oicdKeyRegistryContract: OidcKeyRegistry; + async deployOidcKeyRegistryContract() { + if (!this._oicdKeyRegistryContract) { + const contract = await create2("OidcKeyRegistry", this.wallet, randomBytes(32)); + this._oicdKeyRegistryContract = OidcKeyRegistry__factory.connect(await contract.getAddress(), this.wallet); + } + return this._oicdKeyRegistryContract; + } + async getAccountImplAddress(salt?: ethers.BytesLike) { return (await this.getAccountImplContract(salt)).getAddress(); } From ca9fa355af6393708d8f1dc35dc6c521fcb1b48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Fri, 14 Feb 2025 11:29:51 -0300 Subject: [PATCH 05/10] feat: Add OidcValidator (#274) * Add OidcValidator * Update install and uninstall * Add addValidationKey * Add OidcValidator.validateTransaction * Add pkop to dictionary * Remove OidcValidatorTest --- cspell-config/cspell-misc.txt | 3 + src/validators/OidcValidator.sol | 102 +++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/validators/OidcValidator.sol diff --git a/cspell-config/cspell-misc.txt b/cspell-config/cspell-misc.txt index 625ecfe2..5dd6bd6e 100644 --- a/cspell-config/cspell-misc.txt +++ b/cspell-config/cspell-misc.txt @@ -32,3 +32,6 @@ fren // src/OidcKeyRegistry Oidc oidc + +// src/validators/OidcValidator +pkop diff --git a/src/validators/OidcValidator.sol b/src/validators/OidcValidator.sol new file mode 100644 index 00000000..b5344cbc --- /dev/null +++ b/src/validators/OidcValidator.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; +import { IModule } from "../interfaces/IModule.sol"; +import { VerifierCaller } from "../helpers/VerifierCaller.sol"; +import { OidcKeyRegistry } from "../OidcKeyRegistry.sol"; + +/// @title OidcValidator +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @dev This contract allows secure user authentication using OIDC protocol. +contract OidcValidator is VerifierCaller, IModuleValidator { + event OidcKeyUpdated(address indexed account, bytes iss, bool isNew); + + struct OidcData { + bytes oidcDigest; // PoseidonHash(sub || aud || iss || salt) + bytes iss; // Issuer + bytes aud; // Audience + } + + struct OidcSignature { + bytes zkProof; + bytes32 issHash; // Hash of the issuer + bytes32 kid; // Key id used in the jwt + } + + mapping(address => OidcData) public accountData; + + address public immutable keyRegistry; + address public immutable verifier; + + constructor(address _keyRegistry, address _verifier) { + keyRegistry = _keyRegistry; + verifier = _verifier; + } + + /// @notice Runs on module install + /// @param data ABI-encoded OidcData key to add immediately, or empty if not needed + function onInstall(bytes calldata data) external override { + if (data.length > 0) { + require(addValidationKey(data), "OidcValidator: key already exists"); + } + } + + /// @notice Runs on module uninstall + /// @param data unused + function onUninstall(bytes calldata data) external override { + accountData[msg.sender] = OidcData(bytes(""), bytes(""), bytes("")); + } + + /// @notice Adds an `OidcData` for the caller. + /// @param key ABI-encoded `OidcData`. + /// @return true if the key was added, false if it was updated. + function addValidationKey(bytes calldata key) public returns (bool) { + OidcData memory oidcData = abi.decode(key, (OidcData)); + + bool isNew = accountData[msg.sender].oidcDigest.length == 0; + accountData[msg.sender] = oidcData; + + emit OidcKeyUpdated(msg.sender, oidcData.iss, isNew); + return isNew; + } + + /// @notice Validates a transaction to add a new passkey for the user. + /// @dev Ensures the transaction calls `addValidationKey` in `WebAuthValidator` and verifies the zk proof. + /// - Queries `OidcKeyRegistry` for the provider's public key (`pkop`). + /// - Calls the verifier contract to validate the zk proof. + /// - If the proof is valid, the transaction is approved, allowing `WebAuthValidator` to add the passkey. + /// @param signedHash The hash of the transaction data that was signed. + /// @param signature The signature to be verified, interpreted as an `OidcSignature`. + /// @param transaction The transaction data being validated. + /// @return true if the transaction is valid and authorized, false otherwise. + function validateTransaction( + bytes32 signedHash, + bytes calldata signature, + Transaction calldata transaction + ) external view returns (bool) { + OidcKeyRegistry keyRegistryContract = OidcKeyRegistry(keyRegistry); + OidcSignature memory oidcSignature = abi.decode(signature, (OidcSignature)); + OidcKeyRegistry.Key memory key = keyRegistryContract.getKey(oidcSignature.issHash, oidcSignature.kid); + + revert("OidcValidator: validateTransaction not implemented"); + } + + /// @notice Unimplemented because signature validation is not required. + /// @dev We only need `validateTransaction` to add new passkeys, so this function is intentionally left unimplemented. + function validateSignature(bytes32 signedHash, bytes memory signature) external view returns (bool) { + revert("OidcValidator: validateSignature not implemented"); + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + return + interfaceId == type(IERC165).interfaceId || + interfaceId == type(IModuleValidator).interfaceId || + interfaceId == type(IModule).interfaceId; + } +} From ef73769ef665d2a929ed1681d138bb9e6eb3bbb5 Mon Sep 17 00:00:00 2001 From: Miguel Duarte Date: Mon, 17 Feb 2025 19:38:00 -0300 Subject: [PATCH 06/10] feat: added oidc zk verifier (#297) added auto generated code for verifier added oidc validator and its deps to deploy scripts touches to follow project style and make lint pass --- cspell-config/cspell-sol.txt | 1 + cspell.json | 3 +- scripts/deploy.ts | 9 +- .../ZkemailJwtVerifyVerifier.sol | 1644 +++++++++++++++++ 4 files changed, 1654 insertions(+), 3 deletions(-) create mode 100644 src/autogenerated/ZkemailJwtVerifyVerifier.sol diff --git a/cspell-config/cspell-sol.txt b/cspell-config/cspell-sol.txt index 3967600e..3ff9caf5 100644 --- a/cspell-config/cspell-sol.txt +++ b/cspell-config/cspell-sol.txt @@ -34,3 +34,4 @@ solady xbatch tload tstore +Groth diff --git a/cspell.json b/cspell.json index e52eddd0..1f145adb 100644 --- a/cspell.json +++ b/cspell.json @@ -26,7 +26,8 @@ "**/test-results/**", "**/playwright-report/**", "**/blob-report/**", - "**/playwright/.cache/**" + "**/playwright/.cache/**", + "src/autogenerated/**" ], "caseSensitive": true, "dictionaries": [ diff --git a/scripts/deploy.ts b/scripts/deploy.ts index f35c2d02..6deb18c7 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -7,6 +7,8 @@ import { Wallet } from "zksync-ethers"; const WEBAUTH_NAME = "WebAuthValidator"; const SESSIONS_NAME = "SessionKeyValidator"; const GUARDIAN_RECOVERY_NAME = "GuardianRecoveryValidator"; +const OIDC_RECOVERY_NAME = "OidcValidator"; +const OIDC_VERIFIER_NAME = "Groth16Verifier"; const ACCOUNT_IMPL_NAME = "SsoAccount"; const FACTORY_NAME = "AAFactory"; const PAYMASTER_NAME = "ExampleAuthServerPaymaster"; @@ -84,9 +86,11 @@ task("deploy", "Deploys ZKsync SSO contracts") const factory = await deploy(FACTORY_NAME, deployer, !cmd.noProxy, [beacon]); const guardianInterface = new ethers.Interface((await hre.artifacts.readArtifact(GUARDIAN_RECOVERY_NAME)).abi); const recovery = await deploy(GUARDIAN_RECOVERY_NAME, deployer, !cmd.noProxy, [webauth, factory], guardianInterface.encodeFunctionData("initialize", [webauth, factory])); - const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); const oidcKeyRegistryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_KEY_REGISTRY_NAME)).abi); - await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); + const oidcKeyRegistry = await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); + const oidcVerifier = await deploy(OIDC_VERIFIER_NAME, deployer, false, []); + await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier]); + const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); await fundPaymaster(paymaster, cmd.fund); } else { @@ -113,6 +117,7 @@ task("deploy", "Deploys ZKsync SSO contracts") if (cmd.only == OIDC_KEY_REGISTRY_NAME) { args = []; } + const deployedContract = await deploy(cmd.only, deployer, false, args); if (cmd.only == PAYMASTER_NAME) { diff --git a/src/autogenerated/ZkemailJwtVerifyVerifier.sol b/src/autogenerated/ZkemailJwtVerifyVerifier.sol new file mode 100644 index 00000000..d71a2c5e --- /dev/null +++ b/src/autogenerated/ZkemailJwtVerifyVerifier.sol @@ -0,0 +1,1644 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + +pragma solidity >=0.7.0 <0.9.0; + +contract Groth16Verifier { + // Scalar field size + uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // Verification Key data + uint256 constant alphax = 16428432848801857252194528405604668803277877773566238944394625302971855135431; + uint256 constant alphay = 16846502678714586896801519656441059708016666274385668027902869494772365009666; + uint256 constant betax1 = 3182164110458002340215786955198810119980427837186618912744689678939861918171; + uint256 constant betax2 = 16348171800823588416173124589066524623406261996681292662100840445103873053252; + uint256 constant betay1 = 4920802715848186258981584729175884379674325733638798907835771393452862684714; + uint256 constant betay2 = 19687132236965066906216944365591810874384658708175106803089633851114028275753; + uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant deltax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant deltax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant deltay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 constant deltay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + + uint256 constant IC0x = 14773748881002388138426678055182742267295754834000412787300419197721423479856; + uint256 constant IC0y = 17099854444038694455002099641554952546567275803195927049512581525482795246827; + + uint256 constant IC1x = 17005693695650722879350161528606400616046731331846688674421638879048479873955; + uint256 constant IC1y = 10054204822345012174289044753452050377458791681404504103657720318298853120271; + + uint256 constant IC2x = 16984235863610752313157420534263685461452686649835091313713564683827667297490; + uint256 constant IC2y = 20927385381759377319668497055236489771609161765762117296915867259676411864977; + + uint256 constant IC3x = 21529859948639263085588008054235579795264977367263040514421867054333492726143; + uint256 constant IC3y = 20935239806756970863840140825409162984984016557657823653593123155343642550272; + + uint256 constant IC4x = 6486179950956522950261619284524446196795424368183187249712765581786535981925; + uint256 constant IC4y = 18118307049006636679238848489893618485104447982731868590404742962371064909426; + + uint256 constant IC5x = 16330815010849953551866767268081799586967446753737847943878393716600026160024; + uint256 constant IC5y = 11674658479227784536220007989099232486877204444025901913703014044877175129807; + + uint256 constant IC6x = 11849130354914476419040370638436243679959639447258344602995375769759322641732; + uint256 constant IC6y = 16242686862595548902611507306760364827917861100718534576387114966430962864240; + + uint256 constant IC7x = 11438660553586548248921524908925487866775045093651990896019845248285829592618; + uint256 constant IC7y = 202703004077365807538223031198344518388881190571867311788298118066650825641; + + uint256 constant IC8x = 1179272616342910887839245172701634464835798343954273592041154107962520269181; + uint256 constant IC8y = 1707836075189702464244982207263109526578166546635243460095115710082645387279; + + uint256 constant IC9x = 2421423439175930270477525995760024402042223361421651906125260682585183940366; + uint256 constant IC9y = 15341278155820378373236962175882332460111921095774372694933616819938452842945; + + uint256 constant IC10x = 3606847719418956546331216073342139806879127677444488138934335502072377081361; + uint256 constant IC10y = 1814767866993913260210283879234107049358278475727495219870061136494215909527; + + uint256 constant IC11x = 20843534952645288286130981380343399930274396986963226164437906371589702755880; + uint256 constant IC11y = 14202013630584197969494740802927302338012115686505287462668998765520665582225; + + uint256 constant IC12x = 14882568228542572453417059960063566955765981390928187576898928901954435861772; + uint256 constant IC12y = 6719468876270813694982665735429263452303271307491893738700572064511044530137; + + uint256 constant IC13x = 15164134802039819426990288587258350672590913235760943367816162026654402744589; + uint256 constant IC13y = 18514851851570507261591727817691008032660362011351650923564501366493872803254; + + uint256 constant IC14x = 11731889649218416917157497477416076891111214199598733603666784843298578121663; + uint256 constant IC14y = 10336173829726775838915008976249439269847185722999469581845148390473463469813; + + uint256 constant IC15x = 363593829133959600896003273324732490646205576817107198732199804974287149130; + uint256 constant IC15y = 16776485845408707677069100948812645466921645980973468395203633502651521394816; + + uint256 constant IC16x = 10118468515075267230248859262931967111781735767666939645370752530613871786800; + uint256 constant IC16y = 4877793478658867760324781486948394948650810719866637307103409815180852183652; + + uint256 constant IC17x = 11905551706027189169472466166264701805497814583268852186452967516742421261230; + uint256 constant IC17y = 2057892735331143475782166106170401293633551432353002854928010447350465301630; + + uint256 constant IC18x = 10705818336544432452992670355789016031338191824527475369646281790484225436218; + uint256 constant IC18y = 19980016362172074694032089437899567157599568508951057503658769476144084882258; + + uint256 constant IC19x = 17579319450125304917805579834111760963128858835742105386371167197203367082150; + uint256 constant IC19y = 9297045798346306554317460118754935195220750095022478568466057455526099704230; + + uint256 constant IC20x = 13997660684378404795228847273759756923319396816034351582069269209091093783416; + uint256 constant IC20y = 14123489214747268791074154443306215394630092316135895598067729642059819412025; + + uint256 constant IC21x = 18244699370960719161982161228072500726955073938946128494772152711326103560751; + uint256 constant IC21y = 13157526674499858588197900080419712769214963860067390175938346522123803084758; + + uint256 constant IC22x = 3595342216562859357345748814113402922701764784662357079076084279522220008052; + uint256 constant IC22y = 20501129898604484957752700788320057426790264302122381368770864121443546759960; + + uint256 constant IC23x = 10144237743859688447917861252015335694500592792098062125134520203746239378341; + uint256 constant IC23y = 4067808672447169681872481600887764217210449051528350977880977067375258923838; + + uint256 constant IC24x = 1312597567385009134926562586831402843027832579709043410219382566182824523467; + uint256 constant IC24y = 10072112693685723730081879668628786353795670294235977583619894009844482367010; + + uint256 constant IC25x = 4046074245268127966083303318813894058330046522646981263998876362549729816024; + uint256 constant IC25y = 14426643374795545666739781294009038817763732488584886056368770078995354323820; + + uint256 constant IC26x = 12859897849464356992054685006335120813739968651063306557145697186697468270413; + uint256 constant IC26y = 11331453885167016500054336312442635247203516302287838904703089032240113283431; + + uint256 constant IC27x = 20409100781568045108104573419457469389391287569741841259851671613527929289729; + uint256 constant IC27y = 10259547510642208967168970137729047337147255208705823742125121081123262006038; + + uint256 constant IC28x = 20742065759282289009528126686117314076939317418066767141634228909880097598252; + uint256 constant IC28y = 14984905403160705227756463881648968227003915051644406776289900761995576088498; + + uint256 constant IC29x = 19021506107216399883120035697461615114324433811844938023788478755505451062024; + uint256 constant IC29y = 6286933417697827443111184077050672477307780251152939708806257176838690833901; + + uint256 constant IC30x = 83744525880318306257321199304168990351545232597511450921179226323370605123; + uint256 constant IC30y = 6570163956257564815172639957020972103769246072876138396956453249228737616869; + + uint256 constant IC31x = 10573946407166653475127736862420220716696382459265946271322216323765943611973; + uint256 constant IC31y = 9869664297801148129115937500502737335048884110138832597271028617646018267756; + + uint256 constant IC32x = 18455879757601614568956991852194776203458717347236295352942297815588906348456; + uint256 constant IC32y = 7525703526537160099971093960103025115592734202764897203197499380537907330808; + + uint256 constant IC33x = 17680736761540224159565556955688568548484945808169494847102369187256719318984; + uint256 constant IC33y = 898643690962443755327142258396288238855861056330969047974322064106970767660; + + uint256 constant IC34x = 6916902130315629532830983659460476215529824346851177789541414701321270318668; + uint256 constant IC34y = 7445455717564563470570833282161143786859007570300988386992764111043426176196; + + uint256 constant IC35x = 20105959618934057132342607738171972142190633147630469205910328978949826241420; + uint256 constant IC35y = 6922754532833521166309291331903582467860862119776555814937982087985287616819; + + uint256 constant IC36x = 5645921676038174521786602559034745849346141176440546924511723423359527955502; + uint256 constant IC36y = 8707376815763308152614919601852170603893083116066245577676239509000285465405; + + uint256 constant IC37x = 18345741954343195564558430240730871132641188699200914123270515554361761963023; + uint256 constant IC37y = 5213683416675783312701669237648615022388296941198983965789451813901407236233; + + uint256 constant IC38x = 19071011739385411775898451355886262507129336959017083683062886809553352927559; + uint256 constant IC38y = 3415403905629534575526209118879309170021448529642382927071413273566105286205; + + uint256 constant IC39x = 6587123956164430393024169926781072011751096548890004751445880291466479950168; + uint256 constant IC39y = 17975896285612137446279878104747665900581166771039631273391341629189982451990; + + uint256 constant IC40x = 17796328919059332867747920751207044759500197491362246812564136100192105421828; + uint256 constant IC40y = 6734167968527622186950438590562823374791969289949404340913669919654359277069; + + uint256 constant IC41x = 2617319930818779413960601177938487999775267122830014130985245855060657350267; + uint256 constant IC41y = 18073589204146036190669991565022078122638225991259567543913464441056098683145; + + uint256 constant IC42x = 5768039530872514664476975062644710370547050654729143011333633561620365036692; + uint256 constant IC42y = 11403963969451021182869098611705587905188043626538492198311781688293980966543; + + uint256 constant IC43x = 19920724205476113590559550910167074870913199367289754037273710677925659846308; + uint256 constant IC43y = 8031239805764792915572737214244620887152383928997613864419326866460961102987; + + uint256 constant IC44x = 18672320319029716571883365420156319136926790091276162518336998252927131471808; + uint256 constant IC44y = 20925780353678332566508577693901181315924534586541054935408576012988236952472; + + uint256 constant IC45x = 9291483175317410465191447057485133143612938415199959441133140558487411328150; + uint256 constant IC45y = 10125488774614416223369153331658290353047850219268569661981517093348258479668; + + uint256 constant IC46x = 2924032259652579221460371491459074588422436513521600578735144265868939836427; + uint256 constant IC46y = 13726970899989530778682706335706499110324173143919847370252880519629568822276; + + uint256 constant IC47x = 17871693519998936246662387181449540423742202909278657935753493143327916926045; + uint256 constant IC47y = 13613553309858358292054284974064792791470250970917318488751819629171587978693; + + uint256 constant IC48x = 20209962082136697059821371557486136741414755962445197210416873127049254708176; + uint256 constant IC48y = 18640670960076043292662594593143794315568718855711322654889437987778789599290; + + uint256 constant IC49x = 15535550551324442548343428990084778093649647668610786818844195094273183085936; + uint256 constant IC49y = 10806187178877101821551302768696892397301915904275180263996918407019217143794; + + uint256 constant IC50x = 19599784050223204971752973914943882938104206160239839333195830279539092062448; + uint256 constant IC50y = 14306046592352438375255289608222282484591036337922079092596698408559004046209; + + uint256 constant IC51x = 9395031614438321097935134865334111348182240513981880928143087165239387960138; + uint256 constant IC51y = 2012011583057153402577242861033969910340144554068525371935210841231017809240; + + uint256 constant IC52x = 11012305349884768437184829670435866011229245317741964356941607676493953952304; + uint256 constant IC52y = 17914218844322212979873217388562014665221758121154774554551991895460464943367; + + uint256 constant IC53x = 5467173383938187922232926266650118134428109069103258942659455645777478089234; + uint256 constant IC53y = 17079766809323958825508560262527833018002720740791865792496714993754712353040; + + uint256 constant IC54x = 19117077876656079968938330499460123256663309003957249793239038161254505317998; + uint256 constant IC54y = 20437295934771031186934682660606303594511773442961357917873904980467884748888; + + uint256 constant IC55x = 15155383643085488615410277239329018374215196199859150200578970596477793316081; + uint256 constant IC55y = 6338272226325802243227632712529129184369185294523799339826930810154120409006; + + uint256 constant IC56x = 1062099429295418402645509633117458776053903626583206923010147743016534511490; + uint256 constant IC56y = 2915468730974595712537442407567041825252329849906971791892091020542479800179; + + uint256 constant IC57x = 10408595930903279428698504430174724099078577787077552811629028599031628813396; + uint256 constant IC57y = 18023604511611781595245402635487093101872492432115497923081835819377828123634; + + uint256 constant IC58x = 14614819235331326000956400876132956566942636325078841107868391751756279284582; + uint256 constant IC58y = 11565063559973109375565456907294168723594454397819084716234084864954087435724; + + uint256 constant IC59x = 1674524399035132058663083667186220780560548919925623177998001723662425671820; + uint256 constant IC59y = 12543882691906929687122890813566034408676890524945219250142913570709338786629; + + uint256 constant IC60x = 17183020298556425174261372467066550783519854821659230674999407942297050916447; + uint256 constant IC60y = 19689889542055469025051917023247431389828547118653087373539988600309151576626; + + uint256 constant IC61x = 15927826916601622874805600921055833660092032329131515514175512366868176199912; + uint256 constant IC61y = 20572936971648325911903275443112284588712129191983972203127246334824165487735; + + uint256 constant IC62x = 9479463066132434492934517438680824832517003686729659646244957772886501592996; + uint256 constant IC62y = 13780540498148247482166354722554730895084625916251903861573130395870931155035; + + uint256 constant IC63x = 18584845547865004092669285850408996688397575777560413680031823859027415312517; + uint256 constant IC63y = 6581626374780467721058979524084676179503710563964574072846983367416866615246; + + uint256 constant IC64x = 20196468590377095396633516343977223930134663459451047059482481155282803949742; + uint256 constant IC64y = 19565225004941981752135328824175404017399262754352820349379775256259283699054; + + uint256 constant IC65x = 960380573603729164274412297757919855776778370807263483712410960325580143752; + uint256 constant IC65y = 21298260850043695605083686649898173692174503415707299997540947186030896968120; + + uint256 constant IC66x = 1381317832292639027992833805877306201637184861653242722428567602726448322961; + uint256 constant IC66y = 6895755207391990168460591187801298834534750605988834923239111835674844286889; + + uint256 constant IC67x = 26894071346796056552333493103100043797889777585508553320581724545066667919; + uint256 constant IC67y = 10825237477429737756614257989441086445994725577737437211096813722178143133341; + + uint256 constant IC68x = 21383009166996467880292594053233535880636148959709063049704727553890189133808; + uint256 constant IC68y = 20141933216308444890273519870916135504478408667677369697409335159597166909058; + + uint256 constant IC69x = 6365805334082438244318755132826983484393466363775292949213950526200654199316; + uint256 constant IC69y = 15278035495964111850969192307631314124853728792525166886294522357998696271113; + + uint256 constant IC70x = 20907620198523547013369475624698942168981129123477983949823975199400019633226; + uint256 constant IC70y = 2226071087112280272592778079002314617731854975233447056450217703016448428464; + + uint256 constant IC71x = 21224467901837366892886777770207789633613619059580262796382171386524250675103; + uint256 constant IC71y = 4814118589809028858218623191684778229234022634772014969410785180052504055126; + + uint256 constant IC72x = 12747505492353060272778335711970762102902359596448695231348682618391227491261; + uint256 constant IC72y = 7098715521059814446165502116625317482282760166692568601778514495672877774089; + + uint256 constant IC73x = 9292868066626841993605598664256629790653483134143280460603868535655613574973; + uint256 constant IC73y = 12195677116407972768631892173319465463424812095769679250615198650574879153238; + + uint256 constant IC74x = 1999886418566788477267436737318225538382275733520362065512893474654102461133; + uint256 constant IC74y = 3893301144912956026382138321257220816134387891914336592163312035615042446906; + + uint256 constant IC75x = 10556209325903022909647716966433937379469279999933629064630845473157309979294; + uint256 constant IC75y = 6580242876027235749914923923747381636686840415261603811398270825413067389754; + + uint256 constant IC76x = 7983009749351000050955486365126761224404734638005366057898078169806749190818; + uint256 constant IC76y = 2736908424771430460087100678985795016644240398624732371714326695508794623791; + + uint256 constant IC77x = 16665116086435318048822860179319233473685423956405506567044924326808919803471; + uint256 constant IC77y = 3317302678019617970949282943547284057691560264142957567031683741204366074815; + + uint256 constant IC78x = 19169461791802437916348730709822132402248229429520683965006146393350176332758; + uint256 constant IC78y = 7134550381028074121359441749320038481903728550011694701775364406835572353791; + + uint256 constant IC79x = 145792034407536273482463881895119871925192277629891952281377023580766251706; + uint256 constant IC79y = 15761218718628236562953205726192261408272463262131133728441076519252301732570; + + uint256 constant IC80x = 9684443818806897065554319343577698272848121596573567249832252389971584223107; + uint256 constant IC80y = 10685258165648290291768373155105470154713323101619580796904978589626406367943; + + uint256 constant IC81x = 14491480770251996093555258651150625336955948131684618720038108626723587341038; + uint256 constant IC81y = 18805051988215008068302770717926286344009012422543414410193570448703799241315; + + uint256 constant IC82x = 14558120258389516381240460832335035484869730355251642257730841327727461212466; + uint256 constant IC82y = 13466041130781308710877682388927576838174881008810107850542190491599252051480; + + uint256 constant IC83x = 13347643398664190621758082225387476957905468618295568916245631002740965294066; + uint256 constant IC83y = 16473118604218542262518611017429717564756733242348176675221973801821473520108; + + uint256 constant IC84x = 12741701729995243460868063497091333066820557814764295187187733857652729645215; + uint256 constant IC84y = 8928923449465633225821156523648393577959961641870765775933096793907102592235; + + uint256 constant IC85x = 11923188010634275405071559436619243439968122704579710115023040532124387591819; + uint256 constant IC85y = 8393789681462912094724010251641844423201306276776946780863783214762108651369; + + uint256 constant IC86x = 381147405565785174295905413422641937113167559126313309650288025360208272800; + uint256 constant IC86y = 19777853708107526014827888169244814701112864254172206352270189369273231606644; + + uint256 constant IC87x = 17289731586513073469482380134254507556276880700167943597548263544704497901499; + uint256 constant IC87y = 18556940457033245044651794840364568928553376930679591159871207168993874971006; + + uint256 constant IC88x = 886789148312314342579088021506924103071149801049627022976952243098968919175; + uint256 constant IC88y = 15215164331960733584629339313515051445562028080829330147012057015153615893446; + + uint256 constant IC89x = 4915341873247039700262313762262847786082009968311221214489093422072465063232; + uint256 constant IC89y = 13193927350555240216898513676073615431901007457220651046803640351501044196532; + + uint256 constant IC90x = 7003781383571444641207734371355347453191871743033423885023768587100605120287; + uint256 constant IC90y = 10858943170883326359060760249831773468101203385623560918214538849215502396079; + + uint256 constant IC91x = 2840479707229761552505481981263750628560564742783844001586264325432395780605; + uint256 constant IC91y = 18696757581236654148652450312540887383556285350256948243408633242628910810551; + + uint256 constant IC92x = 12485130122920018968427887531312141530727798844341727215992839997276453071409; + uint256 constant IC92y = 2709035242574319460741409902224599911009339210822061943023288734114679291; + + uint256 constant IC93x = 19695171776479315909777886871698499392122259296958597608155530528301574364966; + uint256 constant IC93y = 13971870070440779449996504131135617938698211249771840096421096221565269912854; + + uint256 constant IC94x = 8406254631711353811864311786742512427235375688803872236368445583873757106944; + uint256 constant IC94y = 1084432273280044788089620589852609078068355743974587027818262297827942144990; + + uint256 constant IC95x = 6593044179437857088404684929727158620760112035604952839584753457877013041161; + uint256 constant IC95y = 3138771359864703243325779540443955970965839494875736924601117907224484285238; + + uint256 constant IC96x = 4820867944088194966168305177444439420516264685505664627720069739547533650785; + uint256 constant IC96y = 8661535008671037113840950172057441623053269965493264857290790752582787585970; + + uint256 constant IC97x = 17129221066588539822941145537595844283391921982219838022797816011681138602985; + uint256 constant IC97y = 5814881070539029397144404276907205479842632718873283564641700299783077162006; + + uint256 constant IC98x = 14484988566306314524759201901518764448031105876919248511239648275720427274889; + uint256 constant IC98y = 342014636252404500540673649662169543452317868209859446025766546924303635160; + + uint256 constant IC99x = 947174519296995857882674514493016088568404588428254945209768150134037568442; + uint256 constant IC99y = 8326787935662130040355990378317851190739823278540636754921264573800010553290; + + uint256 constant IC100x = 13812605490213608587251786945695971028867153559226039783450653634134840324889; + uint256 constant IC100y = 7394649875822135548196948070092544390396729933339145036783962659483495482906; + + uint256 constant IC101x = 5535339280004163334630790662942645203133218820402343146597350767170515974059; + uint256 constant IC101y = 16694395611302976798353368461926101893107669051809460386534714691202478074918; + + uint256 constant IC102x = 14959129102618106925831001919187981998297898526799404512083106827414430608997; + uint256 constant IC102y = 13302457627768269966558617708765122083613836146284267884565569968039641705450; + + uint256 constant IC103x = 6713047684385783526198224827920133819751377145516815811841859593686756236601; + uint256 constant IC103y = 1220862783212145990597864952854306622526830560410711284312967686528258349769; + + uint256 constant IC104x = 247566255237870592589141141505577963667523678261661923002754943480311790654; + uint256 constant IC104y = 19334790055196384649490571946140167270453920975024923633466693276465516347452; + + uint256 constant IC105x = 7158485590623992496383308612170860607727337016869353083677091909658321916100; + uint256 constant IC105y = 16687547759202395165065187612026422420039466252570331363050694126267734337375; + + uint256 constant IC106x = 2525493881519984392544723608929031010185369504168764029945934379167529267550; + uint256 constant IC106y = 19990363222255194395572220490193398976994070003977983023168695451111628172027; + + uint256 constant IC107x = 7919611874167071078722635450882202240395949426945372646452463488756608217500; + uint256 constant IC107y = 13775804206750016855439491608978256447965982569361862321919797909924089149437; + + uint256 constant IC108x = 20596959199322447846743963991865612232822936418834466866299159252561412907314; + uint256 constant IC108y = 7166022816707473894495571572911498678644945258322141688605423035923073936035; + + uint256 constant IC109x = 6569033019189287735253022759452555727323089307837139515773444703706655303489; + uint256 constant IC109y = 16926624106987589439227844510746990485103419199495345227131466111483412225240; + + uint256 constant IC110x = 14722372858068799326660537034368714612736755521408019989951441949618924137728; + uint256 constant IC110y = 13361714258499743943785133689029500970659253587521341579941902950312116445118; + + uint256 constant IC111x = 17690251913431811942199478954986828087979926635015769534902539872625397711516; + uint256 constant IC111y = 9637976146674532532282596271666234841862170911017554228897379770180537395990; + + uint256 constant IC112x = 20837560216492246554102504388968224035024860479664647971482420138172887960531; + uint256 constant IC112y = 317903718164328554623408527488181995400191258330908221017194445437551269240; + + uint256 constant IC113x = 11248937013610040399855562325140772611255203655962957631321578637498433088397; + uint256 constant IC113y = 10623473670515179072413235560238507693536363349580984135609063820825553126802; + + uint256 constant IC114x = 16233643983066887469732442740268426331626234364986933691126101274246152955991; + uint256 constant IC114y = 3529083093850575432317521596102396919387590496440106138156613282477700963537; + + uint256 constant IC115x = 10376452831812280014807199052764393188411239516023561580944346335954295667543; + uint256 constant IC115y = 7750421082139954659191298250321925366670647288192333711456218035727637438998; + + uint256 constant IC116x = 4347064377243197645369324428036161727528776825124893814192790722089206134358; + uint256 constant IC116y = 19242274816360821067239988572015019197252921496565056961479233836200625191967; + + uint256 constant IC117x = 4221335677652549059904836174687003457602174685960699957143405027288352313717; + uint256 constant IC117y = 21125402333031957104034175651641292670556314314778688268318134431092573758894; + + uint256 constant IC118x = 7776038682243131182493992041399788361371778640976993073550807524903816536140; + uint256 constant IC118y = 273909636687939100555686844720712958130833481338193633931565609805794300242; + + uint256 constant IC119x = 5324915851356070812344772298246639309856864443049639945540681262045399168063; + uint256 constant IC119y = 544706471459338545494262389291478559334344719081961276509514366017100882725; + + uint256 constant IC120x = 11111480662143253909673394948703309973626434903466037466850485148767011881626; + uint256 constant IC120y = 11364069235324665583700791384614300695600375363033922060712744487209170058322; + + uint256 constant IC121x = 3991995758717221970755836423511244244308143405179313073514873432088473187277; + uint256 constant IC121y = 3734977974548824150458187071325924849973977525783622015407514718153438230975; + + uint256 constant IC122x = 928951084796431741036002674481767876575322495577163947611287770179994822031; + uint256 constant IC122y = 16362176215975264227599020601040510494525863400266289817172295125866917202341; + + uint256 constant IC123x = 21424220638117304993333552834704240699982092795085853495194564691952092556602; + uint256 constant IC123y = 10470706203843126842965844277688466619555843833871649776019764808982238802638; + + uint256 constant IC124x = 13082207796896154504619938237853885714504962855025440382119718986046103066331; + uint256 constant IC124y = 17569756359836190232823890484961379277617805542214487316474847601291342182507; + + uint256 constant IC125x = 5193592271804383034327925265774992531573328514449051844113761101304634036823; + uint256 constant IC125y = 17055649190818365089160511600946244531191227806166102530448957936324784759083; + + uint256 constant IC126x = 14365019590686460900396710027180500160160794432839603532047961109709335476293; + uint256 constant IC126y = 11789965608704990134600835535701907628869047154507822808112817064927899431238; + + uint256 constant IC127x = 20864071209530294223038353507108842637693631331110595366991089018983180060611; + uint256 constant IC127y = 8194103809629987371657169073708390845053154992891451281794857673102938881954; + + uint256 constant IC128x = 18153277595086139727706472606540203287641677744311514060757577732955460415005; + uint256 constant IC128y = 7463673647777144390703174642767744544818100686391535978109658547648585956432; + + uint256 constant IC129x = 6160453324993344394534808121964038960352654355385375389186159809124522885960; + uint256 constant IC129y = 4136989128631613897865842355107649933557711382066074164750582480595769366060; + + uint256 constant IC130x = 14121807411929845692778174371061490844008727521926113741442581428954858784567; + uint256 constant IC130y = 11853840768950496222411167967460407482826825127411135683629794421474811404858; + + uint256 constant IC131x = 16924204729321764300590364600168644468555396095102248022706813368945623058018; + uint256 constant IC131y = 20760144095957950356813947568665061915903292064907073500224940307128528081567; + + uint256 constant IC132x = 21610097500867029412161904867985721967271694537702392744540881554707479534948; + uint256 constant IC132y = 18972992525534197456142302781893152287438863291553546152046350476872589849821; + + uint256 constant IC133x = 18646819151868515587897844941753809468930756255716882321502396170512738005162; + uint256 constant IC133y = 17605801607776712772042322754645510332100723617007108349159281076787814880060; + + uint256 constant IC134x = 11440539644184070435333174192587902982494169063586928312680750518097377474272; + uint256 constant IC134y = 21155130837666893786197758207140434652217172970618374222613042156458399933385; + + uint256 constant IC135x = 3503705212382403303567534016107574644794970371598630367707855629584202481677; + uint256 constant IC135y = 17445523430051651077578450949183677654790312461727607798693508516824854644894; + + uint256 constant IC136x = 2693627884555067608369607810997678560501811436752202654319342469034540777528; + uint256 constant IC136y = 18317993960692346111197470805373674148953287609578764275112097532321062897488; + + uint256 constant IC137x = 9030199160530157975049474015475736653292806285075841822294874544422344627178; + uint256 constant IC137y = 21158030602110398159253572348867382725091641930289760083659490756663625492775; + + uint256 constant IC138x = 5836888834196577248972557180142345150112215678394818565959902434674600247514; + uint256 constant IC138y = 18527486153778253106155831655774540152803130457228661907239780233702227039383; + + uint256 constant IC139x = 16164387349469764187596694895851346017611690056190335896724199106077113330223; + uint256 constant IC139y = 4435057261718697628019205132474469806901775819251909713946314900812661863741; + + uint256 constant IC140x = 1229389340425718707402630251141662805248238303485763744501876582125587428242; + uint256 constant IC140y = 489275099080201974637317037736620914762929457532378907846646133792412751426; + + uint256 constant IC141x = 9946304183961185668876737301941229165079220283985984569883930545161741174578; + uint256 constant IC141y = 16432027241851317681213431360707696204830202504330612676718995207581645719210; + + uint256 constant IC142x = 13542530534381806375765649861388790793170338824599840763176348130868189714383; + uint256 constant IC142y = 11692003673118816752570251490863538751276841496583006087775837001712275458419; + + uint256 constant IC143x = 10973043636581802426788366093360206167261009908819688495722041117062547316927; + uint256 constant IC143y = 18626935059620048857259174093855627389676884823280699941490958113066145301169; + + uint256 constant IC144x = 15912635697656287490628962709948010811837013303508902286259544869574729040055; + uint256 constant IC144y = 2518968815260633757236427830017874366121673886737210715838817752363814458365; + + uint256 constant IC145x = 17428181521278899848160005932846576010673188634232729657556716697777983175078; + uint256 constant IC145y = 18657424972288906790452528296694486396779891759479957469329984457268629835297; + + uint256 constant IC146x = 13159379630148943287084466052598471002706023717742052267906427665685713369814; + uint256 constant IC146y = 491645269486863987350135548903559711998255109168620099079953541557761830142; + + uint256 constant IC147x = 254384780975369862609352575530949587220833437369323190849536486734775921883; + uint256 constant IC147y = 13960193967129588040370148269956019478461939309694644929951869102415120752743; + + uint256 constant IC148x = 13867677706260988416085195998333043571580508923098420535236847693173399597488; + uint256 constant IC148y = 6310765351653324427901176723229321146563915705083337486975787404357085727688; + + uint256 constant IC149x = 3677274165647281900824947853423557827830651823757812730541048043289832854892; + uint256 constant IC149y = 14888248309746572421490123748664727046703042611659840253124757801046464506615; + + uint256 constant IC150x = 2491036160351827951357989660441629493690558159643134663761902342882030266970; + uint256 constant IC150y = 5359896073444231251659147589804061284250771961800608141652796760011293589259; + + uint256 constant IC151x = 11464934553733776301276900390733652185447595680211581572133659115570305207154; + uint256 constant IC151y = 18952153160115440961144300692408869917115517993182256466332010692866600307124; + + uint256 constant IC152x = 9747554068212242697593838328866751764998029037449645153352161728782566139144; + uint256 constant IC152y = 17812524086444726225703609059812269460843668744439318505490750843302785220484; + + uint256 constant IC153x = 19109426366529792848636515169446385844745620487060994966933060381953567425629; + uint256 constant IC153y = 12611011760049877811458922767632686257749578444714073644654551600501777613281; + + uint256 constant IC154x = 13521375337909821228384529258976123614879820636055741620445248146498001533623; + uint256 constant IC154y = 6332476633614227121116037962400142105965327955208999422805368362496179268696; + + uint256 constant IC155x = 9340853260967035725508248570062733252736748806937967180638854751996210946933; + uint256 constant IC155y = 14500586771624961358032382167946911876726118743944913461400004436559439599808; + + uint256 constant IC156x = 11335499770514509101316566006868222737378429358031466698962387220454267367399; + uint256 constant IC156y = 4737157947777241890953589859320693082134113953719986941674210300354922416573; + + uint256 constant IC157x = 2973627022140027084795059620132255465435623274471150033028031141635045440415; + uint256 constant IC157y = 17792120821943048462243401362026319240293969596895356966822084685486723959695; + + uint256 constant IC158x = 2983052750515096330857336991648530216669026421485799760422137470818169362468; + uint256 constant IC158y = 9279436445273739766525722111331871103574084765812900653882576859101207601634; + + uint256 constant IC159x = 10013846709804056472798380368407593652902371513997716299727940020628896850032; + uint256 constant IC159y = 17306375300637811730481306141096691356049045361508964687394468680911642463298; + + uint256 constant IC160x = 196831864879583270425965737597111330939632031086134086561064507945278623364; + uint256 constant IC160y = 7780392737273500995663265930984481817094492310080733333660926431076469335436; + + uint256 constant IC161x = 2453269400498361343615579750201652729299158830308934654609568994179189594887; + uint256 constant IC161y = 20199958483026452791888071824742414904603793461111373262646933517685613877301; + + uint256 constant IC162x = 15889077587056942298032226700087601907644587021737658060144521757528320846619; + uint256 constant IC162y = 12030590797814278229138359982281095405530802882796568461530073393321052524433; + + uint256 constant IC163x = 1879944753188227875259673197682852920575283546661917640271987253413360078977; + uint256 constant IC163y = 17324447518059924950609709741197810837311725729001335056724777545608488720043; + + uint256 constant IC164x = 20009660932156082287213581110178197659831374500854400385624321702631575793931; + uint256 constant IC164y = 1616420662453608406030298132639496463996561207263789291285889816438072186673; + + uint256 constant IC165x = 2386620238179407889690302585166101336459595888862203504991909049064286023727; + uint256 constant IC165y = 16962654999234170699277904334276803347810112491528047774105106839149597005378; + + uint256 constant IC166x = 4357816058632756768303684246348733151666896506369803799201743638437497872156; + uint256 constant IC166y = 11500924983029530637431710435538069658165106604504467501345576309750414947069; + + uint256 constant IC167x = 12387644077104522388917356686758985598065731076173083433079682376754220573684; + uint256 constant IC167y = 15492417084793526209390744012139310122010067592132131357927437331192958484937; + + uint256 constant IC168x = 381875143847124345380152607383421868064453260743356808889165687835207272416; + uint256 constant IC168y = 20971333155621582784108278611245753360937073148617809488171829026539619657140; + + uint256 constant IC169x = 6713820763335771851518493831789431537158308958100457386585448332766000353838; + uint256 constant IC169y = 13131482751785618953291334655171639321875378536490466194086582047217807085933; + + uint256 constant IC170x = 4173591777097664984106584618136785361224578930433815404395138214370260786359; + uint256 constant IC170y = 14709200160905223820661455652205179742237309127167357348068542149347848128410; + + uint256 constant IC171x = 16599400578317965231289221415220704498610361682204415606151150597941871356291; + uint256 constant IC171y = 15321840081038122699848920263379909990227881606108598006358850016871765437295; + + uint256 constant IC172x = 17467289982597958907945178967398582050775109108727413553575338381832869375213; + uint256 constant IC172y = 8225005484625832606130361613842881030679821621373292256611722247654007386562; + + uint256 constant IC173x = 5481773756475384281634026979606898405424492586026955570237716032437135831301; + uint256 constant IC173y = 7174303711430339284489642634638023104339690768327445949699833292017885437447; + + uint256 constant IC174x = 3596336859047943467706106031157079800636563530818986472457766870894363809339; + uint256 constant IC174y = 11042111889799949249025472189677402959186815973858063237577534333734018247722; + + uint256 constant IC175x = 4727937933616244278481503329752243431153407291745128363225076133508548003071; + uint256 constant IC175y = 2423324486016190537069152844362951072771148662325114408244240709855750930084; + + uint256 constant IC176x = 16015029206774152748786672035322632385720086595681281286419760070824600431185; + uint256 constant IC176y = 11988272992708918030637742272818939003010998021457592381465792895422770381058; + + uint256 constant IC177x = 8259880558377343912393554609104215497395335902186507296470001686363606122462; + uint256 constant IC177y = 13752446980975215338358307796187371255805160299579565833698696419771668431282; + + uint256 constant IC178x = 4012819087506777378404073162950273247362474052657372152877627783333757327669; + uint256 constant IC178y = 418231158946437596054935243415727751206965845995400055395529386472725941836; + + uint256 constant IC179x = 11586260157242113713531032932493026247176731911014041020590722670096886287606; + uint256 constant IC179y = 8550483249733852331172978877001605367959287118197111118277098836594714230188; + + uint256 constant IC180x = 12515566205096857982780207908745647466997166196332104377687167522380517184733; + uint256 constant IC180y = 13358997128236968908731323544799612363488036294335188837472707924568755862498; + + uint256 constant IC181x = 10713656453431717556349111910499470522260523952976136499858087010083158688902; + uint256 constant IC181y = 12137634606455401884639999574426178797943128679639150922328360821682978465674; + + uint256 constant IC182x = 17183290789867018626196392058555586170920366268598686830782239391817389689910; + uint256 constant IC182y = 9168677253703326891783677030951697484260735221839646121087838117744087364231; + + uint256 constant IC183x = 3280148396327268342400263916616292752860485726433381519904837038278646079365; + uint256 constant IC183y = 5355636142048451442533191327453673358944656450124927645983730746865467306488; + + uint256 constant IC184x = 21582455851566765184829256788599211012660156266506952379566087302545973445137; + uint256 constant IC184y = 17930472315699025296629018821290310083746229646840547920214768510016992517495; + + uint256 constant IC185x = 11934038439752633591066742154138595215566988679924950529295906959480700900309; + uint256 constant IC185y = 20207257516577346158604342775878575726519217945395029888585137337920388871685; + + uint256 constant IC186x = 9131070809118836645403967266941477911127943520243276777934644582928975785547; + uint256 constant IC186y = 18458842010712807451913207019485035876079352906590135842689922892823175107039; + + uint256 constant IC187x = 4908729689238769658140101548299070410818292699120855269333272095468055027143; + uint256 constant IC187y = 16409814044314073252757007112467502184264671826657003043665102961360631697347; + + uint256 constant IC188x = 5254107865849259204660590277377890855964722585022255920192813181589855883252; + uint256 constant IC188y = 12291146751522816399674718538558422905565500961756007154023053741152799776996; + + uint256 constant IC189x = 5795970715585471712755795575617193813665680884908803493226000954832576569891; + uint256 constant IC189y = 21262911706740097516914121835198925073079946765469216662207251016506315826757; + + uint256 constant IC190x = 16020227459682476826701043795189440536832974171206058711672931133423824767545; + uint256 constant IC190y = 2374744763112156824131120652200801050422582189427280068772965029742429730101; + + uint256 constant IC191x = 18819288198702855913855694359282511659262079144645885100420405405513767723908; + uint256 constant IC191y = 14645298311494530925036927145113709566075264523989859451294916480205502644930; + + uint256 constant IC192x = 11504114515010773755611394042736314517032537083376405792802142832473516940318; + uint256 constant IC192y = 21720094678289032235364836480494769724636278405542477153795306321417128725559; + + uint256 constant IC193x = 1002161503843793288508790129724078349319920927020843342272602845456698204408; + uint256 constant IC193y = 6656033545425661908705314890266924227360965739765235378002887481231915374061; + + uint256 constant IC194x = 9036156014720468693046575197670218344947593474752573530604958072643786494144; + uint256 constant IC194y = 18731726575198457248024295180299340482246682224738254196226580022824718140705; + + uint256 constant IC195x = 19785142676666517848340703380511074113181224047881765833434548388767239672735; + uint256 constant IC195y = 21074338632506732737956547184108034572087840187335437215236047793787442311041; + + uint256 constant IC196x = 9740171628407240148808896870373048094020789957855374439961150321048832897104; + uint256 constant IC196y = 19484474193474654730924494943107762471834624497679441357835098381503464761266; + + uint256 constant IC197x = 19284582068977534933324621379891466854158885353025776540327268150406973493154; + uint256 constant IC197y = 3237162138896629476965892651959418805395304387067177801148253195035732462915; + + uint256 constant IC198x = 19789116701410152750483374727568527057056062270678246341995684588460138460371; + uint256 constant IC198y = 19647495523070150880654592226634551590830699839509276165279431738645296890378; + + uint256 constant IC199x = 7578214845810634577747381772992920494870762006675582971829030504411427178908; + uint256 constant IC199y = 10824185879924277588510245014334435130840430012164740384630649778396567935415; + + uint256 constant IC200x = 9305312139344152302274614298744608378957351771597063097849631243037641720604; + uint256 constant IC200y = 10687017007477286466701818285268734763025102385098407557140279595867018099141; + + uint256 constant IC201x = 13768814490563437576181815541255769581770427146699079453151801981768404526260; + uint256 constant IC201y = 19612133633319115896734433991569319587642837264957922629466553206980503403736; + + uint256 constant IC202x = 15800429325662152269937374459491838692586424949718188046106763530078449091598; + uint256 constant IC202y = 20031866723807708514767119735677722302712863894007529114751237535724499116740; + + uint256 constant IC203x = 934135118293465883691976697227220066258875852128817170468517136922808880475; + uint256 constant IC203y = 11047279262764310608189714555534541530644208633780759461238261333463722425742; + + uint256 constant IC204x = 14784554649460786227780858004711870268183075923108158712469241385266813006445; + uint256 constant IC204y = 18828667927552402224418075428115220580373338397864741821303416635381116296976; + + uint256 constant IC205x = 4617570438910752025916215008230713347182401144971017158045117377085388015148; + uint256 constant IC205y = 7590959331649499803512716903083844108258078716492242212997213073014907233689; + + uint256 constant IC206x = 3857908065437576096245929838913387390362663771341880093803930389010412969229; + uint256 constant IC206y = 3852886324937109250287061196977948420389257204134924967818073212522910842576; + + uint256 constant IC207x = 2850271460493621008959472113273907789447134952095348762333330304780946378813; + uint256 constant IC207y = 19366854682964741737922431984055295817859537047394924267176047975093811093109; + + uint256 constant IC208x = 11812325337525348958750266284671464101038359804740107627354576798921142807228; + uint256 constant IC208y = 9765121917389181690320011924493245470457301787201030617898720750753033219466; + + uint256 constant IC209x = 20464064048701628681259417706675882822841123546214120767437183446846879158793; + uint256 constant IC209y = 4609132805403208312056237402557368508840471825053197203276227709810862937336; + + uint256 constant IC210x = 15860850116905609657848571149666895600194131077318334296264514039120306689452; + uint256 constant IC210y = 9995003511094096061343450909004662180259412645863718115554190985511012992720; + + uint256 constant IC211x = 16535746870163014422240523378653334612831013176287968552766842806259859454039; + uint256 constant IC211y = 7850304667015905423154061921993915170988654485738006048931252336905737583144; + + uint256 constant IC212x = 47382607541872921605215537143813506521278558183785711211040470180008089791; + uint256 constant IC212y = 11466820772488059764737857991631996222075556396568243901128764309905198749761; + + // Memory data + uint16 constant pVk = 0; + uint16 constant pPairing = 128; + + uint16 constant pLastMem = 896; + + function verifyProof( + uint[2] calldata _pA, + uint[2][2] calldata _pB, + uint[2] calldata _pC, + uint[212] calldata _pubSignals + ) public view returns (bool) { + assembly { + function checkField(v) { + if iszero(lt(v, r)) { + mstore(0, 0) + return(0, 0x20) + } + } + + // G1 function to multiply a G1 value(x,y) to value in an address + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn, 32), y) + mstore(add(mIn, 64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + + mstore(add(mIn, 64), mload(pR)) + mstore(add(mIn, 96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + } + + function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk { + let _pPairing := add(pMem, pPairing) + let _pVk := add(pMem, pVk) + + mstore(_pVk, IC0x) + mstore(add(_pVk, 32), IC0y) + + // Compute the linear combination vk_x + + g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0))) + + g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32))) + + g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64))) + + g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96))) + + g1_mulAccC(_pVk, IC5x, IC5y, calldataload(add(pubSignals, 128))) + + g1_mulAccC(_pVk, IC6x, IC6y, calldataload(add(pubSignals, 160))) + + g1_mulAccC(_pVk, IC7x, IC7y, calldataload(add(pubSignals, 192))) + + g1_mulAccC(_pVk, IC8x, IC8y, calldataload(add(pubSignals, 224))) + + g1_mulAccC(_pVk, IC9x, IC9y, calldataload(add(pubSignals, 256))) + + g1_mulAccC(_pVk, IC10x, IC10y, calldataload(add(pubSignals, 288))) + + g1_mulAccC(_pVk, IC11x, IC11y, calldataload(add(pubSignals, 320))) + + g1_mulAccC(_pVk, IC12x, IC12y, calldataload(add(pubSignals, 352))) + + g1_mulAccC(_pVk, IC13x, IC13y, calldataload(add(pubSignals, 384))) + + g1_mulAccC(_pVk, IC14x, IC14y, calldataload(add(pubSignals, 416))) + + g1_mulAccC(_pVk, IC15x, IC15y, calldataload(add(pubSignals, 448))) + + g1_mulAccC(_pVk, IC16x, IC16y, calldataload(add(pubSignals, 480))) + + g1_mulAccC(_pVk, IC17x, IC17y, calldataload(add(pubSignals, 512))) + + g1_mulAccC(_pVk, IC18x, IC18y, calldataload(add(pubSignals, 544))) + + g1_mulAccC(_pVk, IC19x, IC19y, calldataload(add(pubSignals, 576))) + + g1_mulAccC(_pVk, IC20x, IC20y, calldataload(add(pubSignals, 608))) + + g1_mulAccC(_pVk, IC21x, IC21y, calldataload(add(pubSignals, 640))) + + g1_mulAccC(_pVk, IC22x, IC22y, calldataload(add(pubSignals, 672))) + + g1_mulAccC(_pVk, IC23x, IC23y, calldataload(add(pubSignals, 704))) + + g1_mulAccC(_pVk, IC24x, IC24y, calldataload(add(pubSignals, 736))) + + g1_mulAccC(_pVk, IC25x, IC25y, calldataload(add(pubSignals, 768))) + + g1_mulAccC(_pVk, IC26x, IC26y, calldataload(add(pubSignals, 800))) + + g1_mulAccC(_pVk, IC27x, IC27y, calldataload(add(pubSignals, 832))) + + g1_mulAccC(_pVk, IC28x, IC28y, calldataload(add(pubSignals, 864))) + + g1_mulAccC(_pVk, IC29x, IC29y, calldataload(add(pubSignals, 896))) + + g1_mulAccC(_pVk, IC30x, IC30y, calldataload(add(pubSignals, 928))) + + g1_mulAccC(_pVk, IC31x, IC31y, calldataload(add(pubSignals, 960))) + + g1_mulAccC(_pVk, IC32x, IC32y, calldataload(add(pubSignals, 992))) + + g1_mulAccC(_pVk, IC33x, IC33y, calldataload(add(pubSignals, 1024))) + + g1_mulAccC(_pVk, IC34x, IC34y, calldataload(add(pubSignals, 1056))) + + g1_mulAccC(_pVk, IC35x, IC35y, calldataload(add(pubSignals, 1088))) + + g1_mulAccC(_pVk, IC36x, IC36y, calldataload(add(pubSignals, 1120))) + + g1_mulAccC(_pVk, IC37x, IC37y, calldataload(add(pubSignals, 1152))) + + g1_mulAccC(_pVk, IC38x, IC38y, calldataload(add(pubSignals, 1184))) + + g1_mulAccC(_pVk, IC39x, IC39y, calldataload(add(pubSignals, 1216))) + + g1_mulAccC(_pVk, IC40x, IC40y, calldataload(add(pubSignals, 1248))) + + g1_mulAccC(_pVk, IC41x, IC41y, calldataload(add(pubSignals, 1280))) + + g1_mulAccC(_pVk, IC42x, IC42y, calldataload(add(pubSignals, 1312))) + + g1_mulAccC(_pVk, IC43x, IC43y, calldataload(add(pubSignals, 1344))) + + g1_mulAccC(_pVk, IC44x, IC44y, calldataload(add(pubSignals, 1376))) + + g1_mulAccC(_pVk, IC45x, IC45y, calldataload(add(pubSignals, 1408))) + + g1_mulAccC(_pVk, IC46x, IC46y, calldataload(add(pubSignals, 1440))) + + g1_mulAccC(_pVk, IC47x, IC47y, calldataload(add(pubSignals, 1472))) + + g1_mulAccC(_pVk, IC48x, IC48y, calldataload(add(pubSignals, 1504))) + + g1_mulAccC(_pVk, IC49x, IC49y, calldataload(add(pubSignals, 1536))) + + g1_mulAccC(_pVk, IC50x, IC50y, calldataload(add(pubSignals, 1568))) + + g1_mulAccC(_pVk, IC51x, IC51y, calldataload(add(pubSignals, 1600))) + + g1_mulAccC(_pVk, IC52x, IC52y, calldataload(add(pubSignals, 1632))) + + g1_mulAccC(_pVk, IC53x, IC53y, calldataload(add(pubSignals, 1664))) + + g1_mulAccC(_pVk, IC54x, IC54y, calldataload(add(pubSignals, 1696))) + + g1_mulAccC(_pVk, IC55x, IC55y, calldataload(add(pubSignals, 1728))) + + g1_mulAccC(_pVk, IC56x, IC56y, calldataload(add(pubSignals, 1760))) + + g1_mulAccC(_pVk, IC57x, IC57y, calldataload(add(pubSignals, 1792))) + + g1_mulAccC(_pVk, IC58x, IC58y, calldataload(add(pubSignals, 1824))) + + g1_mulAccC(_pVk, IC59x, IC59y, calldataload(add(pubSignals, 1856))) + + g1_mulAccC(_pVk, IC60x, IC60y, calldataload(add(pubSignals, 1888))) + + g1_mulAccC(_pVk, IC61x, IC61y, calldataload(add(pubSignals, 1920))) + + g1_mulAccC(_pVk, IC62x, IC62y, calldataload(add(pubSignals, 1952))) + + g1_mulAccC(_pVk, IC63x, IC63y, calldataload(add(pubSignals, 1984))) + + g1_mulAccC(_pVk, IC64x, IC64y, calldataload(add(pubSignals, 2016))) + + g1_mulAccC(_pVk, IC65x, IC65y, calldataload(add(pubSignals, 2048))) + + g1_mulAccC(_pVk, IC66x, IC66y, calldataload(add(pubSignals, 2080))) + + g1_mulAccC(_pVk, IC67x, IC67y, calldataload(add(pubSignals, 2112))) + + g1_mulAccC(_pVk, IC68x, IC68y, calldataload(add(pubSignals, 2144))) + + g1_mulAccC(_pVk, IC69x, IC69y, calldataload(add(pubSignals, 2176))) + + g1_mulAccC(_pVk, IC70x, IC70y, calldataload(add(pubSignals, 2208))) + + g1_mulAccC(_pVk, IC71x, IC71y, calldataload(add(pubSignals, 2240))) + + g1_mulAccC(_pVk, IC72x, IC72y, calldataload(add(pubSignals, 2272))) + + g1_mulAccC(_pVk, IC73x, IC73y, calldataload(add(pubSignals, 2304))) + + g1_mulAccC(_pVk, IC74x, IC74y, calldataload(add(pubSignals, 2336))) + + g1_mulAccC(_pVk, IC75x, IC75y, calldataload(add(pubSignals, 2368))) + + g1_mulAccC(_pVk, IC76x, IC76y, calldataload(add(pubSignals, 2400))) + + g1_mulAccC(_pVk, IC77x, IC77y, calldataload(add(pubSignals, 2432))) + + g1_mulAccC(_pVk, IC78x, IC78y, calldataload(add(pubSignals, 2464))) + + g1_mulAccC(_pVk, IC79x, IC79y, calldataload(add(pubSignals, 2496))) + + g1_mulAccC(_pVk, IC80x, IC80y, calldataload(add(pubSignals, 2528))) + + g1_mulAccC(_pVk, IC81x, IC81y, calldataload(add(pubSignals, 2560))) + + g1_mulAccC(_pVk, IC82x, IC82y, calldataload(add(pubSignals, 2592))) + + g1_mulAccC(_pVk, IC83x, IC83y, calldataload(add(pubSignals, 2624))) + + g1_mulAccC(_pVk, IC84x, IC84y, calldataload(add(pubSignals, 2656))) + + g1_mulAccC(_pVk, IC85x, IC85y, calldataload(add(pubSignals, 2688))) + + g1_mulAccC(_pVk, IC86x, IC86y, calldataload(add(pubSignals, 2720))) + + g1_mulAccC(_pVk, IC87x, IC87y, calldataload(add(pubSignals, 2752))) + + g1_mulAccC(_pVk, IC88x, IC88y, calldataload(add(pubSignals, 2784))) + + g1_mulAccC(_pVk, IC89x, IC89y, calldataload(add(pubSignals, 2816))) + + g1_mulAccC(_pVk, IC90x, IC90y, calldataload(add(pubSignals, 2848))) + + g1_mulAccC(_pVk, IC91x, IC91y, calldataload(add(pubSignals, 2880))) + + g1_mulAccC(_pVk, IC92x, IC92y, calldataload(add(pubSignals, 2912))) + + g1_mulAccC(_pVk, IC93x, IC93y, calldataload(add(pubSignals, 2944))) + + g1_mulAccC(_pVk, IC94x, IC94y, calldataload(add(pubSignals, 2976))) + + g1_mulAccC(_pVk, IC95x, IC95y, calldataload(add(pubSignals, 3008))) + + g1_mulAccC(_pVk, IC96x, IC96y, calldataload(add(pubSignals, 3040))) + + g1_mulAccC(_pVk, IC97x, IC97y, calldataload(add(pubSignals, 3072))) + + g1_mulAccC(_pVk, IC98x, IC98y, calldataload(add(pubSignals, 3104))) + + g1_mulAccC(_pVk, IC99x, IC99y, calldataload(add(pubSignals, 3136))) + + g1_mulAccC(_pVk, IC100x, IC100y, calldataload(add(pubSignals, 3168))) + + g1_mulAccC(_pVk, IC101x, IC101y, calldataload(add(pubSignals, 3200))) + + g1_mulAccC(_pVk, IC102x, IC102y, calldataload(add(pubSignals, 3232))) + + g1_mulAccC(_pVk, IC103x, IC103y, calldataload(add(pubSignals, 3264))) + + g1_mulAccC(_pVk, IC104x, IC104y, calldataload(add(pubSignals, 3296))) + + g1_mulAccC(_pVk, IC105x, IC105y, calldataload(add(pubSignals, 3328))) + + g1_mulAccC(_pVk, IC106x, IC106y, calldataload(add(pubSignals, 3360))) + + g1_mulAccC(_pVk, IC107x, IC107y, calldataload(add(pubSignals, 3392))) + + g1_mulAccC(_pVk, IC108x, IC108y, calldataload(add(pubSignals, 3424))) + + g1_mulAccC(_pVk, IC109x, IC109y, calldataload(add(pubSignals, 3456))) + + g1_mulAccC(_pVk, IC110x, IC110y, calldataload(add(pubSignals, 3488))) + + g1_mulAccC(_pVk, IC111x, IC111y, calldataload(add(pubSignals, 3520))) + + g1_mulAccC(_pVk, IC112x, IC112y, calldataload(add(pubSignals, 3552))) + + g1_mulAccC(_pVk, IC113x, IC113y, calldataload(add(pubSignals, 3584))) + + g1_mulAccC(_pVk, IC114x, IC114y, calldataload(add(pubSignals, 3616))) + + g1_mulAccC(_pVk, IC115x, IC115y, calldataload(add(pubSignals, 3648))) + + g1_mulAccC(_pVk, IC116x, IC116y, calldataload(add(pubSignals, 3680))) + + g1_mulAccC(_pVk, IC117x, IC117y, calldataload(add(pubSignals, 3712))) + + g1_mulAccC(_pVk, IC118x, IC118y, calldataload(add(pubSignals, 3744))) + + g1_mulAccC(_pVk, IC119x, IC119y, calldataload(add(pubSignals, 3776))) + + g1_mulAccC(_pVk, IC120x, IC120y, calldataload(add(pubSignals, 3808))) + + g1_mulAccC(_pVk, IC121x, IC121y, calldataload(add(pubSignals, 3840))) + + g1_mulAccC(_pVk, IC122x, IC122y, calldataload(add(pubSignals, 3872))) + + g1_mulAccC(_pVk, IC123x, IC123y, calldataload(add(pubSignals, 3904))) + + g1_mulAccC(_pVk, IC124x, IC124y, calldataload(add(pubSignals, 3936))) + + g1_mulAccC(_pVk, IC125x, IC125y, calldataload(add(pubSignals, 3968))) + + g1_mulAccC(_pVk, IC126x, IC126y, calldataload(add(pubSignals, 4000))) + + g1_mulAccC(_pVk, IC127x, IC127y, calldataload(add(pubSignals, 4032))) + + g1_mulAccC(_pVk, IC128x, IC128y, calldataload(add(pubSignals, 4064))) + + g1_mulAccC(_pVk, IC129x, IC129y, calldataload(add(pubSignals, 4096))) + + g1_mulAccC(_pVk, IC130x, IC130y, calldataload(add(pubSignals, 4128))) + + g1_mulAccC(_pVk, IC131x, IC131y, calldataload(add(pubSignals, 4160))) + + g1_mulAccC(_pVk, IC132x, IC132y, calldataload(add(pubSignals, 4192))) + + g1_mulAccC(_pVk, IC133x, IC133y, calldataload(add(pubSignals, 4224))) + + g1_mulAccC(_pVk, IC134x, IC134y, calldataload(add(pubSignals, 4256))) + + g1_mulAccC(_pVk, IC135x, IC135y, calldataload(add(pubSignals, 4288))) + + g1_mulAccC(_pVk, IC136x, IC136y, calldataload(add(pubSignals, 4320))) + + g1_mulAccC(_pVk, IC137x, IC137y, calldataload(add(pubSignals, 4352))) + + g1_mulAccC(_pVk, IC138x, IC138y, calldataload(add(pubSignals, 4384))) + + g1_mulAccC(_pVk, IC139x, IC139y, calldataload(add(pubSignals, 4416))) + + g1_mulAccC(_pVk, IC140x, IC140y, calldataload(add(pubSignals, 4448))) + + g1_mulAccC(_pVk, IC141x, IC141y, calldataload(add(pubSignals, 4480))) + + g1_mulAccC(_pVk, IC142x, IC142y, calldataload(add(pubSignals, 4512))) + + g1_mulAccC(_pVk, IC143x, IC143y, calldataload(add(pubSignals, 4544))) + + g1_mulAccC(_pVk, IC144x, IC144y, calldataload(add(pubSignals, 4576))) + + g1_mulAccC(_pVk, IC145x, IC145y, calldataload(add(pubSignals, 4608))) + + g1_mulAccC(_pVk, IC146x, IC146y, calldataload(add(pubSignals, 4640))) + + g1_mulAccC(_pVk, IC147x, IC147y, calldataload(add(pubSignals, 4672))) + + g1_mulAccC(_pVk, IC148x, IC148y, calldataload(add(pubSignals, 4704))) + + g1_mulAccC(_pVk, IC149x, IC149y, calldataload(add(pubSignals, 4736))) + + g1_mulAccC(_pVk, IC150x, IC150y, calldataload(add(pubSignals, 4768))) + + g1_mulAccC(_pVk, IC151x, IC151y, calldataload(add(pubSignals, 4800))) + + g1_mulAccC(_pVk, IC152x, IC152y, calldataload(add(pubSignals, 4832))) + + g1_mulAccC(_pVk, IC153x, IC153y, calldataload(add(pubSignals, 4864))) + + g1_mulAccC(_pVk, IC154x, IC154y, calldataload(add(pubSignals, 4896))) + + g1_mulAccC(_pVk, IC155x, IC155y, calldataload(add(pubSignals, 4928))) + + g1_mulAccC(_pVk, IC156x, IC156y, calldataload(add(pubSignals, 4960))) + + g1_mulAccC(_pVk, IC157x, IC157y, calldataload(add(pubSignals, 4992))) + + g1_mulAccC(_pVk, IC158x, IC158y, calldataload(add(pubSignals, 5024))) + + g1_mulAccC(_pVk, IC159x, IC159y, calldataload(add(pubSignals, 5056))) + + g1_mulAccC(_pVk, IC160x, IC160y, calldataload(add(pubSignals, 5088))) + + g1_mulAccC(_pVk, IC161x, IC161y, calldataload(add(pubSignals, 5120))) + + g1_mulAccC(_pVk, IC162x, IC162y, calldataload(add(pubSignals, 5152))) + + g1_mulAccC(_pVk, IC163x, IC163y, calldataload(add(pubSignals, 5184))) + + g1_mulAccC(_pVk, IC164x, IC164y, calldataload(add(pubSignals, 5216))) + + g1_mulAccC(_pVk, IC165x, IC165y, calldataload(add(pubSignals, 5248))) + + g1_mulAccC(_pVk, IC166x, IC166y, calldataload(add(pubSignals, 5280))) + + g1_mulAccC(_pVk, IC167x, IC167y, calldataload(add(pubSignals, 5312))) + + g1_mulAccC(_pVk, IC168x, IC168y, calldataload(add(pubSignals, 5344))) + + g1_mulAccC(_pVk, IC169x, IC169y, calldataload(add(pubSignals, 5376))) + + g1_mulAccC(_pVk, IC170x, IC170y, calldataload(add(pubSignals, 5408))) + + g1_mulAccC(_pVk, IC171x, IC171y, calldataload(add(pubSignals, 5440))) + + g1_mulAccC(_pVk, IC172x, IC172y, calldataload(add(pubSignals, 5472))) + + g1_mulAccC(_pVk, IC173x, IC173y, calldataload(add(pubSignals, 5504))) + + g1_mulAccC(_pVk, IC174x, IC174y, calldataload(add(pubSignals, 5536))) + + g1_mulAccC(_pVk, IC175x, IC175y, calldataload(add(pubSignals, 5568))) + + g1_mulAccC(_pVk, IC176x, IC176y, calldataload(add(pubSignals, 5600))) + + g1_mulAccC(_pVk, IC177x, IC177y, calldataload(add(pubSignals, 5632))) + + g1_mulAccC(_pVk, IC178x, IC178y, calldataload(add(pubSignals, 5664))) + + g1_mulAccC(_pVk, IC179x, IC179y, calldataload(add(pubSignals, 5696))) + + g1_mulAccC(_pVk, IC180x, IC180y, calldataload(add(pubSignals, 5728))) + + g1_mulAccC(_pVk, IC181x, IC181y, calldataload(add(pubSignals, 5760))) + + g1_mulAccC(_pVk, IC182x, IC182y, calldataload(add(pubSignals, 5792))) + + g1_mulAccC(_pVk, IC183x, IC183y, calldataload(add(pubSignals, 5824))) + + g1_mulAccC(_pVk, IC184x, IC184y, calldataload(add(pubSignals, 5856))) + + g1_mulAccC(_pVk, IC185x, IC185y, calldataload(add(pubSignals, 5888))) + + g1_mulAccC(_pVk, IC186x, IC186y, calldataload(add(pubSignals, 5920))) + + g1_mulAccC(_pVk, IC187x, IC187y, calldataload(add(pubSignals, 5952))) + + g1_mulAccC(_pVk, IC188x, IC188y, calldataload(add(pubSignals, 5984))) + + g1_mulAccC(_pVk, IC189x, IC189y, calldataload(add(pubSignals, 6016))) + + g1_mulAccC(_pVk, IC190x, IC190y, calldataload(add(pubSignals, 6048))) + + g1_mulAccC(_pVk, IC191x, IC191y, calldataload(add(pubSignals, 6080))) + + g1_mulAccC(_pVk, IC192x, IC192y, calldataload(add(pubSignals, 6112))) + + g1_mulAccC(_pVk, IC193x, IC193y, calldataload(add(pubSignals, 6144))) + + g1_mulAccC(_pVk, IC194x, IC194y, calldataload(add(pubSignals, 6176))) + + g1_mulAccC(_pVk, IC195x, IC195y, calldataload(add(pubSignals, 6208))) + + g1_mulAccC(_pVk, IC196x, IC196y, calldataload(add(pubSignals, 6240))) + + g1_mulAccC(_pVk, IC197x, IC197y, calldataload(add(pubSignals, 6272))) + + g1_mulAccC(_pVk, IC198x, IC198y, calldataload(add(pubSignals, 6304))) + + g1_mulAccC(_pVk, IC199x, IC199y, calldataload(add(pubSignals, 6336))) + + g1_mulAccC(_pVk, IC200x, IC200y, calldataload(add(pubSignals, 6368))) + + g1_mulAccC(_pVk, IC201x, IC201y, calldataload(add(pubSignals, 6400))) + + g1_mulAccC(_pVk, IC202x, IC202y, calldataload(add(pubSignals, 6432))) + + g1_mulAccC(_pVk, IC203x, IC203y, calldataload(add(pubSignals, 6464))) + + g1_mulAccC(_pVk, IC204x, IC204y, calldataload(add(pubSignals, 6496))) + + g1_mulAccC(_pVk, IC205x, IC205y, calldataload(add(pubSignals, 6528))) + + g1_mulAccC(_pVk, IC206x, IC206y, calldataload(add(pubSignals, 6560))) + + g1_mulAccC(_pVk, IC207x, IC207y, calldataload(add(pubSignals, 6592))) + + g1_mulAccC(_pVk, IC208x, IC208y, calldataload(add(pubSignals, 6624))) + + g1_mulAccC(_pVk, IC209x, IC209y, calldataload(add(pubSignals, 6656))) + + g1_mulAccC(_pVk, IC210x, IC210y, calldataload(add(pubSignals, 6688))) + + g1_mulAccC(_pVk, IC211x, IC211y, calldataload(add(pubSignals, 6720))) + + g1_mulAccC(_pVk, IC212x, IC212y, calldataload(add(pubSignals, 6752))) + + // -A + mstore(_pPairing, calldataload(pA)) + mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q)) + + // B + mstore(add(_pPairing, 64), calldataload(pB)) + mstore(add(_pPairing, 96), calldataload(add(pB, 32))) + mstore(add(_pPairing, 128), calldataload(add(pB, 64))) + mstore(add(_pPairing, 160), calldataload(add(pB, 96))) + + // alpha1 + mstore(add(_pPairing, 192), alphax) + mstore(add(_pPairing, 224), alphay) + + // beta2 + mstore(add(_pPairing, 256), betax1) + mstore(add(_pPairing, 288), betax2) + mstore(add(_pPairing, 320), betay1) + mstore(add(_pPairing, 352), betay2) + + // vk_x + mstore(add(_pPairing, 384), mload(add(pMem, pVk))) + mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32)))) + + // gamma2 + mstore(add(_pPairing, 448), gammax1) + mstore(add(_pPairing, 480), gammax2) + mstore(add(_pPairing, 512), gammay1) + mstore(add(_pPairing, 544), gammay2) + + // C + mstore(add(_pPairing, 576), calldataload(pC)) + mstore(add(_pPairing, 608), calldataload(add(pC, 32))) + + // delta2 + mstore(add(_pPairing, 640), deltax1) + mstore(add(_pPairing, 672), deltax2) + mstore(add(_pPairing, 704), deltay1) + mstore(add(_pPairing, 736), deltay2) + + let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20) + + isOk := and(success, mload(_pPairing)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, pLastMem)) + + // Validate that all evaluations ∈ F + + checkField(calldataload(add(_pubSignals, 0))) + + checkField(calldataload(add(_pubSignals, 32))) + + checkField(calldataload(add(_pubSignals, 64))) + + checkField(calldataload(add(_pubSignals, 96))) + + checkField(calldataload(add(_pubSignals, 128))) + + checkField(calldataload(add(_pubSignals, 160))) + + checkField(calldataload(add(_pubSignals, 192))) + + checkField(calldataload(add(_pubSignals, 224))) + + checkField(calldataload(add(_pubSignals, 256))) + + checkField(calldataload(add(_pubSignals, 288))) + + checkField(calldataload(add(_pubSignals, 320))) + + checkField(calldataload(add(_pubSignals, 352))) + + checkField(calldataload(add(_pubSignals, 384))) + + checkField(calldataload(add(_pubSignals, 416))) + + checkField(calldataload(add(_pubSignals, 448))) + + checkField(calldataload(add(_pubSignals, 480))) + + checkField(calldataload(add(_pubSignals, 512))) + + checkField(calldataload(add(_pubSignals, 544))) + + checkField(calldataload(add(_pubSignals, 576))) + + checkField(calldataload(add(_pubSignals, 608))) + + checkField(calldataload(add(_pubSignals, 640))) + + checkField(calldataload(add(_pubSignals, 672))) + + checkField(calldataload(add(_pubSignals, 704))) + + checkField(calldataload(add(_pubSignals, 736))) + + checkField(calldataload(add(_pubSignals, 768))) + + checkField(calldataload(add(_pubSignals, 800))) + + checkField(calldataload(add(_pubSignals, 832))) + + checkField(calldataload(add(_pubSignals, 864))) + + checkField(calldataload(add(_pubSignals, 896))) + + checkField(calldataload(add(_pubSignals, 928))) + + checkField(calldataload(add(_pubSignals, 960))) + + checkField(calldataload(add(_pubSignals, 992))) + + checkField(calldataload(add(_pubSignals, 1024))) + + checkField(calldataload(add(_pubSignals, 1056))) + + checkField(calldataload(add(_pubSignals, 1088))) + + checkField(calldataload(add(_pubSignals, 1120))) + + checkField(calldataload(add(_pubSignals, 1152))) + + checkField(calldataload(add(_pubSignals, 1184))) + + checkField(calldataload(add(_pubSignals, 1216))) + + checkField(calldataload(add(_pubSignals, 1248))) + + checkField(calldataload(add(_pubSignals, 1280))) + + checkField(calldataload(add(_pubSignals, 1312))) + + checkField(calldataload(add(_pubSignals, 1344))) + + checkField(calldataload(add(_pubSignals, 1376))) + + checkField(calldataload(add(_pubSignals, 1408))) + + checkField(calldataload(add(_pubSignals, 1440))) + + checkField(calldataload(add(_pubSignals, 1472))) + + checkField(calldataload(add(_pubSignals, 1504))) + + checkField(calldataload(add(_pubSignals, 1536))) + + checkField(calldataload(add(_pubSignals, 1568))) + + checkField(calldataload(add(_pubSignals, 1600))) + + checkField(calldataload(add(_pubSignals, 1632))) + + checkField(calldataload(add(_pubSignals, 1664))) + + checkField(calldataload(add(_pubSignals, 1696))) + + checkField(calldataload(add(_pubSignals, 1728))) + + checkField(calldataload(add(_pubSignals, 1760))) + + checkField(calldataload(add(_pubSignals, 1792))) + + checkField(calldataload(add(_pubSignals, 1824))) + + checkField(calldataload(add(_pubSignals, 1856))) + + checkField(calldataload(add(_pubSignals, 1888))) + + checkField(calldataload(add(_pubSignals, 1920))) + + checkField(calldataload(add(_pubSignals, 1952))) + + checkField(calldataload(add(_pubSignals, 1984))) + + checkField(calldataload(add(_pubSignals, 2016))) + + checkField(calldataload(add(_pubSignals, 2048))) + + checkField(calldataload(add(_pubSignals, 2080))) + + checkField(calldataload(add(_pubSignals, 2112))) + + checkField(calldataload(add(_pubSignals, 2144))) + + checkField(calldataload(add(_pubSignals, 2176))) + + checkField(calldataload(add(_pubSignals, 2208))) + + checkField(calldataload(add(_pubSignals, 2240))) + + checkField(calldataload(add(_pubSignals, 2272))) + + checkField(calldataload(add(_pubSignals, 2304))) + + checkField(calldataload(add(_pubSignals, 2336))) + + checkField(calldataload(add(_pubSignals, 2368))) + + checkField(calldataload(add(_pubSignals, 2400))) + + checkField(calldataload(add(_pubSignals, 2432))) + + checkField(calldataload(add(_pubSignals, 2464))) + + checkField(calldataload(add(_pubSignals, 2496))) + + checkField(calldataload(add(_pubSignals, 2528))) + + checkField(calldataload(add(_pubSignals, 2560))) + + checkField(calldataload(add(_pubSignals, 2592))) + + checkField(calldataload(add(_pubSignals, 2624))) + + checkField(calldataload(add(_pubSignals, 2656))) + + checkField(calldataload(add(_pubSignals, 2688))) + + checkField(calldataload(add(_pubSignals, 2720))) + + checkField(calldataload(add(_pubSignals, 2752))) + + checkField(calldataload(add(_pubSignals, 2784))) + + checkField(calldataload(add(_pubSignals, 2816))) + + checkField(calldataload(add(_pubSignals, 2848))) + + checkField(calldataload(add(_pubSignals, 2880))) + + checkField(calldataload(add(_pubSignals, 2912))) + + checkField(calldataload(add(_pubSignals, 2944))) + + checkField(calldataload(add(_pubSignals, 2976))) + + checkField(calldataload(add(_pubSignals, 3008))) + + checkField(calldataload(add(_pubSignals, 3040))) + + checkField(calldataload(add(_pubSignals, 3072))) + + checkField(calldataload(add(_pubSignals, 3104))) + + checkField(calldataload(add(_pubSignals, 3136))) + + checkField(calldataload(add(_pubSignals, 3168))) + + checkField(calldataload(add(_pubSignals, 3200))) + + checkField(calldataload(add(_pubSignals, 3232))) + + checkField(calldataload(add(_pubSignals, 3264))) + + checkField(calldataload(add(_pubSignals, 3296))) + + checkField(calldataload(add(_pubSignals, 3328))) + + checkField(calldataload(add(_pubSignals, 3360))) + + checkField(calldataload(add(_pubSignals, 3392))) + + checkField(calldataload(add(_pubSignals, 3424))) + + checkField(calldataload(add(_pubSignals, 3456))) + + checkField(calldataload(add(_pubSignals, 3488))) + + checkField(calldataload(add(_pubSignals, 3520))) + + checkField(calldataload(add(_pubSignals, 3552))) + + checkField(calldataload(add(_pubSignals, 3584))) + + checkField(calldataload(add(_pubSignals, 3616))) + + checkField(calldataload(add(_pubSignals, 3648))) + + checkField(calldataload(add(_pubSignals, 3680))) + + checkField(calldataload(add(_pubSignals, 3712))) + + checkField(calldataload(add(_pubSignals, 3744))) + + checkField(calldataload(add(_pubSignals, 3776))) + + checkField(calldataload(add(_pubSignals, 3808))) + + checkField(calldataload(add(_pubSignals, 3840))) + + checkField(calldataload(add(_pubSignals, 3872))) + + checkField(calldataload(add(_pubSignals, 3904))) + + checkField(calldataload(add(_pubSignals, 3936))) + + checkField(calldataload(add(_pubSignals, 3968))) + + checkField(calldataload(add(_pubSignals, 4000))) + + checkField(calldataload(add(_pubSignals, 4032))) + + checkField(calldataload(add(_pubSignals, 4064))) + + checkField(calldataload(add(_pubSignals, 4096))) + + checkField(calldataload(add(_pubSignals, 4128))) + + checkField(calldataload(add(_pubSignals, 4160))) + + checkField(calldataload(add(_pubSignals, 4192))) + + checkField(calldataload(add(_pubSignals, 4224))) + + checkField(calldataload(add(_pubSignals, 4256))) + + checkField(calldataload(add(_pubSignals, 4288))) + + checkField(calldataload(add(_pubSignals, 4320))) + + checkField(calldataload(add(_pubSignals, 4352))) + + checkField(calldataload(add(_pubSignals, 4384))) + + checkField(calldataload(add(_pubSignals, 4416))) + + checkField(calldataload(add(_pubSignals, 4448))) + + checkField(calldataload(add(_pubSignals, 4480))) + + checkField(calldataload(add(_pubSignals, 4512))) + + checkField(calldataload(add(_pubSignals, 4544))) + + checkField(calldataload(add(_pubSignals, 4576))) + + checkField(calldataload(add(_pubSignals, 4608))) + + checkField(calldataload(add(_pubSignals, 4640))) + + checkField(calldataload(add(_pubSignals, 4672))) + + checkField(calldataload(add(_pubSignals, 4704))) + + checkField(calldataload(add(_pubSignals, 4736))) + + checkField(calldataload(add(_pubSignals, 4768))) + + checkField(calldataload(add(_pubSignals, 4800))) + + checkField(calldataload(add(_pubSignals, 4832))) + + checkField(calldataload(add(_pubSignals, 4864))) + + checkField(calldataload(add(_pubSignals, 4896))) + + checkField(calldataload(add(_pubSignals, 4928))) + + checkField(calldataload(add(_pubSignals, 4960))) + + checkField(calldataload(add(_pubSignals, 4992))) + + checkField(calldataload(add(_pubSignals, 5024))) + + checkField(calldataload(add(_pubSignals, 5056))) + + checkField(calldataload(add(_pubSignals, 5088))) + + checkField(calldataload(add(_pubSignals, 5120))) + + checkField(calldataload(add(_pubSignals, 5152))) + + checkField(calldataload(add(_pubSignals, 5184))) + + checkField(calldataload(add(_pubSignals, 5216))) + + checkField(calldataload(add(_pubSignals, 5248))) + + checkField(calldataload(add(_pubSignals, 5280))) + + checkField(calldataload(add(_pubSignals, 5312))) + + checkField(calldataload(add(_pubSignals, 5344))) + + checkField(calldataload(add(_pubSignals, 5376))) + + checkField(calldataload(add(_pubSignals, 5408))) + + checkField(calldataload(add(_pubSignals, 5440))) + + checkField(calldataload(add(_pubSignals, 5472))) + + checkField(calldataload(add(_pubSignals, 5504))) + + checkField(calldataload(add(_pubSignals, 5536))) + + checkField(calldataload(add(_pubSignals, 5568))) + + checkField(calldataload(add(_pubSignals, 5600))) + + checkField(calldataload(add(_pubSignals, 5632))) + + checkField(calldataload(add(_pubSignals, 5664))) + + checkField(calldataload(add(_pubSignals, 5696))) + + checkField(calldataload(add(_pubSignals, 5728))) + + checkField(calldataload(add(_pubSignals, 5760))) + + checkField(calldataload(add(_pubSignals, 5792))) + + checkField(calldataload(add(_pubSignals, 5824))) + + checkField(calldataload(add(_pubSignals, 5856))) + + checkField(calldataload(add(_pubSignals, 5888))) + + checkField(calldataload(add(_pubSignals, 5920))) + + checkField(calldataload(add(_pubSignals, 5952))) + + checkField(calldataload(add(_pubSignals, 5984))) + + checkField(calldataload(add(_pubSignals, 6016))) + + checkField(calldataload(add(_pubSignals, 6048))) + + checkField(calldataload(add(_pubSignals, 6080))) + + checkField(calldataload(add(_pubSignals, 6112))) + + checkField(calldataload(add(_pubSignals, 6144))) + + checkField(calldataload(add(_pubSignals, 6176))) + + checkField(calldataload(add(_pubSignals, 6208))) + + checkField(calldataload(add(_pubSignals, 6240))) + + checkField(calldataload(add(_pubSignals, 6272))) + + checkField(calldataload(add(_pubSignals, 6304))) + + checkField(calldataload(add(_pubSignals, 6336))) + + checkField(calldataload(add(_pubSignals, 6368))) + + checkField(calldataload(add(_pubSignals, 6400))) + + checkField(calldataload(add(_pubSignals, 6432))) + + checkField(calldataload(add(_pubSignals, 6464))) + + checkField(calldataload(add(_pubSignals, 6496))) + + checkField(calldataload(add(_pubSignals, 6528))) + + checkField(calldataload(add(_pubSignals, 6560))) + + checkField(calldataload(add(_pubSignals, 6592))) + + checkField(calldataload(add(_pubSignals, 6624))) + + checkField(calldataload(add(_pubSignals, 6656))) + + checkField(calldataload(add(_pubSignals, 6688))) + + checkField(calldataload(add(_pubSignals, 6720))) + + checkField(calldataload(add(_pubSignals, 6752))) + + // Validate all evaluations + let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) + + mstore(0, isValid) + return(0, 0x20) + } + } +} From f15c1b65040286ac3606d62ca8293d24ea580573 Mon Sep 17 00:00:00 2001 From: Miguel Duarte Date: Tue, 18 Feb 2025 07:41:24 -0300 Subject: [PATCH 07/10] OIDC/merge guardians branch (#301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: empty GuardianRecoveryValidator Created GuardianRecoveryValidator structure to start development * feat: methods to add a guardian implemented proposeRecoveryKey and addRecoveryKey with tests. * fix: reverting when guardian not found When a user tries to remove a guardian that does not exist we revert the tx. * fix: uint to uint256 Using uint256 instead of uint everywhere. * feat: add validateTransaction implementation to GuardianRecoveryValidatior * chore: refactor tests * chore: clean up code * feat: improve init method * feat: simplify initRecovery method * chore: resolve build issues * chore: resolve build issues * chore: resolve pr comments * feat: restore guardiansFor method * chore: remove unused access to accountGuardians * feat: make guardian recovery validator contract proxy-able * chore: simplify initializer function name * feat: add function to retrieve guarded accounts * fix: improve recovery validator logic * feat: allow paymaster calls to GuardianRecoveryValidator * feat: fix guardian recovery validator compilation * fix: add compiler version and remove unwanted comments * fix: bugs and jsdoc format to match rest of package * fix: test that included guardian contract * feat: add passkey to account relation * feat: prevent account overlap * feat: improve registered accounts logic * fix: tests * fix: unknown accounts * fix: discard recovery bug * fix: move account verifications * feat: add guardian added time to guardian information * fix: deployment * fix: address to account id is not empty when initiating recovery * fix: remove double save on guardedAccounts * Fix/paymaster-recovery-validator (#291) * fix: add missing functions from recovery validator to paymaster * feat: add guardian not self check --------- Co-authored-by: Lukasz Romanowski <5160687+MiniRoman@users.noreply.github.com> Co-authored-by: Agustin Aon <21188659+aon@users.noreply.github.com> Co-authored-by: Matías Ignacio González --- scripts/deploy.ts | 3 +-- test/utils.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 6deb18c7..6853b016 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -86,11 +86,11 @@ task("deploy", "Deploys ZKsync SSO contracts") const factory = await deploy(FACTORY_NAME, deployer, !cmd.noProxy, [beacon]); const guardianInterface = new ethers.Interface((await hre.artifacts.readArtifact(GUARDIAN_RECOVERY_NAME)).abi); const recovery = await deploy(GUARDIAN_RECOVERY_NAME, deployer, !cmd.noProxy, [webauth, factory], guardianInterface.encodeFunctionData("initialize", [webauth, factory])); + const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); const oidcKeyRegistryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_KEY_REGISTRY_NAME)).abi); const oidcKeyRegistry = await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); const oidcVerifier = await deploy(OIDC_VERIFIER_NAME, deployer, false, []); await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier]); - const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); await fundPaymaster(paymaster, cmd.fund); } else { @@ -117,7 +117,6 @@ task("deploy", "Deploys ZKsync SSO contracts") if (cmd.only == OIDC_KEY_REGISTRY_NAME) { args = []; } - const deployedContract = await deploy(cmd.only, deployer, false, args); if (cmd.only == PAYMASTER_NAME) { diff --git a/test/utils.ts b/test/utils.ts index 9e7dec8b..e5b38893 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -25,7 +25,7 @@ import type { SsoBeacon, AccountProxy, OidcKeyRegistry, - GuardianRecoveryValidator + GuardianRecoveryValidator, } from "../typechain-types"; import { AAFactory__factory, @@ -38,7 +38,7 @@ import { SsoBeacon__factory, TestPaymaster__factory, OidcKeyRegistry__factory, - GuardianRecoveryValidator__factory + GuardianRecoveryValidator__factory, } from "../typechain-types"; export const ethersStaticSalt = new Uint8Array([ @@ -112,7 +112,7 @@ export class ContractFixtures { async getGuardianRecoveryValidator () { if (this._guardianRecoveryValidator === undefined) { const webAuthVerifier = await this.getWebAuthnVerifierContract(); - const aaFactoryAddress = await this.getAaFactoryAddress() + const aaFactoryAddress = await this.getAaFactoryAddress(); const contract = await create2("GuardianRecoveryValidator", this.wallet, ethersStaticSalt, [await webAuthVerifier.getAddress(), aaFactoryAddress]); this._guardianRecoveryValidator = GuardianRecoveryValidator__factory.connect(await contract.getAddress(), this.wallet); } From 49532abf10fa9096cc8ff57bc2cc31de1ddb0875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Tue, 18 Feb 2025 08:38:13 -0300 Subject: [PATCH 08/10] Deploy OidcRecoveryValidator (#299) * Deploy OidcRecoveryValidator * Make OidcRecoveryValidator initializable * Remove immutable * Add OidcValidator to paymaster * lint * fix: test ExampleAuthServerPaymaster * fix: added missing parameters for init added missing parameters for init --------- Co-authored-by: calvo.generico --- cspell-config/cspell-misc.txt | 2 +- scripts/deploy.ts | 6 ++++-- src/test/ExampleAuthServerPaymaster.sol | 18 +++++++++++++++-- ...alidator.sol => OidcRecoveryValidator.sol} | 19 +++++++++++------- test/SessionKeyTest.ts | 3 +++ test/utils.ts | 20 +++++++++++++++++-- 6 files changed, 54 insertions(+), 14 deletions(-) rename src/validators/{OidcValidator.sol => OidcRecoveryValidator.sol} (85%) diff --git a/cspell-config/cspell-misc.txt b/cspell-config/cspell-misc.txt index 5dd6bd6e..af3227c8 100644 --- a/cspell-config/cspell-misc.txt +++ b/cspell-config/cspell-misc.txt @@ -33,5 +33,5 @@ fren Oidc oidc -// src/validators/OidcValidator +// src/validators/OidcRecoveryValidator pkop diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 6853b016..9a2e2b85 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -7,7 +7,7 @@ import { Wallet } from "zksync-ethers"; const WEBAUTH_NAME = "WebAuthValidator"; const SESSIONS_NAME = "SessionKeyValidator"; const GUARDIAN_RECOVERY_NAME = "GuardianRecoveryValidator"; -const OIDC_RECOVERY_NAME = "OidcValidator"; +const OIDC_RECOVERY_NAME = "OidcRecoveryValidator"; const OIDC_VERIFIER_NAME = "Groth16Verifier"; const ACCOUNT_IMPL_NAME = "SsoAccount"; const FACTORY_NAME = "AAFactory"; @@ -86,10 +86,12 @@ task("deploy", "Deploys ZKsync SSO contracts") const factory = await deploy(FACTORY_NAME, deployer, !cmd.noProxy, [beacon]); const guardianInterface = new ethers.Interface((await hre.artifacts.readArtifact(GUARDIAN_RECOVERY_NAME)).abi); const recovery = await deploy(GUARDIAN_RECOVERY_NAME, deployer, !cmd.noProxy, [webauth, factory], guardianInterface.encodeFunctionData("initialize", [webauth, factory])); - const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery]); const oidcKeyRegistryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_KEY_REGISTRY_NAME)).abi); const oidcKeyRegistry = await deploy(OIDC_KEY_REGISTRY_NAME, deployer, !cmd.noProxy, [], oidcKeyRegistryInterface.encodeFunctionData("initialize", [])); + const oidcRecoveryInterface = new ethers.Interface((await hre.artifacts.readArtifact(OIDC_RECOVERY_NAME)).abi); const oidcVerifier = await deploy(OIDC_VERIFIER_NAME, deployer, false, []); + const oidcRecovery = await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier], oidcRecoveryInterface.encodeFunctionData("initialize", [oidcKeyRegistry, oidcVerifier])); + const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery, oidcRecovery]); await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier]); await fundPaymaster(paymaster, cmd.fund); diff --git a/src/test/ExampleAuthServerPaymaster.sol b/src/test/ExampleAuthServerPaymaster.sol index 7f7a1865..a669f721 100644 --- a/src/test/ExampleAuthServerPaymaster.sol +++ b/src/test/ExampleAuthServerPaymaster.sol @@ -11,6 +11,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { AAFactory } from "../AAFactory.sol"; import { SessionKeyValidator } from "../validators/SessionKeyValidator.sol"; import { GuardianRecoveryValidator } from "../validators/GuardianRecoveryValidator.sol"; +import { OidcRecoveryValidator } from "../validators/OidcRecoveryValidator.sol"; /// @author Matter Labs /// @notice This contract does not include any validations other than using the paymaster general flow. @@ -18,6 +19,7 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { address public immutable AA_FACTORY_CONTRACT_ADDRESS; address public immutable SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS; address public immutable ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS; + address public immutable OIDC_RECOVERY_VALIDATOR_CONTRACT_ADDRESS; bytes4 constant DEPLOY_ACCOUNT_SELECTOR = AAFactory.deployProxySsoAccount.selector; bytes4 constant SESSION_CREATE_SELECTOR = SessionKeyValidator.createSession.selector; bytes4 constant SESSION_REVOKE_KEY_SELECTOR = SessionKeyValidator.revokeKey.selector; @@ -26,6 +28,7 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { bytes4 constant GUARDIAN_RECOVERY_PROPOSE_KEY_SELECTOR = GuardianRecoveryValidator.proposeValidationKey.selector; bytes4 constant GUARDIAN_RECOVERY_DISCARD_RECOVERY_SELECTOR = GuardianRecoveryValidator.discardRecovery.selector; bytes4 constant GUARDIAN_RECOVERY_REMOVE_KEY_SELECTOR = GuardianRecoveryValidator.removeValidationKey.selector; + bytes4 constant OIDC_RECOVERY_ADD_KEY_SELECTOR = OidcRecoveryValidator.addValidationKey.selector; modifier onlyBootloader() { require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Only bootloader can call this method"); @@ -33,10 +36,16 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { _; } - constructor(address aaFactoryAddress, address sessionKeyValidatorAddress, address accountRecoveryValidatorAddress) { + constructor( + address aaFactoryAddress, + address sessionKeyValidatorAddress, + address accountRecoveryValidatorAddress, + address oidcRecoveryValidatorAddress + ) { AA_FACTORY_CONTRACT_ADDRESS = aaFactoryAddress; SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS = sessionKeyValidatorAddress; ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS = accountRecoveryValidatorAddress; + OIDC_RECOVERY_VALIDATOR_CONTRACT_ADDRESS = oidcRecoveryValidatorAddress; } function validateAndPayForPaymasterTransaction( @@ -53,7 +62,8 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { require( to == AA_FACTORY_CONTRACT_ADDRESS || to == SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS || - to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS, + to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS || + to == OIDC_RECOVERY_VALIDATOR_CONTRACT_ADDRESS, "Unsupported contract address" ); @@ -82,6 +92,10 @@ contract ExampleAuthServerPaymaster is IPaymaster, Ownable { ); } + if (to == OIDC_RECOVERY_VALIDATOR_CONTRACT_ADDRESS) { + require(methodSelector == OIDC_RECOVERY_ADD_KEY_SELECTOR, "Unsupported method"); + } + bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); require(paymasterInputSelector == IPaymasterFlow.general.selector, "Unsupported paymaster flow"); diff --git a/src/validators/OidcValidator.sol b/src/validators/OidcRecoveryValidator.sol similarity index 85% rename from src/validators/OidcValidator.sol rename to src/validators/OidcRecoveryValidator.sol index b5344cbc..f6bac5b6 100644 --- a/src/validators/OidcValidator.sol +++ b/src/validators/OidcRecoveryValidator.sol @@ -3,17 +3,18 @@ pragma solidity ^0.8.24; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { IModule } from "../interfaces/IModule.sol"; import { VerifierCaller } from "../helpers/VerifierCaller.sol"; import { OidcKeyRegistry } from "../OidcKeyRegistry.sol"; -/// @title OidcValidator +/// @title OidcRecoveryValidator /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @dev This contract allows secure user authentication using OIDC protocol. -contract OidcValidator is VerifierCaller, IModuleValidator { +contract OidcRecoveryValidator is VerifierCaller, IModuleValidator, Initializable { event OidcKeyUpdated(address indexed account, bytes iss, bool isNew); struct OidcData { @@ -30,10 +31,14 @@ contract OidcValidator is VerifierCaller, IModuleValidator { mapping(address => OidcData) public accountData; - address public immutable keyRegistry; - address public immutable verifier; + address public keyRegistry; + address public verifier; constructor(address _keyRegistry, address _verifier) { + initialize(_keyRegistry, _verifier); + } + + function initialize(address _keyRegistry, address _verifier) public initializer { keyRegistry = _keyRegistry; verifier = _verifier; } @@ -42,7 +47,7 @@ contract OidcValidator is VerifierCaller, IModuleValidator { /// @param data ABI-encoded OidcData key to add immediately, or empty if not needed function onInstall(bytes calldata data) external override { if (data.length > 0) { - require(addValidationKey(data), "OidcValidator: key already exists"); + require(addValidationKey(data), "OidcRecoveryValidator: key already exists"); } } @@ -83,13 +88,13 @@ contract OidcValidator is VerifierCaller, IModuleValidator { OidcSignature memory oidcSignature = abi.decode(signature, (OidcSignature)); OidcKeyRegistry.Key memory key = keyRegistryContract.getKey(oidcSignature.issHash, oidcSignature.kid); - revert("OidcValidator: validateTransaction not implemented"); + revert("OidcRecoveryValidator: validateTransaction not implemented"); } /// @notice Unimplemented because signature validation is not required. /// @dev We only need `validateTransaction` to add new passkeys, so this function is intentionally left unimplemented. function validateSignature(bytes32 signedHash, bytes memory signature) external view returns (bool) { - revert("OidcValidator: validateSignature not implemented"); + revert("OidcRecoveryValidator: validateSignature not implemented"); } /// @inheritdoc IERC165 diff --git a/test/SessionKeyTest.ts b/test/SessionKeyTest.ts index e729b1a4..94e06268 100644 --- a/test/SessionKeyTest.ts +++ b/test/SessionKeyTest.ts @@ -312,10 +312,13 @@ describe("SessionKeyModule tests", function () { assert(factoryContract != null, "No AA Factory deployed"); const guardianRecoveryContract = await fixtures.getGuardianRecoveryValidator(); assert(guardianRecoveryContract != null, "No Guardian Recovery deployed"); + const oidcRecoveryContract = await fixtures.getOidcRecoveryValidator(); + assert(oidcRecoveryContract != null, "No OIDC Recovery deployed"); const authServerPaymaster = await fixtures.deployExampleAuthServerPaymaster( await factoryContract.getAddress(), await sessionModuleContract.getAddress(), await guardianRecoveryContract.getAddress(), + await oidcRecoveryContract.getAddress(), ); assert(authServerPaymaster != null, "No Auth Server Paymaster deployed"); diff --git a/test/utils.ts b/test/utils.ts index e5b38893..013f98dd 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -12,7 +12,7 @@ import { } from "@nomicfoundation/hardhat-network-helpers"; import { ContractFactory, Provider, utils, Wallet } from "zksync-ethers"; import { base64UrlToUint8Array, getPublicKeyBytesFromPasskeySignature, unwrapEC2Signature } from "zksync-sso/utils"; -import { Address, isHex, toHex } from "viem"; +import { Address, isHex, toHex, zeroAddress } from "viem"; import { AsyncFunc } from "mocha"; import type { @@ -26,6 +26,7 @@ import type { AccountProxy, OidcKeyRegistry, GuardianRecoveryValidator, + OidcRecoveryValidator, } from "../typechain-types"; import { AAFactory__factory, @@ -39,6 +40,7 @@ import { TestPaymaster__factory, OidcKeyRegistry__factory, GuardianRecoveryValidator__factory, + OidcRecoveryValidator__factory, } from "../typechain-types"; export const ethersStaticSalt = new Uint8Array([ @@ -108,7 +110,7 @@ export class ContractFixtures { return isHex(contractAddress) ? contractAddress : toHex(contractAddress); } - private _guardianRecoveryValidator: GuardianRecoveryValidator + private _guardianRecoveryValidator: GuardianRecoveryValidator; async getGuardianRecoveryValidator () { if (this._guardianRecoveryValidator === undefined) { const webAuthVerifier = await this.getWebAuthnVerifierContract(); @@ -119,6 +121,18 @@ export class ContractFixtures { return this._guardianRecoveryValidator } + private _oidcRecoveryValidator: OidcRecoveryValidator; + async getOidcRecoveryValidator () { + if (this._oidcRecoveryValidator === undefined) { + const verifierAddress = zeroAddress; + const oidcKeyRegistry = await this.deployOidcKeyRegistryContract(); + const contract = await create2("OidcRecoveryValidator", this.wallet, ethersStaticSalt, [await oidcKeyRegistry.getAddress(), verifierAddress]); + this._oidcRecoveryValidator = OidcRecoveryValidator__factory.connect(await contract.getAddress(), this.wallet); + } + + return this._oidcRecoveryValidator; + } + private _accountImplContract: SsoAccount; async getAccountImplContract(salt?: ethers.BytesLike) { if (!this._accountImplContract) { @@ -164,6 +178,7 @@ export class ContractFixtures { aaFactoryAddress: string, sessionKeyValidatorAddress: string, guardianRecoveryValidatorAddress: string, + oidcRecoveryValidatorAddress: string, ): Promise { const contract = await create2( "ExampleAuthServerPaymaster", @@ -173,6 +188,7 @@ export class ContractFixtures { aaFactoryAddress, sessionKeyValidatorAddress, guardianRecoveryValidatorAddress, + oidcRecoveryValidatorAddress, ], ); const paymasterAddress = ExampleAuthServerPaymaster__factory.connect(await contract.getAddress(), this.wallet); From 3fbe4759b031c9b211ecfbbb1df39dc3ab68ad8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Tue, 18 Feb 2025 14:44:26 -0300 Subject: [PATCH 09/10] fix: remove redeployed oidc validator (#302) remove redeployed oidc validator --- scripts/deploy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 9a2e2b85..7e8dc33f 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -92,7 +92,6 @@ task("deploy", "Deploys ZKsync SSO contracts") const oidcVerifier = await deploy(OIDC_VERIFIER_NAME, deployer, false, []); const oidcRecovery = await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier], oidcRecoveryInterface.encodeFunctionData("initialize", [oidcKeyRegistry, oidcVerifier])); const paymaster = await deploy(PAYMASTER_NAME, deployer, false, [factory, sessions, recovery, oidcRecovery]); - await deploy(OIDC_RECOVERY_NAME, deployer, !cmd.noProxy, [oidcKeyRegistry, oidcVerifier]); await fundPaymaster(paymaster, cmd.fund); } else { From b2246745e97142f4f60a7377a212d5d251b1ff69 Mon Sep 17 00:00:00 2001 From: "calvo.generico" Date: Tue, 18 Feb 2025 18:33:13 -0300 Subject: [PATCH 10/10] chore: added oidc to cspell dict added missing word to dict. --- cspell-config/cspell-misc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell-config/cspell-misc.txt b/cspell-config/cspell-misc.txt index af3227c8..72e8fd33 100644 --- a/cspell-config/cspell-misc.txt +++ b/cspell-config/cspell-misc.txt @@ -13,6 +13,7 @@ dockerized ethereum sepolia + // examples/bank-demo ctap Aave