mirror of
https://github.com/AtHeartEngineer/spartan-ecdsa.git
synced 2026-01-09 21:38:02 -05:00
Update lib
- Remove eff_ecdsa prover and verifier - Add support for both addr_membership and pubkey_membership in MembershipProver - Expose an initWasm method from the MembershipProver class and the Poseidon class that takes in a SpartanWasm class instance - Export default configuration for both addr_membership and pubkey_membership
This commit is contained in:
73
packages/circuits/tests/addr_membership.test.ts
Normal file
73
packages/circuits/tests/addr_membership.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
const wasm_tester = require("circom_tester").wasm;
|
||||
var EC = require("elliptic").ec;
|
||||
import * as path from "path";
|
||||
const ec = new EC("secp256k1");
|
||||
import { Poseidon, Tree, SpartanWasm, defaultWasmConfig } from "spartan-ecdsa";
|
||||
import { getEffEcdsaCircuitInput } from "./test_utils";
|
||||
import { privateToAddress } from "@ethereumjs/util";
|
||||
|
||||
describe("membership", () => {
|
||||
it("should verify correct signature and merkle proof", async () => {
|
||||
// Compile the circuit
|
||||
const circuit = await wasm_tester(
|
||||
path.join(__dirname, "./circuits/addr_membership_test.circom"),
|
||||
{
|
||||
prime: "secq256k1" // Specify to use the option --prime secq256k1 when compiling with circom
|
||||
}
|
||||
);
|
||||
|
||||
const wasm = new SpartanWasm(defaultWasmConfig);
|
||||
|
||||
// Construct the tree
|
||||
const poseidon = new Poseidon();
|
||||
await poseidon.initWasm(wasm);
|
||||
|
||||
const nLevels = 10;
|
||||
const tree = new Tree(nLevels, poseidon);
|
||||
|
||||
const privKeys = [
|
||||
Buffer.from("".padStart(16, "🧙"), "utf16le"),
|
||||
Buffer.from("".padStart(16, "🪄"), "utf16le"),
|
||||
Buffer.from("".padStart(16, "🔮"), "utf16le")
|
||||
];
|
||||
|
||||
// Store addresses hashes
|
||||
const addresses: bigint[] = [];
|
||||
|
||||
// Compute public key hashes
|
||||
for (const privKey of privKeys) {
|
||||
const address = privateToAddress(privKey);
|
||||
addresses.push(BigInt("0x" + address.toString("hex")));
|
||||
}
|
||||
|
||||
// Insert the pubkey hashes into the tree
|
||||
for (const address of addresses) {
|
||||
tree.insert(address);
|
||||
}
|
||||
|
||||
// Sanity check (check that there are not duplicate members)
|
||||
expect(new Set(addresses).size === addresses.length).toBeTruthy();
|
||||
|
||||
// Sign
|
||||
const index = 0; // Use privKeys[0] for proving
|
||||
const privKey = privKeys[index];
|
||||
const msg = Buffer.from("hello world");
|
||||
|
||||
// Prepare signature proof input
|
||||
const effEcdsaInput = getEffEcdsaCircuitInput(privKey, msg);
|
||||
|
||||
const merkleProof = tree.createProof(index);
|
||||
|
||||
const input = {
|
||||
...effEcdsaInput,
|
||||
siblings: merkleProof.siblings,
|
||||
pathIndices: merkleProof.pathIndices,
|
||||
root: tree.root()
|
||||
};
|
||||
|
||||
// Generate witness
|
||||
const w = await circuit.calculateWitness(input, true);
|
||||
|
||||
await circuit.checkConstraints(w);
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,26 @@
|
||||
const wasm_tester = require("circom_tester").wasm;
|
||||
var EC = require("elliptic").ec;
|
||||
import * as path from "path";
|
||||
const ec = new EC("secp256k1");
|
||||
import { Poseidon, Tree } from "spartan-ecdsa";
|
||||
import { Poseidon, Tree, SpartanWasm, defaultWasmConfig } from "spartan-ecdsa";
|
||||
import { privateToPublic } from "@ethereumjs/util";
|
||||
import { getEffEcdsaCircuitInput } from "./test_utils";
|
||||
|
||||
describe("membership", () => {
|
||||
describe("pubkey_membership", () => {
|
||||
it("should verify correct signature and merkle proof", async () => {
|
||||
// Compile the circuit
|
||||
const circuit = await wasm_tester(
|
||||
path.join(__dirname, "./circuits/membership_test.circom"),
|
||||
path.join(__dirname, "./circuits/pubkey_membership_test.circom"),
|
||||
{
|
||||
prime: "secq256k1" // Specify to use the option --prime secq256k1 when compiling with circom
|
||||
}
|
||||
);
|
||||
|
||||
const wasm = new SpartanWasm(defaultWasmConfig);
|
||||
|
||||
// Construct the tree
|
||||
const poseidon = new Poseidon();
|
||||
await poseidon.init();
|
||||
await poseidon.initWasm(wasm);
|
||||
|
||||
const nLevels = 10;
|
||||
const tree = new Tree(nLevels, poseidon);
|
||||
|
||||
@@ -32,10 +35,8 @@ describe("membership", () => {
|
||||
|
||||
// Compute public key hashes
|
||||
for (const privKey of privKeys) {
|
||||
const pubKey = ec.keyFromPrivate(privKey).getPublic();
|
||||
const pubKeyX = BigInt(pubKey.x.toString());
|
||||
const pubKeyY = BigInt(pubKey.y.toString());
|
||||
const pubKeyHash = poseidon.hash([pubKeyX, pubKeyY]);
|
||||
const pubKey = privateToPublic(privKey);
|
||||
const pubKeyHash = poseidon.hashPubKey(pubKey);
|
||||
pubKeyHashes.push(pubKeyHash);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,45 @@
|
||||
import * as path from "path";
|
||||
const isWeb = typeof window !== "undefined";
|
||||
import { LeafType, ProverConfig, WasmConfig } from "./types";
|
||||
|
||||
export const DEFAULT_SPARTAN_WASM = isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/spartan_wasm_bg.wasm"
|
||||
: path.join(__dirname, "wasm/build/spartan_wasm_bg.wasm");
|
||||
export const defaultWasmConfig: WasmConfig = {
|
||||
pathOrUrl: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/spartan_wasm_bg.wasm"
|
||||
: path.join(__dirname, "wasm/build/spartan_wasm_bg.wasm")
|
||||
};
|
||||
|
||||
export const DEFAULT_EFF_ECDSA_WITNESS_GEN_WASM = isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/eff_ecdsa/eff_ecdsa.wasm"
|
||||
: path.join(__dirname, "circuits/eff_ecdsa.wasm");
|
||||
// Default configs for MembershipProver
|
||||
|
||||
export const DEFAULT_EFF_ECDSA_CIRCUIT = isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/eff_ecdsa/eff_ecdsa.circuit"
|
||||
: path.join(__dirname, "circuits/eff_ecdsa.circuit");
|
||||
// Default configs for pubkey membership proving
|
||||
export const defaultPubkeyMembershipConfig: ProverConfig = {
|
||||
spartanWasm: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/spartan_wasm_bg.wasm"
|
||||
: path.join(__dirname, "wasm/build/spartan_wasm_bg.wasm"),
|
||||
|
||||
export const DEFAULT_MEMBERSHIP_WITNESS_GEN_WASM = isWeb
|
||||
? "https://storage.googleapis.com/personae-proving-keys/membership/membeship.wasm"
|
||||
: path.join(__dirname, "circuits/membership.wasm");
|
||||
witnessGenWasm: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving-keys/membership/pubkey_membership.wasm"
|
||||
: path.join(__dirname, "circuits/pubkey_membership.wasm"),
|
||||
|
||||
export const DEFAULT_MEMBERSHIP_CIRCUIT = isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/membership/membership.circuit"
|
||||
: path.join(__dirname, "circuits/membership.circuit");
|
||||
circuit: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/membership/pubkey_membership.circuit"
|
||||
: path.join(__dirname, "circuits/pubkey_membership.circuit"),
|
||||
|
||||
leafType: LeafType.PubKeyHash
|
||||
};
|
||||
|
||||
// Default configs for address membership proving
|
||||
export const defaultAddressMembershipConfig: ProverConfig = {
|
||||
spartanWasm: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/spartan_wasm_bg.wasm"
|
||||
: path.join(__dirname, "wasm/build/spartan_wasm_bg.wasm"),
|
||||
|
||||
witnessGenWasm: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving-keys/membership/addr_membership.wasm"
|
||||
: path.join(__dirname, "circuits/addr_membership.wasm"),
|
||||
|
||||
circuit: isWeb
|
||||
? "https://storage.googleapis.com/personae-proving_keys/membership/addr_membership.circuit"
|
||||
: path.join(__dirname, "circuits/addr_membership.circuit"),
|
||||
|
||||
leafType: LeafType.Address
|
||||
};
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import {
|
||||
DEFAULT_EFF_ECDSA_CIRCUIT,
|
||||
DEFAULT_EFF_ECDSA_WITNESS_GEN_WASM
|
||||
} from "../config";
|
||||
import { loadCircuit, snarkJsWitnessGen, fromSig } from "../helpers/utils";
|
||||
import {
|
||||
EffEcdsaPubInput,
|
||||
EffEcdsaCircuitPubInput
|
||||
} from "../helpers/efficient_ecdsa";
|
||||
import { SpartanWasm } from "../wasm";
|
||||
import { hashPersonalMessage } from "@ethereumjs/util";
|
||||
|
||||
import { ProverOptions, IProver, NIZK } from "../types";
|
||||
import { Profiler } from "../helpers/profiler";
|
||||
|
||||
export class EffECDSAProver extends Profiler implements IProver {
|
||||
spartanWasm: SpartanWasm;
|
||||
circuit: string;
|
||||
witnessGenWasm: string;
|
||||
|
||||
constructor(options?: ProverOptions) {
|
||||
super({ enabled: options?.enableProfiler });
|
||||
|
||||
this.spartanWasm = new SpartanWasm({ spartanWasm: options?.spartanWasm });
|
||||
this.circuit = options?.circuit || DEFAULT_EFF_ECDSA_CIRCUIT;
|
||||
this.witnessGenWasm =
|
||||
options?.witnessGenWasm || DEFAULT_EFF_ECDSA_WITNESS_GEN_WASM;
|
||||
}
|
||||
|
||||
// sig: format of the `eth_sign` RPC method
|
||||
// https://ethereum.github.io/execution-apis/api-documentation
|
||||
async prove(sig: string, msg: Buffer): Promise<NIZK> {
|
||||
const { r, s, v } = fromSig(sig);
|
||||
|
||||
const msgHash = hashPersonalMessage(msg);
|
||||
const circuitPubInput = EffEcdsaCircuitPubInput.computeFromSig(
|
||||
r,
|
||||
v,
|
||||
msgHash
|
||||
);
|
||||
|
||||
const effEcdsaPubInput = new EffEcdsaPubInput(
|
||||
r,
|
||||
v,
|
||||
msgHash,
|
||||
circuitPubInput
|
||||
);
|
||||
|
||||
const witnessGenInput = {
|
||||
s,
|
||||
...circuitPubInput
|
||||
};
|
||||
|
||||
this.time("Generate witness");
|
||||
const witness = await snarkJsWitnessGen(
|
||||
witnessGenInput,
|
||||
this.witnessGenWasm
|
||||
);
|
||||
this.timeEnd("Generate witness");
|
||||
|
||||
this.time("Load circuit");
|
||||
const circuitBin = await loadCircuit(this.circuit);
|
||||
this.timeEnd("Load circuit");
|
||||
|
||||
await this.spartanWasm.init();
|
||||
this.time("Prove");
|
||||
let proof = await this.spartanWasm.prove(
|
||||
circuitBin,
|
||||
witness.data,
|
||||
effEcdsaPubInput.circuitPubInput.serialize()
|
||||
);
|
||||
this.timeEnd("Prove");
|
||||
|
||||
return { proof, publicInput: effEcdsaPubInput.serialize() };
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { DEFAULT_EFF_ECDSA_CIRCUIT } from "../config";
|
||||
import { loadCircuit } from "../helpers/utils";
|
||||
import { SpartanWasm } from "../wasm";
|
||||
import {
|
||||
EffEcdsaPubInput,
|
||||
verifyEffEcdsaPubInput
|
||||
} from "../helpers/efficient_ecdsa";
|
||||
import { Profiler } from "../helpers/profiler";
|
||||
import { VerifyOptions, IVerifier } from "../types";
|
||||
|
||||
export class EffECDSAVerifier extends Profiler implements IVerifier {
|
||||
spartanWasm: SpartanWasm;
|
||||
circuit: string;
|
||||
|
||||
constructor(options?: VerifyOptions) {
|
||||
super({ enabled: options?.enableProfiler });
|
||||
|
||||
this.circuit = options?.circuit || DEFAULT_EFF_ECDSA_CIRCUIT;
|
||||
this.spartanWasm = new SpartanWasm({ spartanWasm: options?.spartanWasm });
|
||||
}
|
||||
|
||||
async verify(
|
||||
proof: Uint8Array,
|
||||
publicInputSer: Uint8Array
|
||||
): Promise<boolean> {
|
||||
this.time("Load circuit");
|
||||
const circuitBin = await loadCircuit(this.circuit);
|
||||
this.timeEnd("Load circuit");
|
||||
|
||||
this.time("Verify public input");
|
||||
const publicInput = EffEcdsaPubInput.deserialize(publicInputSer);
|
||||
const isPubInputValid = verifyEffEcdsaPubInput(publicInput);
|
||||
this.timeEnd("Verify public input");
|
||||
|
||||
this.time("Verify NIZK");
|
||||
await this.spartanWasm.init();
|
||||
let nizkValid;
|
||||
try {
|
||||
nizkValid = await this.spartanWasm.verify(
|
||||
circuitBin,
|
||||
proof,
|
||||
publicInput.circuitPubInput.serialize()
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
this.timeEnd("Verify NIZK");
|
||||
|
||||
return isPubInputValid && nizkValid;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Profiler } from "../helpers/profiler";
|
||||
import { IProver, MerkleProof, NIZK, ProverOptions } from "../types";
|
||||
import { IProver, MerkleProof, NIZK, ProverConfig, LeafType } from "../types";
|
||||
import { SpartanWasm } from "../wasm";
|
||||
import {
|
||||
bigIntToBytes,
|
||||
@@ -11,27 +11,27 @@ import {
|
||||
EffEcdsaPubInput,
|
||||
EffEcdsaCircuitPubInput
|
||||
} from "../helpers/efficient_ecdsa";
|
||||
import {
|
||||
DEFAULT_MEMBERSHIP_CIRCUIT,
|
||||
DEFAULT_MEMBERSHIP_WITNESS_GEN_WASM
|
||||
} from "../config";
|
||||
|
||||
/**
|
||||
* ECDSA Membership Prover
|
||||
*/
|
||||
export class MembershipProver extends Profiler implements IProver {
|
||||
spartanWasm: SpartanWasm;
|
||||
spartanWasm!: SpartanWasm;
|
||||
circuit: string;
|
||||
witnessGenWasm: string;
|
||||
leafType: LeafType;
|
||||
|
||||
constructor(options?: ProverOptions) {
|
||||
constructor(options: ProverConfig) {
|
||||
super({ enabled: options?.enableProfiler });
|
||||
|
||||
const spartanWasm = new SpartanWasm({ spartanWasm: options?.spartanWasm });
|
||||
this.spartanWasm = spartanWasm;
|
||||
this.circuit = options?.circuit || DEFAULT_MEMBERSHIP_CIRCUIT;
|
||||
this.witnessGenWasm =
|
||||
options?.witnessGenWasm || DEFAULT_MEMBERSHIP_WITNESS_GEN_WASM;
|
||||
this.leafType = options.leafType;
|
||||
this.circuit = options.circuit;
|
||||
this.witnessGenWasm = options.witnessGenWasm;
|
||||
}
|
||||
|
||||
async initWasm(wasm: SpartanWasm) {
|
||||
this.spartanWasm = wasm;
|
||||
this.spartanWasm.init();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
@@ -81,7 +81,6 @@ export class MembershipProver extends Profiler implements IProver {
|
||||
const circuitBin = await loadCircuit(this.circuit);
|
||||
this.timeEnd("Load circuit");
|
||||
|
||||
await this.spartanWasm.init();
|
||||
this.time("Prove");
|
||||
let proof = await this.spartanWasm.prove(
|
||||
circuitBin,
|
||||
|
||||
@@ -2,16 +2,11 @@ import { SpartanWasm } from "../wasm";
|
||||
import { bigIntToLeBytes, bytesLeToBigInt } from "./utils";
|
||||
|
||||
export class Poseidon {
|
||||
wasm: SpartanWasm;
|
||||
constructor(wasm?: SpartanWasm) {
|
||||
if (typeof wasm === "undefined") {
|
||||
this.wasm = new SpartanWasm();
|
||||
} else {
|
||||
this.wasm = wasm;
|
||||
}
|
||||
}
|
||||
wasm!: SpartanWasm;
|
||||
constructor() {}
|
||||
|
||||
async init() {
|
||||
async initWasm(wasm: SpartanWasm) {
|
||||
this.wasm = wasm;
|
||||
await this.wasm.init();
|
||||
}
|
||||
|
||||
@@ -24,4 +19,12 @@ export class Poseidon {
|
||||
const result = this.wasm.poseidon(inputsBytes);
|
||||
return bytesLeToBigInt(result);
|
||||
}
|
||||
|
||||
hashPubKey(pubKey: Buffer): bigint {
|
||||
const pubKeyX = BigInt("0x" + pubKey.toString("hex").slice(0, 64));
|
||||
const pubKeyY = BigInt("0x" + pubKey.toString("hex").slice(64, 128));
|
||||
|
||||
const pubKeyHash = this.hash([pubKeyX, pubKeyY]);
|
||||
return pubKeyHash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,6 @@ export class Tree {
|
||||
this.treeInner = new IncrementalMerkleTree(hash, this.depth, BigInt(0));
|
||||
}
|
||||
|
||||
private hashPubKey(pubKey: Buffer): bigint {
|
||||
const pubKeyX = BigInt("0x" + pubKey.toString("hex").slice(0, 64));
|
||||
const pubKeyY = BigInt("0x" + pubKey.toString("hex").slice(64, 128));
|
||||
|
||||
const pubKeyHash = this.poseidon.hash([pubKeyX, pubKeyY]);
|
||||
return pubKeyHash;
|
||||
}
|
||||
|
||||
hashAndInsert(pubKey: Buffer) {
|
||||
this.insert(this.hashPubKey(pubKey));
|
||||
}
|
||||
|
||||
insert(leaf: bigint) {
|
||||
this.treeInner.insert(leaf);
|
||||
}
|
||||
@@ -36,8 +24,8 @@ export class Tree {
|
||||
return this.treeInner.root;
|
||||
}
|
||||
|
||||
indexOf(pubKey: Buffer): number {
|
||||
return this.treeInner.indexOf(this.hashPubKey(pubKey));
|
||||
indexOf(leaf: bigint): number {
|
||||
return this.treeInner.indexOf(leaf);
|
||||
}
|
||||
|
||||
createProof(index: number): MerkleProof {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export * from "./types";
|
||||
export * from "./helpers/efficient_ecdsa";
|
||||
export * from "./core/eff_ecdsa_prover";
|
||||
export * from "./core/membership_prover";
|
||||
export * from "./core/eff_ecdsa_verifier";
|
||||
export * from "./helpers/tree";
|
||||
export * from "./helpers/poseidon";
|
||||
export * from "./wasm/index";
|
||||
export * from "./config";
|
||||
|
||||
@@ -14,13 +14,17 @@ export interface NIZK {
|
||||
publicInput: Uint8Array;
|
||||
}
|
||||
|
||||
export interface ProverOptions {
|
||||
export interface ProverConfig {
|
||||
proverWasm?: string;
|
||||
|
||||
witnessGenWasm?: string;
|
||||
circuit?: string;
|
||||
spartanWasm?: string;
|
||||
witnessGenWasm: string;
|
||||
circuit: string;
|
||||
spartanWasm: string;
|
||||
enableProfiler?: boolean;
|
||||
leafType: LeafType;
|
||||
}
|
||||
|
||||
export interface WasmConfig {
|
||||
pathOrUrl: string;
|
||||
}
|
||||
|
||||
export interface VerifyOptions {
|
||||
@@ -45,5 +49,10 @@ export interface IVerifier {
|
||||
}
|
||||
|
||||
export interface SpartanWasmOptions {
|
||||
spartanWasm?: string;
|
||||
spartanWasm: string;
|
||||
}
|
||||
|
||||
export enum LeafType {
|
||||
PubKeyHash,
|
||||
Address
|
||||
}
|
||||
|
||||
@@ -2,20 +2,14 @@ import * as wasm from "./wasm";
|
||||
import _initWeb from "./wasm.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { SpartanWasmOptions } from "../types";
|
||||
import { DEFAULT_SPARTAN_WASM } from "../config";
|
||||
import { WasmConfig } from "../types";
|
||||
|
||||
// TODO: Rename this to just Wasm since it includes not only Spartan but also Poseidon
|
||||
export class SpartanWasm {
|
||||
private spartanWasmPathOrUrl: any;
|
||||
|
||||
constructor(options?: SpartanWasmOptions) {
|
||||
const defaultWasmPath =
|
||||
typeof window === "undefined"
|
||||
? path.join(__dirname, "./build/spartan_wasm_bg.wasm")
|
||||
: DEFAULT_SPARTAN_WASM;
|
||||
|
||||
this.spartanWasmPathOrUrl = options?.spartanWasm || defaultWasmPath;
|
||||
constructor(config: WasmConfig) {
|
||||
this.spartanWasmPathOrUrl = config.pathOrUrl;
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { EffECDSAProver, EffECDSAVerifier } from "../src/lib";
|
||||
import { hashPersonalMessage, ecsign } from "@ethereumjs/util";
|
||||
import * as path from "path";
|
||||
|
||||
describe("eff_ecdsa prove and verify", () => {
|
||||
// Sign message
|
||||
const privKey = Buffer.from(
|
||||
"f5b552f608f5b552f608f5b552f6082ff5b552f608f5b552f608f5b552f6082f",
|
||||
"hex"
|
||||
);
|
||||
let msg = Buffer.from("harry potter");
|
||||
const msgHash = hashPersonalMessage(msg);
|
||||
|
||||
const { v, r, s } = ecsign(msgHash, privKey);
|
||||
const sig = `0x${r.toString("hex")}${s.toString("hex")}${v.toString(16)}`;
|
||||
|
||||
// Init prover and verifier
|
||||
let prover = new EffECDSAProver({
|
||||
enableProfiler: false
|
||||
});
|
||||
let verifier = new EffECDSAVerifier({
|
||||
enableProfiler: false
|
||||
});
|
||||
|
||||
it("should prove and verify valid signature", async () => {
|
||||
const { proof, publicInput } = await prover.prove(sig, msg);
|
||||
|
||||
const result = await verifier.verify(proof, publicInput);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("verifier should return false when the proof is invalid", async () => {
|
||||
const { proof, publicInput } = await prover.prove(sig, msg);
|
||||
proof[0] = proof[0] + 1;
|
||||
|
||||
const result = await verifier.verify(proof, publicInput);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it("verifier should return false when the public input is invalid", async () => {
|
||||
const { proof, publicInput } = await prover.prove(sig, msg);
|
||||
publicInput[0] = publicInput[0] + 1;
|
||||
|
||||
const result = await verifier.verify(proof, publicInput);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,24 @@
|
||||
import * as path from "path";
|
||||
import { MembershipProver, Tree, Poseidon } from "../src/lib";
|
||||
import { hashPersonalMessage, ecsign, privateToPublic } from "@ethereumjs/util";
|
||||
import {
|
||||
MembershipProver,
|
||||
Tree,
|
||||
Poseidon,
|
||||
defaultAddressMembershipConfig,
|
||||
defaultPubkeyMembershipConfig,
|
||||
SpartanWasm,
|
||||
defaultWasmConfig
|
||||
} from "../src/lib";
|
||||
import {
|
||||
hashPersonalMessage,
|
||||
ecsign,
|
||||
privateToAddress,
|
||||
privateToPublic
|
||||
} from "@ethereumjs/util";
|
||||
var EC = require("elliptic").ec;
|
||||
const ec = new EC("secp256k1");
|
||||
|
||||
//! Still doesn't pass. Need to fix.
|
||||
describe("membership prove and verify", () => {
|
||||
// Init prover
|
||||
const treeDepth = 10;
|
||||
const treeDepth = 20;
|
||||
|
||||
const privKeys = ["1", "a", "bb", "ccc", "dddd", "ffff"].map(val =>
|
||||
Buffer.from(val.padStart(64, "0"), "hex")
|
||||
@@ -16,6 +27,7 @@ describe("membership prove and verify", () => {
|
||||
// Sign (Use privKeys[0] for proving)
|
||||
const proverIndex = 0;
|
||||
const proverPrivKey = privKeys[proverIndex];
|
||||
let proverAddress: bigint;
|
||||
|
||||
let msg = Buffer.from("harry potter");
|
||||
const msgHash = hashPersonalMessage(msg);
|
||||
@@ -24,34 +36,83 @@ describe("membership prove and verify", () => {
|
||||
const sig = `0x${r.toString("hex")}${s.toString("hex")}${v.toString(16)}`;
|
||||
|
||||
let poseidon: Poseidon;
|
||||
let tree: Tree;
|
||||
let prover: MembershipProver;
|
||||
let wasm: SpartanWasm;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Init Wasm
|
||||
wasm = new SpartanWasm(defaultWasmConfig);
|
||||
|
||||
// Init Poseidon
|
||||
poseidon = new Poseidon();
|
||||
await poseidon.init();
|
||||
|
||||
tree = new Tree(treeDepth, poseidon);
|
||||
|
||||
prover = new MembershipProver();
|
||||
|
||||
// Insert the members into the tree
|
||||
for (const privKey of privKeys) {
|
||||
const pubKey = privateToPublic(privKey);
|
||||
tree.hashAndInsert(pubKey);
|
||||
}
|
||||
await poseidon.initWasm(wasm);
|
||||
});
|
||||
|
||||
it("should prove and verify valid signature and merkle proof", async () => {
|
||||
const index = tree.indexOf(privateToPublic(proverPrivKey));
|
||||
const merkleProof = tree.createProof(proverIndex);
|
||||
describe("pubkey_membership prover and verify", () => {
|
||||
it("should prove and verify valid signature and merkle proof", async () => {
|
||||
const pubKeyTree = new Tree(treeDepth, poseidon);
|
||||
|
||||
const { proof, publicInput } = await prover.prove(
|
||||
sig,
|
||||
msgHash,
|
||||
merkleProof
|
||||
);
|
||||
let proverPubKeyHash;
|
||||
// Insert the members into the tree
|
||||
for (const privKey of privKeys) {
|
||||
const pubKey = privateToPublic(privKey);
|
||||
const pubKeyHash = poseidon.hashPubKey(pubKey);
|
||||
pubKeyTree.insert(pubKeyHash);
|
||||
|
||||
// TODO: Verify the proof
|
||||
// Set prover's public key hash for the reference below
|
||||
if (proverPrivKey === privKey) proverPubKeyHash = pubKeyHash;
|
||||
}
|
||||
|
||||
const pubKeyMembershipProver = new MembershipProver(
|
||||
defaultPubkeyMembershipConfig
|
||||
);
|
||||
|
||||
await pubKeyMembershipProver.initWasm(wasm);
|
||||
|
||||
const index = pubKeyTree.indexOf(proverPubKeyHash as bigint);
|
||||
const merkleProof = pubKeyTree.createProof(index);
|
||||
|
||||
const { proof, publicInput } = await pubKeyMembershipProver.prove(
|
||||
sig,
|
||||
msgHash,
|
||||
merkleProof
|
||||
);
|
||||
|
||||
// TODO: Verify the proof
|
||||
});
|
||||
});
|
||||
|
||||
describe("adddr_membership prover and verify", () => {
|
||||
it("should prove and verify valid signature and merkle proof", async () => {
|
||||
const addressTree = new Tree(treeDepth, poseidon);
|
||||
|
||||
let proverAddress;
|
||||
// Insert the members into the tree
|
||||
for (const privKey of privKeys) {
|
||||
const address = BigInt(
|
||||
"0x" + privateToAddress(privKey).toString("hex")
|
||||
);
|
||||
addressTree.insert(address);
|
||||
|
||||
// Set prover's public key hash for the reference below
|
||||
if (proverPrivKey === privKey) proverAddress = address;
|
||||
}
|
||||
|
||||
const addressMembershipProver = new MembershipProver(
|
||||
defaultAddressMembershipConfig
|
||||
);
|
||||
|
||||
await addressMembershipProver.initWasm(wasm);
|
||||
|
||||
const index = addressTree.indexOf(proverAddress as bigint);
|
||||
const merkleProof = addressTree.createProof(index);
|
||||
|
||||
const { proof, publicInput } = await addressMembershipProver.prove(
|
||||
sig,
|
||||
msgHash,
|
||||
merkleProof
|
||||
);
|
||||
|
||||
// TODO: Verify the proof
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user