diff --git a/apps/docs/pages/_meta.json b/apps/docs/pages/_meta.json index d90392b..6a1cfa1 100644 --- a/apps/docs/pages/_meta.json +++ b/apps/docs/pages/_meta.json @@ -5,14 +5,16 @@ }, "index": "Herald", "introduction": "Introduction", + "ideas": "Key Ideas", "architecture": "Architecture", "why": "Why", + "future": "Future Features", "---": { "type": "separator", "title": "Build on Herald" }, "benchmarks": "Benchmarks", - "build": "Get started", + "build": "Get Started", "config": "Configure", "tools": "Tooling", "ecosystem": "Ecosystem", diff --git a/apps/docs/pages/architecture.mdx b/apps/docs/pages/architecture.mdx index 1dabb9d..e73e6eb 100644 --- a/apps/docs/pages/architecture.mdx +++ b/apps/docs/pages/architecture.mdx @@ -1,25 +1,116 @@ import { Callout } from 'nextra-theme-docs' -# 1) First title +# The Herald Stack -## 2) Second title +Herald is a thin wrapper around SnarkyJS. Herald currently has three layers: +- [**@herald-sdk/data-model**](#herald-sdkdata-model) - this package provides classes and utilities for managing claims, signed claims, and other data structures relevant to Zero-Knowledge Proofs and their associated cryptographic functions. +- [**@herald-sdk/provable-programs**](#herald-sdkprovable-programs) - this package provides a single `ZkProgram` that allows users to attest to their credentials per some `Rule` provided by a challenger. +- [**@herald-sdk/credentials**](#herald-sdkcredentials) - this package encapsulates both `data-model` and `provable-programs` packages, allowing issuers to create credentials, subjects to attest to claims about their credentials, and verify if a credential was issued by a specific issuer and about a specific subject. -## 3) Third title +## `@herald-sdk/data-model` -## 4) Fourth title +### Modules +#### `dataModel.ts` -## The Herald Stack +##### Class: `Claim` +A claim is a `MerkleMap` of key-value pairs where the key is a string and the value is a `ClaimType`. It provides methods to add, get, and retrieve the Merkle root and witness for a given key. -For now, let's focus on the high level overview and scrap the surface of each of these layers. + - `addField(key: string, value: ClaimType)`: Adds a field to the claim's `MerkleMap`. + - `getField(key: string): Field | undefined`: Gets the Field corresponding to the provided key. + - `getRoot()`: Field: Returns the Merkle root of the claim's `MerkleMap`. + - `getWitness(key: string): MerkleMapWitness`: Retrieves the MerkleMapWitness for a given key. -![stack](../static/img/stack.png) -### Creating credentials +##### Class: `SignedClaim` +A signed claim is a claim that has been signed by an issuer. It contains the root of the claim and the signature of the issuer. -### Proving statements about credentials +##### Class: `CredentialPresentation` +A structure that attests to being the owner of a credential, useful for Zero-Knowledge Proofs. + +##### Class: `Rule` +A rule that can be used to infer a claim from another claim. It can be provided by a challenger to a prover to verify a property on a claim. + +#### `types.ts` + +##### Type: `ClaimType` +Defines the possible data types for a claim. It can be a string, number, or a PublicKey. + +#### `utils/construction.ts` +Utility functions to construct various data structures. + - `constructClaim(claims: {[key: string]: ClaimType}): Claim`: Constructs a claim from a dictionary of claims. + - `constructSignedClaim(claim: Claim, issuerPrvKey: PrivateKey): SignedClaim`: Constructs a signed claim. + - `constructPresentation(signedClaim: SignedClaim, subjectPrvKey: PrivateKey): CredentialPresentation`: Constructs a credential presentation from a signed claim. + +#### `utils/conversion.ts` +Utility functions for converting between various data structures. + - `stringToField(str: string): Field`: Converts a string to a Field. + - `numberToField(num: number): Field`: Converts a number to a Field. + - `publicKeyHash(publicKey: PublicKey): Field`: Hashes a PublicKey to produce a Field. + - `claimToField(claim: ClaimType): Field`: Converts a claim to a Field. + - `fieldToString(field: Field[]): string`: Converts a Field to a string. + - `flattenObject(obj: {[key: string]: any}, prefix = ''): {[key: string]: ClaimType}`: Flattens an object into a dictionary of claims. + +## Usage +The `@herald-sdk/data-model` package allows users to create and manage claims and their associated cryptographic proofs. With utilities for constructing and converting these data structures, developers can effortlessly integrate Zero-Knowledge Proofs and cryptographic functions into their applications. + +## `@herald-sdk/provable-programs` + +### Modules + +#### `ChallengeProgram.ts` + +##### Class: `PublicInputArgs` +This class defines the public inputs for the Zero-Knowledge Program (ZkProgram). These inputs include the public keys of the issuer and subject, as well as a rule (`provingRule`) for attesting the credentials. + +##### `AttestCredentials` +This is a Zero-Knowledge Program (ZkProgram) that works to validate a claim on a credential. The current implementation works for one field on a claim. It has a single `attest` method and requires `PublicInputArgs`. + + - `attest`: This method validates the legitimacy of the signed claim, the credential presentation, and the correctness of the claim value based on a provided rule. It uses the public inputs to ensure that signatures are valid and the claim is correctly attested. - One of the best features Herald offers thanks to being built with SnarkyJS is immediate integration with existing TypeScript applications. You can read more about it [here](https://docs.minaprotocol.com/zkapps/snarkyjs). +Note: Future enhancements include expanding it to work for multiple fields on a claim using a rollup style methodology. +## `@herald-sdk/credentials` + +### Modules + +#### `credential.ts` + +##### Type: `proveReturnType` +The return type for the prove method in the `Credential` class. Contains: + - `attestationProof`: Proof that attests to certain claims. + - `verificationKey`: A string that represents the key used for verification. + +##### Class: `Credential` +This class represents a digital credential containing a `MerkleMap` of claims (claim), a signed version of that map (`signedClaim`), and a dictionary of claims (`credential`). + +###### Constructor: + - Parameters: + - `claim`: A MerkleMap representing claims. + - `signedClaim`: A signed version of the claim by the issuer. + - `credential`: A dictionary of claims. + + +###### Static Method: `create` + Description: An Issuer can construct a `Credential` object by taking in a dictionary of claims and a private key from the issuer. + - Parameters: + - `claims`: A dictionary representing multiple claims. + - `issuerPrvKey`: The private key of the issuer. + + +###### Method: `verify` + Description: Verifies the authenticity of the `Credential` object based on the given issuer and subject public keys. + - Parameters: + - `issuerPubKey`: The public key of the issuer. + - `subjectPubKey`: The public key of the subject (credential holder). + + +###### Method: `prove` + Description: A subject can produces a Zero-Knowledge Proof that attests to a certain claim within the credentials per a given `Rule` from a challenger. + - Parameters: + - `claimKey`: A string representing the key of the claim to attest. + - `issuerPubKey`: The public key of the issuer. + - `rule`: The rule used to create the attestation. + - `subjectPrvKey`: The private key of the subject (credential holder). \ No newline at end of file diff --git a/apps/docs/pages/benchmarks.mdx b/apps/docs/pages/benchmarks.mdx index 04abf96..3f00395 100644 --- a/apps/docs/pages/benchmarks.mdx +++ b/apps/docs/pages/benchmarks.mdx @@ -1,4 +1,5 @@ -import { Steps } from 'nextra-theme-docs' +import { Steps, Callout } from 'nextra-theme-docs' + import ProvingTimeAreaChart from '../components/ProvingTimeAreaChart' import VerificationTimeAreaChart from '../components/VerificationTimeAreaChart' import ProofSizeAreaChart from '../components/ProofSizeAreaChart' @@ -19,13 +20,14 @@ Here are the key hardware specifications of this machine: Efficient proof generation is crucial to the interaction design of applications, especially those that rely on frequent or time-sensitive zk-SNARK proofs. Faster proving times lead to more responsive applications and better user experience. However, it's important to note that proving time can be influenced by several factors, including computational resources, circuit sizes, and the complexity of the computation being proved. Importantly proving time efficiency requirements may vary per user, a sequencer may not need blazing fast proving time, but a wallet may require proving times to be fast given user experience requirements. -**Note: The current proving time includes program compiling time.** - The chart below visualizes the trend in proving times, allowing users to understand how updates or modifications to Herald have influenced proving efficiency over time.
+ + Note: The current proving time includes ZkProgram compiling time. Compiling time takes roughly 45 seconds. This can be improved with artifacts of the program cached for future usage.. + ## Verification Time Benchmarks diff --git a/apps/docs/pages/build.mdx b/apps/docs/pages/build.mdx index c5d44ef..9c8f52e 100644 --- a/apps/docs/pages/build.mdx +++ b/apps/docs/pages/build.mdx @@ -15,4 +15,131 @@ We first need to make sure you have everything needed to complete this tutorial. | TypeScript | latest | `npm install -g typescript` | | snarkyjs | 0.11.* (as a peer dependency) | `npm install snarkyjs@0.11.*` | - \ No newline at end of file +### Create a credential +Let's create a credential for a subject who is 21 years old. +```ts +import { Credential } from @herald-sdk/credentials +import { PrivateKey } from "snarkyjs"; +import { ClaimType } from "@herald-sdk/data-model"; + + +// generate issuer private keys +const issuerPrvKey = PrivateKey.random(); + +// construct a claim about the subject +const claims: {[key: string]: ClaimType} = { + age: 21, + subject: "B62qkAqbeE4h1M5hop288jtVYxK1MsHVMMcBpaWo8qdsAztgXaHH1xq" +}; + +// construct a credential using the claim and the issuer private key +const credential = Credential.create(claims, issuerPrvKey); +``` + +### Create a `Rule` +As a challenger, you want the subject to prove they meet some requirements like whether the subject is older than 18. +```ts +import { Rule } from "@herald-sdk/data-model"; + +// what property of the credentials is the challenger challenging +const property = "age"; +// what constraint does the subject need to prove; in this case that the "age" is greater than or equal to `value` +const operation = "gte"; +// what value does the property "age" need to be greater than or equal to +const value = 18; +// create the `Rule` +const rule = new Rule(property, operation, value); +``` + +### Prove your credentials can satisfy this `Rule`'s constraints +As a holder of a `Credential`, a subject can prove properties about their credentials given constraints. Note: the issuer public key needs to be assigned in the `Rule` (this is a TODO). +```ts +// given a rule and an expected issuer a holder of a `Credential` can `prove` properties of their credential +const proofResponse = await credential.prove("age", "B62qn2bkAtVmN6dptpYtU5i9gnq4SwDakFDo7Je7Fp8Tc8TtXnPxfVv", rule, subjectPrvKey); +``` + +### Verify the Proof +As a challenger, you can verify whether the subject has satisfied your rule's constraints +```ts +import { verify } from 'snarkyjs' + +// verify the proof with a verification key +verify(proofResponse.attestationProof.toJSON(), proofResponse.verificationKey) +``` + + +### W3C Credential Example +As the W3C verifiable credential data model defines, these JSON objects must be flattened to use in a `MerkleMap`. We can use the `flattenObject` function in `@herald-sdk/data-model` to do this! This is what happens: + +![stack](../static/img/credential_conversion.png) + +A Herald is just an abstraction over a SnarkyJS `MerkleMap`! + + +### Create credential for subject +Let's create a W3C example credential for a subject who has a Bachelor's Degree. +```ts +import { Credential } from @herald-sdk/credentials +import { PrivateKey } from "snarkyjs"; +import { ClaimType, flattenObject, constructClaim } from "@herald-sdk/data-model"; + + +// generate issuer private keys +const issuerPrvKey = PrivateKey.random(); +// Define the credential +const credential = { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/565049", + "issuanceDate": new Date().toISOString(), + "credentialSubject": { + "id": "did:mina:B62qkAqbeE4h1M5hop288jtVYxK1MsHVMMcBpaWo8qdsAztgXaHH1xq", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + } +} + +// flatten the credential +const flattenedCredential = flattenObject(credential); + +// construct a claim about the subject +const claims = constructClaim(flattenedCredential) + +// construct a credential using the claim and the issuer private key +const credential = Credential.create(claims, issuerPrvKey); +``` + +### Construct rule +As a challenger you want to know if the credential a subject has is a Bachelor's Degree. We need a new `Rule` for this! Remember the credential's property is now a flattened object so the rule we create must check if the "credentialSubject.degree.type" property equals "BachelorDegree". + +```ts +import { Rule } from "@herald-sdk/data-model"; + +const property = "credentialSubject.degree.type"; +const operation = "eq"; +const value = "BachelorDegree"; + +const rule = new Rule(property, operation, value); +``` + +### Prove that the Credential can satisfy this Rule. +As the subject and credential owner, we can now make a proof using this rule. +```ts +const proofResponse = await credential.prove("credentialSubject.degree.type", "B62qn2bkAtVmN6dptpYtU5i9gnq4SwDakFDo7Je7Fp8Tc8TtXnPxfVv", rule, subjectPrvKey); +``` + +### Verify the Proof +As a challenger, you can verify whether the subject has satisfied your rule's constraints and the user does have a Bachelor's Degree, without revealing other information like the type of degree was a Bachelor of Science and Arts. +```ts +import { verify } from 'snarkyjs' + +// verify the proof with a verification key +verify(proofResponse.attestationProof.toJSON(), proofResponse.verificationKey) +``` + diff --git a/apps/docs/pages/future.mdx b/apps/docs/pages/future.mdx new file mode 100644 index 0000000..94ae6d6 --- /dev/null +++ b/apps/docs/pages/future.mdx @@ -0,0 +1,7 @@ +import { Callout } from 'nextra-theme-docs' + +# Future Features + +## Non-Reusable Proofs + +## Proving multiple `Rule`s in one proof \ No newline at end of file diff --git a/apps/docs/pages/ideas.mdx b/apps/docs/pages/ideas.mdx new file mode 100644 index 0000000..79d73fe --- /dev/null +++ b/apps/docs/pages/ideas.mdx @@ -0,0 +1,51 @@ +import { Steps, Callout } from 'nextra-theme-docs' + +# Key Ideas + +## Digital Identity in the Age of Herald +Identity is a cornerstone of how we interact, both in the physical and digital realms. + +## What Do We Mean by Digital Identity? + +Digital identity transcends just humans. It encompasses organizations, governments, entities like DAOs, Gaming legions, and even objects or bots. In Herald's context, these identities manifest as Mina's public keys (i.e. addresses), and can be either externally owned accounts or smart contracts. + +### Key Concepts: + +- Anything and anyone can have a digital identity. +- An individual or entity can establish and maintain multiple identities. +- Within platforms like Mina, a digital identity can manifest as a public key. +- Any identity can issue claims about another identity. +- An owner of a claim can prove statements about claims issued to them. + +## Digital Claims with Herald + +Just as in the traditional world where an individual or entity can make statements or claims, the same holds in the digital realm. A digital claim is a declaration or assertion made by an identity. + +Often, these claims establish relationships between identities. For instance, when an online educational platform vouches that a user has completed a course, it's a digital claim linking the user and the platform. + +Claims have a broad spectrum. They can be public, private, and can represent nearly any interaction or declaration. Whether it's a digital thumbs-up, an invoice, or an email, in the Herald universe, these are all claims. + +### Examples of `Claims`: + +- Certifications or badges earned online. +- Defeating a monster in a game. +- Leveling up a character. +- Social media interactions. +- Digital invoices. +- Email correspondences. +- Digital roles or permissions within a platform. +- Online endorsements or reviews. + +## Zero-Knowledge Proofs & Herald + +Modern cryptography has gifted us the concept of zero-knowledge proofs (ZKPs). It's a mechanism allowing one entity to prove to another that they possess certain knowledge, without revealing the specifics of that knowledge. + +This matters because in the realm of digital claims, there are moments when we want to validate something discreetly, maintaining privacy while ensuring authenticity. The future of Web3 requires privacy as a feature not as a nice-to-have. + +### Real-world Applications: + +- Digital KYC Version: Imagine proving you meet the jurisdictional requirements for interacting with a zkApp without exposing any other details about yourself; proving only that you meet the requirements the zkApp requires. With ZKPs, you can validate that you possess the digital key tied to an identity that has been KYC'd, without disclosing further information. + +- Anonymous Digital Voting: Just as in the offline world, there are times when you'd want to validate your eligibility to vote without unveiling your identity. ZKPs make this a reality. + +By fusing time-honored concepts of identity with state-of-the-art cryptographic techniques, Herald stands at the forefront of the new digital era. Our library seeks to make these concepts accessible, ensuring that developers and users can navigate this new landscape with confidence and clarity. \ No newline at end of file diff --git a/apps/docs/public/benchmarks/credential-proving.json b/apps/docs/public/benchmarks/credential-proving.json index 9f664df..4812b90 100644 --- a/apps/docs/public/benchmarks/credential-proving.json +++ b/apps/docs/public/benchmarks/credential-proving.json @@ -1 +1 @@ -[{"name":"2023-8-6","duration":263.847},{"name":"2023-8-8","duration":73.13}] \ No newline at end of file +[{"name":"2023-8-8","duration":73.13}] \ No newline at end of file diff --git a/apps/docs/public/benchmarks/proof-verifying.json b/apps/docs/public/benchmarks/proof-verifying.json index d86f4c5..69dfc50 100644 --- a/apps/docs/public/benchmarks/proof-verifying.json +++ b/apps/docs/public/benchmarks/proof-verifying.json @@ -1 +1 @@ -[{"name":"2023-8-6","duration":6.24},{"name":"2023-8-8","duration":1.552}] \ No newline at end of file +[{"name":"2023-8-8","duration":1.552}] \ No newline at end of file diff --git a/apps/docs/static/img/credential_conversion.png b/apps/docs/static/img/credential_conversion.png new file mode 100644 index 0000000..4be5eee Binary files /dev/null and b/apps/docs/static/img/credential_conversion.png differ diff --git a/apps/docs/theme.config.tsx b/apps/docs/theme.config.tsx index 4c8b3f3..073eadc 100644 --- a/apps/docs/theme.config.tsx +++ b/apps/docs/theme.config.tsx @@ -2,6 +2,8 @@ import React from 'react' import { DocsThemeConfig } from 'nextra-theme-docs' import { useRouter } from 'next/router'; + + const config: DocsThemeConfig = { logo: Herald, project: { diff --git a/packages/provable-programs/src/ChallengeProgram.ts b/packages/provable-programs/src/ChallengeProgram.ts index bd0dda6..63899bc 100644 --- a/packages/provable-programs/src/ChallengeProgram.ts +++ b/packages/provable-programs/src/ChallengeProgram.ts @@ -1,18 +1,6 @@ import { Field, Experimental, PublicKey, Bool, Struct, MerkleMapWitness } from 'snarkyjs'; import { CredentialPresentation, Rule, SignedClaim, stringToField } from '@herald-sdk/data-model'; -// WIP 🚧 -/*class PrivateInputArgs extends Struct({ - claim: MerkleMapWitness, - claimValue: Field, - signedClaim: SignedClaim, - credentialPresentation: CredentialPresentation -}) { - constructor(claim: MerkleMapWitness, claimValue: Field, signedClaim: SignedClaim, credentialPresentation: CredentialPresentation) { - super({claim, claimValue, signedClaim, credentialPresentation}); - } -}*/ - export class PublicInputArgs extends Struct({ issuerPubKey: PublicKey, subjectPubKey: PublicKey,