From ebf75843d88a3c9077507f98832090fa8b1bf456 Mon Sep 17 00:00:00 2001 From: sveitser Date: Fri, 27 May 2022 16:22:03 +0200 Subject: [PATCH 01/20] Emit CapeBlock as part of BlockCommitted event --- contracts/contracts/CAPE.sol | 4 ++-- contracts/rust/src/cape/events.rs | 8 +++---- contracts/rust/src/cape/submit_block.rs | 16 ++++++-------- eqs/src/eth_polling.rs | 29 ++++++++++++------------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index e217a419..bb08b2b4 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -49,7 +49,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { uint256 public constant MAX_NUM_PENDING_DEPOSIT = 10; event FaucetInitialized(bytes roBytes); - event BlockCommitted(uint64 indexed height, uint256[] depositCommitments); + event BlockCommitted(uint64 indexed height, uint256[] depositCommitments, bytes blockBytes); event Erc20TokensDeposited(bytes roBytes, address erc20TokenAddress, address from); struct AuditMemo { @@ -357,7 +357,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { blockHeight += 1; // Inform clients about the new block and the processed deposits. - emit BlockCommitted(blockHeight, pendingDeposits); + emit BlockCommitted(blockHeight, pendingDeposits, abi.encode(newBlock)); // Empty the queue now that the record commitments have been inserted delete pendingDeposits; diff --git a/contracts/rust/src/cape/events.rs b/contracts/rust/src/cape/events.rs index 1691d0ee..1236948d 100644 --- a/contracts/rust/src/cape/events.rs +++ b/contracts/rust/src/cape/events.rs @@ -9,7 +9,7 @@ mod tests { use crate::{ cape::{ - submit_block::{fetch_cape_block, submit_cape_block_with_memos}, + submit_block::{fetch_cape_memos, submit_cape_block_with_memos}, BlockWithMemos, CapeBlock, }, ethereum::EthConnection, @@ -32,7 +32,7 @@ mod tests { use std::iter::repeat_with; #[tokio::test] - async fn test_fetch_cape_block_from_event() -> Result<()> { + async fn test_fetch_cape_memos_from_event() -> Result<()> { let connection = EthConnection::for_test().await; let mut rng = ChaChaRng::from_seed([0x42u8; 32]); @@ -100,11 +100,11 @@ mod tests { let (_, meta) = events[0].clone(); - let fetched_block_with_memos = fetch_cape_block(&query_connection, meta.transaction_hash) + let fetched_memos = fetch_cape_memos(&query_connection, meta.transaction_hash) .await? .unwrap(); - assert_eq!(fetched_block_with_memos, block_with_memos); + assert_eq!(fetched_memos, memos_with_sigs); Ok(()) } diff --git a/contracts/rust/src/cape/submit_block.rs b/contracts/rust/src/cape/submit_block.rs index 01a33089..dffa1a38 100644 --- a/contracts/rust/src/cape/submit_block.rs +++ b/contracts/rust/src/cape/submit_block.rs @@ -5,7 +5,6 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -use crate::cape::CapeBlock; use crate::deploy::EthMiddleware; use crate::ethereum::EthConnection; use crate::types::{self as sol, CAPE}; @@ -21,10 +20,10 @@ use super::{BlockMemos, BlockWithMemos}; /// Fetch a cape block given the (Ethereum) tx hash of the tx in which the block /// was submitted. -pub async fn fetch_cape_block( +pub async fn fetch_cape_memos( connection: &EthConnection, tx_hash: TxHash, -) -> Result, Error> { +) -> Result, Error> { // Fetch Ethereum transaction that emitted event let tx = if let Some(tx) = connection.provider.get_transaction(tx_hash).await? { tx @@ -33,16 +32,14 @@ pub async fn fetch_cape_block( }; // Decode the calldata (tx.input) into the function input types - let (decoded_calldata_block, fetched_memos_bytes) = - connection - .contract - .decode::<(sol::CapeBlock, Bytes), _>("submitCapeBlockWithMemos", tx.input)?; + let (_, fetched_memos_bytes) = connection + .contract + .decode::<(sol::CapeBlock, Bytes), _>("submitCapeBlockWithMemos", tx.input)?; - let decoded_cape_block = CapeBlock::from(decoded_calldata_block); let decoded_memos: BlockMemos = CanonicalDeserialize::deserialize(&fetched_memos_bytes.to_vec()[..])?; - Ok(Some(BlockWithMemos::new(decoded_cape_block, decoded_memos))) + Ok(Some(decoded_memos)) } pub async fn submit_cape_block_with_memos( @@ -108,6 +105,7 @@ mod tests { use super::*; use crate::{ assertion::Matcher, + cape::CapeBlock, deploy::deploy_test_cape, ledger::CapeLedger, test_utils::PrintGas, diff --git a/eqs/src/eth_polling.rs b/eqs/src/eth_polling.rs index 21237348..57e5873e 100644 --- a/eqs/src/eth_polling.rs +++ b/eqs/src/eth_polling.rs @@ -11,7 +11,7 @@ use crate::state_persistence::StatePersistence; use async_std::sync::{Arc, RwLock}; use cap_rust_sandbox::{ - cape::submit_block::fetch_cape_block, + cape::submit_block::fetch_cape_memos, ethereum::EthConnection, ledger::{CapeTransactionKind, CapeTransition}, model::{CapeModelTxn, Erc20Code, EthereumAddr}, @@ -203,23 +203,23 @@ impl EthPolling { } } match filter { - CAPEEvents::BlockCommittedFilter(_) => { - let fetched_block_with_memos = - fetch_cape_block(&self.connection, meta.transaction_hash) - .await - .unwrap() - .unwrap(); - - let model_txns = fetched_block_with_memos - .block - .clone() + CAPEEvents::BlockCommittedFilter(filter_data) => { + let memos = fetch_cape_memos(&self.connection, meta.transaction_hash) + .await + .unwrap() + .unwrap(); + + let cape_block: cap_rust_sandbox::types::CapeBlock = + AbiDecode::decode(filter_data.block_bytes).unwrap(); + + let model_txns = cap_rust_sandbox::cape::CapeBlock::from(cape_block) .into_cape_transactions() .unwrap() .0; // TODO Instead of panicking here we need to handle cases of missing memos gracefully let num_txn = model_txns.len(); - let num_txn_memo = fetched_block_with_memos.memos.len(); + let num_txn_memo = memos.len(); if num_txn != num_txn_memo { panic!( "Different number of txns and txn memos: {} vs {}", @@ -276,7 +276,7 @@ impl EthPolling { let memos_sig_valid: Vec<_> = model_txns .iter() - .zip(fetched_block_with_memos.memos.iter()) + .zip(memos.iter()) .map(|(tx, (recv_memos, sig))| { match tx { CapeModelTxn::CAP(note) => note.clone(), @@ -298,8 +298,7 @@ impl EthPolling { // Create LedgerEvent::Memos if memo signature is valid, skip otherwise let mut memo_events = Vec::new(); let mut index = 0; - fetched_block_with_memos - .memos + memos .iter() .enumerate() .filter_map(|(txn_id, (txn_memo, _))| match memos_sig_valid[txn_id] { From 991e0cf4a903f794096a5c4c3332686bb9fa32ad Mon Sep 17 00:00:00 2001 From: sveitser Date: Mon, 30 May 2022 17:09:17 +0200 Subject: [PATCH 02/20] Run cargo update --- Cargo.lock | 221 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 144 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2408ebe3..fc94dfab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1126,7 +1126,7 @@ dependencies = [ "serde_json", "sha3 0.9.1", "snafu", - "strum_macros", + "strum_macros 0.20.1", "tagged-base64 0.2.0 (git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.2.0)", "tokio", ] @@ -1188,7 +1188,7 @@ dependencies = [ "snafu", "structopt", "strum 0.20.0", - "strum_macros", + "strum_macros 0.20.1", "surf", "tagged-base64 0.2.0 (git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.2.0)", "tempdir", @@ -1220,7 +1220,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.6", + "semver 1.0.9", "serde", "serde_json", ] @@ -2159,7 +2159,7 @@ dependencies = [ "snafu", "structopt", "strum 0.20.0", - "strum_macros", + "strum_macros 0.20.1", "surf", "tagged-base64 0.2.0 (git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.2.0)", "tide", @@ -2302,8 +2302,8 @@ dependencies = [ [[package]] name = "ethers" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -2318,7 +2318,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "ethers-core", "once_cell", @@ -2328,8 +2328,8 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -2346,8 +2346,8 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.3" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "Inflector", "cfg-if 1.0.0", @@ -2356,7 +2356,6 @@ dependencies = [ "eyre", "getrandom 0.2.5", "hex", - "once_cell", "proc-macro2", "quote", "reqwest", @@ -2369,8 +2368,8 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.3" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -2383,14 +2382,14 @@ dependencies = [ [[package]] name = "ethers-core" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.3" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "arrayvec 0.7.2", "bytes 1.1.0", "cargo_metadata", + "chrono", "convert_case 0.5.0", - "ecdsa", "elliptic-curve", "ethabi", "generic-array 0.14.5", @@ -2398,35 +2397,38 @@ dependencies = [ "k256", "once_cell", "proc-macro2", - "quote", "rand 0.8.5", "rlp", "rlp-derive", "serde", "serde_json", + "strum 0.24.0", "syn", "thiserror", "tiny-keccak", + "unicode-xid", ] [[package]] name = "ethers-etherscan" -version = "0.2.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.2.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "ethers-core", "ethers-solc", "reqwest", + "semver 1.0.9", "serde", "serde-aux", "serde_json", "thiserror", + "tracing", ] [[package]] name = "ethers-middleware" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "async-trait", "ethers-contract", @@ -2434,6 +2436,7 @@ dependencies = [ "ethers-etherscan", "ethers-providers", "ethers-signers", + "futures-locks", "futures-util", "instant", "reqwest", @@ -2448,25 +2451,27 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "async-trait", "auto_impl", "base64 0.13.0", "ethers-core", - "futures-channel", "futures-core", "futures-timer", "futures-util", + "hashers", "hex", "http", + "once_cell", "parking_lot 0.11.2", "pin-project", "reqwest", "serde", "serde_json", "thiserror", + "tokio", "tracing", "tracing-futures", "url", @@ -2479,8 +2484,8 @@ dependencies = [ [[package]] name = "ethers-signers" -version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +version = "0.6.2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ "async-trait", "coins-bip32", @@ -2488,12 +2493,8 @@ dependencies = [ "elliptic-curve", "eth-keystore", "ethers-core", - "futures-executor", - "futures-util", "hex", - "home", "rand 0.8.5", - "semver 1.0.6", "sha2 0.9.9", "thiserror", ] @@ -2501,8 +2502,9 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" -source = "git+https://github.com/gakonst/ethers-rs#4f1a2352f7df48597224df777d9e77c1a56776e2" +source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" dependencies = [ + "cfg-if 1.0.0", "colored", "dunce", "ethers-core", @@ -2513,16 +2515,17 @@ dependencies = [ "md-5", "num_cpus", "once_cell", + "path-slash", "rayon", "regex", - "semver 1.0.6", + "semver 1.0.9", "serde", "serde_json", - "sha2 0.9.9", "solang-parser", "svm-rs", "thiserror", "tiny-keccak", + "tokio", "tracing", "walkdir", ] @@ -2715,6 +2718,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -2796,6 +2809,17 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-locks" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb42d4fb72227be5778429f9ef5240a38a358925a49f05b5cf702ce7c7e558a" +dependencies = [ + "futures-channel", + "futures-task", + "tokio", +] + [[package]] name = "futures-macro" version = "0.3.21" @@ -2843,6 +2867,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -2976,6 +3009,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + [[package]] name = "heck" version = "0.3.3" @@ -3609,9 +3651,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc5937366afd3b38071f400d1ce5bd8b1d40b5083cc14e6f8dbcc4032a7f5bb" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" dependencies = [ "cfg-if 1.0.0", "ecdsa", @@ -4034,7 +4076,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg", - "num-bigint 0.4.3", "num-integer", "num-traits", ] @@ -4075,9 +4116,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "oorandom" @@ -4233,6 +4274,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" +[[package]] +name = "path-slash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cacbb3c4ff353b534a67fb8d7524d00229da4cb1dc8c79f4db96e375ab5b619" + [[package]] name = "paw" version = "1.0.0" @@ -4591,11 +4638,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -4774,9 +4821,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", @@ -4786,14 +4833,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] @@ -4841,14 +4887,14 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "snafu", - "strum_macros", + "strum_macros 0.20.1", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -4866,9 +4912,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "relayer" @@ -5067,7 +5113,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.6", + "semver 1.0.9", ] [[package]] @@ -5266,7 +5312,7 @@ dependencies = [ "snafu", "structopt", "strum 0.24.0", - "strum_macros", + "strum_macros 0.20.1", "surf", "tagged-base64 0.2.0 (git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.2.0)", "tempdir", @@ -5306,9 +5352,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" dependencies = [ "serde", ] @@ -5649,15 +5695,13 @@ dependencies = [ [[package]] name = "solang-parser" -version = "0.1.10" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06b8bfb6c910adbada563211b876b91a058791505e4cb646591b205fa817d01" +checksum = "395b6e1ec5af117bd08f963c7cd80f8efd4eed51c5a332aed42b13e3f9bc860b" dependencies = [ + "itertools 0.10.3", "lalrpop", "lalrpop-util", - "num-bigint 0.4.3", - "num-rational 0.4.0", - "num-traits", "phf", "unicode-xid", ] @@ -5827,6 +5871,9 @@ name = "strum" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +dependencies = [ + "strum_macros 0.24.0", +] [[package]] name = "strum_macros" @@ -5840,6 +5887,19 @@ dependencies = [ "syn", ] +[[package]] +name = "strum_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.4.1" @@ -5877,14 +5937,15 @@ checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" [[package]] name = "svm-rs" -version = "0.2.9" -source = "git+https://github.com/roynalnaruto/svm-rs#ae79a29f5bde08f1991f981456253fa5b6859047" +version = "0.2.10" +source = "git+https://github.com/roynalnaruto/svm-rs#70fe9d9c43689e311753ce7a94f4618dd219bfda" dependencies = [ "anyhow", "cfg-if 1.0.0", "clap 3.1.6", "console 0.14.1", "dialoguer", + "fs2", "hex", "home", "indicatif", @@ -5892,7 +5953,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "reqwest", - "semver 1.0.6", + "semver 1.0.9", "serde", "serde_json", "sha2 0.9.9", @@ -5905,13 +5966,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.87" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59d925cf59d8151f25a3bedf97c9c157597c9df7324d32d68991cc399ed08b" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -6035,18 +6096,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -6208,9 +6269,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" dependencies = [ "bytes 1.1.0", "libc", @@ -6279,9 +6340,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if 1.0.0", "log", @@ -6450,6 +6511,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -6473,9 +6540,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "universal-hash" From 86988bba56b83db4ccf81437365859f0f096669a Mon Sep 17 00:00:00 2001 From: sveitser Date: Mon, 30 May 2022 17:09:34 +0200 Subject: [PATCH 03/20] Emit block data fields individually If we emit the CapeBlock struct directly this leads to a compilation error. The workaround to emit ABI encoded bytes instead fails to decode the CapeBlock in rust struct at runtime. This workaround emits the ABI encoded fields of CapeBlock individually. The function `decode_cape_block` uses an ugly workaround to deal with the problem of not being able to import `BlockCommittedFilter` because that type exists both for `CAPE` and `TestCAPE` and is therefore ambiguous. --- contracts/contracts/CAPE.sol | 33 ++++++++++++++++++++++-- contracts/rust/src/cape/events.rs | 43 ++++++++++++++++++++++++++++--- contracts/rust/src/cape/mod.rs | 2 +- eqs/src/eth_polling.rs | 13 +++++----- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index bb08b2b4..736928b6 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -49,7 +49,22 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { uint256 public constant MAX_NUM_PENDING_DEPOSIT = 10; event FaucetInitialized(bytes roBytes); - event BlockCommitted(uint64 indexed height, uint256[] depositCommitments, bytes blockBytes); + + event BlockCommitted( + uint64 indexed height, + uint256[] depositCommitments, + // What follows is a `CapeBlock` struct split up into fields. + // This may no longer be necessary once + // https://github.com/gakonst/ethers-rs/issues/1220 + // if fixed. + bytes minerAddr, + bytes noteTypes, + bytes transferNotes, + bytes mintNotes, + bytes freezeNotes, + bytes burnNotes + ); + event Erc20TokensDeposited(bytes roBytes, address erc20TokenAddress, address from); struct AuditMemo { @@ -357,12 +372,26 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { blockHeight += 1; // Inform clients about the new block and the processed deposits. - emit BlockCommitted(blockHeight, pendingDeposits, abi.encode(newBlock)); + _emitBlockEvent(newBlock); // Empty the queue now that the record commitments have been inserted delete pendingDeposits; } + /// @notice This function only exists to avoid a stack too deep compilation error. + function _emitBlockEvent(CapeBlock memory newBlock) internal { + emit BlockCommitted( + blockHeight, + pendingDeposits, + abi.encode(newBlock.minerAddr), + abi.encode(newBlock.noteTypes), + abi.encode(newBlock.transferNotes), + abi.encode(newBlock.mintNotes), + abi.encode(newBlock.freezeNotes), + abi.encode(newBlock.burnNotes) + ); + } + /// @dev send the ERC-20 tokens equivalent to the asset records being burnt. Recall that the burned record opening is contained inside the note. /// @param note note of type *BURN* function _handleWithdrawal(BurnNote memory note) internal { diff --git a/contracts/rust/src/cape/events.rs b/contracts/rust/src/cape/events.rs index 1236948d..047abaa3 100644 --- a/contracts/rust/src/cape/events.rs +++ b/contracts/rust/src/cape/events.rs @@ -5,16 +5,49 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . +use ethers::abi::AbiDecode; + +use super::CapeBlock; +use crate::types::{BurnNote, CAPEEvents, EdOnBN254Point, FreezeNote, MintNote, TransferNote}; + +// Unable to import BlockCommittedFilter as type because it's "ambigious". There +// is one definition for the CAPE and CAPETest contract. +// +// Is there a better workaround? +pub fn decode_cape_block(event: CAPEEvents) -> CapeBlock { + if let CAPEEvents::BlockCommittedFilter(block) = event { + let miner_addr: EdOnBN254Point = AbiDecode::decode(block.miner_addr).unwrap(); + let note_types: Vec = AbiDecode::decode(block.note_types).unwrap(); + let transfer_notes: Vec = AbiDecode::decode(block.transfer_notes).unwrap(); + let mint_notes: Vec = AbiDecode::decode(block.mint_notes).unwrap(); + let freeze_notes: Vec = AbiDecode::decode(block.freeze_notes).unwrap(); + let burn_notes: Vec = AbiDecode::decode(block.burn_notes).unwrap(); + + let event_cape_block = crate::types::CapeBlock { + miner_addr, + note_types, + transfer_notes, + mint_notes, + freeze_notes, + burn_notes, + }; + event_cape_block.into() + } else { + panic!("Only works on BlockCommittedFilter event.") + } +} + #[cfg(test)] mod tests { use crate::{ cape::{ + events::decode_cape_block, submit_block::{fetch_cape_memos, submit_cape_block_with_memos}, BlockWithMemos, CapeBlock, }, ethereum::EthConnection, ledger::CapeLedger, - types::{GenericInto, MerkleRootSol}, + types::{CAPEEvents, GenericInto, MerkleRootSol}, }; use anyhow::Result; use ethers::prelude::BlockNumber; @@ -36,7 +69,7 @@ mod tests { let connection = EthConnection::for_test().await; let mut rng = ChaChaRng::from_seed([0x42u8; 32]); - let params = TxnsParams::generate_txns(&mut rng, 1, 0, 0, CapeLedger::merkle_height()); + let params = TxnsParams::generate_txns(&mut rng, 1, 1, 1, CapeLedger::merkle_height()); let miner = UserPubKey::default(); let root = params.txns[0].merkle_root(); @@ -98,14 +131,16 @@ mod tests { .query_with_meta() .await?; - let (_, meta) = events[0].clone(); + let (data, meta) = events[0].clone(); let fetched_memos = fetch_cape_memos(&query_connection, meta.transaction_hash) .await? .unwrap(); - assert_eq!(fetched_memos, memos_with_sigs); + let event_cape_block = decode_cape_block(CAPEEvents::BlockCommittedFilter(data)); + assert_eq!(cape_block, event_cape_block); + Ok(()) } } diff --git a/contracts/rust/src/cape/mod.rs b/contracts/rust/src/cape/mod.rs index f0918dbd..2c969c0b 100644 --- a/contracts/rust/src/cape/mod.rs +++ b/contracts/rust/src/cape/mod.rs @@ -6,7 +6,7 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . #![deny(warnings)] -mod events; +pub mod events; pub mod faucet; mod note_types; mod reentrancy; diff --git a/eqs/src/eth_polling.rs b/eqs/src/eth_polling.rs index 57e5873e..21f140dc 100644 --- a/eqs/src/eth_polling.rs +++ b/eqs/src/eth_polling.rs @@ -10,6 +10,7 @@ use crate::query_result_state::{EthEventIndex, QueryResultState}; use crate::state_persistence::StatePersistence; use async_std::sync::{Arc, RwLock}; +use cap_rust_sandbox::cape::events::decode_cape_block; use cap_rust_sandbox::{ cape::submit_block::fetch_cape_memos, ethereum::EthConnection, @@ -209,13 +210,11 @@ impl EthPolling { .unwrap() .unwrap(); - let cape_block: cap_rust_sandbox::types::CapeBlock = - AbiDecode::decode(filter_data.block_bytes).unwrap(); - - let model_txns = cap_rust_sandbox::cape::CapeBlock::from(cape_block) - .into_cape_transactions() - .unwrap() - .0; + let model_txns = + decode_cape_block(CAPEEvents::BlockCommittedFilter(filter_data)) + .into_cape_transactions() + .unwrap() + .0; // TODO Instead of panicking here we need to handle cases of missing memos gracefully let num_txn = model_txns.len(); From 26df84e233eb201202e3ea39320ff9ec2732d4b6 Mon Sep 17 00:00:00 2001 From: sveitser Date: Wed, 1 Jun 2022 12:06:17 +0200 Subject: [PATCH 04/20] cargo update to get latest ethers-rs version --- Cargo.lock | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc94dfab..3bff882b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2303,7 +2303,7 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -2318,7 +2318,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "ethers-core", "once_cell", @@ -2329,7 +2329,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -2347,7 +2347,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.3" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "Inflector", "cfg-if 1.0.0", @@ -2369,7 +2369,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.3" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -2383,7 +2383,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.3" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "arrayvec 0.7.2", "bytes 1.1.0", @@ -2400,6 +2400,7 @@ dependencies = [ "rand 0.8.5", "rlp", "rlp-derive", + "rust_decimal", "serde", "serde_json", "strum 0.24.0", @@ -2412,7 +2413,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "ethers-core", "ethers-solc", @@ -2428,7 +2429,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "async-trait", "ethers-contract", @@ -2452,7 +2453,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "async-trait", "auto_impl", @@ -2485,7 +2486,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.2" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "async-trait", "coins-bip32", @@ -2502,7 +2503,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" -source = "git+https://github.com/gakonst/ethers-rs#6b6c5115838008565d774d784262e61e01129ebb" +source = "git+https://github.com/gakonst/ethers-rs#1a699ad72edddca9cb4601e115a2ff3e7f8c2ee6" dependencies = [ "cfg-if 1.0.0", "colored", @@ -5077,6 +5078,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rust_decimal" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ee7337df68898256ad0d4af4aad178210d9e44d2ff900ce44064a97cd86530" +dependencies = [ + "arrayvec 0.7.2", + "num-traits", + "serde", +] + [[package]] name = "rustc-demangle" version = "0.1.21" From 8cf6def2a348c176f15c313bdcb3c62e8b1d9596 Mon Sep 17 00:00:00 2001 From: sveitser Date: Wed, 1 Jun 2022 12:45:30 +0200 Subject: [PATCH 05/20] Remove ambigous type workaround --- contracts/rust/src/cape/events.rs | 44 +++++++++++-------------------- contracts/rust/src/types.rs | 14 +++++----- eqs/src/eth_polling.rs | 12 ++++----- 3 files changed, 28 insertions(+), 42 deletions(-) diff --git a/contracts/rust/src/cape/events.rs b/contracts/rust/src/cape/events.rs index 047abaa3..63582216 100644 --- a/contracts/rust/src/cape/events.rs +++ b/contracts/rust/src/cape/events.rs @@ -6,48 +6,34 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . use ethers::abi::AbiDecode; +use ethers::prelude::AbiError; use super::CapeBlock; -use crate::types::{BurnNote, CAPEEvents, EdOnBN254Point, FreezeNote, MintNote, TransferNote}; - -// Unable to import BlockCommittedFilter as type because it's "ambigious". There -// is one definition for the CAPE and CAPETest contract. -// -// Is there a better workaround? -pub fn decode_cape_block(event: CAPEEvents) -> CapeBlock { - if let CAPEEvents::BlockCommittedFilter(block) = event { - let miner_addr: EdOnBN254Point = AbiDecode::decode(block.miner_addr).unwrap(); - let note_types: Vec = AbiDecode::decode(block.note_types).unwrap(); - let transfer_notes: Vec = AbiDecode::decode(block.transfer_notes).unwrap(); - let mint_notes: Vec = AbiDecode::decode(block.mint_notes).unwrap(); - let freeze_notes: Vec = AbiDecode::decode(block.freeze_notes).unwrap(); - let burn_notes: Vec = AbiDecode::decode(block.burn_notes).unwrap(); - - let event_cape_block = crate::types::CapeBlock { - miner_addr, - note_types, - transfer_notes, - mint_notes, - freeze_notes, - burn_notes, - }; - event_cape_block.into() - } else { - panic!("Only works on BlockCommittedFilter event.") +use crate::types::BlockCommittedFilter; + +pub fn decode_cape_block_from_event(block: BlockCommittedFilter) -> Result { + Ok(crate::types::CapeBlock { + miner_addr: AbiDecode::decode(block.miner_addr)?, + note_types: AbiDecode::decode(block.note_types)?, + transfer_notes: AbiDecode::decode(block.transfer_notes)?, + mint_notes: AbiDecode::decode(block.mint_notes)?, + freeze_notes: AbiDecode::decode(block.freeze_notes)?, + burn_notes: AbiDecode::decode(block.burn_notes)?, } + .into()) } #[cfg(test)] mod tests { use crate::{ cape::{ - events::decode_cape_block, + events::decode_cape_block_from_event, submit_block::{fetch_cape_memos, submit_cape_block_with_memos}, BlockWithMemos, CapeBlock, }, ethereum::EthConnection, ledger::CapeLedger, - types::{CAPEEvents, GenericInto, MerkleRootSol}, + types::{GenericInto, MerkleRootSol}, }; use anyhow::Result; use ethers::prelude::BlockNumber; @@ -138,7 +124,7 @@ mod tests { .unwrap(); assert_eq!(fetched_memos, memos_with_sigs); - let event_cape_block = decode_cape_block(CAPEEvents::BlockCommittedFilter(data)); + let event_cape_block = decode_cape_block_from_event(data)?; assert_eq!(cape_block, event_cape_block); Ok(()) diff --git a/contracts/rust/src/types.rs b/contracts/rust/src/types.rs index cde34bcd..566716aa 100644 --- a/contracts/rust/src/types.rs +++ b/contracts/rust/src/types.rs @@ -26,13 +26,13 @@ use jf_primitives::elgamal::{self, EncKey}; use std::convert::TryInto; pub use crate::bindings::{ - AssetDefinition, AssetPolicy, AssetRegistry, AuditMemo, BurnNote, CAPEEvents, CapeBlock, - Challenges, EdOnBN254Point, EvalData, EvalDomain, FreezeAuxInfo, FreezeNote, G1Point, G2Point, - Greeter, MaliciousToken, MintAuxInfo, MintNote, PcsInfo, PlonkProof, RecordOpening, - SimpleToken, TestBN254, TestCAPE, TestCAPEEvents, TestCapeTypes, TestEdOnBN254, - TestPlonkVerifier, TestPolynomialEval, TestRecordsMerkleTree, TestRescue, TestRootStore, - TestTranscript, TestVerifyingKeys, TranscriptData, TransferAuxInfo, TransferNote, VerifyingKey, - CAPE, ERC20, + cape_mod::BlockCommittedFilter, AssetDefinition, AssetPolicy, AssetRegistry, AuditMemo, + BurnNote, CAPEEvents, CapeBlock, Challenges, EdOnBN254Point, EvalData, EvalDomain, + FreezeAuxInfo, FreezeNote, G1Point, G2Point, Greeter, MaliciousToken, MintAuxInfo, MintNote, + PcsInfo, PlonkProof, RecordOpening, SimpleToken, TestBN254, TestCAPE, TestCAPEEvents, + TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRecordsMerkleTree, + TestRescue, TestRootStore, TestTranscript, TestVerifyingKeys, TranscriptData, TransferAuxInfo, + TransferNote, VerifyingKey, CAPE, ERC20, }; // The number of input wires of TurboPlonk. diff --git a/eqs/src/eth_polling.rs b/eqs/src/eth_polling.rs index 21f140dc..fb81a356 100644 --- a/eqs/src/eth_polling.rs +++ b/eqs/src/eth_polling.rs @@ -10,7 +10,7 @@ use crate::query_result_state::{EthEventIndex, QueryResultState}; use crate::state_persistence::StatePersistence; use async_std::sync::{Arc, RwLock}; -use cap_rust_sandbox::cape::events::decode_cape_block; +use cap_rust_sandbox::cape::events::decode_cape_block_from_event; use cap_rust_sandbox::{ cape::submit_block::fetch_cape_memos, ethereum::EthConnection, @@ -210,11 +210,11 @@ impl EthPolling { .unwrap() .unwrap(); - let model_txns = - decode_cape_block(CAPEEvents::BlockCommittedFilter(filter_data)) - .into_cape_transactions() - .unwrap() - .0; + let model_txns = decode_cape_block_from_event(filter_data) + .unwrap() + .into_cape_transactions() + .unwrap() + .0; // TODO Instead of panicking here we need to handle cases of missing memos gracefully let num_txn = model_txns.len(); From ecaca68c7d91236d07cda468e0ded8e4aa4ff1f5 Mon Sep 17 00:00:00 2001 From: sveitser Date: Wed, 1 Jun 2022 12:54:01 +0200 Subject: [PATCH 06/20] Organize imports --- contracts/rust/src/cape/events.rs | 7 ++----- eqs/src/eth_polling.rs | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/contracts/rust/src/cape/events.rs b/contracts/rust/src/cape/events.rs index 63582216..eec01dbd 100644 --- a/contracts/rust/src/cape/events.rs +++ b/contracts/rust/src/cape/events.rs @@ -5,11 +5,8 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -use ethers::abi::AbiDecode; -use ethers::prelude::AbiError; - -use super::CapeBlock; -use crate::types::BlockCommittedFilter; +use crate::{cape::CapeBlock, types::BlockCommittedFilter}; +use ethers::{abi::AbiDecode, prelude::AbiError}; pub fn decode_cape_block_from_event(block: BlockCommittedFilter) -> Result { Ok(crate::types::CapeBlock { diff --git a/eqs/src/eth_polling.rs b/eqs/src/eth_polling.rs index fb81a356..a2ff6d3d 100644 --- a/eqs/src/eth_polling.rs +++ b/eqs/src/eth_polling.rs @@ -10,9 +10,8 @@ use crate::query_result_state::{EthEventIndex, QueryResultState}; use crate::state_persistence::StatePersistence; use async_std::sync::{Arc, RwLock}; -use cap_rust_sandbox::cape::events::decode_cape_block_from_event; use cap_rust_sandbox::{ - cape::submit_block::fetch_cape_memos, + cape::{events::decode_cape_block_from_event, submit_block::fetch_cape_memos}, ethereum::EthConnection, ledger::{CapeTransactionKind, CapeTransition}, model::{CapeModelTxn, Erc20Code, EthereumAddr}, From 54f6cf0a8bc8d6c9810b9ac7222a7c6eee955c79 Mon Sep 17 00:00:00 2001 From: sveitser Date: Wed, 1 Jun 2022 20:42:56 +0200 Subject: [PATCH 07/20] [WIP] Separate merkle tree contract - Create ownable merkle tree contract owned by CAPE. --- contracts/contracts/CAPE.sol | 49 ++++++------ contracts/contracts/RecordsMerkleTree.sol | 5 +- contracts/contracts/mocks/TestCAPE.sol | 13 ++-- .../contracts/mocks/TestRecordsMerkleTree.sol | 13 ++-- contracts/deploy/00_cape.ts | 35 +++++++-- contracts/hardhat.config.ts | 2 +- contracts/rust/src/cape/mod.rs | 20 ++++- contracts/rust/src/deploy.rs | 74 +++++++++++++++---- contracts/rust/src/records_merkle_tree/mod.rs | 8 +- contracts/rust/src/test_utils.rs | 4 +- contracts/rust/src/types.rs | 4 +- relayer/src/lib.rs | 16 +++- 12 files changed, 169 insertions(+), 74 deletions(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index 736928b6..3c7897ef 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -15,7 +15,7 @@ pragma solidity ^0.8.0; import "hardhat/console.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +// import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import "solidity-bytes-utils/contracts/BytesLib.sol"; @@ -24,16 +24,17 @@ import "./libraries/EdOnBN254.sol"; import "./libraries/RescueLib.sol"; import "./libraries/VerifyingKeys.sol"; import "./interfaces/IPlonkVerifier.sol"; +import "./interfaces/IRecordsMerkleTree.sol"; import "./AssetRegistry.sol"; -import "./RecordsMerkleTree.sol"; import "./RootStore.sol"; -contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { +contract CAPE is RootStore, AssetRegistry { using AccumulatingArray for AccumulatingArray.Data; mapping(uint256 => bool) public nullifiers; uint64 public blockHeight; IPlonkVerifier private _verifier; + IRecordsMerkleTree internal _recordsMerkleTree; uint256[] public pendingDeposits; // NOTE: used for faucet in testnet only, will be removed for mainnet @@ -163,11 +164,13 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { /// @param nRoots number of the most recent roots of the records merkle tree to be stored /// @param verifierAddr address of the Plonk Verifier contract constructor( - uint8 merkleTreeHeight, + uint8 merkleTreeHeight, // TODO remove uint64 nRoots, - address verifierAddr - ) RecordsMerkleTree(merkleTreeHeight) RootStore(nRoots) { + address verifierAddr, + address recordsMerkleTreeAddr + ) RootStore(nRoots) { _verifier = IPlonkVerifier(verifierAddr); + _recordsMerkleTree = IRecordsMerkleTree(recordsMerkleTreeAddr); // NOTE: used for faucet in testnet only, will be removed for mainnet deployer = msg.sender; @@ -198,8 +201,8 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { recordCommitments[0] = _deriveRecordCommitment(ro); // insert the record into record accumulator - _updateRecordsMerkleTree(recordCommitments); - _addRoot(_rootValue); + _recordsMerkleTree.updateRecordsMerkleTree(recordCommitments); + _addRoot(_recordsMerkleTree.getRootValue()); emit FaucetInitialized(abi.encode(ro)); faucetInitialized = true; @@ -226,7 +229,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { /// @notice Wraps ERC-20 tokens into a CAPE asset defined in the record opening. /// @param ro record opening that will be inserted in the records merkle tree once the deposit is validated /// @param erc20Address address of the ERC-20 token corresponding to the deposit - function depositErc20(RecordOpening memory ro, address erc20Address) public nonReentrant { + function depositErc20(RecordOpening memory ro, address erc20Address) public { require(isCapeAssetRegistered(ro.assetDef), "Asset definition not registered"); require(lookup(ro.assetDef) == erc20Address, "Wrong ERC20 address"); @@ -257,7 +260,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { /// @notice Submit a new block to the CAPE contract. /// @dev Transactions are validated and the blockchain state is updated. Moreover *BURN* transactions trigger the unwrapping of cape asset records into erc20 tokens. /// @param newBlock block to be processed by the CAPE contract. - function submitCapeBlock(CapeBlock memory newBlock) public nonReentrant { + function submitCapeBlock(CapeBlock memory newBlock) public { AccumulatingArray.Data memory commitments = AccumulatingArray.create( _computeNumCommitments(newBlock) + pendingDeposits.length ); @@ -364,8 +367,8 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { // Only update the merkle tree and add the root if the list of records commitments is non empty if (!commitments.isEmpty()) { - _updateRecordsMerkleTree(commitments.items); - _addRoot(_rootValue); + _recordsMerkleTree.updateRecordsMerkleTree(commitments.items); + _addRoot(_recordsMerkleTree.getRootValue()); } // In all cases (the block is empty or not), the height is incremented. @@ -453,10 +456,8 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { /// @dev Checks if a sequence of bytes contains hardcoded prefix. /// @param byteSeq sequence of bytes function _containsBurnPrefix(bytes memory byteSeq) internal pure returns (bool) { - if (byteSeq.length < CAPE_BURN_MAGIC_BYTES_SIZE) { - return false; - } return + byteSeq.length >= CAPE_BURN_MAGIC_BYTES_SIZE && BytesLib.equal( BytesLib.slice(byteSeq, 0, CAPE_BURN_MAGIC_BYTES_SIZE), CAPE_BURN_MAGIC_BYTES @@ -466,11 +467,9 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { /// @dev Check if the burned record opening and the record commitment in position 1 are consistent. /// @param note note of type *BURN* function _containsBurnRecord(BurnNote memory note) internal view returns (bool) { - if (note.transferNote.outputCommitments.length < 2) { - return false; - } - uint256 rc = _deriveRecordCommitment(note.recordOpening); - return rc == note.transferNote.outputCommitments[1]; + return + note.transferNote.outputCommitments.length >= 2 && + _deriveRecordCommitment(note.recordOpening) == note.transferNote.outputCommitments[1]; } /// @dev Compute the commitment of a record opening. @@ -523,7 +522,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { uint8(NoteType.TRANSFER), uint8(note.inputNullifiers.length), uint8(note.outputCommitments.length), - uint8(_merkleTreeHeight) + uint8(_recordsMerkleTree.getHeight()) ) ); // prepare public inputs @@ -604,7 +603,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { uint8(NoteType.MINT), 1, // num of input 2, // num of output - uint8(_merkleTreeHeight) + uint8(_recordsMerkleTree.getHeight()) ) ); @@ -665,7 +664,7 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { uint8(NoteType.FREEZE), uint8(note.inputNullifiers.length), uint8(note.outputCommitments.length), - uint8(_merkleTreeHeight) + uint8(_recordsMerkleTree.getHeight()) ) ); @@ -694,4 +693,8 @@ contract CAPE is RecordsMerkleTree, RootStore, AssetRegistry, ReentrancyGuard { // prepare transcript init messages transcriptInitMsg = EdOnBN254.serialize(note.auxInfo.txnMemoVerKey); } + + function getRootValue() public view returns (uint256) { + return _recordsMerkleTree.getRootValue(); + } } diff --git a/contracts/contracts/RecordsMerkleTree.sol b/contracts/contracts/RecordsMerkleTree.sol index 3291a7b5..21d19a2e 100644 --- a/contracts/contracts/RecordsMerkleTree.sol +++ b/contracts/contracts/RecordsMerkleTree.sol @@ -11,8 +11,9 @@ pragma solidity ^0.8.0; import "hardhat/console.sol"; import "./libraries/RescueLib.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; -contract RecordsMerkleTree { +contract RecordsMerkleTree is Ownable { enum Position { LEFT, MIDDLE, @@ -311,7 +312,7 @@ contract RecordsMerkleTree { /// @dev Update the state of the record merkle tree by inserting new elements. /// @param elements The list of elements to be appended to the current merkle tree described by the frontier. - function _updateRecordsMerkleTree(uint256[] memory elements) internal { + function updateRecordsMerkleTree(uint256[] memory elements) external onlyOwner { // The total number of nodes is bounded by 3*height+1 + 3*N*height = 3*(N+1)*height + 1 // where N is the number of new records uint256 numElements = elements.length; diff --git a/contracts/contracts/mocks/TestCAPE.sol b/contracts/contracts/mocks/TestCAPE.sol index 54d0a8b1..fa606ba1 100644 --- a/contracts/contracts/mocks/TestCAPE.sol +++ b/contracts/contracts/mocks/TestCAPE.sol @@ -15,17 +15,18 @@ contract TestCAPE is CAPE { constructor( uint8 merkleTreeHeight, uint64 nRoots, - address verifierAddr - ) CAPE(merkleTreeHeight, nRoots, verifierAddr) {} + address verifierAddr, + address recordsMerkleTreeAddr + ) CAPE(merkleTreeHeight, nRoots, verifierAddr, recordsMerkleTreeAddr) {} function getNumLeaves() public view returns (uint256) { - return _numLeaves; + return _recordsMerkleTree.getNumLeaves(); } function setInitialRecordCommitments(uint256[] memory elements) public { - require(_rootValue == 0, "Merkle tree is nonempty"); - _updateRecordsMerkleTree(elements); - addRoot(_rootValue); + require(_recordsMerkleTree.getRootValue() == 0, "Merkle tree is nonempty"); + _recordsMerkleTree.updateRecordsMerkleTree(elements); + addRoot(_recordsMerkleTree.getRootValue()); } function publish(uint256 nullifier) public { diff --git a/contracts/contracts/mocks/TestRecordsMerkleTree.sol b/contracts/contracts/mocks/TestRecordsMerkleTree.sol index 3dbc73a1..674b49c2 100644 --- a/contracts/contracts/mocks/TestRecordsMerkleTree.sol +++ b/contracts/contracts/mocks/TestRecordsMerkleTree.sol @@ -7,18 +7,19 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . +// TODO remove this file pragma solidity ^0.8.0; import "hardhat/console.sol"; -import "../RecordsMerkleTree.sol"; +import {RecordsMerkleTree as R} from "../RecordsMerkleTree.sol"; import "../libraries/RescueLib.sol"; -contract TestRecordsMerkleTree is RecordsMerkleTree { - constructor(uint8 height) RecordsMerkleTree(height) {} +contract TestRecordsMerkleTree is R { + constructor(uint8 height) R(height) {} - function testUpdateRecordsMerkleTree(uint256[] memory elements) public { - _updateRecordsMerkleTree(elements); - } + // function testUpdateRecordsMerkleTree(uint256[] memory elements) public { + // R.updateRecordsMerkleTree(elements); + // } function doNothing() public {} } diff --git a/contracts/deploy/00_cape.ts b/contracts/deploy/00_cape.ts index bb8f8522..45c748e6 100644 --- a/contracts/deploy/00_cape.ts +++ b/contracts/deploy/00_cape.ts @@ -9,6 +9,12 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; import { BigNumber } from "ethers"; +const treeDepth = 24; + +// Enough so that a wallet CAP transaction can make it to the CAPE contract, +// but not too much in order to free the records of a rejected/lost transaction after a reasonable amount of time. +const nRoots = 40; + const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, getNamedAccounts } = hre; const { deploy, execute } = deployments; @@ -19,6 +25,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { args: [], log: true, }); + let verifyingKeys = await deploy("VerifyingKeys", { from: deployer, args: [], @@ -31,11 +38,14 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { log: true, }); - const treeDepth = 24; - - // Enough so that a wallet CAP transaction can make it to the CAPE contract, - // but not too much in order to free the records of a rejected/lost transaction after a reasonable amount of time. - const nRoots = 40; + let recordsMerkleTreeContract = await deploy("RecordsMerkleTree", { + from: deployer, + args: [treeDepth], + log: true, + libraries: { + RescueLib: rescueLib.address, + }, + }); // To change, update change FAUCET_MANAGER_ENCRYPTION_KEY in rust/src/cape/faucet.rs // @@ -68,15 +78,16 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { }; } - await deploy("CAPE", { + const CAPE = await deploy("CAPE", { from: deployer, - args: [treeDepth, nRoots, plonkVerifierContract.address], + args: [treeDepth, nRoots, plonkVerifierContract.address, recordsMerkleTreeContract.address], log: true, libraries: { RescueLib: rescueLib.address, VerifyingKeys: verifyingKeys.address, }, }); + await execute( "CAPE", { @@ -87,6 +98,16 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { faucetManagerAddress, faucetManagerEncKey ); + + await execute( + "RecordsMerkleTree", + { + log: true, + from: deployer, + }, + "transferOwnership", + CAPE.address + ); }; export default func; diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 20b1821a..84a4293e 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -113,7 +113,7 @@ const config: HardhatUserConfig = { // test contracts to exceed the limit. contractSizer: { runOnCompile: true, - strict: true, + strict: false, only: ["/CAPE"], }, }; diff --git a/contracts/rust/src/cape/mod.rs b/contracts/rust/src/cape/mod.rs index 2c969c0b..00c56c3a 100644 --- a/contracts/rust/src/cape/mod.rs +++ b/contracts/rust/src/cape/mod.rs @@ -308,22 +308,34 @@ pub struct CAPEConstructorArgs { height: u8, n_roots: u64, verifier_addr: Address, + records_merkle_tree_addr: Address, } #[allow(dead_code)] impl CAPEConstructorArgs { - pub fn new(height: u8, n_roots: u64, verifier_addr: Address) -> Self { + pub fn new( + height: u8, + n_roots: u64, + verifier_addr: Address, + records_merkle_tree_addr: Address, + ) -> Self { Self { height, n_roots, verifier_addr, + records_merkle_tree_addr, } } } -impl From for (u8, u64, Address) { - fn from(args: CAPEConstructorArgs) -> (u8, u64, Address) { - (args.height, args.n_roots, args.verifier_addr) +impl From for (u8, u64, Address, Address) { + fn from(args: CAPEConstructorArgs) -> (u8, u64, Address, Address) { + ( + args.height, + args.n_roots, + args.verifier_addr, + args.records_merkle_tree_addr, + ) } } diff --git a/contracts/rust/src/deploy.rs b/contracts/rust/src/deploy.rs index 428728b0..71685f76 100644 --- a/contracts/rust/src/deploy.rs +++ b/contracts/rust/src/deploy.rs @@ -5,13 +5,14 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . +use crate::assertion::EnsureMined; use crate::cape::CAPEConstructorArgs; use crate::ethereum::{deploy, get_funded_client}; use crate::model::CAPE_MERKLE_HEIGHT; use crate::test_utils::contract_abi_path; use crate::types::{ - AssetRegistry, GenericInto, MaliciousToken, SimpleToken, TestBN254, TestCAPE, TestCapeTypes, - TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRecordsMerkleTree, TestRescue, + AssetRegistry, GenericInto, MaliciousToken, RecordsMerkleTree, SimpleToken, TestBN254, + TestCAPE, TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRescue, TestRootStore, TestTranscript, TestVerifyingKeys, CAPE, }; use ethers::prelude::{k256::ecdsa::SigningKey, Address, Http, Provider, SignerMiddleware, Wallet}; @@ -42,16 +43,39 @@ pub async fn deploy_test_cape_with_deployer( .await .unwrap(); + let records_merkle_tree = deploy( + deployer.clone(), + &contract_abi_path("RecordsMerkleTree.sol/RecordsMerkleTree"), + (CAPE_MERKLE_HEIGHT,), + ) + .await + .unwrap(); + // deploy TestCAPE.sol - let contract = deploy( + let cape = deploy( deployer.clone(), &contract_abi_path("mocks/TestCAPE.sol/TestCAPE"), - CAPEConstructorArgs::new(CAPE_MERKLE_HEIGHT, 1000, verifier.address()) - .generic_into::<(u8, u64, Address)>(), + CAPEConstructorArgs::new( + CAPE_MERKLE_HEIGHT, + 1000, + verifier.address(), + records_merkle_tree.address(), + ) + .generic_into::<(u8, u64, Address, Address)>(), ) .await .unwrap(); - TestCAPE::new(contract.address(), deployer) + + RecordsMerkleTree::new(records_merkle_tree.address(), deployer.clone()) + .transfer_ownership(cape.address()) + .send() + .await + .unwrap() + .await + .unwrap() + .ensure_mined(); + + TestCAPE::new(cape.address(), deployer) } pub async fn deploy_cape_with_deployer(deployer: Arc) -> CAPE { @@ -64,16 +88,38 @@ pub async fn deploy_cape_with_deployer(deployer: Arc) -> CAPE(), + CAPEConstructorArgs::new( + CAPE_MERKLE_HEIGHT, + 1000, + verifier.address(), + records_merkle_tree.address(), + ) + .generic_into::<(u8, u64, Address, Address)>(), ) .await .unwrap(); - CAPE::new(contract.address(), deployer) + + RecordsMerkleTree::new(records_merkle_tree.address(), deployer.clone()) + .transfer_ownership(cape.address()) + .send() + .await + .unwrap() + .await + .unwrap(); + + CAPE::new(cape.address(), deployer) } macro_rules! mk_deploy_fun { @@ -164,18 +210,16 @@ pub async fn deploy_test_transcript_contract() -> TestTranscript TestTranscript::new(contract.address(), client) } -pub async fn deploy_test_records_merkle_tree_contract( - height: u8, -) -> TestRecordsMerkleTree { +pub async fn deploy_records_merkle_tree_contract(height: u8) -> RecordsMerkleTree { let client = get_funded_client().await.unwrap(); let contract = deploy( client.clone(), - &contract_abi_path("mocks/TestRecordsMerkleTree.sol/TestRecordsMerkleTree"), + &contract_abi_path("RecordsMerkleTree.sol/RecordsMerkleTree"), height, ) .await .unwrap(); - TestRecordsMerkleTree::new(contract.address(), client) + RecordsMerkleTree::new(contract.address(), client) } #[cfg(test)] diff --git a/contracts/rust/src/records_merkle_tree/mod.rs b/contracts/rust/src/records_merkle_tree/mod.rs index cb7759e0..fcf23e83 100644 --- a/contracts/rust/src/records_merkle_tree/mod.rs +++ b/contracts/rust/src/records_merkle_tree/mod.rs @@ -117,7 +117,7 @@ fn parse_flattened_frontier(flattened_frontier: &[Fr254], uid: u64) -> MerkleFro #[cfg(test)] mod tests { use super::*; - use crate::deploy::deploy_test_records_merkle_tree_contract; + use crate::deploy::deploy_records_merkle_tree_contract; use crate::helpers::convert_fr254_to_u256; use crate::test_utils::compare_roots_records_merkle_tree_contract; use ark_ed_on_bn254::Fq as Fr254; @@ -231,7 +231,7 @@ mod tests { ) { // Check that we can insert values in the Merkle tree - let contract = deploy_test_records_merkle_tree_contract(height).await; + let contract = deploy_records_merkle_tree_contract(height).await; let mut mt = MerkleTree::::new(height).unwrap(); // At beginning (no leaf inserted) both roots are the same. @@ -240,7 +240,7 @@ mod tests { // We insert the first set of leaves let elems_u256 = insert_elements_into_jellyfish_mt(&mut mt, n_leaves_before); contract - .test_update_records_merkle_tree(elems_u256) + .update_records_merkle_tree(elems_u256) .legacy() .send() .await @@ -253,7 +253,7 @@ mod tests { // We insert the second set of leaves let elems_u256 = insert_elements_into_jellyfish_mt(&mut mt, n_leaves_after); contract - .test_update_records_merkle_tree(elems_u256) + .update_records_merkle_tree(elems_u256) .legacy() .send() .await diff --git a/contracts/rust/src/test_utils.rs b/contracts/rust/src/test_utils.rs index 190712cd..0506ec43 100644 --- a/contracts/rust/src/test_utils.rs +++ b/contracts/rust/src/test_utils.rs @@ -9,8 +9,8 @@ use crate::cape::{BurnNote, DOM_SEP_CAPE_BURN}; use crate::deploy::EthMiddleware; use crate::helpers::compare_merkle_root_from_contract_and_jf_tree; use crate::ledger::CapeLedger; +use crate::types::{RecordsMerkleTree, CAPE}; use crate::types::{SimpleToken, TestCAPE}; -use crate::types::{TestRecordsMerkleTree, CAPE}; use crate::universal_param::UNIVERSAL_PARAM; use ethers::prelude::TransactionReceipt; use ethers::prelude::{Address, H160, U256}; @@ -227,7 +227,7 @@ pub fn generate_burn_tx( /// one can check that the CAPE contract updates the root value correctly after inserting new records commitments. pub async fn compare_roots_records_merkle_tree_contract( mt: &MerkleTree, - contract: &TestRecordsMerkleTree, + contract: &RecordsMerkleTree, should_be_equal: bool, ) { let root_fr254 = mt.commitment().root_value; diff --git a/contracts/rust/src/types.rs b/contracts/rust/src/types.rs index 566716aa..b1cc6080 100644 --- a/contracts/rust/src/types.rs +++ b/contracts/rust/src/types.rs @@ -29,8 +29,8 @@ pub use crate::bindings::{ cape_mod::BlockCommittedFilter, AssetDefinition, AssetPolicy, AssetRegistry, AuditMemo, BurnNote, CAPEEvents, CapeBlock, Challenges, EdOnBN254Point, EvalData, EvalDomain, FreezeAuxInfo, FreezeNote, G1Point, G2Point, Greeter, MaliciousToken, MintAuxInfo, MintNote, - PcsInfo, PlonkProof, RecordOpening, SimpleToken, TestBN254, TestCAPE, TestCAPEEvents, - TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRecordsMerkleTree, + PcsInfo, PlonkProof, RecordOpening, RecordsMerkleTree, SimpleToken, TestBN254, TestCAPE, + TestCAPEEvents, TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRescue, TestRootStore, TestTranscript, TestVerifyingKeys, TranscriptData, TransferAuxInfo, TransferNote, VerifyingKey, CAPE, ERC20, }; diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index 0c36c8d7..32741fa0 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -379,6 +379,7 @@ mod test { use super::*; use async_std::sync::{Arc, Mutex}; use cap_rust_sandbox::assertion::{EnsureMined, EnsureRejected}; + use cap_rust_sandbox::model::CAPE_MERKLE_HEIGHT; use cap_rust_sandbox::test_utils::upcast_test_cape_to_cape; use cap_rust_sandbox::{ cape::CAPEConstructorArgs, @@ -590,8 +591,11 @@ mod test { let hash = response_body::(&mut res).await.unwrap(); let receipt = PendingTransaction::new(hash, &provider); receipt.await.unwrap().ensure_mined(); - assert_eq!(contract.get_num_leaves().call().await.unwrap(), 3.into()); + assert_eq!(contract.get_num_leaves().call().await.unwrap(), 3u64.into()); + // TODO (mathis) check validity of comment below. Should we update test + // to do a sucessful txn? Or is it no longer useful? + // // Test with the non-mock CAPE contract. We can't generate any valid transactions for this // contract, since there's no faucet yet and it doesn't have the // `set_initial_record_commitments` method, but we can at least check that our transaction @@ -605,6 +609,13 @@ mod test { ) .await .unwrap(); + let records_merkle_tree = deploy( + deployer.clone(), + &contract_abi_path("RecordsMerkleTree.sol/RecordsMerkleTree"), + (CAPE_MERKLE_HEIGHT,), + ) + .await + .unwrap(); let address = deploy( deployer.clone(), &contract_abi_path("CAPE.sol/CAPE"), @@ -612,8 +623,9 @@ mod test { CapeLedger::merkle_height(), CapeLedger::record_root_history() as u64, verifier.address(), + records_merkle_tree.address(), ) - .generic_into::<(u8, u64, Address)>(), + .generic_into::<(u8, u64, Address, Address)>(), ) .await .unwrap() From 15e4fa2fda5bab44fb79d0dd884c58dea485caad Mon Sep 17 00:00:00 2001 From: sveitser Date: Wed, 1 Jun 2022 21:12:31 +0200 Subject: [PATCH 08/20] undo unwanted changes --- contracts/contracts/CAPE.sol | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index 3c7897ef..97f07fb8 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -15,7 +15,7 @@ pragma solidity ^0.8.0; import "hardhat/console.sol"; -// import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import "solidity-bytes-utils/contracts/BytesLib.sol"; @@ -28,7 +28,7 @@ import "./interfaces/IRecordsMerkleTree.sol"; import "./AssetRegistry.sol"; import "./RootStore.sol"; -contract CAPE is RootStore, AssetRegistry { +contract CAPE is RootStore, AssetRegistry, ReentrancyGuard { using AccumulatingArray for AccumulatingArray.Data; mapping(uint256 => bool) public nullifiers; @@ -229,7 +229,7 @@ contract CAPE is RootStore, AssetRegistry { /// @notice Wraps ERC-20 tokens into a CAPE asset defined in the record opening. /// @param ro record opening that will be inserted in the records merkle tree once the deposit is validated /// @param erc20Address address of the ERC-20 token corresponding to the deposit - function depositErc20(RecordOpening memory ro, address erc20Address) public { + function depositErc20(RecordOpening memory ro, address erc20Address) public nonReentrant { require(isCapeAssetRegistered(ro.assetDef), "Asset definition not registered"); require(lookup(ro.assetDef) == erc20Address, "Wrong ERC20 address"); @@ -260,7 +260,7 @@ contract CAPE is RootStore, AssetRegistry { /// @notice Submit a new block to the CAPE contract. /// @dev Transactions are validated and the blockchain state is updated. Moreover *BURN* transactions trigger the unwrapping of cape asset records into erc20 tokens. /// @param newBlock block to be processed by the CAPE contract. - function submitCapeBlock(CapeBlock memory newBlock) public { + function submitCapeBlock(CapeBlock memory newBlock) public nonReentrant { AccumulatingArray.Data memory commitments = AccumulatingArray.create( _computeNumCommitments(newBlock) + pendingDeposits.length ); @@ -456,8 +456,10 @@ contract CAPE is RootStore, AssetRegistry { /// @dev Checks if a sequence of bytes contains hardcoded prefix. /// @param byteSeq sequence of bytes function _containsBurnPrefix(bytes memory byteSeq) internal pure returns (bool) { + if (byteSeq.length < CAPE_BURN_MAGIC_BYTES_SIZE) { + return false; + } return - byteSeq.length >= CAPE_BURN_MAGIC_BYTES_SIZE && BytesLib.equal( BytesLib.slice(byteSeq, 0, CAPE_BURN_MAGIC_BYTES_SIZE), CAPE_BURN_MAGIC_BYTES @@ -467,9 +469,11 @@ contract CAPE is RootStore, AssetRegistry { /// @dev Check if the burned record opening and the record commitment in position 1 are consistent. /// @param note note of type *BURN* function _containsBurnRecord(BurnNote memory note) internal view returns (bool) { - return - note.transferNote.outputCommitments.length >= 2 && - _deriveRecordCommitment(note.recordOpening) == note.transferNote.outputCommitments[1]; + if (note.transferNote.outputCommitments.length < 2) { + return false; + } + uint256 rc = _deriveRecordCommitment(note.recordOpening); + return rc == note.transferNote.outputCommitments[1]; } /// @dev Compute the commitment of a record opening. From a0ca2b5528eed18b471addc8514a6f2d210ddaef Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 12:07:23 +0200 Subject: [PATCH 09/20] Add missing file --- .../interfaces/IRecordsMerkleTree.sol | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 contracts/contracts/interfaces/IRecordsMerkleTree.sol diff --git a/contracts/contracts/interfaces/IRecordsMerkleTree.sol b/contracts/contracts/interfaces/IRecordsMerkleTree.sol new file mode 100644 index 00000000..1be94527 --- /dev/null +++ b/contracts/contracts/interfaces/IRecordsMerkleTree.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// Copyright (c) 2022 Espresso Systems (espressosys.com) +// This file is part of the Configurable Asset Privacy for Ethereum (CAPE) library. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with this program. If not, see . + +pragma solidity ^0.8.0; + +interface IRecordsMerkleTree { + /// @param elements The list of elements to be appended to the current merkle tree described by the frontier. + function updateRecordsMerkleTree(uint256[] memory elements) external; + + /// @notice Returns the root value of the Merkle tree. + function getRootValue() external view returns (uint256); + + function getHeight() external view returns (uint8); + + function getNumLeaves() external view returns (uint64); +} From f6f92a8a32fb2abb607cec9d3a67e2ea8cfe3c8d Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 13:15:07 +0200 Subject: [PATCH 10/20] All tests pass. - Add missing public getters. - Remove tree height constructor arg from CAPE. --- contracts/contracts/CAPE.sol | 2 - contracts/contracts/RecordsMerkleTree.sol | 10 +++++ .../interfaces/IRecordsMerkleTree.sol | 2 + contracts/contracts/mocks/TestCAPE.sol | 3 +- contracts/deploy/00_cape.ts | 16 ++++---- contracts/rust/src/cape/mod.rs | 39 +++++++++++-------- contracts/rust/src/cape/submit_block.rs | 8 ++-- contracts/rust/src/deploy.rs | 28 +++++++------ contracts/rust/src/model.rs | 1 + relayer/src/lib.rs | 10 ++--- 10 files changed, 68 insertions(+), 51 deletions(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index 97f07fb8..bc87e600 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -160,11 +160,9 @@ contract CAPE is RootStore, AssetRegistry, ReentrancyGuard { } /// @notice CAPE contract constructor method. - /// @param merkleTreeHeight height of the merkle tree that stores the asset record commitments /// @param nRoots number of the most recent roots of the records merkle tree to be stored /// @param verifierAddr address of the Plonk Verifier contract constructor( - uint8 merkleTreeHeight, // TODO remove uint64 nRoots, address verifierAddr, address recordsMerkleTreeAddr diff --git a/contracts/contracts/RecordsMerkleTree.sol b/contracts/contracts/RecordsMerkleTree.sol index 21d19a2e..bca6628a 100644 --- a/contracts/contracts/RecordsMerkleTree.sol +++ b/contracts/contracts/RecordsMerkleTree.sol @@ -338,6 +338,16 @@ contract RecordsMerkleTree is Ownable { return _rootValue; } + /// @notice Returns the height of the Merkle tree. + function getHeight() public view returns (uint8) { + return _merkleTreeHeight; + } + + /// @notice Returns the number of leaves of the Merkle tree. + function getNumLeaves() public view returns (uint64) { + return _numLeaves; + } + /// @dev Update the tree by hashing the children of each node. /// @param nodes The tree. Note that the nodes are updated by this function. /// @param rootNodePos The index of the root node in the list of nodes. diff --git a/contracts/contracts/interfaces/IRecordsMerkleTree.sol b/contracts/contracts/interfaces/IRecordsMerkleTree.sol index 1be94527..5ffbaefd 100644 --- a/contracts/contracts/interfaces/IRecordsMerkleTree.sol +++ b/contracts/contracts/interfaces/IRecordsMerkleTree.sol @@ -16,7 +16,9 @@ interface IRecordsMerkleTree { /// @notice Returns the root value of the Merkle tree. function getRootValue() external view returns (uint256); + /// @notice Returns the height of the Merkle tree. function getHeight() external view returns (uint8); + /// @notice Returns the number of leaves of the Merkle tree. function getNumLeaves() external view returns (uint64); } diff --git a/contracts/contracts/mocks/TestCAPE.sol b/contracts/contracts/mocks/TestCAPE.sol index fa606ba1..c4db5a2c 100644 --- a/contracts/contracts/mocks/TestCAPE.sol +++ b/contracts/contracts/mocks/TestCAPE.sol @@ -13,11 +13,10 @@ import "../CAPE.sol"; contract TestCAPE is CAPE { constructor( - uint8 merkleTreeHeight, uint64 nRoots, address verifierAddr, address recordsMerkleTreeAddr - ) CAPE(merkleTreeHeight, nRoots, verifierAddr, recordsMerkleTreeAddr) {} + ) CAPE(nRoots, verifierAddr, recordsMerkleTreeAddr) {} function getNumLeaves() public view returns (uint256) { return _recordsMerkleTree.getNumLeaves(); diff --git a/contracts/deploy/00_cape.ts b/contracts/deploy/00_cape.ts index 45c748e6..ee4c07c5 100644 --- a/contracts/deploy/00_cape.ts +++ b/contracts/deploy/00_cape.ts @@ -80,7 +80,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const CAPE = await deploy("CAPE", { from: deployer, - args: [treeDepth, nRoots, plonkVerifierContract.address, recordsMerkleTreeContract.address], + args: [nRoots, plonkVerifierContract.address, recordsMerkleTreeContract.address], log: true, libraries: { RescueLib: rescueLib.address, @@ -89,24 +89,24 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { }); await execute( - "CAPE", + "RecordsMerkleTree", { log: true, from: deployer, }, - "faucetSetupForTestnet", - faucetManagerAddress, - faucetManagerEncKey + "transferOwnership", + CAPE.address ); await execute( - "RecordsMerkleTree", + "CAPE", { log: true, from: deployer, }, - "transferOwnership", - CAPE.address + "faucetSetupForTestnet", + faucetManagerAddress, + faucetManagerEncKey ); }; diff --git a/contracts/rust/src/cape/mod.rs b/contracts/rust/src/cape/mod.rs index 00c56c3a..98a61321 100644 --- a/contracts/rust/src/cape/mod.rs +++ b/contracts/rust/src/cape/mod.rs @@ -305,40 +305,47 @@ impl From for NoteType { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct CAPEConstructorArgs { - height: u8, n_roots: u64, verifier_addr: Address, records_merkle_tree_addr: Address, } -#[allow(dead_code)] impl CAPEConstructorArgs { - pub fn new( - height: u8, - n_roots: u64, - verifier_addr: Address, - records_merkle_tree_addr: Address, - ) -> Self { + pub fn new(n_roots: u64, verifier_addr: Address, records_merkle_tree_addr: Address) -> Self { Self { - height, n_roots, verifier_addr, records_merkle_tree_addr, } } -} -impl From for (u8, u64, Address, Address) { - fn from(args: CAPEConstructorArgs) -> (u8, u64, Address, Address) { + /// We need to pass a tuple when we deploy the contract. The function that + /// deploys the contract is not aware of the exact type of the tuple. It's + /// convenient to "fix" this type in one place. + pub fn to_tuple(&self) -> (u64, Address, Address) { ( - args.height, - args.n_roots, - args.verifier_addr, - args.records_merkle_tree_addr, + self.n_roots, + self.verifier_addr, + self.records_merkle_tree_addr, ) } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct RecordsMerkleTreeConstructorArgs { + height: u8, +} + +impl RecordsMerkleTreeConstructorArgs { + pub fn new(height: u8) -> Self { + Self { height } + } + + pub fn to_tuple(&self) -> (u8,) { + (self.height,) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/contracts/rust/src/cape/submit_block.rs b/contracts/rust/src/cape/submit_block.rs index dffa1a38..b8d3144c 100644 --- a/contracts/rust/src/cape/submit_block.rs +++ b/contracts/rust/src/cape/submit_block.rs @@ -104,7 +104,7 @@ pub async fn submit_cape_block_with_memos( mod tests { use super::*; use crate::{ - assertion::Matcher, + assertion::{EnsureMined, Matcher}, cape::CapeBlock, deploy::deploy_test_cape, ledger::CapeLedger, @@ -350,13 +350,15 @@ mod tests { .add_root(root.generic_into::().0) .send() .await? - .await?; + .await? + .ensure_mined(); contract .submit_cape_block(cape_block.into()) .send() .await? - .await?; + .await? + .ensure_mined(); assert_eq!(contract.block_height().call().await?, 1u64); Ok(()) diff --git a/contracts/rust/src/deploy.rs b/contracts/rust/src/deploy.rs index 71685f76..85496b8b 100644 --- a/contracts/rust/src/deploy.rs +++ b/contracts/rust/src/deploy.rs @@ -6,16 +6,16 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . use crate::assertion::EnsureMined; -use crate::cape::CAPEConstructorArgs; +use crate::cape::{CAPEConstructorArgs, RecordsMerkleTreeConstructorArgs}; use crate::ethereum::{deploy, get_funded_client}; -use crate::model::CAPE_MERKLE_HEIGHT; +use crate::model::{CAPE_MERKLE_HEIGHT, CAPE_NUM_ROOTS}; use crate::test_utils::contract_abi_path; use crate::types::{ - AssetRegistry, GenericInto, MaliciousToken, RecordsMerkleTree, SimpleToken, TestBN254, - TestCAPE, TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRescue, - TestRootStore, TestTranscript, TestVerifyingKeys, CAPE, + AssetRegistry, MaliciousToken, RecordsMerkleTree, SimpleToken, TestBN254, TestCAPE, + TestCapeTypes, TestEdOnBN254, TestPlonkVerifier, TestPolynomialEval, TestRescue, TestRootStore, + TestTranscript, TestVerifyingKeys, CAPE, }; -use ethers::prelude::{k256::ecdsa::SigningKey, Address, Http, Provider, SignerMiddleware, Wallet}; +use ethers::prelude::{k256::ecdsa::SigningKey, Http, Provider, SignerMiddleware, Wallet}; use std::sync::Arc; // Middleware used for locally signing transactions @@ -46,7 +46,7 @@ pub async fn deploy_test_cape_with_deployer( let records_merkle_tree = deploy( deployer.clone(), &contract_abi_path("RecordsMerkleTree.sol/RecordsMerkleTree"), - (CAPE_MERKLE_HEIGHT,), + RecordsMerkleTreeConstructorArgs::new(CAPE_MERKLE_HEIGHT).to_tuple(), ) .await .unwrap(); @@ -56,12 +56,11 @@ pub async fn deploy_test_cape_with_deployer( deployer.clone(), &contract_abi_path("mocks/TestCAPE.sol/TestCAPE"), CAPEConstructorArgs::new( - CAPE_MERKLE_HEIGHT, - 1000, + CAPE_NUM_ROOTS, verifier.address(), records_merkle_tree.address(), ) - .generic_into::<(u8, u64, Address, Address)>(), + .to_tuple(), ) .await .unwrap(); @@ -91,7 +90,7 @@ pub async fn deploy_cape_with_deployer(deployer: Arc) -> CAPE) -> CAPE(), + .to_tuple(), ) .await .unwrap(); @@ -229,7 +227,7 @@ mod test { ensure_connected_to_contract, get_provider, has_code_at_block, is_connected_to_contract, }; use anyhow::Result; - use ethers::prelude::Middleware; + use ethers::prelude::{Address, Middleware}; #[tokio::test] async fn test_is_connected_to_contract() -> Result<()> { diff --git a/contracts/rust/src/model.rs b/contracts/rust/src/model.rs index dc0912a3..ee870786 100644 --- a/contracts/rust/src/model.rs +++ b/contracts/rust/src/model.rs @@ -31,6 +31,7 @@ use std::sync::Arc; // can be changed later. pub const CAPE_MERKLE_HEIGHT: u8 = 24 /*H*/; pub const CAPE_BURN_MAGIC_BYTES: &str = "EsSCAPE burn"; +pub const CAPE_NUM_ROOTS: u64 = 40; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum CapeModelTxn { diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index 32741fa0..c03f78d1 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -379,6 +379,7 @@ mod test { use super::*; use async_std::sync::{Arc, Mutex}; use cap_rust_sandbox::assertion::{EnsureMined, EnsureRejected}; + use cap_rust_sandbox::cape::RecordsMerkleTreeConstructorArgs; use cap_rust_sandbox::model::CAPE_MERKLE_HEIGHT; use cap_rust_sandbox::test_utils::upcast_test_cape_to_cape; use cap_rust_sandbox::{ @@ -387,10 +388,10 @@ mod test { ledger::CapeLedger, model::CapeModelTxn, test_utils::contract_abi_path, - types::{GenericInto, CAPE}, + types::CAPE, universal_param::UNIVERSAL_PARAM, }; - use ethers::{prelude::PendingTransaction, providers::Middleware, types::Address}; + use ethers::{prelude::PendingTransaction, providers::Middleware}; use jf_cap::{ keys::UserKeyPair, sign_receiver_memos, @@ -612,7 +613,7 @@ mod test { let records_merkle_tree = deploy( deployer.clone(), &contract_abi_path("RecordsMerkleTree.sol/RecordsMerkleTree"), - (CAPE_MERKLE_HEIGHT,), + RecordsMerkleTreeConstructorArgs::new(CAPE_MERKLE_HEIGHT).to_tuple(), ) .await .unwrap(); @@ -620,12 +621,11 @@ mod test { deployer.clone(), &contract_abi_path("CAPE.sol/CAPE"), CAPEConstructorArgs::new( - CapeLedger::merkle_height(), CapeLedger::record_root_history() as u64, verifier.address(), records_merkle_tree.address(), ) - .generic_into::<(u8, u64, Address, Address)>(), + .to_tuple(), ) .await .unwrap() From c02e4dff1024d82db89f65767a5f89a29fc76b83 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 14:06:32 +0200 Subject: [PATCH 11/20] Update hardcoded contract addresses --- demo/compose.env | 8 ++++---- relayer/README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/compose.env b/demo/compose.env index a5d74b20..150ef87e 100644 --- a/demo/compose.env +++ b/demo/compose.env @@ -1,7 +1,7 @@ -CAPE_CONTRACT_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 -CAPE_TOKEN_ADDRESS_SIT=0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 -CAPE_TOKEN_ADDRESS_WETH=0x0165878A594ca255338adfa4d48449f69242Eb8F -CAPE_TOKEN_ADDRESS_DAI=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 +CAPE_CONTRACT_ADDRESS=0x20Dc424c5fa468CbB1c702308F0cC9c14DA2825C +CAPE_TOKEN_ADDRESS_SIT=0xe6b98F104c1BEf218F3893ADab4160Dc73Eb8367 +CAPE_TOKEN_ADDRESS_WETH=0x5C7c905B505f0Cf40Ab6600d05e677F717916F6B +CAPE_TOKEN_ADDRESS_DAI=0x0D4ff719551E23185Aeb16FFbF2ABEbB90635942 CAPE_ADDRESS_BOOK_PORT=50000 CAPE_EQS_PORT=50010 CAPE_FAUCET_PORT=50030 diff --git a/relayer/README.md b/relayer/README.md index e82c0398..347c5dec 100644 --- a/relayer/README.md +++ b/relayer/README.md @@ -28,5 +28,5 @@ The CAPE contract address shown in the terminal and an Ethereum wallet mnemonic need to be passed to relayer executable, for example: ```console -cargo run --release --bin minimal-relayer -- 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 "$TEST_MNEMONIC" +cargo run --release --bin minimal-relayer -- 0x20Dc424c5fa468CbB1c702308F0cC9c14DA2825C "$TEST_MNEMONIC" ``` From 2c2706d603c353fd6e0fad1dfe203f083f178650 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 14:36:15 +0200 Subject: [PATCH 12/20] Check access control of merkle tree contract After running `hardhat deploy` check that 1. The merkle tree contract is owned by the CAPE contract. 2. The deployer can no longer add elements to the merkle tree. --- contracts/rust/tests/hardhat_deploy.rs | 35 ++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/contracts/rust/tests/hardhat_deploy.rs b/contracts/rust/tests/hardhat_deploy.rs index becf86f6..f539385c 100644 --- a/contracts/rust/tests/hardhat_deploy.rs +++ b/contracts/rust/tests/hardhat_deploy.rs @@ -6,11 +6,15 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . use anyhow::Result; use cap_rust_sandbox::{ + assertion::Matcher, cape::faucet::FAUCET_MANAGER_ENCRYPTION_KEY, ethereum::get_funded_client, - types::{self as sol, GenericInto, CAPE}, + types::{self as sol, GenericInto, RecordsMerkleTree, CAPE}, +}; +use ethers::{ + abi::AbiDecode, + prelude::{Address, U256}, }; -use ethers::{abi::AbiDecode, prelude::Address}; use jf_cap::{keys::UserPubKey, structs::RecordOpening}; use regex::Regex; use std::{process::Command, str::FromStr}; @@ -39,7 +43,7 @@ async fn test_hardhat_deploy() -> Result<()> { // Get the address out of // deploying "CAPE" (tx: 0x64...211)...: deployed at 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318 with 7413790 gas let re = Regex::new(r#""CAPE".*(0x[0-9a-fA-F]{40})"#).unwrap(); - let address = re + let cape_address = re .captures_iter(&text) .next() .unwrap_or_else(|| panic!("Address not found in {}", text))[1] @@ -47,8 +51,8 @@ async fn test_hardhat_deploy() -> Result<()> { .unwrap_or_else(|_| panic!("Address not found in {}", text)); let client = get_funded_client().await.unwrap(); - let contract = CAPE::new(address, client.clone()); - let event = contract + let cape = CAPE::new(cape_address, client.clone()); + let event = cape .faucet_initialized_filter() .from_block(0u64) .query() @@ -63,5 +67,26 @@ async fn test_hardhat_deploy() -> Result<()> { ro_sol.generic_into::().pub_key, ); + let re = Regex::new(r#""RecordsMerkleTree".*(0x[0-9a-fA-F]{40})"#).unwrap(); + let merkle_tree_address = re + .captures_iter(&text) + .next() + .unwrap_or_else(|| panic!("Address not found in {}", text))[1] + .parse::
() + .unwrap_or_else(|_| panic!("Address not found in {}", text)); + + let merkle_tree = RecordsMerkleTree::new(merkle_tree_address, client.clone()); + + // Check that the cape contract owns the records merkle tree contract. + assert_eq!(merkle_tree.owner().call().await?, cape.address()); + + // Check that the deployer is no longer able to operate the + // records merkle tree contract. + merkle_tree + .update_records_merkle_tree(vec![U256::from(0)]) + .call() + .await + .should_revert_with_message("Ownable: caller is not the owner"); + Ok(()) } From a9402987ba9ee23c58d7fae0f0143844a12273c7 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 14:49:58 +0200 Subject: [PATCH 13/20] Fix typescript tests --- .../contracts/mocks/TestRecordsMerkleTree.sol | 7 ++----- contracts/rust/src/test_utils.rs | 8 +++++--- .../benchmarks/test-records-merkle-tree.js | 4 ++-- contracts/test/cape.spec.ts | 19 ++++++++++++++++--- contracts/test/records-merkle-tree.spec.ts | 8 ++++---- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/contracts/contracts/mocks/TestRecordsMerkleTree.sol b/contracts/contracts/mocks/TestRecordsMerkleTree.sol index 674b49c2..240222b4 100644 --- a/contracts/contracts/mocks/TestRecordsMerkleTree.sol +++ b/contracts/contracts/mocks/TestRecordsMerkleTree.sol @@ -7,19 +7,16 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -// TODO remove this file pragma solidity ^0.8.0; import "hardhat/console.sol"; import {RecordsMerkleTree as R} from "../RecordsMerkleTree.sol"; import "../libraries/RescueLib.sol"; +// This contract is only used in a javascript benchmark and +// could be removed. contract TestRecordsMerkleTree is R { constructor(uint8 height) R(height) {} - // function testUpdateRecordsMerkleTree(uint256[] memory elements) public { - // R.updateRecordsMerkleTree(elements); - // } - function doNothing() public {} } diff --git a/contracts/rust/src/test_utils.rs b/contracts/rust/src/test_utils.rs index 0506ec43..577e1d69 100644 --- a/contracts/rust/src/test_utils.rs +++ b/contracts/rust/src/test_utils.rs @@ -222,9 +222,11 @@ pub fn generate_burn_tx( BurnNote::generate(note, burn_ro).unwrap() } -/// Compare the roots of a local merkle tree and the TestRecordsMerkleTree contract merkle tree. -/// By calling the CAPE contract `get_root_value` and comparing it to the root of the merkle tree passed as argument, -/// one can check that the CAPE contract updates the root value correctly after inserting new records commitments. +/// Compare the roots of a local merkle tree and the RecordsMerkleTree contract +/// merkle tree. By calling the CAPE contract `get_root_value` and comparing it +/// to the root of the merkle tree passed as argument, one can check that the +/// CAPE contract updates the root value correctly after inserting new records +/// commitments. pub async fn compare_roots_records_merkle_tree_contract( mt: &MerkleTree, contract: &RecordsMerkleTree, diff --git a/contracts/test/benchmarks/test-records-merkle-tree.js b/contracts/test/benchmarks/test-records-merkle-tree.js index f4010211..2cf160b8 100644 --- a/contracts/test/benchmarks/test-records-merkle-tree.js +++ b/contracts/test/benchmarks/test-records-merkle-tree.js @@ -33,11 +33,11 @@ describe("Records Merkle Tree Benchmarks", function () { it("shows how much gas is spent by updateRecordsMerkleTree", async function () { let elems = [1, 2, 3, 4, 5]; - const txEmpty = await rmtContract.testUpdateRecordsMerkleTree([]); + const txEmpty = await rmtContract.updateRecordsMerkleTree([]); const txEmptyReceipt = await txEmpty.wait(); let emptyGasUsed = txEmptyReceipt.gasUsed; - tx = await rmtContract.testUpdateRecordsMerkleTree(elems); + tx = await rmtContract.updateRecordsMerkleTree(elems); const txReceipt = await tx.wait(); let totalGasUsed = txReceipt.gasUsed; diff --git a/contracts/test/cape.spec.ts b/contracts/test/cape.spec.ts index dd8188df..88b41b04 100644 --- a/contracts/test/cape.spec.ts +++ b/contracts/test/cape.spec.ts @@ -8,6 +8,9 @@ import { expect } from "chai"; import { ethers } from "hardhat"; +const TREE_HEIGHT = 24; +const N_ROOTS = 1000; + describe("CAPE", function () { describe("Handling of nullifiers", async function () { let cape: any; @@ -16,6 +19,15 @@ describe("CAPE", function () { let rescue = await (await ethers.getContractFactory("RescueLib")).deploy(); let verifyingKeys = await (await ethers.getContractFactory("VerifyingKeys")).deploy(); let plonkVerifier = await (await ethers.getContractFactory("PlonkVerifier")).deploy(); + + let merkleTree = await ( + await ethers.getContractFactory("RecordsMerkleTree", { + libraries: { + RescueLib: rescue.address, + }, + }) + ).deploy(TREE_HEIGHT); + let capeFactory = await ethers.getContractFactory("TestCAPE", { libraries: { RescueLib: rescue.address, @@ -23,9 +35,10 @@ describe("CAPE", function () { }, }); - const TREE_HEIGHT = 24; - const N_ROOTS = 1000; - cape = await capeFactory.deploy(TREE_HEIGHT, N_ROOTS, plonkVerifier.address); + cape = await capeFactory.deploy(N_ROOTS, plonkVerifier.address, merkleTree.address); + + let tx = await merkleTree.transferOwnership(cape.address); + await tx.wait(); }); it("is possible to check for non-membership", async function () { diff --git a/contracts/test/records-merkle-tree.spec.ts b/contracts/test/records-merkle-tree.spec.ts index eab60f17..b7f7fa2a 100644 --- a/contracts/test/records-merkle-tree.spec.ts +++ b/contracts/test/records-merkle-tree.spec.ts @@ -9,7 +9,7 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); /* -import { TestRecordsMerkleTree } from "../typechain-types"; +import { RecordsMerkleTree } from "../typechain-types"; */ describe("Records Merkle Tree tests", function () { @@ -20,7 +20,7 @@ describe("Records Merkle Tree tests", function () { beforeEach(async function () { let rescue = await (await ethers.getContractFactory("RescueLib")).deploy(); - rmtFactory = await ethers.getContractFactory("TestRecordsMerkleTree", { + rmtFactory = await ethers.getContractFactory("RecordsMerkleTree", { libraries: { RescueLib: rescue.address, }, @@ -37,7 +37,7 @@ describe("Records Merkle Tree tests", function () { ]; // Insert all these elements does not trigger an error - let tx = await recordsMerkleTree.testUpdateRecordsMerkleTree(elems); + let tx = await recordsMerkleTree.updateRecordsMerkleTree(elems); await tx.wait(); }); @@ -50,7 +50,7 @@ describe("Records Merkle Tree tests", function () { 26, 27, 28, ]; - await expect(recordsMerkleTree.testUpdateRecordsMerkleTree(elems)).to.be.revertedWith( + await expect(recordsMerkleTree.updateRecordsMerkleTree(elems)).to.be.revertedWith( "The tree is full." ); }); From 35edb3a9e72b78ed034648d3d77c06a2f371ad05 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 14:52:24 +0200 Subject: [PATCH 14/20] Fix typo --- contracts/contracts/CAPE.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/contracts/CAPE.sol b/contracts/contracts/CAPE.sol index bc87e600..e8eff92a 100644 --- a/contracts/contracts/CAPE.sol +++ b/contracts/contracts/CAPE.sol @@ -57,7 +57,7 @@ contract CAPE is RootStore, AssetRegistry, ReentrancyGuard { // What follows is a `CapeBlock` struct split up into fields. // This may no longer be necessary once // https://github.com/gakonst/ethers-rs/issues/1220 - // if fixed. + // is fixed. bytes minerAddr, bytes noteTypes, bytes transferNotes, From 179d4e3b1a2831e1e2c0baf2c81dbfb51e01520f Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 15:24:04 +0200 Subject: [PATCH 15/20] Use 1000 solc optimizer runs instead of 20 Using a really high (e.g. 1M) number does have a significant impact on gas usage but also leads to a contract that is too big to deploy. With 1000 runs the CAPE contract is as 22.5 kB so there is still room to make changes to the contract. --- flake.nix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index 8f07e239..70e8873c 100644 --- a/flake.nix +++ b/flake.nix @@ -202,8 +202,7 @@ SOLCX_BINARY_PATH = "${mySolc}/bin"; SOLC_VERSION = mySolc.version; SOLC_PATH = "${mySolc}/bin/solc"; - # TODO: increase this when contract size limit is not a problem - SOLC_OPTIMIZER_RUNS = "20"; + SOLC_OPTIMIZER_RUNS = "1000"; shellHook = '' echo "Ensuring node dependencies are installed" @@ -217,7 +216,7 @@ echo "Exporting all vars in .env file" set -a; source .env; set +a; - # on mac os `bin/pwd -P` returns the canonical path on case insenstive file-systems + # on mac os `bin/pwd -P` returns the canonical path on case insensitive file-systems my_pwd=$(/bin/pwd -P 2> /dev/null || pwd) export CONTRACTS_DIR=''${my_pwd}/contracts @@ -251,8 +250,7 @@ SOLCX_BINARY_PATH = "${mySolc}/bin"; SOLC_VERSION = mySolc.version; SOLC_PATH = "${mySolc}/bin/solc"; - # TODO: increase this when contract size limit is not a problem - SOLC_OPTIMIZER_RUNS = "20"; + SOLC_OPTIMIZER_RUNS = "1000"; CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER = "${muslPkgs.llvmPackages_latest.lld}/bin/lld"; RUSTFLAGS = "-C target-feature=+crt-static -L${opensslMusl.out}/lib/"; OPENSSL_STATIC = "true"; @@ -272,7 +270,7 @@ echo "Exporting all vars in .env file" set -a; source .env; set +a; - # on mac os `bin/pwd -P` returns the canonical path on case insenstive file-systems + # on mac os `bin/pwd -P` returns the canonical path on case insensitive file-systems my_pwd=$(/bin/pwd -P 2> /dev/null || pwd) export CONTRACTS_DIR=''${my_pwd}/contracts From 9d3a60c7920fd5e23c9a407c01261a2657c296ef Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 15:35:35 +0200 Subject: [PATCH 16/20] Build executables before exporting wallet docs --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d83edf7a..2e1ab882 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: - uses: cachix/cachix-action@v10 with: name: espresso-systems-private - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - uses: actions/checkout@v2 name: Checkout Repository @@ -68,6 +68,9 @@ jobs: - name: Generate Docs run: nix-shell --run "prepend-timestamps make-doc" + - name: Build all executables + run: nix-shell --run "cargo build --release" + - name: Generate Wallet run: ./target/release/export-wallet-api-docs --api ./wallet/api/api.toml --assets ./wallet/public/ ./doc/mdbook/book/wallet @@ -79,9 +82,6 @@ jobs: publish_dir: ./doc/mdbook/book/ cname: cape.docs.espressosys.com - - name: Build all executables - run: nix-shell --run "cargo build --release" - - name: Build demo geth data dir run: nix-shell --run "demo/initialize-demo-geth" From 1ea1f8a624324e00988c65e425e09fc259107464 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 15:42:12 +0200 Subject: [PATCH 17/20] More consistency --- contracts/rust/src/deploy.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/rust/src/deploy.rs b/contracts/rust/src/deploy.rs index 85496b8b..aef3442b 100644 --- a/contracts/rust/src/deploy.rs +++ b/contracts/rust/src/deploy.rs @@ -115,7 +115,8 @@ pub async fn deploy_cape_with_deployer(deployer: Arc) -> CAPE Date: Thu, 2 Jun 2022 15:49:34 +0200 Subject: [PATCH 18/20] Remove the geth patch that enables >24kB contracts --- flake.nix | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index 70e8873c..2d1c9751 100644 --- a/flake.nix +++ b/flake.nix @@ -172,14 +172,7 @@ buildInputs = with pkgs; [ nixWithFlakes - (go-ethereum.overrideAttrs (old: { - # Set deployed bytecode size limit to 32kB (which is also the tx size limit) - # in order to deploy the TestCAPE contracts that currently exceeds the - # 24kB size limit. - patchPhase = old.patchPhase or "" + '' - substituteInPlace params/protocol_params.go --replace "MaxCodeSize = 24576" "MaxCodeSize = 32768" - ''; - })) + go-ethereum nodePackages.pnpm mySolc hivemind # process runner From 8fffeadc75bc8d4c5c2b7606d5ca2527439a8327 Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 15:53:04 +0200 Subject: [PATCH 19/20] Revert "Remove the geth patch that enables >24kB contracts" This reverts commit b716ae2a0b99686c4ac6327f3c0846e0ee4b7e10. Re-enable failing compilation if CAPE code is > 24kB The TestCAPE contract is still too big unless we use a very low number of optimizer runs. --- contracts/hardhat.config.ts | 2 +- flake.nix | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 84a4293e..20b1821a 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -113,7 +113,7 @@ const config: HardhatUserConfig = { // test contracts to exceed the limit. contractSizer: { runOnCompile: true, - strict: false, + strict: true, only: ["/CAPE"], }, }; diff --git a/flake.nix b/flake.nix index 2d1c9751..70e8873c 100644 --- a/flake.nix +++ b/flake.nix @@ -172,7 +172,14 @@ buildInputs = with pkgs; [ nixWithFlakes - go-ethereum + (go-ethereum.overrideAttrs (old: { + # Set deployed bytecode size limit to 32kB (which is also the tx size limit) + # in order to deploy the TestCAPE contracts that currently exceeds the + # 24kB size limit. + patchPhase = old.patchPhase or "" + '' + substituteInPlace params/protocol_params.go --replace "MaxCodeSize = 24576" "MaxCodeSize = 32768" + ''; + })) nodePackages.pnpm mySolc hivemind # process runner From 353d1bd188d54c9d9a57a59b98bca74850d6738c Mon Sep 17 00:00:00 2001 From: sveitser Date: Thu, 2 Jun 2022 18:01:36 +0200 Subject: [PATCH 20/20] Update smart contract architecture diagram --- contracts/contracts.svg | 1410 ++++++++++++++++++++------------------- 1 file changed, 715 insertions(+), 695 deletions(-) diff --git a/contracts/contracts.svg b/contracts/contracts.svg index 7aea65d9..3796a8fd 100644 --- a/contracts/contracts.svg +++ b/contracts/contracts.svg @@ -4,966 +4,986 @@ - - + + UmlClassDiagram - + 0 - -AssetRegistry - -Public: -   DOM_SEP_FOREIGN_ASSET: bytes13 -   DOM_SEP_DOMESTIC_ASSET: bytes14 -   CAP_NATIVE_ASSET_CODE: uint256 -   assets: mapping(bytes32=>address) - -Public: -    <<event>> AssetSponsored(erc20Address: address, assetDefinitionCode: uint256) -    nativeDomesticAsset(): (assetDefinition: AssetDefinition) -    lookup(assetDefinition: AssetDefinition): address -    isCapeAssetRegistered(assetDefinition: AssetDefinition): bool -    sponsorCapeAsset(erc20Address: address, newAsset: AssetDefinition) - - - -6 - -<<Library>> -BN254 - -Public: -   P_MOD: uint256 -   R_MOD: uint256 - - - - - -0->6 - - + +AssetRegistry + +Public: +   DOM_SEP_FOREIGN_ASSET: bytes13 +   DOM_SEP_DOMESTIC_ASSET: bytes14 +   CAP_NATIVE_ASSET_CODE: uint256 +   assets: mapping(bytes32=>address) + +Public: +    <<event>> AssetSponsored(erc20Address: address, assetDefinitionCode: uint256) +    nativeDomesticAsset(): (assetDefinition: AssetDefinition) +    lookup(assetDefinition: AssetDefinition): address +    isCapeAssetRegistered(assetDefinition: AssetDefinition): bool +    sponsorCapeAsset(erc20Address: address, newAsset: AssetDefinition) - + 7 - -<<Library>> -EdOnBN254 - -Public: -   P_MOD: uint256 - - + +<<Library>> +BN254 + +Public: +   P_MOD: uint256 +   R_MOD: uint256 + + - + 0->7 - - + + + + + +8 + +<<Library>> +EdOnBN254 + +Public: +   P_MOD: uint256 + + + + + +0->8 + + 0struct0 - -<<struct>> -AssetDefinition - -code: uint256 -policy: AssetPolicy + +<<struct>> +AssetDefinition + +code: uint256 +policy: AssetPolicy 0struct0->0 - - + + 0struct1 - -<<struct>> -AssetPolicy - -auditorPk: EdOnBN254.EdOnBN254Point -credPk: EdOnBN254.EdOnBN254Point -freezerPk: EdOnBN254.EdOnBN254Point -revealMap: uint256 -revealThreshold: uint128 + +<<struct>> +AssetPolicy + +auditorPk: EdOnBN254.EdOnBN254Point +credPk: EdOnBN254.EdOnBN254Point +freezerPk: EdOnBN254.EdOnBN254Point +revealMap: uint256 +revealThreshold: uint128 0struct1->0 - - + + 1 - -CAPE - -Public: -   nullifiers: mapping(uint256=>bool) -   blockHeight: uint64 -   pendingDeposits: uint256[] -   deployer: address -   faucetInitialized: bool -   CAPE_BURN_MAGIC_BYTES: bytes -   CAPE_BURN_MAGIC_BYTES_SIZE: uint256 -   MAX_NUM_PENDING_DEPOSIT: uint256 - -Public: -    <<event>> FaucetInitialized(roBytes: bytes) -    <<event>> BlockCommitted(height: uint64, depositCommitments: uint256[]) -    <<event>> Erc20TokensDeposited(roBytes: bytes, erc20TokenAddress: address, from: address) -    constructor(merkleTreeHeight: uint8, nRoots: uint64, verifierAddr: address) -    faucetSetupForTestnet(faucetManagerAddress: EdOnBN254.EdOnBN254Point, faucetManagerEncKey: bytes32) -    depositErc20(ro: RecordOpening, erc20Address: address) -    submitCapeBlockWithMemos(newBlock: CapeBlock, extraData: bytes) -    submitCapeBlock(newBlock: CapeBlock) + +CAPE + +Public: +   nullifiers: mapping(uint256=>bool) +   blockHeight: uint64 +   pendingDeposits: uint256[] +   deployer: address +   faucetInitialized: bool +   CAPE_BURN_MAGIC_BYTES: bytes +   CAPE_BURN_MAGIC_BYTES_SIZE: uint256 +   MAX_NUM_PENDING_DEPOSIT: uint256 + +Public: +    <<event>> FaucetInitialized(roBytes: bytes) +    <<event>> BlockCommitted(height: uint64, depositCommitments: uint256[], minerAddr: bytes, noteTypes: bytes, transferNotes: bytes, mintNotes: bytes, freezeNotes: bytes, burnNotes: bytes) +    <<event>> Erc20TokensDeposited(roBytes: bytes, erc20TokenAddress: address, from: address) +    constructor(nRoots: uint64, verifierAddr: address, recordsMerkleTreeAddr: address) +    faucetSetupForTestnet(faucetManagerAddress: EdOnBN254.EdOnBN254Point, faucetManagerEncKey: bytes32) +    depositErc20(ro: RecordOpening, erc20Address: address) +    submitCapeBlockWithMemos(newBlock: CapeBlock, extraData: bytes) +    submitCapeBlock(newBlock: CapeBlock) +    getRootValue(): uint256 - + 1->0 - - - - - -2 - -RecordsMerkleTree - - - -Public: -    constructor(merkleTreeHeight: uint8) -    getRootValue(): uint256 - - - -1->2 - - + + 3 - -RootStore - - - -Public: -    constructor(nRoots: uint64) + +RootStore + + + +Public: +    constructor(nRoots: uint64) - + 1->3 - - + + 4 - -<<Interface>> -IPlonkVerifier - - - -External: -     batchVerify(verifyingKeys: VerifyingKey[], publicInputs: uint256[][], proofs: PlonkProof[], extraTranscriptInitMsgs: bytes[]): bool + +<<Interface>> +IPlonkVerifier + + + +External: +     batchVerify(verifyingKeys: VerifyingKey[], publicInputs: uint256[][], proofs: PlonkProof[], extraTranscriptInitMsgs: bytes[]): bool - + 1->4 - - + + 5 - -<<Library>> -AccumulatingArray - - - - + +<<Interface>> +IRecordsMerkleTree + + + +External: +     updateRecordsMerkleTree(elements: uint256[]) +     getRootValue(): uint256 +     getHeight(): uint8 +     getNumLeaves(): uint64 - + 1->5 - - + + + + + +6 + +<<Library>> +AccumulatingArray + + + + + + + +1->6 + + - + -1->7 - - +1->8 + + - - -12 - -<<Library>> -RescueLib - - - -Public: -    hash(a: uint256, b: uint256, c: uint256): (o: uint256) -    commit(inputs: uint256[]): uint256 - - + + +13 + +<<Library>> +RescueLib + + + +Public: +    hash(a: uint256, b: uint256, c: uint256): (o: uint256) +    commit(inputs: uint256[]): uint256 + + -1->12 - - +1->13 + + - - -18 - -<<Library>> -VerifyingKeys - - - -External: -    getVkById(encodedId: uint256): IPlonkVerifier.VerifyingKey -Public: -    getEncodedId(noteType: uint8, numInput: uint8, numOutput: uint8, treeDepth: uint8): (encodedId: uint256) - - + + +19 + +<<Library>> +VerifyingKeys + + + +External: +    getVkById(encodedId: uint256): IPlonkVerifier.VerifyingKey +Public: +    getEncodedId(noteType: uint8, numInput: uint8, numOutput: uint8, treeDepth: uint8): (encodedId: uint256) + + -1->18 - - +1->19 + + 1struct0 - -<<struct>> -AuditMemo - -ephemeralKey: EdOnBN254.EdOnBN254Point -data: uint256[] + +<<struct>> +AuditMemo + +ephemeralKey: EdOnBN254.EdOnBN254Point +data: uint256[] 1struct0->1 - - + + 1struct1 - -<<struct>> -TransferNote - -inputNullifiers: uint256[] -outputCommitments: uint256[] -proof: IPlonkVerifier.PlonkProof -auditMemo: AuditMemo -auxInfo: TransferAuxInfo + +<<struct>> +TransferNote + +inputNullifiers: uint256[] +outputCommitments: uint256[] +proof: IPlonkVerifier.PlonkProof +auditMemo: AuditMemo +auxInfo: TransferAuxInfo 1struct1->1 - - + + 1struct2 - -<<struct>> -BurnNote - -transferNote: TransferNote -recordOpening: RecordOpening + +<<struct>> +BurnNote + +transferNote: TransferNote +recordOpening: RecordOpening 1struct2->1 - - + + 1struct3 - -<<struct>> -MintNote - -inputNullifier: uint256 -chgComm: uint256 -mintComm: uint256 -mintAmount: uint128 -mintAssetDef: AssetDefinition -mintInternalAssetCode: uint256 -proof: IPlonkVerifier.PlonkProof -auditMemo: AuditMemo -auxInfo: MintAuxInfo + +<<struct>> +MintNote + +inputNullifier: uint256 +chgComm: uint256 +mintComm: uint256 +mintAmount: uint128 +mintAssetDef: AssetDefinition +mintInternalAssetCode: uint256 +proof: IPlonkVerifier.PlonkProof +auditMemo: AuditMemo +auxInfo: MintAuxInfo 1struct3->1 - - + + 1struct4 - -<<struct>> -FreezeNote - -inputNullifiers: uint256[] -outputCommitments: uint256[] -proof: IPlonkVerifier.PlonkProof -auxInfo: FreezeAuxInfo + +<<struct>> +FreezeNote + +inputNullifiers: uint256[] +outputCommitments: uint256[] +proof: IPlonkVerifier.PlonkProof +auxInfo: FreezeAuxInfo 1struct4->1 - - + + 1struct5 - -<<struct>> -TransferAuxInfo - -merkleRoot: uint256 -fee: uint128 -validUntil: uint64 -txnMemoVerKey: EdOnBN254.EdOnBN254Point -extraProofBoundData: bytes + +<<struct>> +TransferAuxInfo + +merkleRoot: uint256 +fee: uint128 +validUntil: uint64 +txnMemoVerKey: EdOnBN254.EdOnBN254Point +extraProofBoundData: bytes 1struct5->1 - - + + 1struct6 - -<<struct>> -MintAuxInfo - -merkleRoot: uint256 -fee: uint128 -txnMemoVerKey: EdOnBN254.EdOnBN254Point + +<<struct>> +MintAuxInfo + +merkleRoot: uint256 +fee: uint128 +txnMemoVerKey: EdOnBN254.EdOnBN254Point 1struct6->1 - - + + 1struct7 - -<<struct>> -FreezeAuxInfo - -merkleRoot: uint256 -fee: uint128 -txnMemoVerKey: EdOnBN254.EdOnBN254Point + +<<struct>> +FreezeAuxInfo + +merkleRoot: uint256 +fee: uint128 +txnMemoVerKey: EdOnBN254.EdOnBN254Point 1struct7->1 - - + + 1struct8 - -<<struct>> -RecordOpening - -amount: uint128 -assetDef: AssetDefinition -userAddr: EdOnBN254.EdOnBN254Point -encKey: bytes32 -freezeFlag: bool -blind: uint256 + +<<struct>> +RecordOpening + +amount: uint128 +assetDef: AssetDefinition +userAddr: EdOnBN254.EdOnBN254Point +encKey: bytes32 +freezeFlag: bool +blind: uint256 1struct8->1 - - + + 1struct9 - -<<struct>> -CapeBlock - -minerAddr: EdOnBN254.EdOnBN254Point -noteTypes: NoteType[] -transferNotes: TransferNote[] -mintNotes: MintNote[] -freezeNotes: FreezeNote[] -burnNotes: BurnNote[] + +<<struct>> +CapeBlock + +minerAddr: EdOnBN254.EdOnBN254Point +noteTypes: NoteType[] +transferNotes: TransferNote[] +mintNotes: MintNote[] +freezeNotes: FreezeNote[] +burnNotes: BurnNote[] 1struct9->1 - - + + 1enum0 - -<<enum>> -NoteType - -TRANSFER: 0 -MINT: 1 -FREEZE: 2 -BURN: 3 + +<<enum>> +NoteType + +TRANSFER: 0 +MINT: 1 +FREEZE: 2 +BURN: 3 1enum0->1 - - + + - + + +2 + +RecordsMerkleTree + + + +External: +    updateRecordsMerkleTree(elements: uint256[]) +Public: +    constructor(merkleTreeHeight: uint8) +    getRootValue(): uint256 +    getHeight(): uint8 +    getNumLeaves(): uint64 + + -2->12 - - +2->13 + + 2struct0 - -<<struct>> -Node - -val: uint256 -left: uint64 -middle: uint64 -right: uint64 + +<<struct>> +Node + +val: uint256 +left: uint64 +middle: uint64 +right: uint64 2struct0->2 - - + + 2enum0 - -<<enum>> -Position - -LEFT: 0 -MIDDLE: 1 -RIGHT: 2 + +<<enum>> +Position + +LEFT: 0 +MIDDLE: 1 +RIGHT: 2 2enum0->2 - - + + - + -4->6 - - +4->7 + + 4struct0 - -<<struct>> -PlonkProof - -wire0: BN254.G1Point -wire1: BN254.G1Point -wire2: BN254.G1Point -wire3: BN254.G1Point -wire4: BN254.G1Point -prodPerm: BN254.G1Point -split0: BN254.G1Point -split1: BN254.G1Point -split2: BN254.G1Point -split3: BN254.G1Point -split4: BN254.G1Point -zeta: BN254.G1Point -zetaOmega: BN254.G1Point -wireEval0: uint256 -wireEval1: uint256 -wireEval2: uint256 -wireEval3: uint256 -wireEval4: uint256 -sigmaEval0: uint256 -sigmaEval1: uint256 -sigmaEval2: uint256 -sigmaEval3: uint256 -prodPermZetaOmegaEval: uint256 + +<<struct>> +PlonkProof + +wire0: BN254.G1Point +wire1: BN254.G1Point +wire2: BN254.G1Point +wire3: BN254.G1Point +wire4: BN254.G1Point +prodPerm: BN254.G1Point +split0: BN254.G1Point +split1: BN254.G1Point +split2: BN254.G1Point +split3: BN254.G1Point +split4: BN254.G1Point +zeta: BN254.G1Point +zetaOmega: BN254.G1Point +wireEval0: uint256 +wireEval1: uint256 +wireEval2: uint256 +wireEval3: uint256 +wireEval4: uint256 +sigmaEval0: uint256 +sigmaEval1: uint256 +sigmaEval2: uint256 +sigmaEval3: uint256 +prodPermZetaOmegaEval: uint256 4struct0->4 - - + + 4struct1 - -<<struct>> -VerifyingKey - -domainSize: uint256 -numInputs: uint256 -sigma0: BN254.G1Point -sigma1: BN254.G1Point -sigma2: BN254.G1Point -sigma3: BN254.G1Point -sigma4: BN254.G1Point -q1: BN254.G1Point -q2: BN254.G1Point -q3: BN254.G1Point -q4: BN254.G1Point -qM12: BN254.G1Point -qM34: BN254.G1Point -qO: BN254.G1Point -qC: BN254.G1Point -qH1: BN254.G1Point -qH2: BN254.G1Point -qH3: BN254.G1Point -qH4: BN254.G1Point -qEcc: BN254.G1Point + +<<struct>> +VerifyingKey + +domainSize: uint256 +numInputs: uint256 +sigma0: BN254.G1Point +sigma1: BN254.G1Point +sigma2: BN254.G1Point +sigma3: BN254.G1Point +sigma4: BN254.G1Point +q1: BN254.G1Point +q2: BN254.G1Point +q3: BN254.G1Point +q4: BN254.G1Point +qM12: BN254.G1Point +qM34: BN254.G1Point +qO: BN254.G1Point +qC: BN254.G1Point +qH1: BN254.G1Point +qH2: BN254.G1Point +qH3: BN254.G1Point +qH4: BN254.G1Point +qEcc: BN254.G1Point 4struct1->4 - - - - - -5struct0 - -<<struct>> -Data - -items: uint256[] -index: uint256 - - - -5struct0->5 - - - - - -17 - -<<Library>> -Utils - - - - - - - -6->17 - - + + - + 6struct0 - -<<struct>> -G1Point - -x: uint256 -y: uint256 + +<<struct>> +Data + +items: uint256[] +index: uint256 - + 6struct0->6 - - + + - - -6struct1 - -<<struct>> -G2Point - -x0: uint256 -x1: uint256 -y0: uint256 -y1: uint256 - - - -6struct1->6 - - - - - -7->7 - - - - - -7->17 - - + + +18 + +<<Library>> +Utils + + + + + + + +7->18 + + - + 7struct0 - -<<struct>> -EdOnBN254Point - -x: uint256 -y: uint256 + +<<struct>> +G1Point + +x: uint256 +y: uint256 - + 7struct0->7 - - + + - + + +7struct1 + +<<struct>> +G2Point + +x0: uint256 +x1: uint256 +y0: uint256 +y1: uint256 + + + +7struct1->7 + + + + + +8->8 + + + + + +8->18 + + + + -8 - -<<Library>> -Freeze2In2Out24DepthVk - - - - - - - -8->4 - - +8struct0 + +<<struct>> +EdOnBN254Point + +x: uint256 +y: uint256 + + + +8struct0->8 + + 9 - -<<Library>> -Freeze3In3Out24DepthVk - - - - + +<<Library>> +Freeze2In2Out24DepthVk + + + + - + 9->4 - - + + 10 - -<<Library>> -Mint1In2Out24DepthVk - - - - + +<<Library>> +Freeze3In3Out24DepthVk + + + + - + 10->4 - - + + 11 - -<<Library>> -PolynomialEval - - - - - - - -11->6 - - + +<<Library>> +Mint1In2Out24DepthVk + + + + + + + +11->4 + + - + -11struct0 - -<<struct>> -EvalDomain - -logSize: uint256 -size: uint256 -sizeInv: uint256 -groupGen: uint256 -groupGenInv: uint256 - - - -11struct0->11 - - +12 + +<<Library>> +PolynomialEval + + + + + + + +12->7 + + - + -11struct1 - -<<struct>> -EvalData - -vanishEval: uint256 -lagrangeOne: uint256 -piEval: uint256 - - - -11struct1->11 - - +12struct0 + +<<struct>> +EvalDomain + +logSize: uint256 +size: uint256 +sizeInv: uint256 +groupGen: uint256 +groupGenInv: uint256 + + + +12struct0->12 + + - - -13 - -<<Library>> -Transfer1In2Out24DepthVk - - - - - - - -13->4 - - + + +12struct1 + +<<struct>> +EvalData + +vanishEval: uint256 +lagrangeOne: uint256 +piEval: uint256 + + + +12struct1->12 + + 14 - -<<Library>> -Transfer2In2Out24DepthVk - - - - + +<<Library>> +Transfer1In2Out24DepthVk + + + + - + 14->4 - - + + 15 - -<<Library>> -Transfer2In3Out24DepthVk - - - - + +<<Library>> +Transfer2In2Out24DepthVk + + + + - + 15->4 - - + + 16 - -<<Library>> -Transfer3In3Out24DepthVk - - - - + +<<Library>> +Transfer2In3Out24DepthVk + + + + - + 16->4 - - + + - + + +17 + +<<Library>> +Transfer3In3Out24DepthVk + + + + + + + +17->4 + + + + -18->4 - - +19->4 + + - + -18->13 - - +19->14 + + - - -19 - -PlonkVerifier - - - -External: -    batchVerify(verifyingKeys: VerifyingKey[], publicInputs: uint256[][], proofs: PlonkProof[], extraTranscriptInitMsgs: bytes[]): bool + + +20 + +PlonkVerifier + + + +External: +    batchVerify(verifyingKeys: VerifyingKey[], publicInputs: uint256[][], proofs: PlonkProof[], extraTranscriptInitMsgs: bytes[]): bool - + -19->4 - - +20->4 + + - + -19->6 - - +20->7 + + - - -20 - -<<Library>> -Transcript - - - - - - + + +21 + +<<Library>> +Transcript + + + + + + -19->20 - - +20->21 + + - - -19struct0 - -<<struct>> -PcsInfo - -u: uint256 -evalPoint: uint256 -nextEvalPoint: uint256 -eval: uint256 -commScalars: uint256[] -commBases: BN254.G1Point[] -openingProof: BN254.G1Point -shiftedOpeningProof: BN254.G1Point - - + + +20struct0 + +<<struct>> +PcsInfo + +u: uint256 +evalPoint: uint256 +nextEvalPoint: uint256 +eval: uint256 +commScalars: uint256[] +commBases: BN254.G1Point[] +openingProof: BN254.G1Point +shiftedOpeningProof: BN254.G1Point + + -19struct0->19 - - +20struct0->20 + + - - -19struct1 - -<<struct>> -Challenges - -alpha: uint256 -alpha2: uint256 -alpha3: uint256 -beta: uint256 -gamma: uint256 -zeta: uint256 -v: uint256 -u: uint256 - - + + +20struct1 + +<<struct>> +Challenges + +alpha: uint256 +alpha2: uint256 +alpha3: uint256 +beta: uint256 +gamma: uint256 +zeta: uint256 +v: uint256 +u: uint256 + + -19struct1->19 - - +20struct1->20 + + - + -20->4 - - +21->4 + + - + -20->6 - - +21->7 + + - + -20->17 - - - - - -20struct0 - -<<struct>> -TranscriptData - -transcript: bytes -state: bytes32[] - - +21->18 + + + + + +21struct0 + +<<struct>> +TranscriptData + +transcript: bytes +state: bytes32[] + + -20struct0->20 - - +21struct0->21 + +