Files
zkAuth/backend/contracts/ZkSocialRecoveryWallet.sol
2022-09-25 08:45:54 +02:00

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 {}
}