diff --git a/backend/contracts/ZkOtpValidator.sol b/backend/contracts/ZkOtpValidator.sol index c1b672f..588f1d8 100644 --- a/backend/contracts/ZkOtpValidator.sol +++ b/backend/contracts/ZkOtpValidator.sol @@ -1,55 +1,66 @@ // SPDX-License-Identifier: MIT -pragma solidity^0.8.17; +pragma solidity ^0.8.17; -import "hardhat/console.sol"; +import 'hardhat/console.sol'; interface IOtpMerkleTreeVerifier { - function verifyProof( - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - uint256[2] memory input - ) external view returns (bool); + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[2] memory input + ) external view returns (bool); } +/// @title ZkOtpValidator +/// @author ZK Authentication team +/// @notice You can use this contract for verifying otp verification proof +/// @dev All function calls are currently implemented without side effects +contract ZkOtpValidator { + uint256 immutable root; -contract ZkOtpValidator{ + address immutable verifier; - uint256 immutable root; + uint256 lastValidatedTimestamp; - address immutable verifier; + constructor(uint256 _root, address _verifier) { + root = _root; + verifier = _verifier; + } - uint256 lastValidatedTimestamp; - - constructor(uint256 _root, address _verifier){ - root = _root; - verifier = _verifier; - } - - // modifier verify( - // uint256[2] memory a, - // uint256[2][2] memory b, - // uint256[2] memory c, - // uint256[2] memory input - // ) { - // require(input[0] == root, "Incoorect root"); - // require(input[1] > lastValidatedTimestamp, "Old Proof"); - // require(IOtpMerkleTreeVerifier(verifier).verifyProof(a, b, c, input), "Invalid proof"); - // _; - // lastValidatedTimestamp = input[1]; - // } - - function verifyOTP( - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - uint256[2] memory input - ) public returns(bool success){ - require(input[0] == root, "Incoorect root"); - require(input[1] > lastValidatedTimestamp, "Old Proof"); - require(IOtpMerkleTreeVerifier(verifier).verifyProof(a, b, c, input), "Invalid proof"); - lastValidatedTimestamp = input[1]; - success = true; - } + // modifier verify( + // uint256[2] memory a, + // uint256[2][2] memory b, + // uint256[2] memory c, + // uint256[2] memory input + // ) { + // require(input[0] == root, "Incoorect root"); + // require(input[1] > lastValidatedTimestamp, "Old Proof"); + // require(IOtpMerkleTreeVerifier(verifier).verifyProof(a, b, c, input), "Invalid proof"); + // _; + // lastValidatedTimestamp = input[1]; + // } + /** + * @notice Verify proof for OTP authentication + * @param a OTP verification proof from zk circuit + * @param b OTP verification proof from zk circuit + * @param c OTP verification proof from zk circuit + * @param Input public signals containing the Merkle root and time + */ + function verifyOTP( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[2] memory input + ) public returns (bool success) { + require(input[0] == root, 'Incoorect root'); + require(input[1] > lastValidatedTimestamp, 'Old Proof'); + require( + IOtpMerkleTreeVerifier(verifier).verifyProof(a, b, c, input), + 'Invalid proof' + ); + lastValidatedTimestamp = input[1]; + success = true; + } } \ No newline at end of file diff --git a/backend/contracts/ZkSocialRecoveryWallet.sol b/backend/contracts/ZkSocialRecoveryWallet.sol index 797d89f..3f74ede 100644 --- a/backend/contracts/ZkSocialRecoveryWallet.sol +++ b/backend/contracts/ZkSocialRecoveryWallet.sol @@ -15,30 +15,48 @@ interface IHashCheckVerifier { ) external view returns (bool); } +/// @title ZkSocialRecoveryWallet +/// @author ZK Authentication team +/// @notice You can use this contract for providing 2 factor authentication and social recovery +/// @dev All function calls are currently implemented without side effects contract ZkSocialRecoveryWallet is IERC721Receiver, ZkOtpValidator { + /// Address of hash check verifier address hashCheckVerifier; + /// Address of owner address public owner; + /// password is not stored directly. We store the poseidon(public key user, password) uint256 private ownerPasswordHash; + /// Minimum number of votes a new owner needs to get for execution uint256 private thresholdForRecovery; + /// Unique counter for recovery rounds uint256 public currentRecoveryNumber; - //to retrieve in frontend + + /// Number of trustees(to retrieve in frontend) uint256 public numberTrustees; + + /// List of trustees address[] public Trustees; + /// Map to find if an address is a trustee mapping(address => bool) Trustee; + /// Map to store password hash corresponding to an address mapping(address => uint256) trusteeToPasswordHash; + /// Map to store an owner and prevent trustees to use it again mapping(address => bool) pastOwners; + /// Map to store if a proof is used mapping(uint256 => bool) usedProofs; + /// Its true when recovery is in process bool public isRecoveryOn; + /// Struct to store the variable required for the social recovery procedure struct RecoveryProcedure { uint256 numberOfVotesInSupport; address newOwnerProposed; @@ -46,6 +64,7 @@ contract ZkSocialRecoveryWallet is IERC721Receiver, ZkOtpValidator { mapping(address => bool) trusteeSupporters; } + /// Map to store the RecoveryProcedure for a recovery round number mapping(uint256 => RecoveryProcedure) recoveryRoundNumberToProcedure; event NewRecoveryProcedure( @@ -116,18 +135,27 @@ contract ZkSocialRecoveryWallet is IERC721Receiver, ZkOtpValidator { thresholdForRecovery = _thresholdForRecovery; } - // To set trustees after deployment + /** + * @notice Set the trustees of a wallet after deployment + * @param _trustees the list of trustee addresses + */ function setTrustees(address[] memory _trustees) external isOwner { - for (uint256 i = 0; i < _trustees.length; i++) { + for (uint256 i = 0; i < _trustees.length; i++) { require(!Trustee[_trustees[i]], 'Duplicate trustee in list'); Trustee[_trustees[i]] = true; } Trustees = _trustees; numberTrustees = _trustees.length; - } + } - // Set trustees password after deployment -function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner { + /** + * @notice Set the trustees password's hash of a wallet after deployment + * @param _passwordHashes the list of trustee password hashes + */ + function setTrusteesPasswords(uint256[] memory _passwordHashes) + external + isOwner + { require( Trustees.length == _passwordHashes.length, 'Trustees and hashes length diff' @@ -137,6 +165,14 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner } } + /** + * @notice Start the recovery process + * @param a hash check proof from zk circuit + * @param b hash check proof from zk circuit + * @param c hash check proof from zk circuit + * @param Input public signals containing the password hash + * @param newOwner address of new owner + */ function startRecovery( uint256[2] memory a, uint256[2][2] memory b, @@ -171,6 +207,14 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner return currentRecoveryNumber; } + /** + * @notice Vote in a recovery process already in process + * @param a hash check proof from zk circuit + * @param b hash check proof from zk circuit + * @param c hash check proof from zk circuit + * @param Input public signals containing the password hash + * @param recoveryRoundNumber current recovery round going on + */ function voteInRecovery( uint256[2] memory a, uint256[2][2] memory b, @@ -203,6 +247,14 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner emit VotedInRecovery(msg.sender, recoveryRoundNumber); } + /** + * @notice Execute and finish the recovery process + * @param a hash check proof from zk circuit + * @param b hash check proof from zk circuit + * @param c hash check proof from zk circuit + * @param Input public signals containing the password hash + * @param recoveryRoundNumber current recovery round going on + */ function executeRecoveryChange( uint256[2] memory a, uint256[2][2] memory b, @@ -234,6 +286,14 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner emit RecoveryExecuted(old, owner, recoveryRoundNumber); } + /** + * @notice Vote in a recovery process already in process + * @param a hash check proof from zk circuit + * @param b hash check proof from zk circuit + * @param c hash check proof from zk circuit + * @param Input public signals containing the password hash + * @param recoveryRoundNumber current recovery round going on + */ function cancelRecovery( uint256[2] memory a, uint256[2][2] memory b, @@ -251,6 +311,14 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner emit RecoveryCancelled(owner, recoveryRoundNumber); } + /** + * @notice Execute a transaction after OTP verification + * @param a OTP verification proof from zk circuit + * @param b OTP verification proof from zk circuit + * @param c OTP verification proof from zk circuit + * @param Input public signals containing the Merkle root and time + * @param recoveryRoundNumber current recovery round going on + */ function executeTxn( uint256[2] memory a, uint256[2][2] memory b, @@ -260,7 +328,7 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner uint256 value ) external isOwner returns (bytes memory result) { require(verifyOTP(a, b, c, input), 'Proof failed'); - (bool success, bytes memory result) = callee.call{value: value}(""); + (bool success, bytes memory result) = callee.call{value: value}(''); require(success, 'external call reverted'); // emit TransactionExecuted(callee, value, data); return result; @@ -275,5 +343,8 @@ function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner return this.onERC721Received.selector; } + /** + * @notice recieve funds + */ receive() external payable {} -} +} \ No newline at end of file diff --git a/backend/contracts/ZkWalletFactory.sol b/backend/contracts/ZkWalletFactory.sol index eca5e7b..a4945bd 100644 --- a/backend/contracts/ZkWalletFactory.sol +++ b/backend/contracts/ZkWalletFactory.sol @@ -3,6 +3,10 @@ pragma solidity ^0.8.17; import './ZkSocialRecoveryWallet.sol'; +/// @title ZkWalletFactory +/// @author ZK Authentication team +/// @notice You can use this contract for deploying wallets having otp verification and social recovery +/// @dev All function calls are currently implemented without side effects contract ZkWalletFactory { mapping(address => address) public userAddressToWalletAddress; event WalletCreated(address walletAddress); @@ -13,6 +17,14 @@ contract ZkWalletFactory { hashCheckVerifier = _hashCheckVerifier; } + /** + * @notice Deploy a new wallet + * @param _ownerPasswordHash password is not stored directly. We store the poseidon(public key user, password) + * @param _thresholdForRecovery Minimum number of votes a new owner needs to get for execution + * @param _otpVerifier address of the otp verifier contract + * @param root merkle root value unique to a wallet which is later used in otp verification + * @return walletAddress address of new wallet + */ function deployWallet( uint256 _ownerPasswordHash, uint256 _thresholdForRecovery, @@ -32,7 +44,16 @@ contract ZkWalletFactory { emit WalletCreated(walletAddress); } - function getUserWalletAddress(address _user) external view returns(address _walletAddress) { + /** + * @notice Getter function to get user wallet address + * @param _user address + * @return walletAddress address of user wallet + */ + function getUserWalletAddress(address _user) + external + view + returns (address _walletAddress) + { _walletAddress = userAddressToWalletAddress[_user]; } -} +} \ No newline at end of file