Zero-knowledge Proof extension
The Zero-knowledge Proof (ZKP) extension for Chromia enables developers to integrate advanced privacy-preserving features directly into their decentralized applications. By supporting PLONK (Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge) zero-knowledge proofs, this extension allows applications to verify computations and validate data without disclosing sensitive information.
PLONK is a zero-knowledge proof system that allows one party to prove to another that they have knowledge of certain information without revealing the information itself. PLONK is known for its efficient verification process and universal trusted setup, making it particularly suitable for blockchain applications. It improves upon previous ZK-SNARK systems by requiring only one universal trusted setup, which can be utilized for all circuits of a given size.
Original paper: https://eprint.iacr.org/2019/953.pdf
Circuit and key prerequisites
Chromia does not provide tools for creating zero-knowledge circuits or generating verification keys. You must create these externally using tools like snarkjs before configuring your Chromia blockchain. The verification keys shown in the configuration below are the output of your external circuit development and trusted setup process.
Blockchain configuration
To be able to verify PLONK proofs, you need to add
ZKPGTXModule
to your blockchain configuration. In addition, you also need to add your verification key(s) to it. Since it's possible
to have more than one key, you need to give them an identifier in the configuration. See the example below:
blockchains:
<my_blockchain_name>:
module: <my_module_name>
config:
gtx:
modules:
- "net.postchain.zkp.ZKPGTXModule"
zkp:
plonk:
verification_keys:
<verification_key_id>:
Qc:
x: 0L
y: 0L
Ql:
x: 14834510898339141169329695685511700666258129880115403401229217490569515343302L
y: 9464224989088393801563875716232716915352580613417007162533827452727295446934L
Qm:
x: 14319141532948637259101778790582856386713203034743319527668731828185741400626L
y: 18803411281712541135482987792568462445904566200816418568722397188523035189143L
Qo:
x: 21364495085187694205438833340386123229650091635008748320283906080029143641039L
y: 20444368598332986012476714370015271579755130392073789088854120974413040917081L
Qr:
x: 16351212189779639003908022196613349497559341482069657547941244577951374625350L
y: 4031150659123603989265639197462571629326313607153532089054168665871713116509L
S1:
x: 18126157909848214547544885505634038550836716698610211113963616409746950617323L
y: 9480441132510474920656224855370744169816505579366946377741851539331522216404L
S2:
x: 15312771180064260077439958970242428939484644647049778052207315835174955686500L
y: 3394171162668419319900753190450314872629638145244728026210508800204487774112L
S3:
x: 15657500937529974074969620066780498543702080929706007838285907462589433927278L
y: 10353909101117571868937190939592663817839933444432483255231136649076311981690L
X_2:
x1: 19518502430870438181592443054401419581386850463392492809261261189226441340111L
x2: 8184812567585147824016587988320202893865389288562095514010129273046904740147L
y1: 10435902743221815611441838668065138241026433470816816910474433321658510434042L
y2: 3114164934987634673554850993955208553008076750558125585683111415888626769753L
curve: bn128
k1: 2L
k2: 3L
nPublic: 1
power: 11
As you can see, you also specify which curve that should be used in the key. Currently, the following values are allowed:
bn128
bls12381
These correspond to the supported curves: BN128
and BLS12-381
.
Rell library
There is a small Rell library
that
you can install to help verify proofs:
libs:
zkp:
registry: https://gitlab.com/chromaway/postchain-chromia.git
path: chromia-infrastructure/rell/src/lib/zkp
rid: x"8934250CED0D8C7FB46C458CDB303236AA70F3666DA67376E75E89D16F125FF9"
This library exposes the following functions that you can use to verify if a proof is present in the current transaction:
/**
* Checks whether or not the current transaction contains a valid PLONK proof.
*
* @param verification_key_id ID of the verification key that the proof must have been validated with
* @param public_signals The public signals that the proof must have been validated with
*/
function check_plonk_proof(
verification_key_id: text,
public_signals: list<big_integer>
)
/**
* Checks whether or not the current transaction contains a valid PLONK proof operation before current operation.
*
* @param verification_key_id ID of the verification key that the proof must have been validated with
* @return The public signals of the preceding proof operation
*/
function extract_signals_from_preceeding_proof_op(verification_key_id: text): list<big_integer>
To add a proof to a transaction, you need to call a gtx operation zkp_plonk_verify
with the following arguments:
[
GtvString, // verification key ID, needs to match verification key id defined in chromia.yml
PlonkProof, // Proof itself, see details below
GtvArray<GtvBigInteger> // Public signals
]
The PlonkProof
consists of a GtvArray
with the following structure:
[
GtvArray<GtvBigInteger>, // A [x, y]
GtvArray<GtvBigInteger>, // B [x, y],
GtvArray<GtvBigInteger>, // C [x, y],
GtvArray<GtvBigInteger>, // Z [x, y],
GtvArray<GtvBigInteger>, // T1 [x, y],
GtvArray<GtvBigInteger>, // T2 [x, y],
GtvArray<GtvBigInteger>, // T3 [x, y],
GtvArray<GtvBigInteger>, // Wxi [x, y],
GtvArray<GtvBigInteger>, // Wxiw [x, y],
GtvBigInteger, // eval a
GtvBigInteger, // eval b
GtvBigInteger, // eval c
GtvBigInteger, // eval s1
GtvBigInteger, // eval s2
GtvBigInteger, // eval zW
]
Example dapp operations
Converts public FT4 tokens to private tokens
This operation, known as "shielding," converts publicly visible FT4 tokens into private commitments on the blockchain.
The zero-knowledge proof ensures that the conversion is valid and that the correct amount is being shielded without
revealing the actual token amounts or the user's private information.
operation shield_tokens()
Converts private tokens back to public FT4 tokens
This operation, known as "unshielding," converts private token commitments back to publicly visible FT4 tokens, making
them available for standard blockchain operations without using zero-knowledge proof operations.
operation unshield_tokens()
Private transfer functionality using zero-knowledge proofs
This operation allows users to transfer private tokens while ensuring complete privacy regarding the transaction amounts. The zero-knowledge proof verifies that the sender has a sufficient amount of tokens without revealing the actual number of tokens.
Submitting a Proof from a Client
Here is an example of how to call the zkp_plonk_verify
operation from the
client side.
In this example, we use the shield_tokens
operation.
Note that the string used for the second argument in the zkp_plonk_verify
operation matches both the string used in
the call to
extract_signals_from_preceding_proof_op
within the shield_token
operation and the verification_key_id
defined in the
chromia.yml
.
let txBuilder = this.session.transactionBuilder();
txBuilder.add(op("zkp_plonk_verify", "shield_operation", proofResult.proof, proofResult.publicSignals), {
authenticator: authenticator1,
});
txBuilder.add(op("shield_tokens", Buffer.from(encryptedNote)), {
authenticator: authenticator2,
});
const result = await txBuilder.buildAndSend();