Skip to content

Commit

Permalink
Merge pull request #1 from zksecurity/feature/private-credentials
Browse files Browse the repository at this point in the history
Validation for mina_storePrivateCredential
  • Loading branch information
mrcnk authored Nov 11, 2024
2 parents 758fc6f + 0b7f905 commit df00082
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 0 deletions.
82 changes: 82 additions & 0 deletions apps/docs/src/components/test-zkapp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,46 @@ import { useState, useSyncExternalStore } from "react";

const store = createStore();

const sampleCredential = {
version: "v0",
witness: {
type: "simple",
issuer: {
_type: "PublicKey",
value: "B62qqMxueXzenrchT5CKC5eCSmfcbHic9wJd9GEdHVcd9uCWrjPJjHS",
},
issuerSignature: {
_type: "Signature",
value: {
r: "27355434072539307953235904941558417174103383443074165997458891331674091021280",
s: "22156398191479529717864137276005168653180340733374387165875910835098679659803",
},
},
},
credential: {
owner: {
_type: "PublicKey",
value: "B62qqCMx9YvvjhMFVcRXBqHtAbjWWUhyA9HmgpYCehLHTGKgXsxiZpz",
},
data: {
age: {
_type: "Field",
value: "25",
},
},
},
};

export const TestZkApp = () => {
const [currentProvider, setCurrentProvider] = useLocalStorage(
"minajs:provider",
"",
);
const [message, setMessage] = useState("A message to sign");
const [fields, setFields] = useState('["1", "2", "3"]');
const [credentialInput, setCredentialInput] = useState(
JSON.stringify(sampleCredential, null, 2),
);
const [transactionBody, setTransactionBody] = useObjectState({
to: "B62qnVUL6A53E4ZaGd3qbTr6RCtEZYTu3kTijVrrquNpPo4d3MuJ3nb",
amount: "3000000000",
Expand All @@ -27,11 +60,31 @@ export const TestZkApp = () => {
mina_signFields: "",
mina_signTransaction: "",
mina_switchChain: "",
mina_storePrivateCredential: "",
});
const providers = useSyncExternalStore(store.subscribe, store.getProviders);
const provider = providers.find(
(p) => p.info.slug === currentProvider,
)?.provider;

const storePrivateCredential = async () => {
if (!provider) return;
try {
const parsedCredential = JSON.parse(credentialInput);
const { result } = await provider.request({
method: "mina_storePrivateCredential",
params: [parsedCredential],
});
setResults(() => ({
mina_storePrivateCredential: JSON.stringify(result, null, 2),
}));
} catch (error) {
setResults(() => ({
mina_storePrivateCredential: `Error: ${error.message}`,
}));
}
};

const fetchAccounts = async () => {
if (!provider) return;
const { result } = await provider.request({
Expand Down Expand Up @@ -397,6 +450,35 @@ export const TestZkApp = () => {
</div>
</div>
</section>
<section className="card bg-neutral">
<div className="card-body gap-4">
<h2 className="card-title">Store Private Credential</h2>
<p>mina_storePrivateCredential</p>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-4">
<textarea
value={credentialInput}
onChange={(event) => setCredentialInput(event.target.value)}
className="textarea textarea-bordered h-48 font-mono text-sm"
placeholder="Enter credential JSON..."
/>
<button
type="button"
className="btn btn-primary"
onClick={storePrivateCredential}
>
Store Private Credential
</button>
</div>
<label>Result</label>
<textarea
value={results.mina_storePrivateCredential}
readOnly
className="textarea textarea-bordered h-24 resize-none font-mono"
/>
</div>
</div>
</section>
</main>
);
};
14 changes: 14 additions & 0 deletions packages/providers/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SignedFieldsSchema,
SignedMessageSchema,
SignedTransactionSchema,
StoredCredentialSchema,
TransactionPayloadSchema,
TransactionReceiptSchema,
TypedSendableSchema,
Expand Down Expand Up @@ -85,6 +86,11 @@ export const GetStateRequestParamsSchema = RequestWithContext.extend({
method: z.literal("mina_getState"),
params: z.array(JsonSchema),
}).strict();
export const StorePrivateCredentialRequestParamsSchema =
RequestWithContext.extend({
method: z.literal("mina_storePrivateCredential"),
params: z.array(StoredCredentialSchema),
}).strict();

// Returns
export const AccountsRequestReturnSchema = z
Expand Down Expand Up @@ -171,6 +177,12 @@ export const GetStateRequestReturnSchema = z
result: JsonSchema,
})
.strict();
export const StorePrivateCredentialReturnSchema = z
.object({
method: z.literal("mina_storePrivateCredential"),
result: z.object({ success: z.boolean() }).strict(),
})
.strict();

export const RpcReturnTypesUnion = z.discriminatedUnion("method", [
AccountsRequestReturnSchema,
Expand All @@ -187,6 +199,7 @@ export const RpcReturnTypesUnion = z.discriminatedUnion("method", [
AddChainRequestReturnSchema,
SetStateRequestReturnSchema,
GetStateRequestReturnSchema,
StorePrivateCredentialReturnSchema,
]);

export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [
Expand All @@ -204,6 +217,7 @@ export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [
AddChainRequestParamsSchema,
SetStateRequestParamsSchema,
GetStateRequestParamsSchema,
StorePrivateCredentialRequestParamsSchema,
]);
export type RpcReturnTypesUnionType = z.infer<typeof RpcReturnTypesUnion>;
export type ResultType<M extends string> = {
Expand Down
6 changes: 6 additions & 0 deletions packages/utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
SignedFieldsSchema,
SignedMessageSchema,
SignedTransactionSchema,
StoredCredentialSchema,
TransactionBodySchema,
TransactionPayloadSchema,
TransactionReceiptSchema,
Expand Down Expand Up @@ -48,3 +49,8 @@ export type TransactionReceipt = z.infer<typeof TransactionReceiptSchema>;
export type KlesiaRpcMethodType = z.infer<typeof KlesiaRpcMethod>;
export type KlesiaRpcRequestType = z.infer<typeof KlesiaRpcMethodSchema>;
export type KlesiaRpcResponseType = z.infer<typeof KlesiaRpcResponseSchema>;

/**
* Private Credential types
*/
export type StoredPrivateCredential = z.infer<typeof StoredCredentialSchema>;
200 changes: 200 additions & 0 deletions packages/utils/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,203 @@ export const KlesiaRpcResponseSchema = z.union([
]),
ErrorSchema,
]);

// TODO: Should probably move these validations to a separate file

interface ProofType {
name: string;
publicInput: SerializedType;
publicOutput: SerializedType;
maxProofsVerified: number;
featureFlags: Record<string, unknown>;
}

interface SerializedType {
_type?: string;
// TODO: update based on mina-credentials
type?: "Constant";
value?: string;
size?: number;
proof?: ProofType;
innerType?: SerializedType;
[key: string]: SerializedType | string | number | ProofType | undefined;
}

// Private Credentials: Serialized Type and Value Schemas

const SerializedValueSchema = z
.object({
_type: z.string(),
value: JsonSchema,
properties: z.record(z.any()).optional(),
})
.strict();

const ProofTypeSchema: z.ZodType<ProofType> = z.lazy(() =>
z
.object({
name: z.string(),
publicInput: SerializedTypeSchema,
publicOutput: SerializedTypeSchema,
maxProofsVerified: z.number(),
featureFlags: z.record(z.any()),
})
.strict(),
);

const SerializedTypeSchema: z.ZodType<SerializedType> = z.lazy(() =>
z.union([
// Basic type
z
.object({
_type: z.string(),
})
.strict(),
// Constant type
z
.object({
type: z.literal("Constant"),
value: z.string(),
})
.strict(),
// Bytes type
z
.object({
_type: z.literal("Bytes"),
size: z.number(),
})
.strict(),
// Proof type
z
.object({
_type: z.literal("Proof"),
proof: ProofTypeSchema,
})
.strict(),
// Array type
z
.object({
_type: z.literal("Array"),
innerType: SerializedTypeSchema,
size: z.number(),
})
.strict(),
// Allow records of nested types for Struct
z.record(SerializedTypeSchema),
]),
);

const SerializedFieldSchema = z
.object({
_type: z.literal("Field"),
value: z.string(),
})
.strict();

const SerializedPublicKeySchema = z
.object({
_type: z.literal("PublicKey"),
value: z.string(),
})
.strict();

const SerializedPublicKeyTypeSchema = z
.object({
_type: z.literal("PublicKey"),
})
.strict();

const SerializedSignatureSchema = z
.object({
_type: z.literal("Signature"),
value: z.object({
r: z.string(),
s: z.string(),
}),
})
.strict();

// Private Credentials: Witness Schemas

const SimpleWitnessSchema = z
.object({
type: z.literal("simple"),
issuer: SerializedPublicKeySchema,
issuerSignature: SerializedSignatureSchema,
})
.strict();

const RecursiveWitnessSchema = z
.object({
type: z.literal("recursive"),
vk: z
.object({
data: z.string(),
hash: SerializedFieldSchema,
})
.strict(),
proof: z
.object({
_type: z.literal("Proof"),
value: z
.object({
publicInput: JsonSchema,
publicOutput: JsonSchema,
maxProofsVerified: z.number().min(0).max(2),
proof: z.string(),
})
.strict(),
})
.strict(),
})
.strict();

const UnsignedWitnessSchema = z
.object({
type: z.literal("unsigned"),
})
.strict();

const WitnessSchema = z.discriminatedUnion("type", [
SimpleWitnessSchema,
RecursiveWitnessSchema,
UnsignedWitnessSchema,
]);

// Private Credentials: Credential Schemas

const SimpleCredentialSchema = z
.object({
owner: SerializedPublicKeySchema,
data: z.record(SerializedValueSchema),
})
.strict();

const StructCredentialSchema = z
.object({
_type: z.literal("Struct"),
properties: z
.object({
owner: SerializedPublicKeyTypeSchema,
data: JsonSchema,
})
.strict(),
value: z
.object({
owner: PublicKeySchema,
data: JsonSchema,
})
.strict(),
})
.strict();

// Private Credentials: Stored Credential Schema

export const StoredCredentialSchema = z
.object({
version: z.literal("v0"),
witness: WitnessSchema,
metadata: JsonSchema.optional(),
credential: z.union([SimpleCredentialSchema, StructCredentialSchema]),
})
.strict();

0 comments on commit df00082

Please sign in to comment.