Skip to content

Commit a6c91b4

Browse files
authored
Merge pull request #1 from mlabs-haskell/connor/simple-password-tree
Simple password tree plugin
2 parents c027956 + e0dcc10 commit a6c91b4

File tree

5 files changed

+141
-12
lines changed

5 files changed

+141
-12
lines changed

src/pluginInteropServer/plugin.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { JsonProof } from "snarkyjs";
2+
3+
export type PluginType = {
4+
compile: () => Promise<string>;
5+
getInputs: () => Promise<string[]>;
6+
verify: (
7+
jsonProof: JsonProof,
8+
verificationKey: string,
9+
) => Promise<[string | boolean | undefined, string]>;
10+
prove: (inputs: string[]) => Promise<undefined | JsonProof>;
11+
};

src/pluginInteropServer/pluginServer.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import express, { Request, Response } from 'express';
22
import bodyParser from 'body-parser';
3-
import { PluginType, SimplePreimage } from './plugins/simplePreimage';
3+
import { SimplePreimage } from './plugins/simplePreimage';
44
import { JsonProof } from 'snarkyjs';
5+
import { PluginType } from './plugin';
6+
import SimplePasswordTree from './plugins/simplePasswordTree';
57

68
const app = express();
79
const PORT = 3001;
@@ -16,6 +18,10 @@ const EntryPoints: Record<string, EntryPoint> = {
1618
plugin: SimplePreimage,
1719
verification_key: null,
1820
},
21+
SimplePasswordTree: {
22+
plugin: SimplePasswordTree,
23+
verification_key: null,
24+
}
1925
};
2026

2127
app.use(bodyParser.json());
@@ -48,6 +54,7 @@ async function prepareProofFunction(
4854

4955
const plugin = entry.plugin;
5056
const jsonProof = await plugin.prove(data.arguments);
57+
if (!jsonProof) throw 'entrypoint/plugin ${plugin_name} unable to generate proof';
5158
return jsonProof;
5259
}
5360

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Experimental, Field, JsonProof, MerkleTree, Poseidon, verify } from "snarkyjs";
2+
import ProvePasswordInTreeProgram, { PASSWORD_TREE_HEIGHT, PasswordTreePublicInput, PasswordTreeWitness } from "../zkPrograms/passwordTreeProof";
3+
import { PluginType } from "../plugin";
4+
5+
const PasswordInTreeProofClass = Experimental.ZkProgram.Proof(ProvePasswordInTreeProgram);
6+
7+
abstract class TreeStorage {
8+
abstract getRoot(): Promise<Field>;
9+
abstract getWitness(uid: bigint): Promise<undefined | PasswordTreeWitness>;
10+
abstract getRole(uid: bigint): Promise<undefined | string>;
11+
}
12+
13+
class InMemoryStorage implements TreeStorage {
14+
roles: Map<bigint, string>;
15+
merkleTree: MerkleTree;
16+
17+
constructor(roleMappings: Array<[bigint, Field, string]> = []) {
18+
this.roles = new Map();
19+
this.merkleTree = new MerkleTree(PASSWORD_TREE_HEIGHT);
20+
21+
roleMappings.forEach(([uid, password, role]) => {
22+
this.roles.set(uid, role);
23+
this.merkleTree.setLeaf(uid, Poseidon.hash([password]));
24+
})
25+
}
26+
27+
async getRoot() { return this.merkleTree.getRoot(); }
28+
29+
async getWitness(uid: bigint) {
30+
if (!this.roles.has(uid)) return undefined;
31+
return new PasswordTreeWitness(this.merkleTree.getWitness(uid))
32+
}
33+
34+
async getRole(uid: bigint) { return this.roles.get(uid); }
35+
}
36+
37+
const storage = new InMemoryStorage([
38+
[BigInt(0), Field('7555220006856562833147743033256142154591945963958408607501861037584894828141'), 'admin'],
39+
[BigInt(1), Field('21565680844461314807147611702860246336805372493508489110556896454939225549736'), 'member']
40+
]);
41+
42+
const compile = async (): Promise<string> => {
43+
console.log('Compiling SimplePasswordTree program');
44+
console.log(ProvePasswordInTreeProgram);
45+
const { verificationKey } = await ProvePasswordInTreeProgram.compile();
46+
return verificationKey;
47+
}
48+
49+
const verifyAndGetRoleProgram = async (
50+
jsonProof: JsonProof,
51+
verificationKey: string,
52+
): Promise<[string | boolean | undefined, string]> => {
53+
if (!verify(jsonProof, verificationKey)) {
54+
return [false, 'proof invalid'];
55+
}
56+
const proof = PasswordInTreeProofClass.fromJSON(jsonProof);
57+
const role = await storage.getRole(proof.publicInput.witness.calculateIndex().toBigInt());
58+
if (!role) { return [undefined, 'unknown public input']; }
59+
return [role, 'role proved'];
60+
}
61+
62+
async function fetchPublicInput(uid: bigint): Promise<undefined | PasswordTreePublicInput> {
63+
const root = await storage.getRoot();
64+
const witness = await storage.getWitness(uid);
65+
if (!witness) return undefined;
66+
return new PasswordTreePublicInput({ root, witness });
67+
}
68+
69+
const prove = async (inputs: string[]): Promise<undefined | JsonProof> => {
70+
const [uidStr, secretInput] = inputs;
71+
const uid: bigint = BigInt(uidStr);
72+
const publicInput = await fetchPublicInput(uid);
73+
if (!publicInput) return undefined;
74+
const proof = await ProvePasswordInTreeProgram.baseCase(
75+
publicInput, Field(secretInput));
76+
return proof.toJSON();
77+
}
78+
79+
// FIXME: I have no idea what this should do
80+
const getInputs = async (): Promise<string[]> => {
81+
return Array.from(storage.roles.keys()).map(k => k.toString());
82+
};
83+
84+
export const SimplePasswordTree: PluginType = {
85+
compile,
86+
getInputs,
87+
verify: verifyAndGetRoleProgram,
88+
prove,
89+
}
90+
91+
export default SimplePasswordTree;

src/pluginInteropServer/plugins/simplePreimage.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
import { verify, Proof, Field, JsonProof, Experimental } from 'snarkyjs';
22
import { ProvePreimageProgram } from '../zkPrograms/hashPreimageProof';
3+
import { PluginType } from '../plugin';
34

45
const ProvePreimageProofClass = Experimental.ZkProgram.Proof(ProvePreimageProgram);
56

6-
export type PluginType = {
7-
compile: () => Promise<string>;
8-
getInputs: () => Promise<string[]>;
9-
verify: (
10-
jsonProof: JsonProof,
11-
verificationKey: string,
12-
) => Promise<[string | boolean | undefined, string]>;
13-
prove: (inputs: string[]) => Promise<JsonProof>;
14-
};
15-
167
const roleMapping: Record<string, string> = {
178
'7555220006856562833147743033256142154591945963958408607501861037584894828141':
189
'admin',
@@ -42,7 +33,7 @@ const verifyAndGetRoleProgram = async (
4233
return [role, 'role proved'];
4334
};
4435

45-
const prove = async (inputs: string[]): Promise<JsonProof> => {
36+
const prove = async (inputs: string[]): Promise<undefined | JsonProof> => {
4637
const [publicInput, secretInput] = inputs;
4738
console.log('simplePreimage proving for', publicInput, secretInput);
4839
const proof = await ProvePreimageProgram.baseCase(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Experimental, Field, MerkleWitness, Poseidon, Struct } from "snarkyjs";
2+
3+
export const PASSWORD_TREE_HEIGHT = 10;
4+
5+
export class PasswordTreeWitness extends MerkleWitness(PASSWORD_TREE_HEIGHT) { }
6+
7+
export class PasswordTreePublicInput extends Struct({
8+
witness: PasswordTreeWitness,
9+
root: Field
10+
}) { };
11+
12+
export const ProvePasswordInTreeProgram = Experimental.ZkProgram({
13+
publicInput: PasswordTreePublicInput,
14+
publicOutput: Field,
15+
16+
methods: {
17+
baseCase: {
18+
privateInputs: [Field],
19+
method(publicInput: PasswordTreePublicInput, privateInput: Field): Field {
20+
publicInput.witness
21+
.calculateRoot(Poseidon.hash([privateInput]))
22+
.assertEquals(publicInput.root);
23+
return publicInput.witness.calculateIndex();
24+
}
25+
}
26+
}
27+
});
28+
29+
export default ProvePasswordInTreeProgram;

0 commit comments

Comments
 (0)