From c4f936ea210eb389ef1a31f04333e404e8f8cf85 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Thu, 31 Oct 2024 12:11:43 +0100 Subject: [PATCH 01/13] feat(providers): add validation for mina_storePrivateCredential --- packages/providers/src/validation.ts | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 9ab78dc..5222d7d 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -3,6 +3,7 @@ import { FieldSchema, NullifierSchema, PublicKeySchema, + SignatureSchema, SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, @@ -26,6 +27,50 @@ export const AddChainRequestParams = z }) .strict(); +// Private Credentials: Witness Schemas + +export const SimpleWitnessSchema = z.object({ + type: z.literal("simple"), + issuer: PublicKeySchema, + issuerSignature: SignatureSchema + }).strict(); + +export const RecursiveWitnessSchema = z.object({ + type: z.literal("recursive"), + vk: z.object({ + data: z.string(), + hash: FieldSchema + }).strict(), + proof: z.object({ + publicInput: JsonSchema, + publicOutput: JsonSchema, + maxProofsVerified: z.number(), + proof: z.string() + }).strict() + }).strict(); + +export const UnsignedWitnessSchema = z.object({ + type: z.literal("unsigned") + }).strict(); + +export const WitnessSchema = z.discriminatedUnion("type", [ + SimpleWitnessSchema, + RecursiveWitnessSchema, + UnsignedWitnessSchema + ]); + +// Private Credentials: Stored Credential Schema + +export const StoredCredentialSchema = z.object({ + version: z.string(), + witness: WitnessSchema, + metadata: JsonSchema, + credential: z.object({ + owner: PublicKeySchema, + data: JsonSchema + }).strict() + }).strict(); + // Params export const RequestWithContext = z .object({ @@ -83,6 +128,10 @@ 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 @@ -169,6 +218,11 @@ 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, @@ -185,6 +239,7 @@ export const RpcReturnTypesUnion = z.discriminatedUnion("method", [ AddChainRequestReturnSchema, SetStateRequestReturnSchema, GetStateRequestReturnSchema, + StorePrivateCredentialReturnSchema, ]); export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [ @@ -202,6 +257,7 @@ export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [ AddChainRequestParamsSchema, SetStateRequestParamsSchema, GetStateRequestParamsSchema, + StorePrivateCredentialRequestParamsSchema, ]); export type RpcReturnTypesUnionType = z.infer; export type ResultType = { From 345284ee9cee61d5999b835d80090b09f070973c Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Mon, 4 Nov 2024 09:07:14 +0100 Subject: [PATCH 02/13] feat(providers): move StoredCredentialSchema from mina-credentials repo --- packages/providers/src/validation.ts | 174 ++++++++++++++++++++------- 1 file changed, 131 insertions(+), 43 deletions(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 5222d7d..9cdc751 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -27,49 +27,135 @@ export const AddChainRequestParams = z }) .strict(); +// Private Credentials: Serialized Schemas + +const SerializedValueSchema = z + .object({ + _type: z.string(), + value: z.union([z.string(), z.record(z.any())]), + }) + .strict(); + +const SerializedTypeSchema = z + .object({ + _type: z.string(), + }) + .strict(); + +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 -export const SimpleWitnessSchema = z.object({ - type: z.literal("simple"), - issuer: PublicKeySchema, - issuerSignature: SignatureSchema - }).strict(); - -export const RecursiveWitnessSchema = z.object({ - type: z.literal("recursive"), - vk: z.object({ - data: z.string(), - hash: FieldSchema - }).strict(), - proof: z.object({ - publicInput: JsonSchema, - publicOutput: JsonSchema, - maxProofsVerified: z.number(), - proof: z.string() - }).strict() - }).strict(); - -export const UnsignedWitnessSchema = z.object({ - type: z.literal("unsigned") - }).strict(); - -export const WitnessSchema = z.discriminatedUnion("type", [ +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 - ]); + 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.string(), - witness: WitnessSchema, - metadata: JsonSchema, - credential: z.object({ - owner: PublicKeySchema, - data: JsonSchema - }).strict() - }).strict(); +export const StoredCredentialSchema = z + .object({ + version: z.literal("v0"), + witness: WitnessSchema, + metadata: JsonSchema.optional(), + credential: z.union([SimpleCredentialSchema, StructCredentialSchema]), + }) + .strict(); // Params export const RequestWithContext = z @@ -128,10 +214,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(); +export const StorePrivateCredentialRequestParamsSchema = + RequestWithContext.extend({ + method: z.literal("mina_storePrivateCredential"), + params: z.array(StoredCredentialSchema), + }).strict(); // Returns export const AccountsRequestReturnSchema = z @@ -218,9 +305,10 @@ export const GetStateRequestReturnSchema = z result: JsonSchema, }) .strict(); -export const StorePrivateCredentialReturnSchema = z.object({ - method: z.literal("mina_storePrivateCredential"), - result: z.object({ success: z.boolean() }).strict() +export const StorePrivateCredentialReturnSchema = z + .object({ + method: z.literal("mina_storePrivateCredential"), + result: z.object({ success: z.boolean() }).strict(), }) .strict(); From f0d01cd6e68428f31dc3178879451879de3bab36 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Mon, 4 Nov 2024 09:08:37 +0100 Subject: [PATCH 03/13] chore(providers): remove unused import --- packages/providers/src/validation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 9cdc751..59b6748 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -3,7 +3,6 @@ import { FieldSchema, NullifierSchema, PublicKeySchema, - SignatureSchema, SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, From 0bdd070757b07b2cf0e958e6997f460ccb827516 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Mon, 4 Nov 2024 09:52:13 +0100 Subject: [PATCH 04/13] chore(providers): SerializedValueSchema.value JsonSchema --- packages/providers/src/validation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 59b6748..48ee750 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -31,7 +31,7 @@ export const AddChainRequestParams = z const SerializedValueSchema = z .object({ _type: z.string(), - value: z.union([z.string(), z.record(z.any())]), + value: JsonSchema, }) .strict(); From e6a1b733ad429b6b30b90ec297482bd42cc9b9a8 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Mon, 4 Nov 2024 19:52:33 +0100 Subject: [PATCH 05/13] chore(providers): comment out unused schema --- packages/providers/src/validation.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 6450fe0..047a923 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -37,11 +37,11 @@ const SerializedValueSchema = z }) .strict(); -const SerializedTypeSchema = z - .object({ - _type: z.string(), - }) - .strict(); +// const SerializedTypeSchema = z +// .object({ +// _type: z.string(), +// }) +// .strict(); const SerializedFieldSchema = z .object({ From 4ad274191bb35d201c21eea650741c30d2d8ccba Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Tue, 5 Nov 2024 09:00:35 +0100 Subject: [PATCH 06/13] feat(providers): add PresentationRequestSchema --- packages/providers/src/validation.ts | 332 ++++++++++++++++++++++++++- 1 file changed, 326 insertions(+), 6 deletions(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 047a923..86279ce 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -28,20 +28,89 @@ export const AddChainRequestParams = z }) .strict(); -// Private Credentials: Serialized Schemas +// TODO: Should probably move these validations to a separate file + +interface ProofType { + name: string; + publicInput: SerializedType; + publicOutput: SerializedType; + maxProofsVerified: number; + featureFlags: Record; +} + +interface SerializedType { + _type?: string; + 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 SerializedTypeSchema = z -// .object({ -// _type: z.string(), -// }) -// .strict(); +const ProofTypeSchema: z.ZodType = z.lazy(() => + z + .object({ + name: z.string(), + publicInput: SerializedTypeSchema, + publicOutput: SerializedTypeSchema, + maxProofsVerified: z.number(), + featureFlags: z.record(z.any()), + }) + .strict(), +); + +const SerializedTypeSchema: z.ZodType = 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({ @@ -73,6 +142,257 @@ const SerializedSignatureSchema = z }) .strict(); +// Private Credentials: Node schemas + +type Node = + | { type: "owner" } + | { type: "issuer"; credentialKey: string } + | { type: "constant"; data: z.infer } + | { type: "root" } + | { type: "property"; key: string; inner: Node } + | { type: "record"; data: Record } + | { type: "equals"; left: Node; right: Node } + | { type: "equalsOneOf"; input: Node; options: Node[] | Node } + | { type: "lessThan"; left: Node; right: Node } + | { type: "lessThanEq"; left: Node; right: Node } + | { type: "add"; left: Node; right: Node } + | { type: "sub"; left: Node; right: Node } + | { type: "mul"; left: Node; right: Node } + | { type: "div"; left: Node; right: Node } + | { type: "and"; inputs: Node[] } + | { type: "or"; left: Node; right: Node } + | { type: "not"; inner: Node } + | { type: "hash"; inputs: Node[]; prefix?: string | null } + | { type: "ifThenElse"; condition: Node; thenNode: Node; elseNode: Node }; + +const NodeSchema: z.ZodType = z.lazy(() => + z.discriminatedUnion("type", [ + z + .object({ + type: z.literal("owner"), + }) + .strict(), + + z + .object({ + type: z.literal("issuer"), + credentialKey: z.string(), + }) + .strict(), + + z + .object({ + type: z.literal("constant"), + data: SerializedValueSchema, + }) + .strict(), + + z + .object({ + type: z.literal("root"), + }) + .strict(), + + z + .object({ + type: z.literal("property"), + key: z.string(), + inner: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("record"), + data: z.record(NodeSchema), + }) + .strict(), + + z + .object({ + type: z.literal("equals"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("equalsOneOf"), + input: NodeSchema, + options: z.union([ + z.array(NodeSchema), // For array of nodes case + NodeSchema, + ]), + }) + .strict(), + + z + .object({ + type: z.literal("lessThan"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("lessThanEq"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("add"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("sub"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("mul"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("div"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("and"), + inputs: z.array(NodeSchema), + }) + .strict(), + + z + .object({ + type: z.literal("or"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("not"), + inner: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("hash"), + inputs: z.array(NodeSchema), + prefix: z.union([z.string(), z.null()]).optional(), + }) + .strict(), + + z + .object({ + type: z.literal("ifThenElse"), + condition: NodeSchema, + thenNode: NodeSchema, + elseNode: NodeSchema, + }) + .strict(), + ]), +); + +// Private Credentials: Input Schema + +const InputSchema = z.discriminatedUnion("type", [ + z + .object({ + type: z.literal("credential"), + credentialType: z.union([ + z.literal("simple"), + z.literal("unsigned"), + z.literal("recursive"), + ]), + witness: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + }) + .strict(), + + z + .object({ + type: z.literal("constant"), + data: SerializedTypeSchema, + value: z.union([z.string(), z.record(z.string())]), + }) + .strict(), + + z + .object({ + type: z.literal("claim"), + data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + }) + .strict(), +]); + +// Private Credentials: Context schemas + +const HttpsContextSchema = z + .object({ + type: z.literal("https"), + action: z.string(), + serverNonce: SerializedFieldSchema, + }) + .strict(); + +const ZkAppContextSchema = z + .object({ + type: z.literal("zk-app"), + action: SerializedFieldSchema, + serverNonce: SerializedFieldSchema, + }) + .strict(); + +const ContextSchema = z.union([HttpsContextSchema, ZkAppContextSchema]); + +// Private Credentials: PresentationRequestSchema + +export const PresentationRequestSchema = z + .object({ + type: z.union([ + z.literal("no-context"), + z.literal("zk-app"), + z.literal("https"), + ]), + spec: z + .object({ + inputs: z.record(InputSchema), + logic: z + .object({ + assert: NodeSchema, + outputClaim: NodeSchema, + }) + .strict(), + }) + .strict(), + claims: z.record(SerializedValueSchema), + inputContext: z.union([ContextSchema, z.null()]), + }) + .strict(); + // Private Credentials: Witness Schemas const SimpleWitnessSchema = z From 2f7ea6c06ce17833e6c838f9be3cfb500d4aeab2 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Tue, 5 Nov 2024 10:18:11 +0100 Subject: [PATCH 07/13] chore(providers): comment --- packages/providers/src/validation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 86279ce..fe1ed7c 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -40,6 +40,7 @@ interface ProofType { interface SerializedType { _type?: string; + // TODO: update based on mina-credentials type?: "Constant"; value?: string; size?: number; From 727e0b5f046770719bc3649c7ad587a312e71c45 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Tue, 5 Nov 2024 12:13:31 +0100 Subject: [PATCH 08/13] chore(providers): export StoredCredential type --- packages/providers/src/validation.ts | 452 +-------------------------- packages/utils/src/types.ts | 6 + packages/utils/src/validation.ts | 451 ++++++++++++++++++++++++++ 3 files changed, 458 insertions(+), 451 deletions(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index fe1ed7c..f7ce318 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -7,6 +7,7 @@ import { SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, + StoredCredentialSchema, TransactionPayloadSchema, TransactionReceiptSchema, TypedSendableSchema, @@ -28,457 +29,6 @@ export const AddChainRequestParams = z }) .strict(); -// TODO: Should probably move these validations to a separate file - -interface ProofType { - name: string; - publicInput: SerializedType; - publicOutput: SerializedType; - maxProofsVerified: number; - featureFlags: Record; -} - -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 = z.lazy(() => - z - .object({ - name: z.string(), - publicInput: SerializedTypeSchema, - publicOutput: SerializedTypeSchema, - maxProofsVerified: z.number(), - featureFlags: z.record(z.any()), - }) - .strict(), -); - -const SerializedTypeSchema: z.ZodType = 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: Node schemas - -type Node = - | { type: "owner" } - | { type: "issuer"; credentialKey: string } - | { type: "constant"; data: z.infer } - | { type: "root" } - | { type: "property"; key: string; inner: Node } - | { type: "record"; data: Record } - | { type: "equals"; left: Node; right: Node } - | { type: "equalsOneOf"; input: Node; options: Node[] | Node } - | { type: "lessThan"; left: Node; right: Node } - | { type: "lessThanEq"; left: Node; right: Node } - | { type: "add"; left: Node; right: Node } - | { type: "sub"; left: Node; right: Node } - | { type: "mul"; left: Node; right: Node } - | { type: "div"; left: Node; right: Node } - | { type: "and"; inputs: Node[] } - | { type: "or"; left: Node; right: Node } - | { type: "not"; inner: Node } - | { type: "hash"; inputs: Node[]; prefix?: string | null } - | { type: "ifThenElse"; condition: Node; thenNode: Node; elseNode: Node }; - -const NodeSchema: z.ZodType = z.lazy(() => - z.discriminatedUnion("type", [ - z - .object({ - type: z.literal("owner"), - }) - .strict(), - - z - .object({ - type: z.literal("issuer"), - credentialKey: z.string(), - }) - .strict(), - - z - .object({ - type: z.literal("constant"), - data: SerializedValueSchema, - }) - .strict(), - - z - .object({ - type: z.literal("root"), - }) - .strict(), - - z - .object({ - type: z.literal("property"), - key: z.string(), - inner: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("record"), - data: z.record(NodeSchema), - }) - .strict(), - - z - .object({ - type: z.literal("equals"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("equalsOneOf"), - input: NodeSchema, - options: z.union([ - z.array(NodeSchema), // For array of nodes case - NodeSchema, - ]), - }) - .strict(), - - z - .object({ - type: z.literal("lessThan"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("lessThanEq"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("add"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("sub"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("mul"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("div"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("and"), - inputs: z.array(NodeSchema), - }) - .strict(), - - z - .object({ - type: z.literal("or"), - left: NodeSchema, - right: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("not"), - inner: NodeSchema, - }) - .strict(), - - z - .object({ - type: z.literal("hash"), - inputs: z.array(NodeSchema), - prefix: z.union([z.string(), z.null()]).optional(), - }) - .strict(), - - z - .object({ - type: z.literal("ifThenElse"), - condition: NodeSchema, - thenNode: NodeSchema, - elseNode: NodeSchema, - }) - .strict(), - ]), -); - -// Private Credentials: Input Schema - -const InputSchema = z.discriminatedUnion("type", [ - z - .object({ - type: z.literal("credential"), - credentialType: z.union([ - z.literal("simple"), - z.literal("unsigned"), - z.literal("recursive"), - ]), - witness: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), - data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), - }) - .strict(), - - z - .object({ - type: z.literal("constant"), - data: SerializedTypeSchema, - value: z.union([z.string(), z.record(z.string())]), - }) - .strict(), - - z - .object({ - type: z.literal("claim"), - data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), - }) - .strict(), -]); - -// Private Credentials: Context schemas - -const HttpsContextSchema = z - .object({ - type: z.literal("https"), - action: z.string(), - serverNonce: SerializedFieldSchema, - }) - .strict(); - -const ZkAppContextSchema = z - .object({ - type: z.literal("zk-app"), - action: SerializedFieldSchema, - serverNonce: SerializedFieldSchema, - }) - .strict(); - -const ContextSchema = z.union([HttpsContextSchema, ZkAppContextSchema]); - -// Private Credentials: PresentationRequestSchema - -export const PresentationRequestSchema = z - .object({ - type: z.union([ - z.literal("no-context"), - z.literal("zk-app"), - z.literal("https"), - ]), - spec: z - .object({ - inputs: z.record(InputSchema), - logic: z - .object({ - assert: NodeSchema, - outputClaim: NodeSchema, - }) - .strict(), - }) - .strict(), - claims: z.record(SerializedValueSchema), - inputContext: z.union([ContextSchema, z.null()]), - }) - .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(); - // Params export const RequestWithContext = z .object({ diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index b523f96..6ae1973 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -12,6 +12,7 @@ import type { SignedFieldsSchema, SignedMessageSchema, SignedTransactionSchema, + StoredCredentialSchema, TransactionBodySchema, TransactionPayloadSchema, TransactionReceiptSchema, @@ -48,3 +49,8 @@ export type TransactionReceipt = z.infer; export type KlesiaRpcMethodType = z.infer; export type KlesiaRpcRequestType = z.infer; export type KlesiaRpcResponseType = z.infer; + +/** + * Private Credential types + */ +export type StoredCredential = z.infer; diff --git a/packages/utils/src/validation.ts b/packages/utils/src/validation.ts index b173013..b2f08e6 100644 --- a/packages/utils/src/validation.ts +++ b/packages/utils/src/validation.ts @@ -240,3 +240,454 @@ 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; +} + +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 = z.lazy(() => + z + .object({ + name: z.string(), + publicInput: SerializedTypeSchema, + publicOutput: SerializedTypeSchema, + maxProofsVerified: z.number(), + featureFlags: z.record(z.any()), + }) + .strict(), +); + +const SerializedTypeSchema: z.ZodType = 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: Node schemas + +type Node = + | { type: "owner" } + | { type: "issuer"; credentialKey: string } + | { type: "constant"; data: z.infer } + | { type: "root" } + | { type: "property"; key: string; inner: Node } + | { type: "record"; data: Record } + | { type: "equals"; left: Node; right: Node } + | { type: "equalsOneOf"; input: Node; options: Node[] | Node } + | { type: "lessThan"; left: Node; right: Node } + | { type: "lessThanEq"; left: Node; right: Node } + | { type: "add"; left: Node; right: Node } + | { type: "sub"; left: Node; right: Node } + | { type: "mul"; left: Node; right: Node } + | { type: "div"; left: Node; right: Node } + | { type: "and"; inputs: Node[] } + | { type: "or"; left: Node; right: Node } + | { type: "not"; inner: Node } + | { type: "hash"; inputs: Node[]; prefix?: string | null } + | { type: "ifThenElse"; condition: Node; thenNode: Node; elseNode: Node }; + +const NodeSchema: z.ZodType = z.lazy(() => + z.discriminatedUnion("type", [ + z + .object({ + type: z.literal("owner"), + }) + .strict(), + + z + .object({ + type: z.literal("issuer"), + credentialKey: z.string(), + }) + .strict(), + + z + .object({ + type: z.literal("constant"), + data: SerializedValueSchema, + }) + .strict(), + + z + .object({ + type: z.literal("root"), + }) + .strict(), + + z + .object({ + type: z.literal("property"), + key: z.string(), + inner: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("record"), + data: z.record(NodeSchema), + }) + .strict(), + + z + .object({ + type: z.literal("equals"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("equalsOneOf"), + input: NodeSchema, + options: z.union([ + z.array(NodeSchema), // For array of nodes case + NodeSchema, + ]), + }) + .strict(), + + z + .object({ + type: z.literal("lessThan"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("lessThanEq"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("add"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("sub"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("mul"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("div"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("and"), + inputs: z.array(NodeSchema), + }) + .strict(), + + z + .object({ + type: z.literal("or"), + left: NodeSchema, + right: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("not"), + inner: NodeSchema, + }) + .strict(), + + z + .object({ + type: z.literal("hash"), + inputs: z.array(NodeSchema), + prefix: z.union([z.string(), z.null()]).optional(), + }) + .strict(), + + z + .object({ + type: z.literal("ifThenElse"), + condition: NodeSchema, + thenNode: NodeSchema, + elseNode: NodeSchema, + }) + .strict(), + ]), +); + +// Private Credentials: Input Schema + +const InputSchema = z.discriminatedUnion("type", [ + z + .object({ + type: z.literal("credential"), + credentialType: z.union([ + z.literal("simple"), + z.literal("unsigned"), + z.literal("recursive"), + ]), + witness: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + }) + .strict(), + + z + .object({ + type: z.literal("constant"), + data: SerializedTypeSchema, + value: z.union([z.string(), z.record(z.string())]), + }) + .strict(), + + z + .object({ + type: z.literal("claim"), + data: z.union([z.record(SerializedTypeSchema), SerializedTypeSchema]), + }) + .strict(), +]); + +// Private Credentials: Context schemas + +const HttpsContextSchema = z + .object({ + type: z.literal("https"), + action: z.string(), + serverNonce: SerializedFieldSchema, + }) + .strict(); + +const ZkAppContextSchema = z + .object({ + type: z.literal("zk-app"), + action: SerializedFieldSchema, + serverNonce: SerializedFieldSchema, + }) + .strict(); + +const ContextSchema = z.union([HttpsContextSchema, ZkAppContextSchema]); + +// Private Credentials: PresentationRequestSchema + +export const PresentationRequestSchema = z + .object({ + type: z.union([ + z.literal("no-context"), + z.literal("zk-app"), + z.literal("https"), + ]), + spec: z + .object({ + inputs: z.record(InputSchema), + logic: z + .object({ + assert: NodeSchema, + outputClaim: NodeSchema, + }) + .strict(), + }) + .strict(), + claims: z.record(SerializedValueSchema), + inputContext: z.union([ContextSchema, z.null()]), + }) + .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(); From c5144b1ef868c9629fe5e1e64cef02147c4fb474 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Wed, 6 Nov 2024 10:53:37 +0100 Subject: [PATCH 09/13] chore(providers): PresentationRequestParamsSchema --- packages/providers/src/validation.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index f7ce318..4042784 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -3,6 +3,7 @@ import { JsonSchema, NetworkId, NullifierSchema, + PresentationRequestSchema, PublicKeySchema, SignedFieldsSchema, SignedMessageSchema, @@ -91,6 +92,10 @@ export const StorePrivateCredentialRequestParamsSchema = method: z.literal("mina_storePrivateCredential"), params: z.array(StoredCredentialSchema), }).strict(); +export const PresentationRequestParamsSchema = RequestWithContext.extend({ + method: z.literal("mina_requestPresentation"), + params: z.array(PresentationRequestSchema), +}).strict(); // Returns export const AccountsRequestReturnSchema = z @@ -218,6 +223,7 @@ export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [ SetStateRequestParamsSchema, GetStateRequestParamsSchema, StorePrivateCredentialRequestParamsSchema, + PresentationRequestParamsSchema ]); export type RpcReturnTypesUnionType = z.infer; export type ResultType = { From 3223990c24609ecb5e539509d576c37a4fe8b482 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Wed, 6 Nov 2024 10:55:44 +0100 Subject: [PATCH 10/13] chore(providers): format --- packages/providers/src/validation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/providers/src/validation.ts b/packages/providers/src/validation.ts index 4042784..e51b0de 100644 --- a/packages/providers/src/validation.ts +++ b/packages/providers/src/validation.ts @@ -223,7 +223,7 @@ export const ProviderRequestParamsUnion = z.discriminatedUnion("method", [ SetStateRequestParamsSchema, GetStateRequestParamsSchema, StorePrivateCredentialRequestParamsSchema, - PresentationRequestParamsSchema + PresentationRequestParamsSchema, ]); export type RpcReturnTypesUnionType = z.infer; export type ResultType = { From 261950df5163b6092ed003f327f21833cdb74d91 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Wed, 6 Nov 2024 15:04:47 +0100 Subject: [PATCH 11/13] chore(providers): rename type to StoredPrivateCredential --- packages/utils/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 6ae1973..ff76188 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -53,4 +53,4 @@ export type KlesiaRpcResponseType = z.infer; /** * Private Credential types */ -export type StoredCredential = z.infer; +export type StoredPrivateCredential = z.infer; From 67ad8e89890666720b2579b1ab40624b1daf7664 Mon Sep 17 00:00:00 2001 From: Marton Moro Date: Thu, 7 Nov 2024 14:23:51 +0100 Subject: [PATCH 12/13] chore(docs): mina_storePrivateCredential test --- apps/docs/src/components/test-zkapp.tsx | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/apps/docs/src/components/test-zkapp.tsx b/apps/docs/src/components/test-zkapp.tsx index fb24a28..d8bedaf 100644 --- a/apps/docs/src/components/test-zkapp.tsx +++ b/apps/docs/src/components/test-zkapp.tsx @@ -5,6 +5,36 @@ 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", @@ -12,6 +42,9 @@ export const TestZkApp = () => { ); 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", @@ -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({ @@ -397,6 +450,35 @@ export const TestZkApp = () => { +
+
+

Store Private Credential

+

mina_storePrivateCredential

+
+
+