Skip to content

Commit 230514e

Browse files
author
adamczykm
committed
Some renaming + changes to the password tree program
1 parent 33b3edb commit 230514e

File tree

5 files changed

+144
-102
lines changed

5 files changed

+144
-102
lines changed

src/library/plugin/pluginType.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import z from 'zod';
44

55
// Interfaces used on the server side.
66

7-
export interface IMinAuthPlugin<PublicInputsArgs, Output> {
7+
export interface IMinAuthPlugin<PublicInputArgs, Output> {
88
// Verify a proof give the arguments for fetching public inputs, and return
99
// the output.
1010
verifyAndGetOutput(
11-
publicInputArgs: PublicInputsArgs,
11+
publicInputArgs: PublicInputArgs,
1212
serializedProof: JsonProof): Promise<Output>;
1313

1414
// The schema of the arguments for fetching public inputs.
15-
readonly publicInputArgsSchema: z.ZodType<PublicInputsArgs>;
15+
readonly publicInputArgsSchema: z.ZodType<PublicInputArgs>;
1616

1717
// TODO: enable plugins to invalidate a proof.
1818
// FIXME(Connor): I still have some questions regarding the validation functionality.
1919
// In particular, what if a plugin want to invalidate the proof once the public inputs change?
20-
// We have to at least pass PublicInputsArgs.
20+
// We have to at least pass PublicInputArgs.
2121
//
2222
// checkOutputValidity(output: Output): Promise<boolean>;
2323

@@ -30,8 +30,8 @@ export interface IMinAuthPlugin<PublicInputsArgs, Output> {
3030

3131
// TODO: generic type inference?
3232
export interface IMinAuthPluginFactory<
33-
T extends IMinAuthPlugin<PublicInputsArgs, Output>,
34-
Configuration, PublicInputsArgs, Output> {
33+
T extends IMinAuthPlugin<PublicInputArgs, Output>,
34+
Configuration, PublicInputArgs, Output> {
3535

3636
// Initialize the plugin given the configuration. The underlying zk program is
3737
// typically compiled here.
@@ -42,20 +42,20 @@ export interface IMinAuthPluginFactory<
4242

4343
// Interfaces used on the client side.
4444

45-
export interface IMinAuthProver<PublicInputsArgs, PublicInput, PrivateInput> {
45+
export interface IMinAuthProver<PublicInputArgs, PublicInput, PrivateInput> {
4646
prove(publicInput: PublicInput, secretInput: PrivateInput): Promise<JsonProof>;
4747

48-
fetchPublicInputs(args: PublicInputsArgs): Promise<PublicInput>;
48+
fetchPublicInputs(args: PublicInputArgs): Promise<PublicInput>;
4949
}
5050

5151
export interface IMinAuthProverFactory<
5252
T extends IMinAuthProver<
53-
PublicInputsArgs,
53+
PublicInputArgs,
5454
PublicInput,
5555
PrivateInput>,
5656
Configuration,
57-
PublicInputsArgs,
57+
PublicInputArgs,
5858
PublicInput,
5959
PrivateInput> {
6060
initialize(cfg: Configuration): Promise<T>;
61-
}
61+
}

src/library/tools/pluginServer/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import env from 'env-var';
44
import fs from 'fs';
55
import yaml from 'yaml';
66
import { SimplePreimagePlugin } from "./plugins/simplePreimage/server";
7-
import { SimplePasswordTreePlugin } from "./plugins/passwordTree/server";
7+
import { MemberSetPlugin } from "./plugins/passwordTree/server";
88

99
// TODO: make use of heterogeneous lists
1010
/**
@@ -15,7 +15,7 @@ export const untypedPlugins:
1515
IMinAuthPluginFactory<IMinAuthPlugin<any, any>, any, any, any>>
1616
= {
1717
"SimplePreimagePlugin": SimplePreimagePlugin,
18-
"SimplePasswordTreePlugin": SimplePasswordTreePlugin
18+
"MemberSetPlugin": MemberSetPlugin
1919
};
2020

2121
const serverConfigurationsSchema = z.object({
Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,61 @@
1+
// TODO requires changes
12
import { Field, JsonProof } from "o1js";
2-
import ProvePasswordInTreeProgram, { PasswordTreePublicInput, PasswordTreeWitness } from "../common/passwordTreeProgram";
3+
import ProvePasswordInTreeProgram, { PasswordInTreeWitness } from "../common/passwordTreeProgram";
34
import { IMinAuthProver, IMinAuthProverFactory } from '../../../library/plugin/pluginType';
45

56
import axios from "axios";
67

7-
export type SimplePasswordTreeProverConfiguration = {
8-
apiServer: URL,
8+
export type MemberSetProverConfiguration = {
9+
apiServer: URL,
910
}
1011

11-
export class SimplePasswordTreeProver implements
12-
IMinAuthProver<bigint, PasswordTreePublicInput, Field>
12+
13+
// Prove that you belong to a set of user without revealing which user you are.
14+
export class MemberSetProver implements
15+
IMinAuthProver<bigint, PasswordInTreeWitness, Field>
1316
{
14-
private readonly cfg: SimplePasswordTreeProverConfiguration;
15-
16-
async prove(publicInput: PasswordTreePublicInput, secretInput: Field)
17-
: Promise<JsonProof> {
18-
const proof = await ProvePasswordInTreeProgram.baseCase(
19-
publicInput, Field.from(secretInput));
20-
return proof.toJSON();
21-
}
22-
23-
async fetchPublicInputs(uid: bigint): Promise<PasswordTreePublicInput> {
24-
const mkUrl = (endpoint: string) => `${this.cfg.apiServer}/${endpoint}`;
25-
const getWitness = async (): Promise<PasswordTreeWitness> => {
26-
const resp = await axios.get(mkUrl(`/witness/${uid.toString()}`));
27-
if (resp.status != 200) {
28-
throw `unable to fetch witness for ${uid.toString()}, error: ${(resp.data as { error: string }).error}`;
29-
}
30-
return PasswordTreeWitness.fromJSON(resp.data);
31-
};
32-
const getRoot = async (): Promise<Field> => {
33-
const resp = await axios.get(mkUrl('/root'));
34-
return Field.fromJSON(resp.data);
17+
private readonly cfg: MemberSetProverConfiguration;
18+
19+
async prove(publicInput: PasswordInTreeWitness, secretInput: Field)
20+
: Promise<JsonProof> {
21+
const proof = await ProvePasswordInTreeProgram.baseCase(
22+
publicInput, Field.from(secretInput));
23+
return proof.toJSON();
3524
}
36-
const witness = await getWitness();
37-
const root = await getRoot();
3825

39-
return new PasswordTreePublicInput({ witness, root });
40-
}
26+
async fetchPublicInputs(uid: bigint): Promise<PasswordInTreeWitness> {
27+
const mkUrl = (endpoint: string) => `${this.cfg.apiServer}/${endpoint}`;
28+
const getWitness = async (): Promise<PasswordInTreeWitness> => {
29+
const resp = await axios.get(mkUrl(`/witness/${uid.toString()}`));
30+
if (resp.status != 200) {
31+
throw `unable to fetch witness for ${uid.toString()}, error: ${(resp.data as { error: string }).error}`;
32+
}
33+
return PasswordInTreeWitness.fromJSON(resp.data);
34+
};
35+
const getRoot = async (): Promise<Field> => {
36+
const resp = await axios.get(mkUrl('/root'));
37+
return Field.fromJSON(resp.data);
38+
}
39+
const witness = await getWitness();
40+
const root = await getRoot();
41+
42+
return new PasswordInTreeWitness({ witness, root });
43+
}
4144

42-
constructor(cfg: SimplePasswordTreeProverConfiguration) {
43-
this.cfg = cfg;
44-
}
45+
constructor(cfg: MemberSetProverConfiguration) {
46+
this.cfg = cfg;
47+
}
4548

46-
static async initialize(cfg: SimplePasswordTreeProverConfiguration):
47-
Promise<SimplePasswordTreeProver> {
48-
return new SimplePasswordTreeProver(cfg);
49-
}
49+
static async initialize(cfg: MemberSetProverConfiguration):
50+
Promise<MemberSetProver> {
51+
return new MemberSetProver(cfg);
52+
}
5053
}
5154

52-
SimplePasswordTreeProver satisfies IMinAuthProverFactory<
53-
SimplePasswordTreeProver,
54-
SimplePasswordTreeProverConfiguration,
55-
bigint,
56-
PasswordTreePublicInput,
57-
Field
58-
>
55+
MemberSetProver satisfies IMinAuthProverFactory<
56+
MemberSetProver,
57+
MemberSetProverConfiguration,
58+
bigint,
59+
PasswordInTreeWitness,
60+
Field
61+
>
Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,57 @@
1-
import { Experimental, Field, MerkleWitness, Poseidon, Struct } from "o1js";
1+
import { Experimental, Field, MerkleWitness, Poseidon, SelfProof, Struct } from "o1js";
22

3+
// TODO how can this be made dynamic
34
export const PASSWORD_TREE_HEIGHT = 10;
45

5-
export class PasswordTreeWitness extends MerkleWitness(PASSWORD_TREE_HEIGHT) { }
6+
export class PasswordTreeWitness extends MerkleWitness(PASSWORD_TREE_HEIGHT) {}
67

7-
export class PasswordTreePublicInput extends Struct({
8-
witness: PasswordTreeWitness,
9-
root: Field
10-
}) { };
8+
export class PasswordInTreeWitness extends Struct({
9+
witness: PasswordTreeWitness,
10+
preImage: Field
11+
}) {};
1112

13+
export class MerkleRoot extends Struct({
14+
root: Field
15+
}) {};
16+
17+
18+
export class ProvePasswordInTreeOutput extends Struct({
19+
recursiveMekleRootHash: Field,
20+
}) {};
21+
22+
// Prove knowledge of a preimage of a hash in a merkle tree.
23+
// The proof does not reveal the preimage nor the hash.
24+
// The output contains a recursive hash of all the roots for which the preimage is known.
25+
// output = hash(lastRoot + hash(secondLastRoot, ... hash(xLastRoot, lastRoot) ...)
26+
// Therefore the order of the proofs matters.
1227
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-
}
28+
publicInput: MerkleRoot,
29+
publicOutput: ProvePasswordInTreeOutput,
30+
31+
methods: {
32+
baseCase: {
33+
privateInputs: [PasswordInTreeWitness],
34+
method(publicInput: MerkleRoot, privateInput: PasswordInTreeWitness): ProvePasswordInTreeOutput {
35+
privateInput.witness
36+
.calculateRoot(Poseidon.hash([publicInput.root]))
37+
.assertEquals(publicInput.root);
38+
return new ProvePasswordInTreeOutput(
39+
{ recursiveMekleRootHash: publicInput.root });
40+
}
41+
},
42+
43+
inductiveCase: {
44+
privateInputs: [SelfProof, PasswordInTreeWitness],
45+
method(publicInput: MerkleRoot, earlierProof: SelfProof<MerkleRoot, ProvePasswordInTreeOutput>, privateInput: PasswordInTreeWitness): ProvePasswordInTreeOutput {
46+
earlierProof.verify();
47+
privateInput.witness
48+
.calculateRoot(Poseidon.hash([publicInput.root]))
49+
.assertEquals(publicInput.root);
50+
return new ProvePasswordInTreeOutput(
51+
{ recursiveMekleRootHash: Poseidon.hash([publicInput.root, earlierProof.publicOutput.recursiveMekleRootHash]) });
52+
}
53+
}
2554
}
26-
}
2755
});
2856

29-
export default ProvePasswordInTreeProgram;
57+
export default ProvePasswordInTreeProgram;

src/plugins/passwordTree/server/index.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -184,30 +184,39 @@ class MinaBlockchainStorage
184184
}
185185
}
186186

187-
export class SimplePasswordTreePlugin implements IMinAuthPlugin<bigint, string>{
187+
const PoseidonHashSchema = z.bigint();
188+
189+
const publicInputArgsSchema = z.array(PoseidonHashSchema);
190+
191+
export class MemberSetPlugin implements IMinAuthPlugin<z.infer<typeof publicInputArgsSchema>, string>{
188192
readonly verificationKey: string;
189193
private readonly storage: TreeStorage
190194

191195
customRoutes: Record<string, RequestHandler> = {
192-
"/witness/:uid": async (req, resp) => {
193-
if (req.method != 'GET') {
194-
resp.status(400);
195-
return;
196-
}
197-
198-
const uid = BigInt(req.params['uid']);
199-
const witness = await this.storage.getWitness(uid);
200-
201-
if (!witness) {
202-
resp
203-
.status(400)
204-
.json({ error: "requested user doesn't exist" });
205-
return;
206-
}
207-
208-
resp.status(200).json(witness);
209-
},
210-
"/root": async (req, resp) => {
196+
// NOTE: witnesses are not public inputs now
197+
// "/witness/:uid": async (req, resp) => {
198+
// if (req.method != 'GET') {
199+
// resp.status(400);
200+
// return;
201+
// }
202+
203+
// const uid = BigInt(req.params['uid']);
204+
// const witness = await this.storage.getWitness(uid);
205+
206+
// if (!witness) {
207+
// resp
208+
// .status(400)
209+
// .json({ error: "requested user doesn't exist" });
210+
// return;
211+
// }
212+
213+
// resp.status(200).json(witness);
214+
// },
215+
216+
// TODO:
217+
// input: array of merkle roots (eg. [root1, root2, root3])
218+
// output: object of the form { root1: tree1, root2: tree2, root3: tree3 }
219+
"/roots": async (req, resp) => {
211220
if (req.method != 'GET') {
212221
resp.status(400);
213222
return;
@@ -228,10 +237,12 @@ export class SimplePasswordTreePlugin implements IMinAuthPlugin<bigint, string>{
228237
}
229238
};
230239

231-
publicInputArgsSchema: z.ZodType<bigint> = z.bigint();
240+
publicInputArgsSchema = publicInputArgsSchema;
232241

233-
async verifyAndGetOutput(uid: bigint, jsonProof: JsonProof):
242+
async verifyAndGetOutput(uid: z.infer<typeof publicInputArgsSchema>, jsonProof: JsonProof):
234243
Promise<string> {
244+
245+
// build an array of merkle trees
235246
const proof = PasswordInTreeProofClass.fromJSON(jsonProof);
236247
const expectedWitness = await this.storage.getWitness(uid);
237248
const expectedRoot = await this.storage.getRoot();
@@ -253,15 +264,15 @@ export class SimplePasswordTreePlugin implements IMinAuthPlugin<bigint, string>{
253264
storageFile: string,
254265
contractPrivateKey: string,
255266
feePayerPrivateKey: string
256-
}): Promise<SimplePasswordTreePlugin> {
267+
}): Promise<MemberSetPlugin> {
257268
const { verificationKey } = await ProvePasswordInTreeProgram.compile();
258269
const storage = await MinaBlockchainStorage
259270
.initialize(
260271
configuration.storageFile,
261272
PrivateKey.fromBase58(configuration.contractPrivateKey),
262273
PrivateKey.fromBase58(configuration.feePayerPrivateKey)
263274
)
264-
return new SimplePasswordTreePlugin(verificationKey, storage);
275+
return new MemberSetPlugin(verificationKey, storage);
265276
}
266277

267278
static readonly configurationSchema:
@@ -277,7 +288,7 @@ export class SimplePasswordTreePlugin implements IMinAuthPlugin<bigint, string>{
277288
})
278289
}
279290

280-
SimplePasswordTreePlugin satisfies
291+
MemberSetPlugin satisfies
281292
IMinAuthPluginFactory<
282293
IMinAuthPlugin<bigint, string>,
283294
{

0 commit comments

Comments
 (0)