las commit

This commit is contained in:
daniecon
2022-09-25 15:10:58 +02:00
parent caede763be
commit 188d70ff71
16 changed files with 376 additions and 522 deletions

View File

@@ -25,6 +25,9 @@ contract ZkSocialRecoveryWallet is IERC721Receiver {
uint256 private thresholdForRecovery;
uint256 public currentRecoveryNumber;
//to retrieve in frontend
uint256 public numberTrustees;
address[] public Trustees;
mapping(address => bool) Trustee;
@@ -104,37 +107,40 @@ contract ZkSocialRecoveryWallet is IERC721Receiver {
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;
thresholdForRecovery = _thresholdForRecovery;
otpVerifierAddress = _otpVerifier;
otpVerifier = new ZkOtpValidator(_root, _otpVerifier);
}
for (uint256 i = 0; i < _trustees.length; i++) {
// To set trustees after deployment
function setTrustees(address[] memory _trustees) external isOwner {
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);
Trustees = _trustees;
numberTrustees = _trustees.length;
}
// Set trustees password after deployment
function setTrusteesPasswords(uint256[] memory _passwordHashes) external isOwner {
require(
Trustees.length == _passwordHashes.length,
'Trustees and hashes length diff'
);
for (uint256 i = 0; i < Trustees.length; i++) {
trusteeToPasswordHash[Trustees[i]] = _passwordHashes[i];
}
}
function startRecovery(

View File

@@ -15,18 +15,13 @@ contract ZkWalletFactory {
function deployWallet(
uint256 _ownerPasswordHash,
address[] memory _trustees,
uint256[] memory _passwordHashes,
uint256 _thresholdForRecovery,
address _otpVerifier,
uint256 _root
) external returns (address walletAddress) {
ZkSocialRecoveryWallet wallet = new ZkSocialRecoveryWallet(
hashCheckVerifier,
_ownerPasswordHash,
_trustees,
_passwordHashes,
_thresholdForRecovery,
_root,
_otpVerifier

View File

@@ -29,32 +29,44 @@ import type {
export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
functions: {
"Trustees(uint256)": FunctionFragment;
"cancelRecovery(uint256[2],uint256[2][2],uint256[2],uint256[1],uint256)": FunctionFragment;
"currentRecoveryNumber()": FunctionFragment;
"executeRecoveryChange(uint256[2],uint256[2][2],uint256[2],uint256[1],uint256)": FunctionFragment;
"executeTxn(uint256[2],uint256[2][2],uint256[2],uint256[2],address,uint256)": FunctionFragment;
"isRecoveryOn()": FunctionFragment;
"numberTrustees()": FunctionFragment;
"onERC721Received(address,address,uint256,bytes)": FunctionFragment;
"otpVerifierAddress()": FunctionFragment;
"owner()": FunctionFragment;
"setTrustees(address[])": FunctionFragment;
"setTrusteesPasswords(uint256[])": FunctionFragment;
"startRecovery(uint256[2],uint256[2][2],uint256[2],uint256[1],address)": FunctionFragment;
"voteInRecovery(uint256[2],uint256[2][2],uint256[2],uint256[1],uint256)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "Trustees"
| "cancelRecovery"
| "currentRecoveryNumber"
| "executeRecoveryChange"
| "executeTxn"
| "isRecoveryOn"
| "numberTrustees"
| "onERC721Received"
| "otpVerifierAddress"
| "owner"
| "setTrustees"
| "setTrusteesPasswords"
| "startRecovery"
| "voteInRecovery"
): FunctionFragment;
encodeFunctionData(
functionFragment: "Trustees",
values: [PromiseOrValue<BigNumberish>]
): string;
encodeFunctionData(
functionFragment: "cancelRecovery",
values: [
@@ -103,6 +115,10 @@ export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
functionFragment: "isRecoveryOn",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "numberTrustees",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "onERC721Received",
values: [
@@ -117,6 +133,14 @@ export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
values?: undefined
): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "setTrustees",
values: [PromiseOrValue<string>[]]
): string;
encodeFunctionData(
functionFragment: "setTrusteesPasswords",
values: [PromiseOrValue<BigNumberish>[]]
): string;
encodeFunctionData(
functionFragment: "startRecovery",
values: [
@@ -144,6 +168,7 @@ export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
]
): string;
decodeFunctionResult(functionFragment: "Trustees", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "cancelRecovery",
data: BytesLike
@@ -161,6 +186,10 @@ export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
functionFragment: "isRecoveryOn",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "numberTrustees",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "onERC721Received",
data: BytesLike
@@ -170,6 +199,14 @@ export interface ZkSocialRecoveryWalletInterface extends utils.Interface {
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setTrustees",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setTrusteesPasswords",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "startRecovery",
data: BytesLike
@@ -268,6 +305,11 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
removeListener: OnEvent<this>;
functions: {
Trustees(
arg0: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<[string]>;
cancelRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -309,6 +351,8 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
isRecoveryOn(overrides?: CallOverrides): Promise<[boolean]>;
numberTrustees(overrides?: CallOverrides): Promise<[BigNumber]>;
onERC721Received(
arg0: PromiseOrValue<string>,
arg1: PromiseOrValue<string>,
@@ -321,6 +365,16 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
owner(overrides?: CallOverrides): Promise<[string]>;
setTrustees(
_trustees: PromiseOrValue<string>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
setTrusteesPasswords(
_passwordHashes: PromiseOrValue<BigNumberish>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
startRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -346,6 +400,11 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
): Promise<ContractTransaction>;
};
Trustees(
arg0: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<string>;
cancelRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -387,6 +446,8 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
isRecoveryOn(overrides?: CallOverrides): Promise<boolean>;
numberTrustees(overrides?: CallOverrides): Promise<BigNumber>;
onERC721Received(
arg0: PromiseOrValue<string>,
arg1: PromiseOrValue<string>,
@@ -399,6 +460,16 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
owner(overrides?: CallOverrides): Promise<string>;
setTrustees(
_trustees: PromiseOrValue<string>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
setTrusteesPasswords(
_passwordHashes: PromiseOrValue<BigNumberish>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
startRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -424,6 +495,11 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
): Promise<ContractTransaction>;
callStatic: {
Trustees(
arg0: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<string>;
cancelRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -465,6 +541,8 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
isRecoveryOn(overrides?: CallOverrides): Promise<boolean>;
numberTrustees(overrides?: CallOverrides): Promise<BigNumber>;
onERC721Received(
arg0: PromiseOrValue<string>,
arg1: PromiseOrValue<string>,
@@ -477,6 +555,16 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
owner(overrides?: CallOverrides): Promise<string>;
setTrustees(
_trustees: PromiseOrValue<string>[],
overrides?: CallOverrides
): Promise<void>;
setTrusteesPasswords(
_passwordHashes: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<void>;
startRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -545,6 +633,11 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
};
estimateGas: {
Trustees(
arg0: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<BigNumber>;
cancelRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -586,6 +679,8 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
isRecoveryOn(overrides?: CallOverrides): Promise<BigNumber>;
numberTrustees(overrides?: CallOverrides): Promise<BigNumber>;
onERC721Received(
arg0: PromiseOrValue<string>,
arg1: PromiseOrValue<string>,
@@ -598,6 +693,16 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
owner(overrides?: CallOverrides): Promise<BigNumber>;
setTrustees(
_trustees: PromiseOrValue<string>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
setTrusteesPasswords(
_passwordHashes: PromiseOrValue<BigNumberish>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
startRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -624,6 +729,11 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
};
populateTransaction: {
Trustees(
arg0: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
cancelRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [
@@ -667,6 +777,8 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
isRecoveryOn(overrides?: CallOverrides): Promise<PopulatedTransaction>;
numberTrustees(overrides?: CallOverrides): Promise<PopulatedTransaction>;
onERC721Received(
arg0: PromiseOrValue<string>,
arg1: PromiseOrValue<string>,
@@ -681,6 +793,16 @@ export interface ZkSocialRecoveryWallet extends BaseContract {
owner(overrides?: CallOverrides): Promise<PopulatedTransaction>;
setTrustees(
_trustees: PromiseOrValue<string>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
setTrusteesPasswords(
_passwordHashes: PromiseOrValue<BigNumberish>[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
startRecovery(
a: [PromiseOrValue<BigNumberish>, PromiseOrValue<BigNumberish>],
b: [

View File

@@ -29,7 +29,7 @@ import type {
export interface ZkWalletFactoryInterface extends utils.Interface {
functions: {
"deployWallet(uint256,address[],uint256[],uint256,address,uint256)": FunctionFragment;
"deployWallet(uint256,uint256,address,uint256)": FunctionFragment;
"getUserWalletAddress(address)": FunctionFragment;
"hashCheckVerifier()": FunctionFragment;
"userAddressToWalletAddress(address)": FunctionFragment;
@@ -47,8 +47,6 @@ export interface ZkWalletFactoryInterface extends utils.Interface {
functionFragment: "deployWallet",
values: [
PromiseOrValue<BigNumberish>,
PromiseOrValue<string>[],
PromiseOrValue<BigNumberish>[],
PromiseOrValue<BigNumberish>,
PromiseOrValue<string>,
PromiseOrValue<BigNumberish>
@@ -127,8 +125,6 @@ export interface ZkWalletFactory extends BaseContract {
functions: {
deployWallet(
_ownerPasswordHash: PromiseOrValue<BigNumberish>,
_trustees: PromiseOrValue<string>[],
_passwordHashes: PromiseOrValue<BigNumberish>[],
_thresholdForRecovery: PromiseOrValue<BigNumberish>,
_otpVerifier: PromiseOrValue<string>,
_root: PromiseOrValue<BigNumberish>,
@@ -150,8 +146,6 @@ export interface ZkWalletFactory extends BaseContract {
deployWallet(
_ownerPasswordHash: PromiseOrValue<BigNumberish>,
_trustees: PromiseOrValue<string>[],
_passwordHashes: PromiseOrValue<BigNumberish>[],
_thresholdForRecovery: PromiseOrValue<BigNumberish>,
_otpVerifier: PromiseOrValue<string>,
_root: PromiseOrValue<BigNumberish>,
@@ -173,8 +167,6 @@ export interface ZkWalletFactory extends BaseContract {
callStatic: {
deployWallet(
_ownerPasswordHash: PromiseOrValue<BigNumberish>,
_trustees: PromiseOrValue<string>[],
_passwordHashes: PromiseOrValue<BigNumberish>[],
_thresholdForRecovery: PromiseOrValue<BigNumberish>,
_otpVerifier: PromiseOrValue<string>,
_root: PromiseOrValue<BigNumberish>,
@@ -202,8 +194,6 @@ export interface ZkWalletFactory extends BaseContract {
estimateGas: {
deployWallet(
_ownerPasswordHash: PromiseOrValue<BigNumberish>,
_trustees: PromiseOrValue<string>[],
_passwordHashes: PromiseOrValue<BigNumberish>[],
_thresholdForRecovery: PromiseOrValue<BigNumberish>,
_otpVerifier: PromiseOrValue<string>,
_root: PromiseOrValue<BigNumberish>,
@@ -226,8 +216,6 @@ export interface ZkWalletFactory extends BaseContract {
populateTransaction: {
deployWallet(
_ownerPasswordHash: PromiseOrValue<BigNumberish>,
_trustees: PromiseOrValue<string>[],
_passwordHashes: PromiseOrValue<BigNumberish>[],
_thresholdForRecovery: PromiseOrValue<BigNumberish>,
_otpVerifier: PromiseOrValue<string>,
_root: PromiseOrValue<BigNumberish>,

File diff suppressed because one or more lines are too long

View File

@@ -1,33 +0,0 @@
/* global BigInt */
import { generateWitness } from './generate_witness';
import { groth16 } from 'snarkjs';
export async function generateCalldata(input, circuit_name) {
let generateWitnessSuccess = true;
let witness = await generateWitness(input, circuit_name).then()
.catch((error) => {
console.error(error);
generateWitnessSuccess = false;
});
//console.log(witness);
if (!generateWitnessSuccess) { return; }
const { proof, publicSignals } = await groth16.prove(`${circuit_name}_circuit_final.zkey`, witness);
const calldata = await groth16.exportSolidityCallData(proof, publicSignals);
const argv = calldata.replace(/["[\]\s]/g, "").split(',').map(x => BigInt(x).toString());
//console.log(argv);
const a = [argv[0], argv[1]];
const b = [[argv[2], argv[3]], [argv[4], argv[5]]];
const c = [argv[6], argv[7]];
const Input = argv.slice(8);
return [a, b, c, Input];
}

View File

@@ -1,13 +0,0 @@
import wc from "./witness_calculator";
export async function generateWitness (input, circuit_name) {
const response = await fetch(`${circuit_name}_circuit.wasm`);
const buffer = await response.arrayBuffer();
//console.log(buffer);
let buff;
await wc(buffer).then(async witnessCalculator => {
buff = await witnessCalculator.calculateWTNSBin(input, 0);
});
return buff;
}

View File

@@ -1,289 +0,0 @@
/* global BigInt */
module.exports = async function builder(code, options) {
options = options || {};
const wasmModule = await WebAssembly.compile(code);
let wc;
const instance = await WebAssembly.instantiate(wasmModule, {
runtime: {
exceptionHandler : function(code) {
let errStr;
if (code === 1) {
errStr= "Signal not found. ";
} else if (code === 2) {
errStr= "Too many signals set. ";
} else if (code === 3) {
errStr= "Signal already set. ";
} else if (code === 4) {
errStr= "Assert Failed. ";
} else if (code === 5) {
errStr= "Not enough memory. ";
} else {
errStr= "Unknown error\n";
}
// get error message from wasm
errStr += getMessage();
throw new Error(errStr);
},
showSharedRWMemory: function() {
printSharedRWMemory ();
}
}
});
const sanityCheck =
options
// options &&
// (
// options.sanityCheck ||
// options.logGetSignal ||
// options.logSetSignal ||
// options.logStartComponent ||
// options.logFinishComponent
// );
wc = new WitnessCalculator(instance, sanityCheck);
return wc;
function getMessage() {
var message = "";
var c = instance.exports.getMessageChar();
while ( c !== 0 ) {
message += String.fromCharCode(c);
c = instance.exports.getMessageChar();
}
return message;
}
function printSharedRWMemory () {
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
const arr = new Uint32Array(shared_rw_memory_size);
for (let j=0; j<shared_rw_memory_size; j++) {
arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
}
console.log(fromArray32(arr));
}
};
class WitnessCalculator {
constructor(instance, sanityCheck) {
this.instance = instance;
this.version = this.instance.exports.getVersion();
this.n32 = this.instance.exports.getFieldNumLen32();
this.instance.exports.getRawPrime();
const arr = new Array(this.n32);
for (let i=0; i<this.n32; i++) {
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
}
this.prime = fromArray32(arr);
this.witnessSize = this.instance.exports.getWitnessSize();
this.sanityCheck = sanityCheck;
}
circom_version() {
return this.instance.exports.getVersion();
}
async _doCalculateWitness(input, sanityCheck) {
//input is assumed to be a map from signals to arrays of bigints
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
const keys = Object.keys(input);
var input_counter = 0;
keys.forEach( (k) => {
const h = fnvHash(k);
const hMSB = parseInt(h.slice(0,8), 16);
const hLSB = parseInt(h.slice(8,16), 16);
const fArr = flatArray(input[k]);
for (let i=0; i<fArr.length; i++) {
const arrFr = toArray32(fArr[i],this.n32)
for (let j=0; j<this.n32; j++) {
this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
}
try {
this.instance.exports.setInputSignal(hMSB, hLSB,i);
input_counter++;
} catch (err) {
// console.log(`After adding signal ${i} of ${k}`)
throw new Error(err);
}
}
});
if (input_counter < this.instance.exports.getInputSize()) {
throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
}
}
async calculateWitness(input, sanityCheck) {
const w = [];
await this._doCalculateWitness(input, sanityCheck);
for (let i=0; i<this.witnessSize; i++) {
this.instance.exports.getWitness(i);
const arr = new Uint32Array(this.n32);
for (let j=0; j<this.n32; j++) {
arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
}
w.push(fromArray32(arr));
}
return w;
}
async calculateBinWitness(input, sanityCheck) {
const buff32 = new Uint32Array(this.witnessSize*this.n32);
const buff = new Uint8Array( buff32.buffer);
await this._doCalculateWitness(input, sanityCheck);
for (let i=0; i<this.witnessSize; i++) {
this.instance.exports.getWitness(i);
const pos = i*this.n32;
for (let j=0; j<this.n32; j++) {
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
}
}
return buff;
}
async calculateWTNSBin(input, sanityCheck) {
const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
const buff = new Uint8Array( buff32.buffer);
await this._doCalculateWitness(input, sanityCheck);
//"wtns"
buff[0] = "w".charCodeAt(0)
buff[1] = "t".charCodeAt(0)
buff[2] = "n".charCodeAt(0)
buff[3] = "s".charCodeAt(0)
//version 2
buff32[1] = 2;
//number of sections: 2
buff32[2] = 2;
//id section 1
buff32[3] = 1;
const n8 = this.n32*4;
//id section 1 length in 64bytes
const idSection1length = 8 + n8;
const idSection1lengthHex = idSection1length.toString(16);
buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
//this.n32
buff32[6] = n8;
//prime number
this.instance.exports.getRawPrime();
var pos = 7;
for (let j=0; j<this.n32; j++) {
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
}
pos += this.n32;
// witness size
buff32[pos] = this.witnessSize;
pos++;
//id section 2
buff32[pos] = 2;
pos++;
// section 2 length
const idSection2length = n8*this.witnessSize;
const idSection2lengthHex = idSection2length.toString(16);
buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
pos += 2;
for (let i=0; i<this.witnessSize; i++) {
this.instance.exports.getWitness(i);
for (let j=0; j<this.n32; j++) {
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
}
pos += this.n32;
}
return buff;
}
}
function toArray32(s,size) {
const res = []; //new Uint32Array(size); //has no unshift
let rem = BigInt(s);
const radix = BigInt(0x100000000);
while (rem) {
res.unshift( Number(rem % radix));
rem = rem / radix;
}
if (size) {
var i = size - res.length;
while (i>0) {
res.unshift(0);
i--;
}
}
return res;
}
function fromArray32(arr) { //returns a BigInt
var res = BigInt(0);
const radix = BigInt(0x100000000);
for (let i = 0; i<arr.length; i++) {
res = res*radix + BigInt(arr[i]);
}
return res;
}
function flatArray(a) {
var res = [];
fillArray(res, a);
return res;
function fillArray(res, a) {
if (Array.isArray(a)) {
for (let i=0; i<a.length; i++) {
fillArray(res, a[i]);
}
} else {
res.push(a);
}
}
}
function fnvHash(str) {
const uint64_max = BigInt(2) ** BigInt(64);
let hash = BigInt("0xCBF29CE484222325");
for (var i = 0; i < str.length; i++) {
hash ^= BigInt(str[i].charCodeAt());
hash *= BigInt(0x100000001B3);
hash %= uint64_max;
}
let shash = hash.toString(16);
let n = 16 - shash.length;
shash = '0'.repeat(n).concat(shash);
return shash;
}

View File

@@ -1,6 +1,10 @@
import { ModalSetSocial } from './'
const BoxSocialRecovery = () => {
interface BoxSocialProps {
numberTrustees: string
}
const BoxSocialRecovery = (props: BoxSocialProps) => {
return (
<div
className="grid grid-cols-12 col-span-4 w-full p-6 rounded-lg border shadow-md
@@ -11,13 +15,17 @@ const BoxSocialRecovery = () => {
<div className="text-sm text-gray-600 dark:text-gray-400 mb-3">
Social Recovery
</div>
<div className="text-2xl mb-5">Enabled</div>
<ModalSetSocial />
{props.numberTrustees != '0' ? (
<div className="text-2xl mb-5">Enabled</div>
) : (
<div className="text-2xl mb-5">Disabled</div>
)}
<ModalSetSocial enabled={props.numberTrustees == '0'} />
</div>
<div className="flex flex-col col-span-4 items-center justify-center">
<div className="text-[6rem] text-gray-900 dark:text-gray-200 leading-none -mt-4">
3
{props.numberTrustees}
</div>
<div className="text-xs text-center text-gray-600 dark:text-gray-400">
Recovery accounts

View File

@@ -85,7 +85,7 @@ const ModalChangePassword = () => {
className="w-3/4 px-2 py-2 button-unsaturated"
onClick={(e) => onSubmit(e)}
>
Set Accounts
Reset Password
</button>
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setOpen}>

View File

@@ -10,7 +10,9 @@ import { useTheme } from 'next-themes'
import { PlusIcon } from '@heroicons/react/24/solid'
import { PasswordTOTPBox, PasswordZKBox } from '.'
interface ModalSetSocialProps {}
interface ModalSetSocialProps {
enabled: boolean
}
const ModalSetSocial = (props: ModalSetSocialProps) => {
const [open, setOpen] = useState(false)
@@ -193,12 +195,16 @@ const ModalSetSocial = (props: ModalSetSocialProps) => {
return (
<>
<button
className="w-3/4 px-2 py-2 button-unsaturated"
onClick={(e) => onSubmit(e)}
>
Set Accounts
</button>
{props.enabled ? (
<button
className="w-3/4 px-2 py-2 button-unsaturated"
onClick={(e) => onSubmit(e)}
>
Set Accounts
</button>
) : (
<></>
)}
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setOpen}>
<Transition.Child
@@ -354,7 +360,7 @@ const ModalSetSocial = (props: ModalSetSocialProps) => {
id="password"
className="w-full mt-3 flex flex-row gap-3 justify-center"
>
{false ? (
{true ? (
<PasswordTOTPBox
setOpen={setOpen}
allVerified={allVerified}

View File

@@ -7,6 +7,7 @@ import {
} from '@heroicons/react/24/outline'
import { ethers } from 'ethers'
import { useResolveName } from '@usedapp/core'
import PasswordTOTPBox from './PasswordTOTPBox'
interface ModalTxDetailsProps {}
@@ -123,6 +124,11 @@ const ModalTxDetails = (props: ModalTxDetailsProps) => {
</div>
</div>
</div>
<div>
{/* // TODO FLORIAN */}
<PasswordTOTPBox setOpen={setOpen} allVerified={false} />
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button
type="button"

View File

@@ -1,71 +0,0 @@
import { ethers } from "ethers";
import address from '../artifacts/address.json';
import ZkWallet from '../artifacts/ZkSocialRecoveryWallet.json';
import ZkWalletFactory from '../artifacts/ZkWalletFactory.json';
import { generateCalldata } from '../circuit_js/generate_calldata.js';
let factory: ethers.Contract;
let zkWallet: ethers.Contract;
export async function connectContract(addr: string) {
const { ethereum } = window;
let provider = new ethers.providers.Web3Provider(ethereum);
let signer = provider.getSigner();
console.log('signer: ', await signer.getAddress());
zkWallet = new ethers.Contract(addr, ZkWallet.abi, signer);
console.log("Connect to ZkWalletAddress Contract:", addr);
}
export async function connectZkWalletFactory() {
const { ethereum } = window;
let provider = new ethers.providers.Web3Provider(ethereum);
let signer = provider.getSigner();
console.log('signer: ', await signer.getAddress());
factory = new ethers.Contract(address['ZkWalletFactory'], ZkWalletFactory.abi, signer);
console.log("Connect to ZkWalletFactory Contract:", ZkWalletFactory);
}
export async function deployZkWallet(ownerPasswordHash: BigInt, trustees: string[], passwordHashes: BigInt[], thresholdForRecovery: number, root: BigInt) {
await connectZkWalletFactory();
let txn = await factory.deployWallet(address["HashCheckVerfier"], ownerPasswordHash, trustees, passwordHashes, thresholdForRecovery, root, address["OtpMerkleTreeVerifier"]);
let rc = await txn.wait()
let newWalletAddress = txn.events[0].args.walletAddress;
localStorage.setItem("ZkWalletAddress", newWalletAddress);
return newWalletAddress;
}
export async function hashCheckGenerateCalldata(input: Object) {
if (localStorage.getItem('ZkWalletAddress')) {
console.log(localStorage.getItem('ZkWalletAddress'));
await connectContract(localStorage.getItem('ZkWalletAddress')!);
} else {
throw new Error("No zkWallet contract address found. Deploy first.");
}
let calldata = await generateCalldata(input, 'hash_check');
return calldata;
//a: calldata[0], b: calldata[1], c: calldata[2], Input: calldata[3]
}
export async function otpVerificationGenerateCalldata(input: Object) {
if (localStorage.getItem('ZkWalletAddress')) {
console.log(localStorage.getItem('ZkWalletAddress'));
await connectContract(localStorage.getItem('ZkWalletAddress')!);
} else {
throw new Error("No zkWallet contract address found. Deploy first.");
}
let calldata = await generateCalldata(input, 'otp_verification');
return calldata;
//a: calldata[0], b: calldata[1], c: calldata[2], Input: calldata[3]
}

View File

@@ -1,6 +1,7 @@
import { ethers } from 'ethers'
import { address } from '../../backend/config'
import { generateCalldata } from './circuits/generate_calldata'
import { buildPoseidon } from 'circomlibjs'
import zkWalletFactoryJson from '../../backend/artifacts/contracts/ZkWalletFactory.sol/ZkWalletFactory.json'
@@ -10,6 +11,7 @@ import ZkWalletJson from '../../backend/artifacts/contracts/ZkSocialRecoveryWall
let zkWalletFactory: ethers.Contract
let ZkOtpValidator: ethers.Contract
let ZkWallet: ethers.Contract
export async function connectTOTPVerifier(
provider: ethers.providers.JsonRpcProvider,
@@ -86,8 +88,6 @@ export async function deployZkWallet(
const zkWalletFactoryContract = await zkWalletFactory.deployWallet(
0,
[],
[],
0,
otpVerifier,
root
@@ -96,6 +96,19 @@ export async function deployZkWallet(
return zkWalletFactoryContract.address
}
export function connectZkWallet(
provider: ethers.providers.JsonRpcProvider,
walletAddress: string
) {
let signer = provider.getSigner()
const iface = new ethers.utils.Interface(ZkWalletJson.abi)
ZkWallet = new ethers.Contract(walletAddress, iface, signer)
console.log('Connect to ZkWallet Contract:', ZkWallet)
return ZkWallet
}
// export async function zkProof(input: Object) {
// let calldata = await generateCalldata(input);
@@ -148,3 +161,29 @@ export async function zkTimestampProof(input: Object) {
}
return tx
}
export async function setSocialRecovery(
provider: ethers.providers.JsonRpcProvider,
accounts: string[]
) {
const signer = provider.getSigner()
const zkWalletContract = ZkWallet.connect(signer)
await zkWalletContract.setTrustees(accounts)
}
export async function initiateSocialRecovery(
provider: ethers.providers.JsonRpcProvider,
passwords: string[]
) {
const signer = provider.getSigner()
const zkWalletContract = ZkWallet.connect(signer)
let hashes: string[] = []
let poseidon = await buildPoseidon()
for (let password in passwords) {
const hash = poseidon.F.toObject(poseidon(password))
hashes.push(hash)
}
await zkWalletContract.setTrusteesPasswords(hashes)
}

View File

@@ -1,14 +1,19 @@
import { useEthers } from '@usedapp/core'
import { info } from 'console'
import { ethers } from 'ethers'
import { motion } from 'framer-motion'
import type { NextPage } from 'next'
import { useCallback, useEffect } from 'react'
import { useTheme } from 'next-themes'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useState } from 'react'
import { ThreeDots } from 'react-loader-spinner'
import {
BoxApprovedTxs,
BoxAuthSystem,
BoxSocialRecovery,
BoxPendingTransaction,
} from '../components'
import { connectFactory, connectZkWallet } from '../helpers/contracts'
const containerUpperBoxes = {
hidden: {},
@@ -64,7 +69,7 @@ const txBox = {
}
const Dashboard: NextPage = () => {
const { activate, library } = useEthers()
const { activate, library, account } = useEthers()
// Set up provider if already connected
const checkMetaMaskConnected = useCallback(async () => {
@@ -83,44 +88,99 @@ const Dashboard: NextPage = () => {
checkMetaMaskConnected()
}, [checkMetaMaskConnected])
// Load blockchain info
const router = useRouter()
const [loadingBlock, setLoadingBlock] = useState(true)
const [info, setInfo] = useState({
trustees: [],
numberTrustees: '0',
})
useEffect(() => {
const loadData = async () => {
if (library && account) {
//load factory
const zkWalletFactory = connectFactory(library)
const walletAddress = await zkWalletFactory.userAddressToWalletAddress(
account
)
if (walletAddress == ethers.constants.AddressZero) {
router.push('./login')
return
}
//load wallet
const zkWallet = connectZkWallet(library, walletAddress)
const numberTrustees = (await zkWallet.numberTrustees()).toString()
const trustees = [] //await zkWallet.Trustees(1)
setInfo({ ...info, numberTrustees: numberTrustees })
setLoadingBlock(false)
}
}
if (library && account) {
loadData()
}
}, [library, account])
// Manage theme hydration
const { theme } = useTheme()
const [loaded, setLoaded] = useState(false)
useEffect(() => {
if (theme) {
setLoaded(true)
}
})
if (!loaded) return null
return (
<div className="h-[calc(100vh-100px)] flex justify-center mt-10">
<div className="w-[90%] max-w-6xl flex flex-col">
<motion.div
variants={containerUpperBoxes}
initial="hidden"
animate="show"
className="w-full grid grid-cols-12 gap-4"
>
<motion.div variants={upperBox} className="col-span-4">
<BoxAuthSystem />
</motion.div>
<motion.div variants={upperBox} className="col-span-4">
<BoxSocialRecovery />
</motion.div>
<motion.div variants={upperBox} className="col-span-4">
<BoxApprovedTxs />
</motion.div>
</motion.div>
<motion.div
variants={containerTxBoxes}
initial="hidden"
animate="show"
className="w-full mt-10"
>
{' '}
{loadingBlock ? (
<div className="flex justify-center">
<ThreeDots
height="50"
width="70"
radius="9"
color={theme == 'dark' ? '#ffffff' : '#3b83f6'}
ariaLabel="three-dots-loading"
/>
</div>
) : (
<div className="w-[90%] max-w-6xl flex flex-col">
<motion.div
variants={txTitle}
className="text-2xl text-gray-800 dark:text-white mb-6"
variants={containerUpperBoxes}
initial="hidden"
animate="show"
className="w-full grid grid-cols-12 gap-4"
>
Pending transactions
</motion.div>
{[1, 2].map((key, tx) => (
<motion.div variants={txBox} key={key} className="w-full mb-6">
<BoxPendingTransaction />
<motion.div variants={upperBox} className="col-span-4">
<BoxAuthSystem />
</motion.div>
))}
</motion.div>
</div>
<motion.div variants={upperBox} className="col-span-4">
<BoxSocialRecovery numberTrustees={info['numberTrustees']} />
</motion.div>
<motion.div variants={upperBox} className="col-span-4">
<BoxApprovedTxs />
</motion.div>
</motion.div>
<motion.div
variants={containerTxBoxes}
initial="hidden"
animate="show"
className="w-full mt-10"
>
<motion.div
variants={txTitle}
className="text-2xl text-gray-800 dark:text-white mb-6"
>
Pending transactions
</motion.div>
{[1, 2].map((key, tx) => (
<motion.div variants={txBox} key={key} className="w-full mb-6">
<BoxPendingTransaction />
</motion.div>
))}
</motion.div>
</div>
)}
</div>
)
}