Skip to content

Commit

Permalink
Circuit improvements (summa-dev#198)
Browse files Browse the repository at this point in the history
* chore: apply clippy fixes

* fix: audit comment 1 - Lookup table lives outside of `RangeCheckChip`

* fix: audit comment 2 - `swap_constraint`

* feat: optimize number of balances to be range checked

* chore: update `InclusionVerifier` contract

* chore: comment

* Record circuit config parameters in the contract

* Fix contract deployment parameters

* Fix typo

* chore: rename to `assetsCount`

* chore: update backend

* chore: fix comments

* chore: minor changes

---------

Co-authored-by: Alex Kuzmin <[email protected]>
  • Loading branch information
enricobottazzi and alxkzmn authored Dec 1, 2023
1 parent 3ba1397 commit 0ce4a0d
Show file tree
Hide file tree
Showing 21 changed files with 9,224 additions and 8,543 deletions.
2 changes: 1 addition & 1 deletion backend/src/contracts/abi/InclusionVerifier.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion backend/src/contracts/abi/Summa.json

Large diffs are not rendered by default.

12,820 changes: 6,486 additions & 6,334 deletions backend/src/contracts/generated/inclusion_verifier.rs

Large diffs are not rendered by default.

2,997 changes: 1,700 additions & 1,297 deletions backend/src/contracts/generated/summa_contract.rs

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions backend/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{sync::Arc, time::Duration};

use ethers::{
abi::Token,
prelude::SignerMiddleware,
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
Expand Down Expand Up @@ -56,22 +57,35 @@ pub async fn initialize_test_env(
.await;
}

if block_time != None {
if block_time.is_some() {
time::sleep(Duration::from_secs(block_time.unwrap())).await;
};

let inclusion_verifer_contract = InclusionVerifier::deploy(Arc::clone(&client), ())
let inclusion_verifier_contract = InclusionVerifier::deploy(Arc::clone(&client), ())
.unwrap()
.send()
.await
.unwrap();

if block_time != None {
if block_time.is_some() {
time::sleep(Duration::from_secs(block_time.unwrap())).await;
};

// The number of levels of the Merkle sum tree
let mst_levels = 4;
//The number of cryptocurrency assets per user included in the Merkle sum tree
let assets_count = 2;
// The number of bytes used to represent the balance of a cryptocurrency in the Merkle sum tree
let balance_byte_range = 14;

let args: &[Token] = &[
Token::Address(inclusion_verifier_contract.address()),
Token::Uint(mst_levels.into()),
Token::Uint(assets_count.into()),
Token::Uint(balance_byte_range.into()),
];
// Deploy Summa contract
let summa_contract = Summa::deploy(Arc::clone(&client), inclusion_verifer_contract.address())
let summa_contract = Summa::deploy(Arc::clone(&client), args)
.unwrap()
.send()
.await
Expand Down
6 changes: 6 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,11 @@ npx hardhat coverage
npx hardhat run scripts/deploy.ts --network localhost
```

The following Summa contract parameters are passed to its constructor inside the deployment script:

- verifier contract address (set automatically after the script deploys the verifier);
- the number of levels of the Merkle sum tree;
- the number of bytes used to represent the balance of a cryptocurrency in the Merkle sum tree.

The deployment script writes the latest deployment address for the chain to the [deployments](./../backend/src/contracts/deployments.json) file in the backend project. This data can later be used by the backend module to connect to the deployed contract.
The deployment script will copy the contract ABIs from the ./artifacts/src/ to the [backend](./../backend/src/contracts/abi/) module. The backend buildscript will then be able to generate the updated contract interfaces (see the backend [readme](./../backend/README.md)).
9 changes: 9 additions & 0 deletions contracts/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ async function main() {
);
await inclusionVerifier.deployed();

// The number of levels of the Merkle sum tree
const mstLevels = 4;
//The number of cryptocurrency assets per user included in the Merkle sum tree
const assetsCount = 2;
// The number of bytes used to represent the balance of a cryptocurrency in the Merkle sum tree
const balanceByteRange = 14;
const summa = await ethers.deployContract("Summa", [
inclusionVerifier.address,
mstLevels,
assetsCount,
balanceByteRange,
]);

await summa.deployed();
Expand Down
4 changes: 2 additions & 2 deletions contracts/src/InclusionVerifier.sol

Large diffs are not rendered by default.

1,614 changes: 809 additions & 805 deletions contracts/src/InclusionVerifier.yul

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion contracts/src/Summa.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IVerifier.sol";

contract Summa is Ownable {
/**
* @dev Struct representing the configuration of the Summa instance
* @param mstLevels The number of levels of the Merkle sum tree
* @param assetsCount The number of cryptocurrency assets per user included in the Merkle sum tree
* @param balanceByteRange The number of bytes used to represent the balance of a cryptocurrency in the Merkle sum tree
*/
struct SummaConfig {
uint16 mstLevels;
uint16 assetsCount;
uint8 balanceByteRange;
}
/**
* @dev Struct representing an address ownership proof submitted by the CEX
* @param cexAddress The address owned by the CEX (submitted as a string, as it can be a non-EVM address)
Expand Down Expand Up @@ -47,6 +58,9 @@ contract Summa is Ownable {
string[] blockchainNames;
}

// Summa configuration
SummaConfig public config;

// User inclusion proof verifier
IVerifier private immutable inclusionVerifier;

Expand Down Expand Up @@ -81,8 +95,14 @@ contract Summa is Ownable {
Cryptocurrency[] cryptocurrencies
);

constructor(IVerifier _inclusionVerifier) {
constructor(
IVerifier _inclusionVerifier,
uint16 mstLevels,
uint16 assetsCount,
uint8 balanceByteRange
) {
inclusionVerifier = _inclusionVerifier;
config = SummaConfig(mstLevels, assetsCount, balanceByteRange);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions contracts/test/Summa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ describe("Summa Contract", () => {

const summa = await ethers.deployContract("Summa", [
inclusionVerifier.address,
4, // The number of levels of the Merkle sum tree
2, // The number of cryptocurrency assets per user included in the Merkle sum tree
14, // The number of bytes used to represent the balance of a cryptocurrency in the Merkle sum tree
]);
await summa.deployed();

Expand Down
2 changes: 1 addition & 1 deletion zk_prover/examples/inclusion_proof_solidity_calldata.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"proof": "0x1c7a5d502a29a2c3c3e2d2d518ec2a8543c19bdce5135bbe59e14ef5d178efe9139c3838b4b11d607cc8ee22c32509d3d14227953cb313518d80be18655af6ba245790f70d7dcf5f046fb8580dfc9b13cd24f332e7f2cfc17f591aa3d69f11f4252f8e212b1ccc58ab58aba219d55b5e2f595e357ad30f0dc7d4675c2bdd2d780cacd8f89bcc10d41525a0395a64086b74826bb451a851f1d0379f4764b735400794ffe274246e09667ec9a321c8e9367fe1a4fa3ba21bfded36795e6e9d09bb1524bafd5994754519bdafef12f7191e50df6b1a6dce272ffcefa1d436119e1b057250dfa475092a7f9aafdec82ec0fecc9cc9b3bea3858fbc890bc41d013d402fd44923523a328647a94dedca98ba214ed7369079b114aa1ec4e8ba322628c91b648bc0336a5643a58a00ba809f772e7afaff4e857f25eac8c9458303fb2a5b18961ab5f9e67915a387d986b3b07efb1ecbdde42e4cbe8e3d5af03a59952ac61356f8dcbae85a0978509355d663eb844c5acd48726310c8415c7a3aafe6535a1b0e9765adcb39bc4afdfced654cad6f397d9892ee1ae503884c1db9a0c168af2797b4f4dee6f03f476b01941ceecad88cf3036b0f706d5b7cab16b0a3f71b440bb7c5afaedcd7c00b29aa45261b7e85e76a5777e8c0560439c5a121fa2c3bdd0395ac07afde7f5d1c0cdf752434721295767f238f178d6812bd28b99444047b121546f5a344aaca511e65d52de324bf27e51f432d511af05eb95f37e43aef0f1659bab2049626661782686ad5468d094cd7e301e580754f398ebec01867db1023333e83253fae9f8412781499ec436e0368bdff9da07288e96cc38606bc1f6d227f325d5a1c8757441d2d84a3b2f5afd7092d17df5525edea2b8f0dd69012af063061290aa7ec3ab913d1cf2869be3e739b3d3deacbb6a0f81a185ad42d8d8801147c4f8346d59f98cb77e8b8240988911e89ec7fdb53ad9848f721cf7167eb30124b79fdec15f3993c137462ba73410e3299530163790f0fc433adb808cfc928d38ff9793b82f359d27367c29d4d426e9dff1bc6a4391249df6a478818ac7910c7b98cca67e4bfb8b51e5b1060c3645cb9190abc407ccdd7f556fcb21730d9106a526538708a8202297cf5d454b7b49060b7c5de6d3fc199b376fc071b22830304cdc209c8dcd6f387ca9508e682ed717adba04c0c94e15cfb023f3a32739e1e8803aeb32c8b5dcc0ff1f49c734a50df27ba77beb4b25ed51ecc7642dae797051cfa5298b0c5928587dd3db85d4f701df82f32ea95aef6b8f349f289775cb12657e6d4996793f259ff15db0c84474a130613f1c261908972cd62e8ff8e69730e001a12ce4d710e0367c7f10206852aac0ed64c2b1354ab7030e97e2ccc914729427f7619b8b5f3174c4beed8666b7bc4edfdf9347fc2e48eabd43ddbde5b1b22545e1acd6f2b75161ad7520d8789d8732a4b716c01327c8f04cd05807fe98e0a7c235e358c069e583dca4a1a4651652f33305b2552c2f7233e7c876f55702c2f430c3236e25d642849059e3b3ac44decdbc041bdb3dc40953ba1cc6c3ca73b03dc19085ce3281e342c51f5331e245209afd51743d14495adec32c3679de8962deef6d7a950f0512ab3e7f5fb324b68315bf321d77a0b3d79c943fe1791174f2091f458d2e8a0e0524f4439e8d5f1a9a6ae80337942661f316ae6b619be2c1f2e71f83a586b6de95226f7fe5dc1b24bc82d2ea56d06e37d9f63008388ab9f1311d69721625f59ce4b2047b54ca06f42eaf9745919f8b1da480ad2931a1e63261ec10afd1ef6c9403c088dc958773610059645fab6b09b621adffa689fb6fb822cd3c726bc692e33c6bbb255f6238b9083e802817c59203e294494abf57813340898377954dd72fc368ea59cc7c75cbd098954b58498fcb1cca5d7552ae808e501c30108153b1ee4f77511ba5cff4e5578347413b9a0e9a5317bd194c04797bf11ec726054bda2d4103ab9831ea235f8eef31a6a4103eaed66a22b957bd6ca9c108948fe9c61f804148229c49be669e36e605c75800d69bfac1e856e6c54e72605c473b79cd7ac2883dd5a2bb83ce384c62e4a77ebf210444754645a983acb422214fbbf18f0c418257539a0e113cd1dd974111f23d9cb76f07bd92278321b060ce9a08de60fe584ad879d145cbfd0402776b572b37b8103410c277ca4bbc7201f694bf1f66c90b081200be85f0421c92573660273d303034d7ba80b996eb79230194ca93f1f565261aaa7e2dc4ac5be538503af632c9f3129f6a91e984fc7522d28537a9b256610ee029d6a3741445c939e7801a24c2b2e8a7f3b57d29137d92af9029cda44a25342151145dac9d679e9187c6efe4913ce3b8279f8b9962a1e06698d5777ca294f3d234b86bec1ffb670d61a51c6e9b7015e2a67ff59348df422fba53644e696f92bac9513b14a512c5ad1323cf02c2212af25c31dea96565016af135a3067869dfafa1f72de025b129fc79173da2c2b39ad8c2c01928ee8b92409600b405fb6980cc5cdf0b2b91756cad269889b924c7518a6d822f87193800bc0da2d76c38c1b7604293523827f0356dc594d7a442dba34a5a82a521a38bd11e98aab00ca4a7862552cb7621b5910b449a1ae1a12b26c6d94c7845ccc7d8229232baf261186ee10d14f6b1fb56464090dc1392cbd945609a9fa67beecb7041c015398c138b2359ddfe16745071076693338adba97cc636d71f04b2e0d44ba2a7b0447db18d72189fa27d174d1b379070aefbe30a6522f099025e8562de1a91a9ff2fa4564862f66f1bc37aac344ce2eb6a137b53bf76dce07c3ba31cf4d8c1b784c93768215706873f7c934b39aee003210f81527fd8c1c4845f39830adac02f4ab84229a373628b681f40e0c145277e2aae03438c155079fe26455f031631b0b835dbc2c654236708eb9831082be7976a05dc924ada3e44ebf6043074406248e34900dfc647ed17480f698e93556f2ce5a0cee266378a39fce8e20dd2c6c",
"proof": "0x1e94df5c9e64286482e5050d12f10c53aa607cacd84fcd3a408737cead3693ad0f8e478f584318016f885c0e4be56a1636097d8b7424d64855de037d349fd92825862131d1648f278249284b604b4f5d93dfc50d0004f8474bf569f972b113650da88dc30d36b0ae248d8754d55662af2c7aac4ba1c86372785af882100e61d217bd21ad7a8819c037ef132f2a340dd8fcf4c5b5dd661bf1844993c6ec82dfd92b91b7f6fb83a72a5262b18f8f324c425dffa7ad2b23a285c1b1bdb1c8bde4030f42e31819c3537ff954577c47ae8a0747a8a8390bfed8faa59188112f61216e1549c7aeeb46d2963adde4bd51d844784d449fdd54c75fdbb713dd8fd8764a1607159a94b280978d855eb0a11c7a578b6ea07c6d6d0c97350e18b5e8d36927372d79f80df140bc930b5678f407342dd9067a82fe9c75e4113754fc19310e4757289e2025af670afc8aafc721d3e66834c4867a1d1dbd63359f7b5b354f9f93e327550e310805831e15dfa2376775d7e66c47747a89e4494fdb4d8e72a6476e2e2c741d4eed06fcb84ca69d7aee5bcdb9963187e098fedb7d5f405a31febb6bc3106004d8dad642cc32ff95aab5fc3330d8dc260197db3ba080fb2ebb1bf238351ffdfa4e5035ebf3e987f6d3da44ee87ee96d16e6a187d6a9b3bb28fc1570492240ee384c6682c222540ecbc3d6f4d1cba8f403d28ca6e1a395e64dd4527665c08810e1fb264f8ce06552f268fea148055c1f5729c207dd576ca28765b4cc0900155eefd6fd19255a20eacbbaf3c87a7b62dffa5b73c046bad798f27c6623fe610290fc95e4a0073e52b3b5708aac83f505e15940956abe3db3d2f312bbc01ea0577ffc2ecd8e5f1326a255af26d19adbddf7f495843ab0bf1957693530ec9410d47037d00345c5743c63fb3c6fbd771823d63558584fc4f6fe571d449ae1bb62fbf0906b43c8b81d657380606f2e6012e081fe1a702380875161832e19e9eb22df226ad3769820128866d631b0c366ad69ae7df6039bacc0856444abb45587e2841231df7c4edd7b664087d46b39a53c3cc010466169d3c4b1f6bf3b80cf7661c7b1c59e43a04d737851b448273a1c9f41680c8b024921541a08ed2b737ba8e25a921838b6d2730e8058c91ef25d2251a6d09e3056eed5f0948418ad2e106ec14f7100b366d055525317cb40ed75d9bda85c8fd98cd29f0a93aa6f90539334119f762877fbf7f4f2a6b6f9a10145f709257104b9ae2dec184a7c1df8275159f2d2fd9fff812ec5fc42f93c472e617fc1563e813dc6cd6ca7cdbfb41bea2a72e228ecbf9bab2c50e5304b664772db087ac2a53d45d8a4b8c63f55335ee644ef52fd5f9b6e746b25bb6cd19e744d76cd1414ebe8d6b5fde6b468cca5107819a6d05ba9c2c1091a4eb1d79629636ca3400a3b69a781ba8ecbc7f59779634e5ae132f250945ffd72ade50063fb5a07dc6f56c3ff1cae9ae3d1c2bf201258bf79f1329ed689867a7e51e0d50920019f42a45f1b812b8f4de5a47ecabe2f32c20384f16e94dc9c5f78799506b33b59723e7dc5386bc31002eb3867b50ded5b92658de12f0e914b260e8f61a1184842721087c79021c54283e68635b0cb0cd9aa7d6bf0a145ade522e4faa01a78eca82f4ad4660745a05ab763007c402b6ee8a9e73a30f3e4dc893d23caf4fc757ebe0de6123978dea5bee1648833f6c60c413a250461d8cf808b8922b6ae123ba013b570cb4ab6fe663d1170d6859972496534965a40e5c2dfaabae417a4468039254f1107b358691045edff5154b2c88cdc99e770a0a4dda09ffc7d86bff1c6bdb1ae00a0ac582cb2c0110327ded5e85524cb9257a11825499dbc673b3d771b00a33480190425999ca32f3d8dd40c950ab81f6dc662a51907294f6687dcc481c02cf15914cd073eeba34113a52f2e04dccc79e6b401d485d4fff8488abd6b5eb1c7bf74a3ee86b2f7c07c1e93bd08e9ea5dc62566b2078fec5aea206f3dc10b9e7afdf448e0089d47006beb576b244aa692b961dde236f7ab501573043b9ae540c63bd80688ade75b29939f0b58145e0c6171da4dc29f6e6bc99aab9e2a2c95d2049a5081a7e1f617ab947f89a6d7e242bd546dfa50ebb5a4c63e3d88ba029db0a8f43f88e55d365ed589ea83df35e0c1098b80f2d0fe7960a8b16c190c9fc70a01aa7b0fc48c1b9bf3c02d0e73f3a0a3f40c4a47b2580c46f97a5ae8d92ce9d7c04d4a3c532e0585a706121a2ff2a447026b05c9b16c0ed803fe1c901e0e92899f9ca4a4aa1207c9e94cc11647793e4df7d809eb214d54b9e5052eb2fcf087abc5e14a6fa9018bb445e622651e0202060f4d92ca22231adfacf0e4feda0dfc1c4f51c54b401b6a95a696b1796c24ab0949e3120d311ab9d6e2a30f1a20e147490b61683674cdc926e8ce8665c8c9c2dff8cd5ccfd2e96ce6ef75f0bfe0c4edbd192b0a5dcd672a4e18119f5bbeb38a542b1b8ccc111294bfa7bc3ff716b4496dfc23ebdaf113d0b29a1266fcf3b5c81fadd4a91cd1221117b444b3f63414069ff07237707db17728f39cca47506702b091ce6830e2dd956d03068cd2fba7314c65f231475288e147754408534d19eea99a58469331d0149d618165409b780bb02759a92f08372beb9764a3c1e5e517902dab8000409ddfd38080e91bb0d01d218380020d24050975a27d354bf512dd785754a486218b02373e541d2bf55ce1b77c8912c0135a2c0775686f7c6734b8627cb93f72d2950397efe6daa89cbaef859e7dd639fe91c94f422eeec53065ba6ed8f78dfa72d1105af9e61d70ecb7ecea72e275d7dbce40fce85c795e3c20ae719251abd33061ca0cc43ec1421947e6a285a1f40790386d19fed65f26875a6656b282193ef10a7a763e61584de359c71f9dceae78189ff2272024b4cf141f58a7e94ec3c121150de85e9401461b0ffd26bb373e2dd4865572dd298b5f92babc182f8e229682d454f436f48d2857b8739b904cff81f5d5f3bb3088e9d2510140753fbc59900",
"public_inputs": [
"0xe113acd03b98f0bab0ef6f577245d5d008cbcc19ef2dab3608aa4f37f72a407",
"0x18d6ab953235a811edffa4cead74ea045e7cd2085771a2269d59dca054c955b1",
Expand Down
Binary file modified zk_prover/prints/mst-inclusion-layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 15 additions & 9 deletions zk_prover/src/chips/merkle_sum_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ pub struct MerkleSumTreeConfig {
/// Chip that performs various constraints related to a Merkle Sum Tree data structure such as:
///
/// * `s * swap_bit * (1 - swap_bit) = 0` (if `bool_and_swap_selector` is toggled). It basically enforces that swap_bit is either a 0 or 1.
/// * `s * (swap_bit * 2 * (elelment_r_cur - elelment_l_cur) - (elelment_l_next - elelment_l_cur) - (elelment_r_cur - elelment_r_next)) = 0`. Enforces that if the swap_bit is equal to 1, the values will be swapped on the next row (if `bool_and_swap_selector` is toggled).
/// If the swap_bit is equal to 0, the values will remain the same on the next row (if `bool_and_swap_selector` is toggled).
/// * `s * (element_r_cur - element_l_cur) * swap_bit + element_l_cur - element_l_next = 0` (if `bool_and_swap_selector` is toggled).
/// * `s * (element_l_cur - element_r_cur) * swap_bit + element_r_cur - element_r_next = 0` (if `bool_and_swap_selector` is toggled).
/// These 2 constraints enforce that if the swap_bit is equal to 1, the values will be swapped on the next row. If the swap_bit is equal to 0, the values will not be swapped on the next row.
/// * `s * (left_balance + right_balance - computed_sum)`. It constraints the computed sum to be equal to the sum of the left and right balances (if `sum_selector` is toggled).
#[derive(Debug, Clone)]
pub struct MerkleSumTreeChip<const N_ASSETS: usize> {
config: MerkleSumTreeConfig,
Expand Down Expand Up @@ -59,14 +61,18 @@ impl<const N_ASSETS: usize> MerkleSumTreeChip<N_ASSETS> {
let element_l_next = meta.query_advice(col_a, Rotation::next());
let element_r_next = meta.query_advice(col_b, Rotation::next());

let swap_constraint = s
* ((swap_bit
* Expression::Constant(Fp::from(2))
* (element_r_cur.clone() - element_l_cur.clone())
- (element_l_next - element_l_cur))
- (element_r_cur - element_r_next));
// element_l_next = (element_r_cur - element_l_cur)*s + element_l_cur
let swap_constraint_1 = s.clone()
* ((element_r_cur.clone() - element_l_cur.clone()) * swap_bit.clone()
+ element_l_cur.clone()
- element_l_next);

// element_r_next = (element_l_cur - element_r_cur)*s + element_r_cur
let swap_constraint_2 = s
* ((element_l_cur - element_r_cur.clone()) * swap_bit + element_r_cur
- element_r_next);

vec![swap_constraint]
vec![swap_constraint_1, swap_constraint_2]
});

meta.create_gate("sum constraint", |meta| {
Expand Down
Loading

0 comments on commit 0ce4a0d

Please sign in to comment.