Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KIP-0021: keccak256 and egcd support #45

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions kip-0021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
KIP: 21
Title: Kadena Primitives for PLONK Zero Knowledge Proofs
Author: Davi Bauer @davibauer, João Veiga @veigajoao
Status: Draft
Type: Standard
Category: Pact
Created: 2023-06-19
---

- [Abstract](#abstract)
- [Motivation](#motivation)
- [Modular multiplicative inverses](#modular-multiplicative-inverses)
- [Keccak hashing function](#keccak-hashing-function)
- [Backwards Compatibility](#backwards-compatibility)
- [References](#references)

# Abstract

This KIP proposes that the Pact language implements builtin functions that allow the implementation of the Plonk Zero Knowledge algorithm as described in [iden3's implementation](https://blog.iden3.io/circom-snarkjs-plonk.html).

It is a part of the Opact project, which is a Kadena grantee implementing compatibility to snarkjs/circom stack and developing privacy applications on Kadena.

# Motivation

This KIP proposes that the Pact language implements builtin functions that allow the implementation of the Plonk Zero Knowledge algorithm as described in [iden3's implementation](https://blog.iden3.io/circom-snarkjs-plonk.html).

Zero Knowledge cryptography requires a series of primitives which, sometimes, cannot be implemented directly in the blockchain's application layer, either due to computational limitations or due to the lack of a certain operation in the application language (Pact).

Even though Pact already has the `Zk` module of builtins, there are 2 operations missing to allow the implementation of Plonk: (1) performing multiplicative inversion of modular integers and (2) performing Keccak hashes.
The former cannot be built directly into Pact as it requires unbounded recursion to work, the latter is computationally intensive and better to be implemented as a low-level builtin for gas optimization factors.


The addition of these builtins is beneficial to Kadena for the following reasons:
- The Kadena ecosystem currently only has the primitives to support Groth16 proof systems.
- Groth16 proof systems require trusted ceremonies to generate the proving and verifying keys. Which require a high level of trust and must be redone for every new circuit update.
- Plonk allows developers to utilize public well trusted ceremonies from other projects to bootstrap trust in their own circuits.
- These primitives actually allow a lot of different low level mathematical operations to be performed on Kadena such as replicating EVM's Keccak hashes, finding modular inverses and many more, which allow developers more flexilibity in building high class applications.

## Modular multiplicative inverses

The use of modular arithmetic is ubiquitous in cryptography. Pact, however, does not provide native support for some of the operations necessary within this branch of mathematics. This is specially true for finding multiplicative inverses of modular integers, which cannot even be implemented in Pact code, as it requires unbounded recursion.

The inversion operation is necessary to perform lagrange interpolation (one of the mathematical operations that constitutes PLONK).

We can however implement a builtin function that does this for us. Considering that builtins should be as reusable as possible we propose the implementation of the [Extended Euclidean Algorithm](https://math.stackexchange.com/questions/747342/extended-euclidean-algorithm-for-modular-inverse). This algorithm performs an unbounded recursion and returns a set of values that can be used to compute the modular inverse of a number and other interesting numerical properties, such as greatest common divisor.

The proposed interface of the builtin function is as follows:

```lisp
(egcd a m)
```

Where `a` is the numbers for which we want to find the modular inverse using modulo `m`. The function returns a list of three elements: `(g x y)`. Where `g` is the greatest common divisor of `a` and `m` and `x` and `y` are the coefficients of the Bezout's identity.

The multiplicative modular inverse can be obtained in Pact by wrapping this operation in a Pact function:

```lisp
(defun mod-inverse (a: integer m: integer)
(
let*
(
(g_x_y (egcd a m))
(g (at 0 g_x_y))
(x (at 1 g_x_y))
)
(if (= g 1) (mod x m) 0)
)
)
```

## Keccak hashing function

The Plonk algorithm also makes use of the keccak hashing function. This function is present as a builtin in diverse blockchain ecosystems as the preferred hashing function for transaction IDs, accounts hashes and others.

In Kadena, the function is not implemented as the [Blake2b hash function is used instead](https://pact-language.readthedocs.io/en/latest/pact-properties-api.html?highlight=hash#hash).

Implementing Keccak will not only allow the construction of Plonk verifying systems but also allow developers to port their applications from other ecosystems to Kadena with ease.

Moreover, Pact does not have a bytes type in its code. This causes an issue when intersecting byte-oriented operations such as hashing with modular arithmetic.
Integers in Pact follow the haskell implementation, which does not limit the size of integers to a certain quantity of bytes (as is seen on other languages such as [Solidity's uint256](https://docs.soliditylang.org/en/v0.8.20/types.html#integers) or [Rust's u128](https://doc.rust-lang.org/book/ch03-02-data-types.html)).

However, the keccak algorithm does not perform its hashing operations on top of integers - it first converts them to bytes and then applies the hash operation on the bytes values. Its return value is also in bytes, which can then be converted back to an integer.

This becomes a problem when dealing with integer padding. The function must allow a way for the user to specify the padding of an integer. Consider for instance a small integer `10`. If we convert it to bytes, we get the following byte array: `[10]`. If we convert the integer `10` to a 16 bytes representation, we get the following byte array: `[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]`. The keccak hash of these two byte arrays will be different. The Pact integer type does not allow us to produce a padded representation of the integer, so our builtin function must allow the user to specify the number of bytes that the integer will be converted to.

The proposed interface of the builtin function is as follows:

```lisp
(keccak256-bs bytes-size bytes)
```
Where `bytes-size` is the number of bytes that the integer will be converted to and `bytes` is the integer to be converted.

The function returns the keccak256 hash of the value, converted to a Pact integer.

## Backwards Compatibility

New functions generally do not cause backward-compatibility issues in Pact.

## References
- Pact PR [#1238](https://github.com/kadena-io/pact/pull/1238)