mirror of
https://github.com/3lLobo/zkAuth.git
synced 2026-01-27 13:17:58 -05:00
280 lines
7.0 KiB
Solidity
280 lines
7.0 KiB
Solidity
//SPDX-License-Identifier: Unlicense
|
|
pragma solidity ^0.8.17;
|
|
|
|
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
|
|
import './Verifiers/HashCheckVerifier.sol';
|
|
import './ZkOtpValidator.sol';
|
|
import 'hardhat/console.sol';
|
|
|
|
interface IHashCheckVerifier {
|
|
function verifyProof(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory input
|
|
) external view returns (bool);
|
|
}
|
|
|
|
contract ZkSocialRecoveryWallet is IERC721Receiver {
|
|
address hashCheckVerifier;
|
|
|
|
address public owner;
|
|
|
|
uint256 private ownerPasswordHash;
|
|
|
|
uint256 private thresholdForRecovery;
|
|
|
|
uint256 public currentRecoveryNumber;
|
|
|
|
mapping(address => bool) Trustee;
|
|
|
|
mapping(address => uint256) trusteeToPasswordHash;
|
|
|
|
mapping(address => bool) pastOwners;
|
|
|
|
mapping(uint256 => bool) usedProofs;
|
|
|
|
bool public isRecoveryOn;
|
|
|
|
address public otpVerifierAddress;
|
|
ZkOtpValidator otpVerifier;
|
|
|
|
struct RecoveryProcedure {
|
|
uint256 numberOfVotesInSupport;
|
|
address newOwnerProposed;
|
|
bool isPassed;
|
|
mapping(address => bool) trusteeSupporters;
|
|
}
|
|
|
|
mapping(uint256 => RecoveryProcedure) recoveryRoundNumberToProcedure;
|
|
|
|
event NewRecoveryProcedure(
|
|
address indexed newProposedOwner,
|
|
address indexed trusteeInitializer,
|
|
uint256 currRecoveryRound
|
|
);
|
|
|
|
event VotedInRecovery(address indexed trustee, uint256 RecoveryRound);
|
|
|
|
event RecoveryExecuted(
|
|
address indexed oldOwner,
|
|
address indexed newOwner,
|
|
uint256 RecoveryRound
|
|
);
|
|
|
|
event RecoveryCancelled(address indexed Owner, uint256 RecoveryRound);
|
|
|
|
modifier RecoveryShouldBeInProcess() {
|
|
require(isRecoveryOn, 'Recovery has not started');
|
|
_;
|
|
}
|
|
|
|
modifier RecoveryShouldNotBeInProcess() {
|
|
require(!isRecoveryOn, 'Recovery is in process');
|
|
_;
|
|
}
|
|
|
|
modifier isOwner() {
|
|
require(owner == msg.sender, 'Not Owner');
|
|
_;
|
|
}
|
|
|
|
modifier isTrustee() {
|
|
require(Trustee[msg.sender], 'Not Trustee');
|
|
_;
|
|
}
|
|
|
|
modifier verifyProofForTrustee(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory Input
|
|
) {
|
|
require(
|
|
IHashCheckVerifier(hashCheckVerifier).verifyProof(a, b, c, Input),
|
|
'Password proof invalid!'
|
|
);
|
|
require(!usedProofs[a[0]], 'Proof is used');
|
|
|
|
_;
|
|
|
|
usedProofs[a[0]] = true;
|
|
}
|
|
|
|
constructor(
|
|
address _hashCheckVerifier,
|
|
uint256 _ownerPasswordHash,
|
|
address[] memory _trustees,
|
|
uint256[] memory _passwordHashes,
|
|
uint256 _thresholdForRecovery,
|
|
uint256 _root,
|
|
address _otpVerifier
|
|
) {
|
|
require(_hashCheckVerifier != address(0), 'Zero address verifier');
|
|
require(
|
|
_trustees.length == _passwordHashes.length,
|
|
'Trustees and hashes length diff'
|
|
);
|
|
require(
|
|
_trustees.length >= _thresholdForRecovery,
|
|
'Threshold is greater than number of trustees'
|
|
);
|
|
|
|
hashCheckVerifier = _hashCheckVerifier;
|
|
owner = msg.sender;
|
|
ownerPasswordHash = _ownerPasswordHash;
|
|
|
|
for (uint256 i = 0; i < _trustees.length; i++) {
|
|
require(!Trustee[_trustees[i]], 'Duplicate trustee in list');
|
|
Trustee[_trustees[i]] = true;
|
|
trusteeToPasswordHash[_trustees[i]] = _passwordHashes[i];
|
|
}
|
|
|
|
thresholdForRecovery = _thresholdForRecovery;
|
|
|
|
otpVerifierAddress = _otpVerifier;
|
|
|
|
otpVerifier = new ZkOtpValidator(_root, _otpVerifier);
|
|
}
|
|
|
|
function startRecovery(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory Input,
|
|
address newOwner
|
|
)
|
|
external
|
|
isTrustee
|
|
RecoveryShouldNotBeInProcess
|
|
verifyProofForTrustee(a, b, c, Input)
|
|
returns (uint256)
|
|
{
|
|
console.log('trustee hash', trusteeToPasswordHash[msg.sender]);
|
|
console.log('Input', Input[0]);
|
|
require(Input[0] == trusteeToPasswordHash[msg.sender], 'Wrong password');
|
|
require(newOwner != address(0), 'Zero address');
|
|
require(!pastOwners[newOwner], 'Owner should not be a past address');
|
|
|
|
currentRecoveryNumber++;
|
|
|
|
RecoveryProcedure storage recovery = recoveryRoundNumberToProcedure[
|
|
currentRecoveryNumber
|
|
];
|
|
recovery.newOwnerProposed = newOwner;
|
|
recovery.numberOfVotesInSupport++;
|
|
recovery.trusteeSupporters[msg.sender] = true;
|
|
|
|
isRecoveryOn = true;
|
|
emit NewRecoveryProcedure(newOwner, msg.sender, currentRecoveryNumber);
|
|
|
|
return currentRecoveryNumber;
|
|
}
|
|
|
|
function voteInRecovery(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory Input,
|
|
uint256 recoveryRoundNumber
|
|
)
|
|
external
|
|
isTrustee
|
|
RecoveryShouldBeInProcess
|
|
verifyProofForTrustee(a, b, c, Input)
|
|
returns (bool success)
|
|
{
|
|
require(Input[0] == trusteeToPasswordHash[msg.sender], 'Wrong password');
|
|
console.log('Here1');
|
|
require(
|
|
recoveryRoundNumber <= currentRecoveryNumber,
|
|
'Wrong Recovery round number'
|
|
);
|
|
console.log('Here');
|
|
RecoveryProcedure storage recovery = recoveryRoundNumberToProcedure[
|
|
recoveryRoundNumber
|
|
];
|
|
require(!recovery.trusteeSupporters[msg.sender], 'Trustee already voted');
|
|
recovery.numberOfVotesInSupport++;
|
|
recovery.trusteeSupporters[msg.sender] = true;
|
|
|
|
success = true;
|
|
|
|
emit VotedInRecovery(msg.sender, recoveryRoundNumber);
|
|
}
|
|
|
|
function executeRecoveryChange(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory Input,
|
|
uint256 recoveryRoundNumber
|
|
)
|
|
external
|
|
isTrustee
|
|
RecoveryShouldBeInProcess
|
|
verifyProofForTrustee(a, b, c, Input)
|
|
{
|
|
require(Input[0] == trusteeToPasswordHash[msg.sender], 'Wrong password');
|
|
RecoveryProcedure storage recovery = recoveryRoundNumberToProcedure[
|
|
recoveryRoundNumber
|
|
];
|
|
|
|
require(
|
|
recovery.numberOfVotesInSupport >= thresholdForRecovery,
|
|
'Votes Not enough'
|
|
);
|
|
|
|
recovery.isPassed = true;
|
|
isRecoveryOn = false;
|
|
address old = owner;
|
|
owner = recovery.newOwnerProposed;
|
|
pastOwners[owner] = true;
|
|
|
|
emit RecoveryExecuted(old, owner, recoveryRoundNumber);
|
|
}
|
|
|
|
function cancelRecovery(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[1] memory Input,
|
|
uint256 recoveryRoundNumber
|
|
)
|
|
external
|
|
isOwner
|
|
RecoveryShouldBeInProcess
|
|
verifyProofForTrustee(a, b, c, Input)
|
|
{
|
|
require(Input[0] == ownerPasswordHash, 'Wrong password');
|
|
isRecoveryOn = false;
|
|
emit RecoveryCancelled(owner, recoveryRoundNumber);
|
|
}
|
|
|
|
function executeTxn(
|
|
uint256[2] memory a,
|
|
uint256[2][2] memory b,
|
|
uint256[2] memory c,
|
|
uint256[2] memory input,
|
|
address callee,
|
|
uint256 value
|
|
) external isOwner returns (bytes memory result) {
|
|
require(otpVerifier.verifyOTP(a, b, c, input), 'Proof failed');
|
|
(bool success, bytes memory result) = callee.call{value: value}("");
|
|
require(success, 'external call reverted');
|
|
// emit TransactionExecuted(callee, value, data);
|
|
return result;
|
|
}
|
|
|
|
function onERC721Received(
|
|
address,
|
|
address,
|
|
uint256,
|
|
bytes memory
|
|
) public pure override returns (bytes4) {
|
|
return this.onERC721Received.selector;
|
|
}
|
|
|
|
receive() external payable {}
|
|
}
|