├── nightfall_bindings ├── src │ ├── lib.rs │ └── artifacts.rs ├── Cargo.toml └── build.rs ├── rust-toolchain.toml ├── nightfall_client ├── src │ ├── test_helpers │ │ └── mod.rs │ ├── driven │ │ ├── notifier │ │ │ ├── mod.rs │ │ │ └── webhook_notifier.rs │ │ ├── event_handlers │ │ │ └── mod.rs │ │ ├── db │ │ │ ├── mod.rs │ │ │ └── commitment_tree.rs │ │ ├── plonk_prover_tests │ │ │ ├── mod.rs │ │ │ └── verify_encryption_gadget_tests.rs │ │ ├── contract_functions │ │ │ ├── mod.rs │ │ │ └── contract_type_conversions.rs │ │ ├── mod.rs │ │ └── primitives │ │ │ └── mod.rs │ ├── domain │ │ ├── mod.rs │ │ └── notifications.rs │ ├── drivers │ │ ├── mod.rs │ │ ├── blockchain │ │ │ └── mod.rs │ │ └── rest │ │ │ ├── synchronisation.rs │ │ │ ├── token_info.rs │ │ │ ├── proposers.rs │ │ │ ├── commitment.rs │ │ │ ├── request_status.rs │ │ │ ├── withdraw.rs │ │ │ ├── keys.rs │ │ │ └── balance.rs │ ├── services │ │ ├── mod.rs │ │ ├── process_events.rs │ │ └── data_publisher.rs │ ├── ports │ │ ├── mod.rs │ │ ├── notifier.rs │ │ ├── key_provider.rs │ │ ├── public_data.rs │ │ ├── secret_hash.rs │ │ ├── events.rs │ │ ├── keys.rs │ │ ├── commitments.rs │ │ ├── trees.rs │ │ └── contracts.rs │ ├── main.rs │ └── lib.rs ├── benches │ ├── bench.md │ └── poseidon_hash_gadgets_bench.rs ├── Dockerfile └── Cargo.toml ├── .cargo └── config.toml ├── nightfall_deployer ├── src │ ├── lib.rs │ └── main.rs ├── Dockerfile ├── Cargo.toml └── build.rs ├── nightfall_proposer ├── src │ ├── domain │ │ ├── mod.rs │ │ ├── error.rs │ │ └── entities.rs │ ├── drivers │ │ ├── mod.rs │ │ ├── blockchain │ │ │ ├── mod.rs │ │ │ └── event_listener_manager.rs │ │ └── rest │ │ │ ├── block_data.rs │ │ │ ├── synchronisation.rs │ │ │ ├── block_assembly.rs │ │ │ ├── client_transactions.rs │ │ │ └── mod.rs │ ├── services │ │ ├── mod.rs │ │ ├── process_events.rs │ │ └── process_client_transaction.rs │ ├── driven │ │ ├── db │ │ │ ├── mod.rs │ │ │ ├── historic_root_tree.rs │ │ │ ├── nullifier_tree.rs │ │ │ └── commitment_tree.rs │ │ └── mod.rs │ ├── ports │ │ ├── block_assembly_trigger.rs │ │ ├── mod.rs │ │ ├── events.rs │ │ ├── contracts.rs │ │ └── transactions.rs │ └── main.rs ├── Dockerfile └── Cargo.toml ├── rustfmt.toml ├── configuration ├── src │ ├── lib.rs │ └── logging.rs ├── bin │ └── .gitignore ├── nginx.conf ├── Dockerfile ├── Cargo.toml └── trust │ └── ZscalerRootCertificate-2048-SHA256.crt ├── lib ├── src │ ├── merkle_trees │ │ └── mod.rs │ ├── plonk_prover │ │ ├── circuits │ │ │ ├── verify │ │ │ │ ├── mod.rs │ │ │ │ ├── verify_duplicates_gadgets.rs │ │ │ │ └── verify_encryption_gadgets.rs │ │ │ ├── mod.rs │ │ │ └── kemdem_gadgets.rs │ │ ├── circuit_builder.rs │ │ └── mod.rs │ ├── constants.rs │ ├── health_check.rs │ ├── models.rs │ ├── entities.rs │ ├── blockchain_client.rs │ ├── lib.rs │ └── tests_utils.rs └── Cargo.toml ├── nightfall_test ├── bin │ └── config.toml ├── src │ ├── lib.rs │ ├── results.txt │ ├── validate_certs.rs │ ├── main.rs │ └── webhook.rs ├── Dockerfile └── Cargo.toml ├── doc ├── Contract Optimization.xlsx └── images │ └── nf_4_architecture.png ├── .githooks ├── pre-add ├── pre-push └── readme.md ├── blockchain_assets ├── test_contracts │ ├── X509 │ │ ├── _certificates │ │ │ ├── user │ │ │ │ ├── user-1.der │ │ │ │ ├── user-2.der │ │ │ │ ├── user-3.der │ │ │ │ ├── user-4.der │ │ │ │ ├── user-1.priv_key │ │ │ │ ├── user-2.priv_key │ │ │ │ ├── user-3.priv_key │ │ │ │ └── user-4.priv_key │ │ │ ├── intermediate_ca.der │ │ │ ├── intermediate_ca.priv_key │ │ │ ├── conf │ │ │ │ ├── extfile.conf │ │ │ │ └── ca_extfile.conf │ │ │ ├── root_ca.crt │ │ │ ├── intermediate_ca.pem │ │ │ ├── gen-end_user-certificates.sh │ │ │ ├── root_ca_key.pem │ │ │ ├── root_ca_priv_key.pem │ │ │ └── gen-root_ca-certificate.sh │ │ └── allowlist.md │ ├── README.md │ ├── BlockHashTest.t.sol │ └── RollupProofVerificationKey_V2.t.sol ├── contracts │ ├── X509 │ │ ├── X509Interface.sol │ │ ├── SanctionsListInterface.sol │ │ ├── Allowlist.sol │ │ └── Certified.sol │ ├── proof_verification │ │ ├── IVKProvider.sol │ │ ├── INFVerifier.sol │ │ ├── MockVerifier.sol │ │ └── RollupProofVerificationKey.sol │ ├── SanctionsListMock.sol │ ├── ERC721Mock.sol │ ├── Nightfall_V2.sol │ ├── ERC1155Mock.sol │ ├── ProposerManager.sol │ ├── ERC20Mock.sol │ ├── RoundRobinV2.sol │ ├── RoundRobinV3.sol │ ├── ERC3525Mock.sol │ └── Nightfall_V3.sol └── script │ ├── mock_deployment.s.sol │ └── upgrade_X509_V3.s.sol ├── .dockerignore ├── Cargo.toml ├── .gitignore ├── nightfall_sync_test ├── Cargo.toml └── Dockerfile ├── foundry.lock ├── README.md ├── anvil ├── entrypoint.sh └── Dockerfile ├── .gitmodules ├── .github └── ISSUE_TEMPLATE │ └── feature_request.md ├── foundry.toml ├── .env └── nightfall_test.toml /nightfall_bindings/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod artifacts; 2 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.88.0" -------------------------------------------------------------------------------- /nightfall_client/src/test_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mocks; 2 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [net] 2 | git-fetch-with-cli = true 3 | 4 | [env] 5 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/notifier/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod webhook_notifier; 2 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/event_handlers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod nightfall_event; 2 | -------------------------------------------------------------------------------- /nightfall_deployer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod deployment; 2 | pub mod vk_contract; 3 | -------------------------------------------------------------------------------- /nightfall_proposer/src/domain/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entities; 2 | pub mod error; 3 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blockchain; 2 | pub mod rest; 3 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod commitment_tree; 2 | pub mod mongo; 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | edition = "2021" 3 | unstable_features = true 4 | -------------------------------------------------------------------------------- /configuration/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod addresses; 2 | pub mod logging; 3 | 4 | pub mod settings; 5 | -------------------------------------------------------------------------------- /nightfall_client/src/domain/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entities; 2 | pub mod error; 3 | pub mod notifications; 4 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blockchain; 2 | pub mod derive_key; 3 | pub mod rest; 4 | -------------------------------------------------------------------------------- /lib/src/merkle_trees/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod append_only; 2 | pub mod indexed; 3 | pub mod mutable; 4 | pub mod trees; 5 | -------------------------------------------------------------------------------- /nightfall_test/bin/config.toml: -------------------------------------------------------------------------------- 1 | [config] 2 | url = "http://localhost:3000" 3 | mnemonic = "test" 4 | 5 | 6 | -------------------------------------------------------------------------------- /configuration/bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /doc/Contract Optimization.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/doc/Contract Optimization.xlsx -------------------------------------------------------------------------------- /nightfall_client/src/drivers/blockchain/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod event_listener_manager; 2 | pub mod nightfall_event_listener; 3 | -------------------------------------------------------------------------------- /doc/images/nf_4_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/doc/images/nf_4_architecture.png -------------------------------------------------------------------------------- /nightfall_client/src/driven/plonk_prover_tests/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod unified_circuit_tests; 2 | pub mod verify_encryption_gadget_tests; 3 | -------------------------------------------------------------------------------- /nightfall_proposer/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assemble_block; 2 | pub mod process_client_transaction; 3 | pub mod process_events; 4 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/contract_functions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod contract_type_conversions; 2 | pub mod nightfall_contract; 3 | pub mod token_contracts; 4 | -------------------------------------------------------------------------------- /nightfall_proposer/src/driven/db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod commitment_tree; 2 | pub mod historic_root_tree; 3 | pub mod mongo_db; 4 | pub mod nullifier_tree; 5 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/blockchain/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_assembly; 2 | pub mod event_listener_manager; 3 | pub mod nightfall_event_listener; 4 | -------------------------------------------------------------------------------- /nightfall_client/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client_operation; 2 | pub mod commitment_selection; 3 | pub mod data_publisher; 4 | pub mod process_events; 5 | -------------------------------------------------------------------------------- /.githooks/pre-add: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Restore the original file before committing 3 | cp ../blockchain_assets/dummy_artifacts/artifacts.rs ../nigthfall_bindings/src/artifacts.rs 4 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-1.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-1.der -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-2.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-2.der -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-3.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-3.der -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-4.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-4.der -------------------------------------------------------------------------------- /nightfall_proposer/src/ports/block_assembly_trigger.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | 3 | #[async_trait] 4 | pub trait BlockAssemblyTrigger { 5 | async fn await_trigger(&self); 6 | } 7 | -------------------------------------------------------------------------------- /nightfall_proposer/src/ports/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_assembly_trigger; 2 | pub mod contracts; 3 | pub mod db; 4 | pub mod events; 5 | pub mod proving; 6 | pub mod transactions; 7 | pub mod trees; 8 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuits/verify/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod verify_commitments_gadgets; 2 | pub mod verify_duplicates_gadgets; 3 | pub mod verify_encryption_gadgets; 4 | pub mod verify_nullifiers_gadgets; 5 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/intermediate_ca.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/intermediate_ca.der -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-1.priv_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-1.priv_key -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-2.priv_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-2.priv_key -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-3.priv_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-3.priv_key -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/user/user-4.priv_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/user/user-4.priv_key -------------------------------------------------------------------------------- /nightfall_client/src/driven/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod contract_functions; 2 | pub mod db; 3 | pub mod event_handlers; 4 | pub mod notifier; 5 | pub mod plonk_prover_tests; 6 | pub mod primitives; 7 | pub mod queue; 8 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/intermediate_ca.priv_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EYBlockchain/nightfall_4_CE/HEAD/blockchain_assets/test_contracts/X509/_certificates/intermediate_ca.priv_key -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | .github 3 | .vscode 4 | .githooks 5 | **/Dockerfile 6 | docker-compose* 7 | LICENSE 8 | **/tests 9 | .env.example 10 | remappings.txt 11 | **/*.ptau 12 | nightfall_bindings/build.rs 13 | 14 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/X509/X509Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | pragma solidity ^0.8.3; 4 | 5 | interface X509Interface { 6 | function x509Check(address user) external view returns (bool); 7 | } 8 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/X509/SanctionsListInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface SanctionsListInterface { 6 | function isSanctioned(address addr) external view returns (bool); 7 | } 8 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod commitments; 2 | pub mod contracts; 3 | pub mod db; 4 | pub mod events; 5 | pub mod key_provider; 6 | pub mod keys; 7 | pub mod notifier; 8 | pub mod public_data; 9 | pub mod secret_hash; 10 | pub mod trees; 11 | -------------------------------------------------------------------------------- /nightfall_proposer/src/driven/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_assembler; 2 | pub mod db; 3 | pub mod in_memory_db; 4 | pub mod mock_prover; 5 | pub mod nightfall_client_transaction; 6 | pub mod nightfall_contract; 7 | pub mod nightfall_event; 8 | pub mod rollup_prover; 9 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/notifier.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::notifications::NotificationPayload; 2 | use async_trait::async_trait; 3 | 4 | #[async_trait] 5 | pub trait Notifier: Send + Sync { 6 | async fn notify(&self, payload: &NotificationPayload) -> Result<(), String>; 7 | } 8 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/conf/extfile.conf: -------------------------------------------------------------------------------- 1 | [ extensions ] 2 | subjectKeyIdentifier = hash 3 | authorityKeyIdentifier = keyid,issuer 4 | keyUsage = digitalSignature, nonRepudiation 5 | extendedKeyUsage = emailProtection, timeStamping 6 | certificatePolicies = 1.5.6.7, 1.2.3.4 7 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/key_provider.rs: -------------------------------------------------------------------------------- 1 | /// Trait for any structure that can provide a key. Agnostic to 2 | /// how the key is stored. 3 | pub trait KeyProvider { 4 | fn get_key(key_id: &str) -> Option; 5 | fn set_key(key_id: &str, key: K) -> Result<(), Box>; 6 | } 7 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/conf/ca_extfile.conf: -------------------------------------------------------------------------------- 1 | [ extensions ] 2 | basicConstraints = CA:FALSE 3 | keyUsage = digitalSignature, keyEncipherment 4 | extendedKeyUsage = emailProtection, timeStamping 5 | certificatePolicies = @policies 6 | 7 | [ policies ] 8 | policyIdentifier = 1.5.6.7 9 | -------------------------------------------------------------------------------- /.githooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if ! cargo +nightly fmt 6 | then 7 | echo "There are some cargo fmt issues." 8 | exit 1 9 | fi 10 | 11 | if ! cargo clippy --all-targets -- -D warnings 12 | then 13 | echo "There are some clippy issues." 14 | exit 1 15 | fi 16 | exit 0 17 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/public_data.rs: -------------------------------------------------------------------------------- 1 | use lib::{error::ConversionError, shared_entities::CompressedSecrets}; 2 | 3 | /// trait which public data (in the form of a struct, e.g. ERC20DepositData) must implement 4 | pub trait PublicData { 5 | fn get_compressed_secrets(&self) -> Result; 6 | } 7 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/proof_verification/IVKProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity >=0.8.20; 3 | 4 | import "./lib/Types.sol"; 5 | 6 | interface IVKProvider { 7 | function getVerificationKey() 8 | external 9 | view 10 | returns (Types.VerificationKey memory vk); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/constants.rs: -------------------------------------------------------------------------------- 1 | //! # Global Constants Module 2 | //! 3 | //! This module contains cryptographic and system constants used throughout 4 | //! the Nightfall protocol. These values are compile-time constants that 5 | //! should remain consistent across all components. 6 | 7 | /// Maximum KZG degree 8 | pub const MAX_KZG_DEGREE: usize = 26; 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "nightfall_client", 4 | "nightfall_deployer", 5 | "configuration", 6 | "nightfall_test", 7 | "nightfall_proposer", 8 | "nightfall_sync_test", 9 | "lib", 10 | "nightfall_bindings", 11 | ] 12 | resolver = "2" 13 | 14 | [workspace.package] 15 | version = "0.1.0" 16 | edition = "2024" 17 | -------------------------------------------------------------------------------- /configuration/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | # Increase timeouts to handle long uploads 4 | client_max_body_size 500M; 5 | location / { 6 | root /var/www/html; 7 | dav_methods PUT DELETE MKCOL COPY MOVE; 8 | create_full_put_path on; 9 | dav_access group:rw all:r; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/proof_verification/INFVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity >=0.8.20; 3 | 4 | interface INFVerifier { 5 | function verify( 6 | bytes calldata accBytes, 7 | bytes calldata proofBytes, 8 | bytes calldata publicInputsHashBytes, 9 | uint256 rollup_batch_size 10 | ) external view returns (bool result); 11 | } 12 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/primitives/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kemdem_functions; 2 | 3 | use ark_bn254::Fr as Fr254; 4 | use ark_ff::MontFp; 5 | 6 | pub const DOMAIN_KEM: Fr254 = 7 | MontFp!("21033365405711675223813179268586447041622169155539365736392974498519442361181"); 8 | pub const DOMAIN_DEM: Fr254 = 9 | MontFp!("1241463701002173366467794894814691939898321302682516549591039420117995599097"); 10 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/secret_hash.rs: -------------------------------------------------------------------------------- 1 | //! Defines interface for something to be used as a secret hash in Nightfall 4. 2 | use ark_bn254::Fr as Fr254; 3 | use jf_primitives::poseidon::PoseidonError; 4 | 5 | /// Secret hash interface 6 | pub trait SecretHash { 7 | /// Create the secret hash from the pre-image. 8 | fn hash(&self) -> Result; 9 | /// Turn it into an array for circuit purposes. 10 | fn to_array(&self) -> [Fr254; 3]; 11 | } 12 | -------------------------------------------------------------------------------- /nightfall_bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_bindings" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | alloy = { version = "1.0.23", features = ["full"] } 8 | configuration = { path = "../configuration" } 9 | log = "0.4.27" 10 | 11 | [build-dependencies] 12 | configuration = { path = "../configuration" } 13 | log = "0.4.27" 14 | serde = { version = "1", features = ["derive"] } 15 | serde_json = "1" 16 | alloy = { version = "1.0.23", features = ["full"] } 17 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/events.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::TxHash; 2 | use lib::error::EventHandlerError; 3 | 4 | use super::contracts::NightfallContract; 5 | 6 | // we need a transaction hash associated with the event so that we can find 7 | // the transaction that triggered the event from the blockchain record 8 | #[async_trait::async_trait] 9 | pub trait EventHandler { 10 | async fn handle_event(&self, transaction_hash: Option) 11 | -> Result<(), EventHandlerError>; 12 | } 13 | -------------------------------------------------------------------------------- /.githooks/readme.md: -------------------------------------------------------------------------------- 1 | # Git hooks 2 | 3 | The code in this folder will locally run at various points in your commit cycle and do useful checks before you push your code. 4 | 5 | They need installing locally once by pointing your local git instance at them. Do this from the root of your NF_4 repository. It will only affect this repository: 6 | 7 | ```sh 8 | git config --local core.hooksPath .githooks/ 9 | ``` 10 | 11 | After you do that, any updates to the githooks will be applied automatically so that the team has a consistent set. 12 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/SanctionsListMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "./X509/SanctionsListInterface.sol"; 6 | 7 | contract SanctionsListMock is SanctionsListInterface { 8 | address sanctionedUser; 9 | constructor(address _sanctionedUser) { 10 | sanctionedUser = _sanctionedUser; 11 | } 12 | function isSanctioned(address user) external view returns (bool) { 13 | if (user == sanctionedUser) return true; 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nightfall_client/benches/bench.md: -------------------------------------------------------------------------------- 1 | # Plonk circuits generation/verification benchmark 2 | 3 | ## Desktop 4 | 5 | - Processor: Apple M1 Pro 10 cores 6 | - Memory: 16 GB 7 | - OS: macOS Monterey Version 12.6.1 8 | - rustc 1.68.0 (2c8cc3432 2023-03-06) 9 | - `RAYON_NUM_THREADS=10 cargo bench` 10 | - `RAYON_NUM_THREADS=10 cargo bench --bench bench_unified_circuit` 11 | 12 | ### Benchmark Metrics 13 | 14 | - Constraint count before padding and after padding 15 | - Witness generation 16 | - Proving time 17 | - Verification time 18 | - Srs generation time 19 | -------------------------------------------------------------------------------- /nightfall_test/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod run_tests; 2 | pub mod test; 3 | pub mod test_settings; 4 | pub mod validate_certs; 5 | pub mod webhook; 6 | 7 | #[derive(Debug)] 8 | pub struct TestError { 9 | message: String, 10 | } 11 | impl TestError { 12 | fn new(message: String) -> Self { 13 | TestError { 14 | message: message.to_string(), 15 | } 16 | } 17 | } 18 | impl std::fmt::Display for TestError { 19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 | write!(f, "{}", self.message) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /nightfall_client/src/domain/notifications.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::BlockNumber; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] 5 | #[serde(tag = "event_type", rename_all = "snake_case")] 6 | pub enum NotificationPayload { 7 | BlockchainEvent { 8 | l1_txn_hash: String, 9 | l2_block_number: BlockNumber, 10 | commitments: Vec, 11 | request_ids: Vec, 12 | }, 13 | TransactionEvent { 14 | response: String, 15 | uuid: String, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /nightfall_proposer/src/ports/events.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::TxHash; 2 | use lib::{ 3 | error::EventHandlerError, 4 | nf_client_proof::{Proof, ProvingEngine}, 5 | }; 6 | 7 | use super::contracts::NightfallContract; 8 | 9 | // we can't reuse client's version because we want to implement on a foreign type 10 | #[async_trait::async_trait] 11 | pub trait EventHandler 12 | where 13 | P: Proof, 14 | E: ProvingEngine

, 15 | N: NightfallContract, 16 | { 17 | async fn handle_event(&self, transaction_hash: TxHash) -> Result<(), EventHandlerError>; 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | # Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .DS_Store 13 | .vscode 14 | **/config/local.* 15 | soljson* 16 | **/blockchain_assets/artifacts 17 | **/blockchain_assets/cache 18 | **/blockchain_assets/logs 19 | configuration/toml 20 | remappings.txt 21 | local.env 22 | -------------------------------------------------------------------------------- /nightfall_client/src/services/process_events.rs: -------------------------------------------------------------------------------- 1 | use crate::ports::{contracts::NightfallContract, events::EventHandler}; 2 | use alloy::rpc::types::Log; 3 | use lib::error::EventHandlerError; 4 | 5 | // A simple function that passes the Event through to be handled by an implementation in the respository 6 | // This will probably do more as the service develops, otherwise consider calling directly from driver to repository. 7 | pub async fn process_events( 8 | e: impl EventHandler, 9 | log: Log, 10 | ) -> Result<(), EventHandlerError> { 11 | e.handle_event(log.transaction_hash).await 12 | } 13 | -------------------------------------------------------------------------------- /nightfall_sync_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_sync_test" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lib = { path = "../lib" } 10 | nightfall_test = { path = "../nightfall_test" } 11 | reqwest = { version = "0.12.15", features = ["json"] } 12 | tokio = { version = "1.45.0", features = ["full"] } 13 | configuration = { path = "../configuration" } 14 | log = "0.4.27" 15 | nightfall_client = { path = "../nightfall_client" } 16 | nightfall_bindings = {path = "../nightfall_bindings"} 17 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/ERC721Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | contract ERC721Mock is ERC721 { 7 | constructor( 8 | uint256 initial_id, 9 | address initialOwner, 10 | address spender 11 | ) ERC721("ERC721Mock", "E721") { 12 | mint(initialOwner, spender, initial_id); 13 | } 14 | 15 | function mint(address owner, address spender, uint256 tokenId) public { 16 | ERC721._setApprovalForAll(owner, spender, true); 17 | ERC721._mint(owner, tokenId); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/Nightfall_V2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity ^0.8.20; 3 | 4 | import "./Nightfall.sol"; 5 | 6 | /// @custom:oz-upgrades-from blockchain_assets/contracts/Nightfall.sol:Nightfall 7 | contract NightfallV2 is Nightfall { 8 | 9 | /// @custom:oz-upgrades-unsafe-allow constructor 10 | constructor() { 11 | _disableInitializers(); 12 | } 13 | 14 | // simple marker to prove behavior changed post-upgrade 15 | // This is used in contract unit test. 16 | function versionMarker() external pure returns (string memory) { 17 | return "NightfallV2"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nightfall_proposer/src/ports/contracts.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the interface that a smart contract must work with to be classed as a Nightfall contract by a proposer. 2 | 3 | use alloy::primitives::I256; 4 | use lib::error::NightfallContractError; 5 | 6 | use crate::domain::entities::Block; 7 | 8 | #[async_trait::async_trait] 9 | pub trait NightfallContract { 10 | /// Proposes a block 11 | async fn propose_block(block: Block) -> Result<(), NightfallContractError>; 12 | 13 | /// Gets the current layer 2 block number from the blockchain 14 | async fn get_current_layer2_blocknumber() -> Result; 15 | } 16 | -------------------------------------------------------------------------------- /foundry.lock: -------------------------------------------------------------------------------- 1 | { 2 | "blockchain_assets/solidity_lib/erc-3525": { 3 | "rev": "d80df770e34b0407b7e72551940f24f4334a289c" 4 | }, 5 | "blockchain_assets/solidity_lib/forge-std": { 6 | "rev": "551a2d30d7fecba7092ab45a587b5268149a48fb" 7 | }, 8 | "blockchain_assets/solidity_lib/openzeppelin-contracts": { 9 | "rev": "24a641d9c9e0137093592a466c5496315626d98d" 10 | }, 11 | "blockchain_assets/solidity_lib/openzeppelin-contracts-upgradeable": { 12 | "rev": "723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1" 13 | }, 14 | "blockchain_assets/solidity_lib/openzeppelin-foundry-upgrades": { 15 | "rev": "cbce1e00305e943aa1661d43f41e5ac72c662b07" 16 | } 17 | } -------------------------------------------------------------------------------- /nightfall_client/src/ports/keys.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | 3 | use nf_curves::ed_on_bn254::{BJJTEProjective as JubJub, Fr as FqJubJub}; 4 | 5 | pub trait KeySpending { 6 | fn get_nullifier_key(&self) -> Fr254; 7 | } 8 | pub trait KeyViewing { 9 | fn get_private_key(&self) -> FqJubJub; 10 | fn get_public_key(&self) -> JubJub; 11 | } 12 | 13 | pub trait KeyZK: KeySpending + KeyViewing { 14 | fn get_root_key(&self) -> Fr254; 15 | } 16 | 17 | pub trait ProvingKey { 18 | type PK; 19 | fn get_proving_key(&self) -> Self::PK; 20 | } 21 | 22 | pub trait VerifyingKey { 23 | type VK; 24 | fn get_verifying_key(&self) -> Self::VK; 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nightfall_4_CE 2 | Community edition of Nightfall_4 3 | 4 | _This code is not owned by EY and EY provides no warranty and disclaims any and all liability for use of this code. Users must conduct their own diligence with respect to use for their purposes and any and all usage is on an as-is basis and at your own risk._ 5 | 6 | Nightfall_4 is a ZK rollup build around the ZK Privacy of Nightfall. It enables one to transfer ERC20, ERC721, ERC1155 and ERC3525 tokens in privacy. Full details can be found in the /doc folder of this repository. 7 | 8 | Please note that this software should be treated as experimental. It should not be used to make significant value transactions. 9 | 10 | -------------------------------------------------------------------------------- /nightfall_proposer/src/driven/db/historic_root_tree.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use jf_primitives::poseidon::PoseidonParams; 3 | 4 | use crate::ports::trees::HistoricRootTree; 5 | 6 | /// Implementation of a historic root tree. This is a mutable tree because although we don't make use 7 | /// of membership proofs, we do need to be able to tell if a particular leaf is in the tree or not. We can do that 8 | /// by querying the node database, which only a mutable tree can do. 9 | 10 | #[async_trait::async_trait] 11 | impl HistoricRootTree for mongodb::Client 12 | where 13 | F: PrimeField + PoseidonParams, 14 | { 15 | const TREE_NAME: &'static str = "historic_root_tree"; 16 | } 17 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/README.md: -------------------------------------------------------------------------------- 1 | # Running Smart Contract Tests 2 | 3 | To run smart contract tests it's necessary to install Foundry (or at least Forge). The easiest way to do that is: 4 | ```sh 5 | curl -L https://foundry.paradigm.xyz | bash 6 | ``` 7 | Then, in the nightfall_4 root directory, install the forge standard libary (do _not_ omit the `--no-git` argument): 8 | ```sh 9 | forge install --no-git foundry-rs/forge-std 10 | ``` 11 | This will install the forge standard library in the directory `forge` 12 | 13 | Both of the above steps need only be done once! 14 | 15 | Once installation is complete, you can run all the tests in `nightfall_deployer/test_contracts` with: 16 | ```sh 17 | forge test 18 | ``` 19 | -------------------------------------------------------------------------------- /anvil/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Start anvil 5 | anvil --base-fee 58000000000 --block-time 5 --gas-limit 500000000 & 6 | ANVIL_PID=$! 7 | 8 | # Wait for anvil to be ready (JSON-RPC POST) 9 | for i in $(seq 1 30); do 10 | if curl -s -X POST --data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \ 11 | -H "Content-Type: application/json" http://localhost:8545 | grep -q '"result"'; then 12 | break 13 | fi 14 | echo "Waiting for anvil to be ready... ($i)" 15 | sleep 1 16 | done 17 | 18 | # Run the deployment script with Foundry 19 | forge script MockDeployer \ 20 | --fork-url ws://localhost:8545 \ 21 | --broadcast \ 22 | --force 23 | 24 | wait $ANVIL_PID 25 | 26 | exec "$@" 27 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/proof_verification/MockVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity >=0.8.20; 3 | 4 | import "./INFVerifier.sol"; 5 | 6 | // Mock verifier contract that just returns `true`. 7 | contract MockVerifier is INFVerifier { 8 | bool private defaultResult = true; 9 | 10 | function verify( 11 | bytes calldata accBytes, 12 | bytes calldata proofBytes, 13 | bytes calldata publicInputsHashBytes, 14 | uint256 rollup_batch_size 15 | ) external view override returns (bool result) { 16 | accBytes; 17 | proofBytes; 18 | publicInputsHashBytes; 19 | rollup_batch_size; 20 | result = defaultResult; 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/health_check.rs: -------------------------------------------------------------------------------- 1 | use warp::{hyper::StatusCode, path, reply, Filter}; 2 | 3 | pub fn health_route() -> impl Filter + Clone 4 | { 5 | path!("v1" / "health") 6 | .map(warp::reply) 7 | .map(|_| reply::with_status("Healthy", StatusCode::OK)) 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | #[tokio::test] 14 | async fn rest_healthy() { 15 | let filter = health_route(); 16 | let res = warp::test::request() 17 | .method("GET") 18 | .path("/v1/health") 19 | .reply(&filter) 20 | .await; 21 | assert_eq!(res.status(), StatusCode::OK); 22 | assert_eq!(res.body(), "Healthy"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /nightfall_proposer/src/services/process_events.rs: -------------------------------------------------------------------------------- 1 | use crate::ports::{contracts::NightfallContract, events::EventHandler}; 2 | use alloy::rpc::types::Log; 3 | use lib::{ 4 | error::EventHandlerError, 5 | nf_client_proof::{Proof, ProvingEngine}, 6 | }; 7 | 8 | // A simple function that passes the Event through to be handled by an implementation in the respository 9 | // This will probably do more as the service develops, otherwise consider calling directly from driver to repository. 10 | pub async fn process_events( 11 | e: impl EventHandler, 12 | log: Log, 13 | ) -> Result<(), EventHandlerError> 14 | where 15 | P: Proof, 16 | E: ProvingEngine

, 17 | N: NightfallContract, 18 | { 19 | e.handle_event(log.transaction_hash.unwrap()).await 20 | } 21 | -------------------------------------------------------------------------------- /configuration/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | # Install additional CA certificates if needed 4 | COPY ./configuration/trust/* /usr/local/share/ca-certificates/ 5 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates || true 6 | 7 | # Remove default nginx config so only ours is used 8 | RUN rm /etc/nginx/conf.d/default.conf 9 | 10 | # Create config directory 11 | RUN mkdir -p /var/www/html/configuration/toml 12 | 13 | # Copy our nginx config 14 | COPY ./configuration/nginx.conf /etc/nginx/conf.d/default.conf 15 | 16 | # Copy static files (if any go into /var/www/html/bin) 17 | COPY ./configuration/bin/* /var/www/html/ 18 | 19 | RUN chown -R nginx:nginx /var/www/html \ 20 | && chmod -R 755 /var/www/html 21 | 22 | CMD ["nginx", "-g", "daemon off;"] 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kemdem_gadgets; 2 | 3 | pub mod unified_circuit; 4 | pub mod verify; 5 | 6 | use ark_bn254::Fr as Fr254; 7 | use ark_ff::MontFp; 8 | 9 | pub const DOMAIN_KEM: Fr254 = 10 | MontFp!("21033365405711675223813179268586447041622169155539365736392974498519442361181"); 11 | pub const DOMAIN_DEM: Fr254 = 12 | MontFp!("1241463701002173366467794894814691939898321302682516549591039420117995599097"); 13 | 14 | // The DOMAIN_SHARED_SALT was derived from 15 | /* 16 | const LABEL: &str = "Nightfall|SharedSalt"; 17 | let digest = Sha256::digest(LABEL.as_bytes()); 18 | let fr = Fr254::from_le_bytes_mod_order(&digest); 19 | */ 20 | pub const DOMAIN_SHARED_SALT: Fr254 = 21 | MontFp!("4832298308599927878911686715232824310149976768223104556783163253807065458"); 22 | -------------------------------------------------------------------------------- /configuration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "configuration" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | config = "0.13.4" 10 | alloy = { version = "1.0.23", features = ["transport-ws"] } 11 | rand = "0.8" 12 | serde = { version = "1.0.219", features = ["derive"] } 13 | serial_test = "3.2.0" 14 | serde_json = "1.0.140" 15 | reqwest = { version = "0.11.27", features = ["json", "blocking"] } 16 | url = "2.5.4" 17 | toml = "0.7.8" 18 | log = "0.4.27" 19 | env_logger = "0.10.2" 20 | log-panics = { version = "2", default-features = false } 21 | lazy_static = "1.5.0" 22 | figment = { version = "0.10.19", features = ["toml", "env"] } 23 | hex = "0.4.3" 24 | tokio = { version = "1.45.0", features = ["full"] } 25 | 26 | -------------------------------------------------------------------------------- /nightfall_test/src/results.txt: -------------------------------------------------------------------------------- 1 | 2 | OK 3 | 4 | [2025-06-13T23:09:38Z INFO nightfall_test::run_tests] Not yet able to withdraw funds DeEscrowDataReq { token_id: "0x00", erc_address: "9a676e781a523b5d0c0e43731313a708cb607508", recipient_address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", value: "0x01", token_type: "0", withdraw_fund_salt: "0f71780ecb0ae9aaa59ebe2dbbf08bce14d3750574e253e5a2ddfaa1b0664c0f" } 5 | 6 | Not OK? 7 | 8 | [2025-06-13T17:35:28Z WARN nightfall_test::run_tests] Ignoring request with leading zero in withdraw_fund_salt: DeEscrowDataReq { token_id: "0x01aa", erc_address: "0b306bf915c4d645ff596e518faf3f9669b97016", recipient_address: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", value: "0x00", token_type: "2", withdraw_fund_salt: "0f11fef111139e9a57a05c318e89cb70141551d16083f5bc6dfefb93ba4ea177" } 9 | 10 | 11 | Not OK 12 | 13 | -------------------------------------------------------------------------------- /nightfall_sync_test/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1-labs 2 | FROM rust:1.88.0 AS runtime 3 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 4 | COPY ./configuration/trust/* /usr/local/share/ca-certificates/ 5 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 6 | 7 | WORKDIR /app 8 | RUN mkdir bin 9 | COPY --exclude=configuration/bin/* . . 10 | 11 | RUN --mount=type=cache,id=cargo-registry-v4,target=/usr/local/cargo/registry,sharing=locked \ 12 | --mount=type=cache,id=cargo-git-v4,target=/usr/local/cargo/git,sharing=locked \ 13 | --mount=type=cache,id=cargo-target-client-v4,target=/app/target,sharing=private \ 14 | cargo build --package nightfall_sync_test --release && \ 15 | mv /app/target/release/nightfall_sync_test /app/bin/sync_test 16 | CMD ["/app/bin/sync_test"] -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/allowlist.md: -------------------------------------------------------------------------------- 1 | # Nightfall Allowlisting adaptions 2 | 3 | Nightfall now incorporates the ability to manage a allowlist of accounts. It is an abstract contract, intended to be subclassed by a form of Allowlist Manager contract (currentyl `X509.sol` performs this role). When allowlisting is enabled, only accounts that are added to the allowlist are able to move funds from Layer 1 to Layer 2 and to withdraw Layer 1 funds from the Nightfall contract. 4 | 5 | ## Enabling Allowlisting 6 | 7 | To enable allowlisting, the deployer container should have its `ALLOWLISTING` environment variable set to `enable`. Setting the `ALLOWLISTING` variable to anything else will desable whitlisting. 8 | 9 | ## Operating Allowlisting 10 | 11 | All allowlisting functionality is managed by the contract `Allowlist.sol`, the functions therein are self-explanatory. 12 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/ERC1155Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 5 | 6 | contract ERC1155Mock is ERC1155 { 7 | constructor( 8 | address spender, 9 | uint256 initial_id, 10 | uint256 value, 11 | uint256 initial_id_two, 12 | uint256 value_two, 13 | address initialOwner 14 | ) ERC1155("ERC1155Mock") { 15 | mint(initialOwner, spender, initial_id, value); 16 | mint(initialOwner, spender, initial_id_two, value_two); 17 | } 18 | 19 | function mint( 20 | address owner, 21 | address spender, 22 | uint256 tokenId, 23 | uint256 value 24 | ) public { 25 | ERC1155._setApprovalForAll(owner, spender, true); 26 | ERC1155._mint(owner, tokenId, value, ""); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::fmt::Debug; 3 | use warp::http::StatusCode; 4 | 5 | #[derive(Debug, Deserialize, Serialize, Default, Clone)] 6 | /// A structure representing a certificate validation request 7 | pub struct CertificateReq { 8 | pub certificate_private_key: Vec, 9 | pub certificate: Vec, 10 | } 11 | #[derive(Serialize)] 12 | struct ErrorBody<'a> { 13 | code: &'a str, 14 | message: &'a str, 15 | } 16 | 17 | type JsonWithStatus = warp::reply::WithStatus; 18 | 19 | fn json_error(status: StatusCode, code: &'static str, message: &'static str) -> JsonWithStatus { 20 | let body = warp::reply::json(&ErrorBody { code, message }); 21 | warp::reply::with_status(body, status) 22 | } 23 | 24 | pub fn bad_request(msg: &'static str) -> JsonWithStatus { 25 | json_error(StatusCode::BAD_REQUEST, "bad_request", msg) 26 | } 27 | -------------------------------------------------------------------------------- /nightfall_proposer/src/services/process_client_transaction.rs: -------------------------------------------------------------------------------- 1 | use crate::driven::nightfall_client_transaction::process_nightfall_client_transaction; 2 | use lib::{ 3 | nf_client_proof::{Proof, ProvingEngine}, 4 | shared_entities::ClientTransaction, 5 | }; 6 | use std::error::Error; 7 | 8 | pub async fn process_client_transaction( 9 | client_transaction: ClientTransaction

, 10 | ) -> Result<(), Box> 11 | where 12 | P: Proof, 13 | E: ProvingEngine

, 14 | { 15 | // This just calls through to the repository, because both the event processor and the REST API 16 | // need to call the following function. Thus, it can't really be located in services, otherwise we'd be 17 | // doing a reentrant call from repository to services, which is a bit of an odd pattern. 18 | process_nightfall_client_transaction::(client_transaction).await?; 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/ProposerManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity ^0.8.20; 3 | 4 | /// @title Proposers 5 | /// @notice An interface for any contract that is used to choose proposers 6 | 7 | struct Proposer { 8 | uint stake; 9 | address addr; 10 | string url; 11 | address next_addr; 12 | address previous_addr; 13 | } 14 | 15 | interface ProposerManager { 16 | event ProposerRotated(Proposer proposer); 17 | 18 | /// @notice Rotates the proposer; errors if rotation is not currently allowed 19 | // Emits ProposerRotated event 20 | function rotate_proposer() external; 21 | function add_proposer(string calldata proposer_url) external payable; 22 | function remove_proposer() external; 23 | function get_current_proposer_address() external view returns (address); 24 | function get_proposers() external view returns (Proposer[] memory); 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuit_builder.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | 3 | use jf_plonk::errors::PlonkError; 4 | 5 | use jf_relation::PlonkCircuit; 6 | 7 | use crate::nf_client_proof::{PrivateInputs, PublicInputs}; 8 | 9 | use super::circuits::unified_circuit::unified_circuit_builder; 10 | 11 | pub trait CircuitBuilder 12 | where 13 | Self: Sized, 14 | { 15 | type Error: std::error::Error; 16 | 17 | fn build_circuit( 18 | public_inputs: &mut PublicInputs, 19 | private_inputs: &mut PrivateInputs, 20 | ) -> Result; 21 | } 22 | 23 | impl CircuitBuilder for PlonkCircuit { 24 | type Error = PlonkError; 25 | 26 | fn build_circuit( 27 | public_inputs: &mut PublicInputs, 28 | private_inputs: &mut PrivateInputs, 29 | ) -> Result, PlonkError> { 30 | unified_circuit_builder(public_inputs, private_inputs) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/synchronisation.rs: -------------------------------------------------------------------------------- 1 | use warp::{ 2 | path, 3 | reply::{self, Reply}, 4 | Filter, 5 | }; 6 | 7 | use crate::{ 8 | drivers::blockchain::nightfall_event_listener::get_synchronisation_status, 9 | ports::contracts::NightfallContract, 10 | }; 11 | 12 | pub fn synchronisation( 13 | ) -> impl Filter + Clone { 14 | path!("v1" / "synchronisation") 15 | .and(warp::get()) 16 | .and_then(handle_synchronisation::) 17 | } 18 | 19 | pub async fn handle_synchronisation() -> Result { 20 | match get_synchronisation_status::().await { 21 | Ok(status) => Ok(reply::json(&status)), 22 | Err(_) => Err(warp::reject::custom( 23 | crate::domain::error::ClientRejection::SynchronisationUnavailable, 24 | )), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/entities.rs: -------------------------------------------------------------------------------- 1 | use crate::serialization::{ark_de_hex, ark_se_hex}; 2 | use ark_bn254::Fr as Fr254; 3 | use serde::{Deserialize, Serialize}; 4 | /// Struct used to represent deposit data, used in making deposit proofs by the proposer. 5 | #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)] 6 | pub struct DepositData { 7 | /// The Nightfall token ID 8 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 9 | pub nf_token_id: Fr254, 10 | /// The Nightfall slot ID 11 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 12 | pub nf_slot_id: Fr254, 13 | /// The value of the deposit 14 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 15 | pub value: Fr254, 16 | /// The secret hash used to redeem the deposit 17 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 18 | pub secret_hash: Fr254, 19 | } 20 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/ERC20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract ERC20Mock is ERC20 { 7 | constructor( 8 | uint initialSupply, 9 | address spender, 10 | address initialOwner, 11 | address other_client 12 | ) ERC20("ERC20Mock", "E20") { 13 | mint(initialOwner, spender, initialSupply / 2); 14 | mint(other_client, spender, initialSupply / 2); 15 | } 16 | 17 | function decimals() public view virtual override returns (uint8) { 18 | return 9; 19 | } 20 | 21 | function mint( 22 | address mintTo_, 23 | address spender, 24 | uint256 value_ 25 | ) public virtual { 26 | // Mint tokens to the specified address 27 | ERC20._approve(mintTo_, spender, value_); 28 | ERC20._mint(mintTo_, value_); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/RoundRobinV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity ^0.8.20; 3 | 4 | import "./RoundRobin.sol"; 5 | 6 | /// Tell OZ validator what this upgrades from (fully-qualified path + name) 7 | /// @custom:oz-upgrades-from blockchain_assets/contracts/RoundRobinUUPS.sol:RoundRobin 8 | contract RoundRobinV2 is RoundRobin { 9 | 10 | /// @custom:oz-upgrades-unsafe-allow constructor 11 | constructor() { 12 | _disableInitializers(); 13 | } 14 | 15 | /// Behavior change for the test: disable rotations in V2 16 | /// NOTE: V1's `rotate_proposer` must be `virtual` for this to override cleanly. 17 | function rotate_proposer() external pure override { 18 | revert("RoundRobinV2: rotate disabled for test"); 19 | } 20 | 21 | /// Add a tiny helper to detect V2 onchain 22 | function version() external pure returns (string memory) { 23 | return "RoundRobinV2: revert rotate_proposer"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/rest/block_data.rs: -------------------------------------------------------------------------------- 1 | /// Module for querying block data 2 | use warp::{ 3 | path, 4 | reply::{self, Reply}, 5 | Filter, 6 | }; 7 | 8 | use crate::domain::error::ProposerRejection; 9 | use crate::driven::nightfall_event::get_expected_layer2_blocknumber; 10 | 11 | /// GET request for a specific commitment by key 12 | pub fn get_block_data() -> impl Filter + Clone 13 | { 14 | path!("v1" / "blockdata") 15 | .and(warp::get()) 16 | .and_then(handle_block_data) 17 | } 18 | 19 | async fn handle_block_data() -> Result { 20 | let result: Result = (*get_expected_layer2_blocknumber().await.read().await).try_into(); 21 | match result { 22 | Ok(block_number) => Ok(reply::json(&block_number)), 23 | Err(_) => Err(warp::reject::custom( 24 | ProposerRejection::BlockDataUnavailable, 25 | )), 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "blockchain_assets/solidity_lib/openzeppelin-contracts"] 2 | path = blockchain_assets/solidity_lib/openzeppelin-contracts 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 4 | [submodule "blockchain_assets/solidity_lib/erc-3525"] 5 | path = blockchain_assets/solidity_lib/erc-3525 6 | url = https://github.com/solv-finance/erc-3525 7 | [submodule "blockchain_assets/solidity_lib/forge-std"] 8 | path = blockchain_assets/solidity_lib/forge-std 9 | url = https://github.com/foundry-rs/forge-std 10 | [submodule "blockchain_assets/solidity_lib/openzeppelin-contracts-upgradeable"] 11 | path = blockchain_assets/solidity_lib/openzeppelin-contracts-upgradeable 12 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 13 | [submodule "blockchain_assets/solidity_lib/openzeppelin-foundry-upgrades"] 14 | path = blockchain_assets/solidity_lib/openzeppelin-foundry-upgrades 15 | url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades 16 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/commitments.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use ark_bn254::Fr as Fr254; 4 | use nf_curves::ed_on_bn254::BJJTEAffine as JubJub; 5 | 6 | use crate::domain::entities::{DepositSecret, Preimage}; 7 | use jf_primitives::{poseidon::PoseidonError, trees::MembershipProof}; 8 | 9 | pub trait Commitment: Debug { 10 | fn hash(&self) -> Result; 11 | fn get_preimage(&self) -> Preimage; 12 | fn get_value(&self) -> Fr254; 13 | fn get_salt(&self) -> Fr254; 14 | fn get_nf_token_id(&self) -> Fr254; 15 | fn get_public_key(&self) -> JubJub; 16 | fn get_nf_slot_id(&self) -> Fr254; 17 | fn get_secret_preimage(&self) -> DepositSecret; 18 | } 19 | 20 | pub trait Nullifiable: Commitment + Copy { 21 | fn nullifier_hash(&self, nullifier_key: &Fr254) -> Result; 22 | } 23 | 24 | pub trait TreeMember { 25 | fn get_membership_proof(&self) -> MembershipProof; 26 | fn get_root(&self) -> Fr254; 27 | } 28 | -------------------------------------------------------------------------------- /nightfall_test/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1-labs 2 | FROM ghcr.io/foundry-rs/foundry:v1.1.0 AS foundry 3 | FROM rust:1.88.0 AS runtime 4 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 5 | COPY ./configuration/trust/* /usr/local/share/ca-certificates/ 6 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 7 | 8 | WORKDIR /app 9 | RUN mkdir bin 10 | COPY --from=foundry /usr/local/bin/forge ./forge 11 | COPY --exclude=configuration/bin/* . . 12 | ENV PATH=/app:$PATH 13 | WORKDIR /app/nightfall_test 14 | RUN --mount=type=cache,id=cargo-registry-v4,target=/usr/local/cargo/registry,sharing=locked \ 15 | --mount=type=cache,id=cargo-git-v4,target=/usr/local/cargo/git,sharing=locked \ 16 | --mount=type=cache,id=cargo-target-client-v4,target=/app/target,sharing=private \ 17 | cargo build --package nightfall_test --release && \ 18 | mv /app/target/release/nightfall_test /app/bin/test 19 | CMD ["/app/bin/test"] -------------------------------------------------------------------------------- /anvil/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/foundry-rs/foundry:v1.1.0 AS anvil 2 | FROM debian:bullseye-slim 3 | ENV ANVIL_IP_ADDR=0.0.0.0 4 | COPY --from=anvil /usr/local/bin/anvil /usr/local/bin/anvil 5 | COPY --from=anvil /usr/local/bin/forge /usr/local/bin/forge 6 | 7 | RUN apt-get update && apt-get install -y --no-install-recommends \ 8 | curl \ 9 | ca-certificates \ 10 | && rm -rf /var/lib/apt/lists/* 11 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 12 | COPY ./configuration/trust/* /usr/local/share/ca-certificates/ 13 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 14 | # Although a mining interval (block time) is set here, the tests will override it. 15 | 16 | # Copy entrypoint script 17 | COPY ./anvil/entrypoint.sh entrypoint.sh 18 | RUN chmod +x entrypoint.sh 19 | 20 | COPY blockchain_assets blockchain_assets/ 21 | COPY foundry.toml ./ 22 | COPY .env ./ 23 | 24 | # Set entrypoint 25 | ENTRYPOINT ["./entrypoint.sh"] 26 | 27 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/rest/synchronisation.rs: -------------------------------------------------------------------------------- 1 | use warp::{ 2 | path, 3 | reply::{self, Reply}, 4 | Filter, 5 | }; 6 | 7 | use crate::drivers::blockchain::nightfall_event_listener::get_synchronisation_status; 8 | 9 | pub fn synchronisation( 10 | ) -> impl Filter + Clone { 11 | path!("v1" / "synchronisation") 12 | .and(warp::get()) 13 | .and_then(handle_synchronisation) 14 | } 15 | 16 | pub async fn handle_synchronisation() -> Result { 17 | let synchronised = get_synchronisation_status() 18 | .await 19 | .read() 20 | .await 21 | .is_synchronised(); 22 | if synchronised { 23 | Ok(reply::with_status( 24 | "Synchronised", 25 | warp::http::StatusCode::OK, 26 | )) 27 | } else { 28 | Ok(reply::with_status( 29 | "Not synchronised", 30 | warp::http::StatusCode::OK, 31 | )) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/RoundRobinV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity ^0.8.20; 3 | 4 | import "./RoundRobin.sol"; 5 | 6 | /// @custom:oz-upgrades-from blockchain_assets/contracts/RoundRobin.sol:RoundRobin 7 | contract RoundRobinV3 is RoundRobin { 8 | 9 | /// @custom:oz-upgrades-unsafe-allow constructor 10 | constructor() { 11 | _disableInitializers(); 12 | } 13 | 14 | event StakeRequirementUpdated( 15 | uint oldStake, 16 | uint newStake, 17 | uint effectiveFromL1Block 18 | ); 19 | 20 | // Owner can raise/lower the stake required for *new* proposers. 21 | function setStakeRequirement(uint newStake) external onlyOwner { 22 | require(newStake >= EXIT_PENALTY, "new stake < exit penalty"); 23 | uint old = STAKE; 24 | STAKE = newStake; 25 | emit StakeRequirementUpdated(old, newStake, block.number); 26 | } 27 | 28 | // optional sanity marker 29 | function version() external pure returns (string memory) { 30 | return "RoundRobinV3-stake-config"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/notifier/webhook_notifier.rs: -------------------------------------------------------------------------------- 1 | use crate::{domain::notifications::NotificationPayload, ports::notifier::Notifier}; 2 | use async_trait::async_trait; 3 | use reqwest::Client; 4 | 5 | pub struct WebhookNotifier { 6 | pub endpoint: String, 7 | client: Client, 8 | } 9 | 10 | impl WebhookNotifier { 11 | pub fn new(endpoint: impl Into) -> Self { 12 | Self { 13 | endpoint: endpoint.into(), 14 | client: Client::new(), 15 | } 16 | } 17 | } 18 | 19 | #[async_trait] 20 | impl Notifier for WebhookNotifier { 21 | async fn notify(&self, payload: &NotificationPayload) -> Result<(), String> { 22 | let res = self 23 | .client 24 | .post(&self.endpoint) 25 | .json(&payload) 26 | .send() 27 | .await 28 | .map_err(|e| e.to_string())?; 29 | 30 | if res.status().is_success() { 31 | Ok(()) 32 | } else { 33 | Err(format!("HTTP request failed with status: {}", res.status())) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /nightfall_test/src/validate_certs.rs: -------------------------------------------------------------------------------- 1 | use crate::test::validate_certificate_with_server; 2 | use lib::models::CertificateReq; 3 | use log::info; 4 | use std::fs; 5 | use url::Url; 6 | 7 | // Validate all client and proposer certificates 8 | pub async fn validate_all_certificates( 9 | certs: [(&'static str, &'static str, &'static str, Url); N], 10 | http_client: &reqwest::Client, 11 | ) { 12 | for (name, cert_path, key_path, url) in certs.iter() { 13 | info!("Validating {name}'s certificate"); 14 | let cert = 15 | fs::read(cert_path).unwrap_or_else(|_| panic!("Failed to read {name} certificate")); 16 | let key = fs::read(key_path).unwrap_or_else(|_| panic!("Failed to read {name} priv_key")); 17 | let cert_req = CertificateReq { 18 | certificate: cert, 19 | certificate_private_key: key, 20 | }; 21 | validate_certificate_with_server(http_client, url.clone(), cert_req) 22 | .await 23 | .unwrap_or_else(|e| panic!("{name} Certificate validation failed: {e}")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Create an issue that adds a new feature 4 | title: "[Feature]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Overview 11 | **Give an idea of what this feature adds, the problems it aims to solve or the reason why it is being added** 12 | 13 | ### Tasks 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. Including if possible nice descriptions of how this will interface with the rest of the codebase. 16 | 17 | ### Optional 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered and whether they should be tried if the original idea is infeasible. 20 | 21 | ### Definition of done 22 | **When is this task complete?** 23 | Give a description of what this code should be able to do to be considered complete, including what should be tested. 24 | 25 | #### Links 26 | **Include relevant links here** 27 | These could be links to papers, articles or other issues this is linked to. 28 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/rest/block_assembly.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | 3 | use warp::{path, Filter}; 4 | 5 | use crate::initialisation::get_block_assembly_status; 6 | 7 | pub fn pause_block_assembly( 8 | ) -> impl Filter + Clone { 9 | path!("v1" / "pause") 10 | .and(warp::get()) 11 | .and_then(handle_pause_block_assembly) 12 | } 13 | 14 | pub async fn handle_pause_block_assembly() -> Result { 15 | debug!("Block assembly is being paused"); 16 | get_block_assembly_status().await.write().await.pause(); 17 | Ok(warp::http::StatusCode::OK) 18 | } 19 | pub fn resume_block_assembly( 20 | ) -> impl Filter + Clone { 21 | path!("v1" / "resume") 22 | .and(warp::get()) 23 | .and_then(handle_resume_block_assembly) 24 | } 25 | pub async fn handle_resume_block_assembly() -> Result { 26 | debug!("Block assembly is being resumed"); 27 | get_block_assembly_status().await.write().await.resume(); 28 | Ok(warp::http::StatusCode::OK) 29 | } 30 | -------------------------------------------------------------------------------- /nightfall_client/src/services/data_publisher.rs: -------------------------------------------------------------------------------- 1 | use futures::future::join_all; 2 | 3 | use crate::{domain::notifications::NotificationPayload, ports::notifier::Notifier}; 4 | 5 | pub struct DataPublisher { 6 | notifiers: Vec>, 7 | } 8 | 9 | impl DataPublisher { 10 | pub fn new() -> Self { 11 | Self { 12 | notifiers: Vec::new(), 13 | } 14 | } 15 | 16 | pub fn register_notifier(&mut self, notifier: Box) { 17 | self.notifiers.push(notifier); 18 | } 19 | 20 | pub async fn publish(&self, notification: NotificationPayload) { 21 | let futures = self 22 | .notifiers 23 | .iter() 24 | .map(|notifier| notifier.notify(¬ification)); 25 | let results = join_all(futures).await; 26 | 27 | for result in results { 28 | match result { 29 | Ok(_) => log::debug!("Notification sent successfully"), 30 | Err(e) => log::warn!("Failed to send notification: {e}"), 31 | } 32 | } 33 | } 34 | } 35 | 36 | impl Default for DataPublisher { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nightfall_proposer/src/ports/transactions.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | use ark_ec::twisted_edwards::Affine as TEAffine; 3 | use ark_ed_on_bn254::EdwardsConfig; 4 | use ark_ff::BigInteger256; 5 | use lib::{ 6 | nf_client_proof::{Proof, PublicInputs}, 7 | shared_entities::TokenType, 8 | }; 9 | use serde::Serialize; 10 | use std::fmt::Debug; 11 | 12 | pub trait ClientTx: Send { 13 | fn verify_transaction(&self) -> bool; 14 | fn get_value(&self) -> Fr254; 15 | fn get_fee(&self) -> Fr254; 16 | fn get_historic_commitment_roots(&self) -> Vec; 17 | fn get_circuit_hash(&self) -> Fr254; 18 | fn get_token_type(&self) -> TokenType; 19 | fn get_token_id(&self) -> BigInteger256; 20 | fn get_erc_address(&self) -> Fr254; 21 | fn get_recipient_address(&self) -> Fr254; 22 | fn get_commitments(&self) -> Vec; 23 | fn get_nullifiers(&self) -> Vec; 24 | fn get_compressed_secrets(&self) -> Vec; 25 | fn get_roots(&self) -> Vec; 26 | fn get_fee_address(&self) -> Fr254; 27 | fn get_pub_point(&self) -> TEAffine; 28 | fn get_proof(&self) -> P; 29 | fn get_public_inputs(&self) -> PublicInputs; 30 | } 31 | -------------------------------------------------------------------------------- /nightfall_proposer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.88.0 AS builder 2 | WORKDIR /app 3 | RUN mkdir bin 4 | COPY . . 5 | RUN --mount=type=cache,id=cargo-registry-v4,target=/usr/local/cargo/registry,sharing=locked \ 6 | --mount=type=cache,id=cargo-git-v4,target=/usr/local/cargo/git,sharing=locked \ 7 | --mount=type=cache,id=cargo-target-client-v4,target=/app/target,sharing=private \ 8 | cargo build --package nightfall_proposer --release && \ 9 | mv /app/target/release/nightfall_proposer /app/bin/proposer 10 | FROM debian:bookworm-slim 11 | WORKDIR /app 12 | 13 | # Install runtime dependencies 14 | RUN apt-get update && apt-get install -y --no-install-recommends \ 15 | libssl3 \ 16 | wget \ 17 | ca-certificates && \ 18 | rm -rf /var/lib/apt/lists/* 19 | 20 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 21 | COPY configuration/trust/* /usr/local/share/ca-certificates/ 22 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 23 | # Copy application binaries and configuration 24 | COPY --from=builder /app/bin/proposer bin/ 25 | COPY configuration/bin/* configuration/bin/ 26 | COPY .env *.env nightfall.toml . 27 | 28 | EXPOSE 3000 29 | CMD ["/app/bin/proposer"] -------------------------------------------------------------------------------- /nightfall_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1-labs 2 | FROM rust:1.88.0 AS builder 3 | 4 | WORKDIR /app 5 | RUN mkdir bin 6 | COPY --exclude=configuration/bin/* . . 7 | COPY configuration/bin/proving_key configuration/bin/deposit_proving_key configuration/bin/ 8 | RUN --mount=type=cache,target=/usr/local/cargo/registry \ 9 | --mount=type=cache,target=/app/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | cargo build --package nightfall_client --release && \ 12 | mv /app/target/release/nightfall_client /app/bin/client 13 | 14 | FROM debian:bookworm-slim 15 | WORKDIR /app 16 | 17 | # Install runtime dependencies 18 | RUN apt-get update && apt-get install -y --no-install-recommends \ 19 | libssl3 \ 20 | wget \ 21 | ca-certificates && \ 22 | rm -rf /var/lib/apt/lists/* 23 | 24 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 25 | COPY configuration/trust/* /usr/local/share/ca-certificates/ 26 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 27 | 28 | # Copy application binaries and configuration 29 | COPY --from=builder /app/bin/client bin/ 30 | COPY configuration/bin/proving_key configuration/bin/deposit_proving_key configuration/bin/ 31 | COPY .env *.env nightfall.toml . 32 | 33 | EXPOSE 3000 34 | CMD ["/app/bin/client"] -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/token_info.rs: -------------------------------------------------------------------------------- 1 | use crate::ports::contracts::NightfallContract; 2 | use ark_bn254::Fr as Fr254; 3 | use lib::hex_conversion::HexConvertible; 4 | use reqwest::StatusCode; 5 | use warp::{ 6 | path, reject, 7 | reply::{self, Reply}, 8 | Filter, 9 | }; 10 | 11 | #[derive(Debug)] 12 | pub struct InvalidQuery; 13 | impl reject::Reject for InvalidQuery {} 14 | #[derive(Debug)] 15 | pub struct NotFound; 16 | impl reject::Reject for NotFound {} 17 | 18 | /// GET request for a getting information about a token if you happen to know the nightfall token id 19 | pub fn get_token_info( 20 | ) -> impl Filter + Clone { 21 | path!("v1" / "token" / String) 22 | .and(warp::get()) 23 | .and_then(handle_get_token_info::) 24 | } 25 | 26 | /// Handler for the GET request to retrieve token information 27 | async fn handle_get_token_info( 28 | nf_token_id: String, 29 | ) -> Result { 30 | let nf_token_id = 31 | Fr254::from_hex_string(&nf_token_id).map_err(|_| reject::custom(InvalidQuery))?; 32 | let token_info = N::get_token_info(nf_token_id) 33 | .await 34 | .map_err(|_| reject::custom(NotFound))?; 35 | Ok(reply::with_status(reply::json(&token_info), StatusCode::OK)) 36 | } 37 | -------------------------------------------------------------------------------- /nightfall_proposer/src/driven/db/nullifier_tree.rs: -------------------------------------------------------------------------------- 1 | use crate::ports::trees::NullifierTree; 2 | use ark_ff::PrimeField; 3 | use jf_primitives::{poseidon::PoseidonParams, trees::imt::IMTCircuitInsertionInfo}; 4 | use lib::merkle_trees::trees::{IndexedTree, MutableTree}; 5 | 6 | #[async_trait::async_trait] 7 | impl NullifierTree for mongodb::Client 8 | where 9 | F: PrimeField + PoseidonParams, 10 | { 11 | type CircuitInfo = IMTCircuitInsertionInfo; 12 | const TREE_NAME: &'static str = "Nullifiers"; 13 | 14 | async fn insert_for_circuit( 15 | &mut self, 16 | leaves: &[F], 17 | ) -> Result>::Error> { 18 | >::insert_nullifiers_for_circuit( 19 | self, 20 | leaves, 21 | >::TREE_NAME, 22 | ) 23 | .await 24 | } 25 | /// let's multiple sub trees be added in a single batch - it calls insert_subtree for each sub tree 26 | async fn batch_insert_with_circuit_info( 27 | &self, 28 | commitments: &[F], 29 | ) -> Result, >::Error> { 30 | >::batch_insert_nullifiers_with_circuit_info( 31 | self, 32 | commitments, 33 | >::TREE_NAME, 34 | ) 35 | .await 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "blockchain_assets/contracts" 3 | out = "blockchain_assets/artifacts" 4 | build_info = true 5 | extra_output = ["storageLayout"] 6 | ast = true 7 | ffi = true 8 | script = "blockchain_assets/script" 9 | libs = ["blockchain_assets/solidity_lib"] 10 | test = "blockchain_assets/test_contracts" 11 | cache_path = "blockchain_assets/cache" 12 | broadcast = "blockchain_assets/logs" 13 | gas_reports = ["Nightfall"] 14 | fs_permissions = [{ access = "read-write", path = "./" }] 15 | evm_version = "cancun" 16 | remappings = [ 17 | "@openzeppelin/contracts/=blockchain_assets/solidity_lib/openzeppelin-contracts/contracts/", 18 | "@forge-std/=blockchain_assets/solidity_lib/forge-std/src/", 19 | "@erc-3525/=blockchain_assets/solidity_lib/erc-3525/", 20 | "@openzeppelin/contracts-upgradable/=blockchain_assets/solidity_lib/openzeppelin-contracts-upgradeable/contracts/", 21 | "@openzeppelin/foundry-upgrades/=blockchain_assets/solidity_lib/openzeppelin-foundry-upgrades/src/", 22 | ] 23 | optimizer = true # Enable the optimizer 24 | optimizer_runs = 200 # Lower = smaller bytecode (but higher runtime gas) 25 | via_ir = false # Disable IR compilation (can sometimes reduce size) 26 | debug = false # Strip debug symbols 27 | # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options 28 | 29 | [profile.deploy] 30 | inherit = "default" 31 | ffi = true -------------------------------------------------------------------------------- /nightfall_client/src/driven/contract_functions/contract_type_conversions.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::entities::{Proposer, WithdrawData}; 2 | use alloy::primitives::Address; 3 | use lib::contract_conversions::{Addr, Uint256}; 4 | use nightfall_bindings::artifacts::{Nightfall, RoundRobin}; 5 | /// enables conversion between a Proposer as used in the ProposerManager contract, and a for suitable for serialisation 6 | impl From for Proposer { 7 | fn from(proposer: RoundRobin::Proposer) -> Self { 8 | Proposer { 9 | stake: proposer.stake, 10 | addr: proposer.addr, 11 | url: proposer.url, 12 | next_addr: proposer.next_addr, 13 | previous_addr: proposer.previous_addr, 14 | } 15 | } 16 | } 17 | 18 | impl From for Nightfall::WithdrawData { 19 | fn from(data: WithdrawData) -> Nightfall::WithdrawData { 20 | let nf_token_id = Uint256::from(data.nf_token_id).0; 21 | let recipient_address = Address::from( 22 | Addr::try_from(data.withdraw_address) 23 | .expect("Could not convert WithdrawData withdraw address to Solidity address"), 24 | ); 25 | let value = Uint256::from(data.value).0; 26 | let withdraw_fund_salt = Uint256::from(data.withdraw_fund_salt).0; 27 | Nightfall::WithdrawData { 28 | nf_token_id, 29 | recipient_address, 30 | value, 31 | withdraw_fund_salt, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/blockchain_client.rs: -------------------------------------------------------------------------------- 1 | use crate::error::BlockchainClientConnectionError; 2 | use crate::wallets::WalletType; 3 | use alloy::primitives::{Address, U256}; 4 | use alloy::providers::Provider; 5 | use alloy::pubsub::PubSubConnect; 6 | use alloy::signers::local::PrivateKeySigner; 7 | use alloy::signers::Signer; 8 | use async_trait::async_trait; 9 | use serde::Deserialize; 10 | use std::{marker::Sync, sync::Arc}; 11 | use url::Url; 12 | 13 | /// A blockchain client is able to connect to a blockchain node and sign transactions. 14 | /// It is also able to listen for events on the blockchain (although it does not need 15 | /// signing capabilty to do this). 16 | /// 17 | #[async_trait] 18 | pub trait BlockchainClientConnection: Clone + Send + Sync { 19 | type W: Signer + 'static; 20 | type T: PubSubConnect + 'static; 21 | type S: for<'a> Deserialize<'a>; 22 | 23 | async fn is_connected(&self) -> bool; 24 | 25 | async fn get_balance(&self) -> Option; 26 | 27 | fn get_address(&self) -> Address; 28 | 29 | fn get_client(&self) -> Arc; 30 | 31 | fn get_wallet_type(&self) -> &WalletType; 32 | 33 | fn get_signer(&self) -> PrivateKeySigner 34 | where 35 | Self: Sized; 36 | 37 | async fn new(url: Url, wallet: Self::W) -> Result 38 | where 39 | Self: Sized; 40 | 41 | async fn try_from_settings(settings: &Self::S) -> Result 42 | where 43 | Self: Sized; 44 | } 45 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | #These are some test keys used for the 'local' wallet setting in the configuration toml. 2 | #They are used for TESTING ONLY. 3 | #They must be replaced by other environment variables in a production environment, or better, use 4 | #a more secure type of wallet. 5 | CLIENT_SIGNING_KEY="0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97" # key(8) 6 | CLIENT2_SIGNING_KEY="0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e" # key(6) 7 | CLIENT_ADDRESS="0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" # Account(8) 8 | CLIENT2_ADDRESS="0x976EA74026E726554dB657fA54763abd0C3a0aa9" # Account(6) 9 | PROPOSER_SIGNING_KEY="0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" # key(9) 10 | PROPOSER_2_SIGNING_KEY="0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" # key(7) 11 | DEPLOYER_SIGNING_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" # key(0) 12 | NIGHTFALL_ADDRESS="0x68B1D87F95878fE05B998F19b66F4baba5De1aed" # Contract(0) 13 | WEBHOOK_URL= 14 | # Azure Key Vault configuration 15 | AZURE_KEY_NAME=ethereum-signing-key 16 | AZURE_VAULT_URL=https://nightfall-kv-1760976507.vault.azure.net/ 17 | CLIENT_SIGNING_KEY_NAME=ethereum-signing-key 18 | CLIENT2_SIGNING_KEY_NAME=ethereum-signing-key 19 | DEPLOYER_SIGNING_KEY_NAME=ethereum-signing-key 20 | PROPOSER_SIGNING_KEY_NAME=ethereum-signing-key 21 | PROPOSER_2_SIGNING_KEY_NAME=ethereum-signing-key 22 | # Azure Authentication (KEEP THESE SECRET!) 23 | AZURE_CLIENT_ID=your-client-id-here 24 | AZURE_CLIENT_SECRET=your-client-secret-her 25 | AZURE_TENANT_ID=your-tenant-id-here 26 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/rest/client_transactions.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::error::ProposerRejection; 2 | use crate::driven::nightfall_client_transaction::process_nightfall_client_transaction; 3 | use lib::{ 4 | nf_client_proof::{Proof, ProvingEngine}, 5 | shared_entities::ClientTransaction, 6 | }; 7 | use log::{error, info}; 8 | 9 | use warp::{hyper::StatusCode, path, Filter}; 10 | 11 | pub fn client_transaction( 12 | ) -> impl Filter + Clone 13 | where 14 | P: Proof, 15 | E: ProvingEngine

, 16 | { 17 | path!("v1" / "transaction") 18 | .and(warp::body::json()) 19 | .and_then(|transaction| handle_client_transaction::(transaction)) 20 | } 21 | 22 | async fn handle_client_transaction( 23 | transaction: ClientTransaction

, 24 | ) -> Result 25 | where 26 | P: Proof, 27 | E: ProvingEngine

, 28 | { 29 | // first we should check that the transaction is valid 30 | // then we should check that the transaction is not already in the database 31 | // then we should add the transaction to the database 32 | // Luckily, there is a function that does that. 33 | info!("Received client transaction"); 34 | let result = process_nightfall_client_transaction::(transaction).await; 35 | match result { 36 | Ok(_) => Ok(StatusCode::CREATED), 37 | Err(e) => { 38 | error!("Error processing client transaction: {e}"); 39 | Err(warp::reject::custom( 40 | ProposerRejection::ClientTransactionFailed, 41 | )) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuits/verify/verify_duplicates_gadgets.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | 3 | use jf_relation::{errors::CircuitError, Circuit, PlonkCircuit, Variable}; 4 | 5 | pub trait VerifyDuplicatesCircuit 6 | where 7 | F: PrimeField, 8 | { 9 | fn verify_duplicates( 10 | &mut self, 11 | nullifiers_vars: &[Variable; 4], 12 | commitments_vars: &[Variable; 4], 13 | ) -> Result<(), CircuitError>; 14 | } 15 | 16 | impl VerifyDuplicatesCircuit for PlonkCircuit 17 | where 18 | F: PrimeField, 19 | { 20 | fn verify_duplicates( 21 | &mut self, 22 | nullifiers_vars: &[Variable; 4], 23 | commitments_vars: &[Variable; 4], 24 | ) -> Result<(), CircuitError> { 25 | // Check that no nullifiers are duplicated. 26 | for i in 0..4 { 27 | for j in i + 1..4 { 28 | let tmp_one = self.is_equal(nullifiers_vars[j], self.zero())?; 29 | let tmp_two = self.is_equal(nullifiers_vars[j], nullifiers_vars[i])?; 30 | let tmp_three = self.logic_neg(tmp_two)?; 31 | self.logic_or_gate(tmp_one, tmp_three)?; 32 | } 33 | } 34 | 35 | // Check that no commitments are duplicated. 36 | for i in 0..4 { 37 | for j in i + 1..4 { 38 | let tmp_one = self.is_equal(commitments_vars[j], self.zero())?; 39 | let tmp_two = self.is_equal(commitments_vars[j], commitments_vars[i])?; 40 | let tmp_three = self.logic_neg(tmp_two)?; 41 | self.logic_or_gate(tmp_one, tmp_three)?; 42 | } 43 | } 44 | 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/proposers.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::entities::Proposer; 2 | use configuration::addresses::get_addresses; 3 | use lib::{ 4 | blockchain_client::BlockchainClientConnection, initialisation::get_blockchain_client_connection, 5 | }; 6 | use nightfall_bindings::artifacts::ProposerManager; 7 | use warp::{path, reply, reply::Reply, Filter}; 8 | 9 | /// Get request for obtaining a list of proposers 10 | pub fn get_proposers() -> impl Filter + Clone { 11 | path!("v1" / "proposers") 12 | .and(warp::get()) 13 | .and_then(handle_get_proposers) 14 | } 15 | 16 | async fn handle_get_proposers() -> Result { 17 | // get a ManageProposers instance 18 | let blockchain_client = get_blockchain_client_connection() 19 | .await 20 | .read() 21 | .await 22 | .get_client(); // returns impl Provider or dyn Provider 23 | 24 | let proposer_manager = 25 | ProposerManager::new(get_addresses().round_robin, blockchain_client.root()); 26 | // get the proposers 27 | let proposer_list = 28 | proposer_manager.get_proposers().call().await.map_err(|_| { 29 | warp::reject::custom(crate::domain::error::ClientRejection::ProposerError) 30 | })?; 31 | let list = proposer_list 32 | .into_iter() 33 | .map(|p| Proposer { 34 | stake: p.stake, 35 | addr: p.addr, 36 | url: p.url, 37 | next_addr: p.next_addr, 38 | previous_addr: p.previous_addr, 39 | }) 40 | .collect::>(); 41 | Ok(reply::json(&list)) 42 | } 43 | -------------------------------------------------------------------------------- /nightfall_proposer/src/driven/db/commitment_tree.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use jf_primitives::{poseidon::PoseidonParams, trees::CircuitInsertionInfo}; 3 | use lib::merkle_trees::trees::MutableTree; 4 | 5 | use crate::ports::trees::CommitmentTree; 6 | 7 | /// Implementation of a commitment tree. This is bascially an append-only tree because we don't make use 8 | /// of membership proofs, unlike the client crate, which needs to prove membership of a historic commitment tree in order 9 | /// to spend commitments. 10 | 11 | #[async_trait::async_trait] 12 | impl CommitmentTree for mongodb::Client 13 | where 14 | F: PrimeField + PoseidonParams, 15 | { 16 | type CircuitInfo = CircuitInsertionInfo; 17 | const TREE_NAME: &'static str = "Commitments"; 18 | 19 | async fn batch_insert_with_circuit_info( 20 | &self, 21 | commitments: &[F], 22 | ) -> Result, Self::Error> { 23 | >::batch_insert_with_circuit_info( 24 | self, 25 | commitments, 26 | >::TREE_NAME, 27 | ) 28 | .await 29 | } 30 | 31 | async fn insert_for_circuit( 32 | &mut self, 33 | commitments: &[F], 34 | ) -> Result { 35 | >::insert_for_circuit( 36 | self, 37 | commitments, 38 | >::TREE_NAME, 39 | ) 40 | .await 41 | } 42 | 43 | async fn get_root(&self) -> Result { 44 | >::get_root(self, >::TREE_NAME).await 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/ERC3525Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.1; 4 | 5 | import "@erc-3525/contracts/ERC3525.sol"; 6 | 7 | contract ERC3525Mock is ERC3525 { 8 | constructor( 9 | address spender, 10 | uint256 initial_id, 11 | uint256 value, 12 | uint256 initial_id_two, 13 | uint256 value_two, 14 | uint256 slot, 15 | address initialOwner 16 | ) ERC3525("ERC3525Mock", "ERC3525", 100) { 17 | mint(spender, initialOwner, initial_id, slot, value); 18 | mint(spender, initialOwner, initial_id_two, slot, value_two); 19 | } 20 | 21 | function mint( 22 | address spender, 23 | address mintTo_, 24 | uint256 tokenId_, 25 | uint256 slot_, 26 | uint256 value_ 27 | ) public virtual { 28 | ERC3525._setApprovalForAll(mintTo_, spender, true); 29 | ERC3525._mint(mintTo_, tokenId_, slot_, value_); 30 | } 31 | 32 | function mintValue(uint256 tokenId_, uint256 value_) public virtual { 33 | ERC3525._mintValue(tokenId_, value_); 34 | } 35 | 36 | function burn(uint256 tokenId_) public virtual { 37 | require( 38 | _isApprovedOrOwner(_msgSender(), tokenId_), 39 | "ERC3525: caller is not token owner nor approved" 40 | ); 41 | ERC3525._burn(tokenId_); 42 | } 43 | 44 | function burnValue(uint256 tokenId_, uint256 burnValue_) public virtual { 45 | require( 46 | _isApprovedOrOwner(_msgSender(), tokenId_), 47 | "ERC3525: caller is not token owner nor approved" 48 | ); 49 | ERC3525._burnValue(tokenId_, burnValue_); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/BlockHashTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity >=0.8.20; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/console2.sol"; 6 | 7 | contract BlockHashTest is Test { 8 | function sha256_and_shift( 9 | bytes memory inputs 10 | ) private pure returns (uint256) { 11 | bytes32 hash = sha256(inputs); 12 | return uint256(hash) >> 3; 13 | } 14 | 15 | function test_block_hash() public pure { 16 | uint256[] memory transactions = new uint256[](8); 17 | 18 | for (uint i; i < 8; i++) { 19 | transactions[i] = sha256_and_shift( 20 | abi.encode( 21 | uint256(0), 22 | uint256(1), 23 | uint256(2), 24 | uint256(3), 25 | uint256(4), 26 | uint256(5), 27 | uint256(6), 28 | uint256(7), 29 | uint256(8), 30 | uint256(9), 31 | uint256(10), 32 | uint256(11) 33 | ) 34 | ); 35 | } 36 | 37 | uint256 length = 4; 38 | 39 | while (length >= 1) { 40 | for (uint256 i = 0; i < length; i++) { 41 | transactions[i] = uint256( 42 | sha256_and_shift( 43 | abi.encodePacked( 44 | transactions[2 * i], 45 | transactions[(2 * i) + 1] 46 | ) 47 | ) 48 | ); // hash a row 49 | } 50 | length = length / 2; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod circuit_builder; 2 | pub mod circuits; 3 | pub mod plonk_proof; 4 | 5 | use crate::{rollup_circuit_checks::find_file_with_path, utils::load_key_from_server}; 6 | use ark_bn254::Bn254; 7 | use ark_serialize::CanonicalDeserialize; 8 | use jf_plonk::nightfall::ipa_structs::ProvingKey; 9 | use jf_primitives::pcs::prelude::UnivariateKzgPCS; 10 | use log::warn; 11 | use std::{ 12 | path::Path, 13 | sync::{Arc, OnceLock}, 14 | }; 15 | 16 | /// This function is used to retrieve the client proving key. 17 | pub fn get_client_proving_key() -> &'static Arc>> { 18 | static PK: OnceLock>>> = OnceLock::new(); 19 | PK.get_or_init(|| { 20 | // We'll try to load from the configuration server first. 21 | if let Some(key_bytes) = load_key_from_server("proving_key") { 22 | let pk = ProvingKey::>::deserialize_compressed_unchecked( 23 | &*key_bytes, 24 | ) 25 | .expect("Could not deserialise proving key"); 26 | return Arc::new(pk); 27 | } 28 | // If that fails, we'll try to load from a local file 29 | warn!("Could not load proving key from server. Loading from local file"); 30 | let path = Path::new("./configuration/bin/proving_key"); 31 | let source_file = find_file_with_path(path).expect("Could not find path"); 32 | let pk = ProvingKey::>::deserialize_compressed_unchecked( 33 | &*std::fs::read(source_file).expect("Could not read proving key"), 34 | ) 35 | .expect("Could not deserialise proving key"); 36 | Arc::new(pk) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/X509/Allowlist.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | abstract contract Allowlist is Initializable { 6 | address public owner; 7 | bool public allowlisting; 8 | mapping(address => bool) public users; 9 | 10 | modifier onlyOwner() { 11 | require(msg.sender == owner, "Caller is not the owner"); 12 | _; 13 | } 14 | 15 | // === Initializer === 16 | // Call this from proxy's initializer (X509.initialize) 17 | function __Allowlist_init(address owner_) internal onlyInitializing { 18 | __Allowlist_init_unchained(owner_); 19 | } 20 | 21 | function __Allowlist_init_unchained( 22 | address owner_ 23 | ) internal onlyInitializing { 24 | require(owner == address(0), "Allowlist: already initialized"); 25 | owner = owner_; 26 | allowlisting = true; 27 | } 28 | 29 | // ownership management to mirror old pattern 30 | function transferOwnership(address newOwner) external onlyOwner { 31 | require(newOwner != address(0), "Invalid owner"); 32 | owner = newOwner; 33 | } 34 | 35 | function addUserToAllowlist(address _user) internal { 36 | users[_user] = true; 37 | } 38 | 39 | function enableAllowlisting(bool _allowlisting) external onlyOwner { 40 | allowlisting = _allowlisting; 41 | } 42 | 43 | function isAllowlisted(address _user) public view returns (bool) { 44 | if (allowlisting == false) return true; // allowlisting is turned off 45 | return users[_user]; 46 | } 47 | 48 | // Reserve storage for future upgrades 49 | uint256[50] private __gap; 50 | } 51 | -------------------------------------------------------------------------------- /nightfall_deployer/src/main.rs: -------------------------------------------------------------------------------- 1 | use configuration::{logging::init_logging, settings::Settings}; 2 | use log::{info, warn}; 3 | use nightfall_deployer::deployment::deploy_contracts; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | // Print banner immediately on startup 8 | println!( 9 | r#" 10 | ███╗ ██╗██╗ ██████╗ ██╗ ██╗████████╗███████╗ █████╗ ██╗ ██╗ ██╗ ██╗ 11 | ████╗ ██║██║██╔════╝ ██║ ██║╚══██╔══╝██╔════╝██╔══██╗██║ ██║ ██║ ██║ 12 | ██╔██╗ ██║██║██║ ███╗███████║ ██║ █████╗ ███████║██║ ██║ ███████║ 13 | ██║╚██╗██║██║██║ ██║██╔══██║ ██║ ██╔══╝ ██╔══██║██║ ██║ ╚════██║ 14 | ██║ ╚████║██║╚██████╔╝██║ ██║ ██║ ██║ ██║ ██║███████╗███████╗███████╗██║ 15 | ╚═╝ ╚═══╝╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ 16 | 17 | A Zero-Knowledge Proof (ZKP) based application that enables private transfers of ERC20, ERC721, ERC1155, and ERC3525 tokens on public blockchains, 18 | leveraging a Layer 2 ZK-ZK rollup architecture to combine scalability with enterprise-grade privacy. 19 | https://github.com/EYBlockchain/nightfall_4_CE 20 | "# 21 | ); 22 | let settings: Settings = Settings::new().unwrap(); 23 | init_logging( 24 | settings.nightfall_deployer.log_level.as_str(), 25 | settings.log_app_only, 26 | ); 27 | info!("Deployer has started"); 28 | 29 | if &settings.signing_key == "set by environment" { 30 | panic!("Set the deployer signing key in env variable NF4_DEPLOYER_SIGNING_KEY") 31 | }; 32 | if settings.contracts.deploy_contracts { 33 | info!("Deploying contracts"); 34 | deploy_contracts(&settings).await.unwrap(); 35 | } else { 36 | warn!("Skipping contract deployment"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /configuration/src/logging.rs: -------------------------------------------------------------------------------- 1 | use env_logger::Builder; 2 | use log::LevelFilter; 3 | use log_panics; 4 | 5 | pub fn init_logging(log_level: &str, app_only: bool) { 6 | log_panics::init(); // this ensures that panics are logged 7 | if app_only { 8 | match log_level { 9 | "debug" => Builder::new() 10 | .filter_level(LevelFilter::Debug) 11 | .filter_module("alloy_provider", LevelFilter::Error) 12 | .filter_module("warp", LevelFilter::Warn) 13 | .filter_module("hyper", LevelFilter::Warn) 14 | .filter_module("tungstenite", LevelFilter::Warn) 15 | .init(), 16 | "info" => Builder::new() 17 | .filter_level(LevelFilter::Info) 18 | .filter_module("alloy_provider", LevelFilter::Error) 19 | .filter_module("warp", LevelFilter::Warn) 20 | .filter_module("hyper", LevelFilter::Warn) 21 | .filter_module("tungstenite", LevelFilter::Warn) 22 | .init(), 23 | "warn" => Builder::new().filter_level(LevelFilter::Warn).init(), 24 | "error" => Builder::new().filter_level(LevelFilter::Error).init(), 25 | _ => Builder::new().filter_level(LevelFilter::Info).init(), 26 | }; 27 | } else { 28 | match log_level { 29 | "debug" => Builder::new().filter_level(LevelFilter::Debug).init(), 30 | "info" => Builder::new().filter_level(LevelFilter::Info).init(), 31 | "warn" => Builder::new().filter_level(LevelFilter::Warn).init(), 32 | "error" => Builder::new().filter_level(LevelFilter::Error).init(), 33 | _ => Builder::new().filter_level(LevelFilter::Info).init(), 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /configuration/trust/ZscalerRootCertificate-2048-SHA256.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE0zCCA7ugAwIBAgIJANu+mC2Jt3uTMA0GCSqGSIb3DQEBCwUAMIGhMQswCQYD 3 | VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2Ux 4 | FTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMMWnNjYWxlciBJbmMuMRgw 5 | FgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG9w0BCQEWE3N1cHBvcnRA 6 | enNjYWxlci5jb20wHhcNMTQxMjE5MDAyNzU1WhcNNDIwNTA2MDAyNzU1WjCBoTEL 7 | MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBK 8 | b3NlMRUwEwYDVQQKEwxac2NhbGVyIEluYy4xFTATBgNVBAsTDFpzY2FsZXIgSW5j 9 | LjEYMBYGA1UEAxMPWnNjYWxlciBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNzdXBw 10 | b3J0QHpzY2FsZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 11 | qT7STSxZRTgEFFf6doHajSc1vk5jmzmM6BWuOo044EsaTc9eVEV/HjH/1DWzZtcr 12 | fTj+ni205apMTlKBW3UYR+lyLHQ9FoZiDXYXK8poKSV5+Tm0Vls/5Kb8mkhVVqv7 13 | LgYEmvEY7HPY+i1nEGZCa46ZXCOohJ0mBEtB9JVlpDIO+nN0hUMAYYdZ1KZWCMNf 14 | 5J/aTZiShsorN2A38iSOhdd+mcRM4iNL3gsLu99XhKnRqKoHeH83lVdfu1XBeoQz 15 | z5V6gA3kbRvhDwoIlTBeMa5l4yRdJAfdpkbFzqiwSgNdhbxTHnYYorDzKfr2rEFM 16 | dsMU0DHdeAZf711+1CunuQIDAQABo4IBCjCCAQYwHQYDVR0OBBYEFLm33UrNww4M 17 | hp1d3+wcBGnFTpjfMIHWBgNVHSMEgc4wgcuAFLm33UrNww4Mhp1d3+wcBGnFTpjf 18 | oYGnpIGkMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8G 19 | A1UEBxMIU2FuIEpvc2UxFTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMM 20 | WnNjYWxlciBJbmMuMRgwFgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG 21 | 9w0BCQEWE3N1cHBvcnRAenNjYWxlci5jb22CCQDbvpgtibd7kzAMBgNVHRMEBTAD 22 | AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw0NdJh8w3NsJu4KHuVZUrmZgIohnTm0j+ 23 | RTmYQ9IKA/pvxAcA6K1i/LO+Bt+tCX+C0yxqB8qzuo+4vAzoY5JEBhyhBhf1uK+P 24 | /WVWFZN/+hTgpSbZgzUEnWQG2gOVd24msex+0Sr7hyr9vn6OueH+jj+vCMiAm5+u 25 | kd7lLvJsBu3AO3jGWVLyPkS3i6Gf+rwAp1OsRrv3WnbkYcFf9xjuaf4z0hRCrLN2 26 | xFNjavxrHmsH8jPHVvgc1VD0Opja0l/BRVauTrUaoW6tE+wFG5rEcPGS80jjHK4S 27 | pB5iDj2mUZH1T8lzYtuZy0ZPirxmtsk3135+CKNa2OCAhhFjE0xd 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /blockchain_assets/script/mock_deployment.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {Script} from "@forge-std/Script.sol"; 5 | import "@forge-std/StdToml.sol"; 6 | import "../contracts/ERC20Mock.sol"; 7 | import "../contracts/ERC721Mock.sol"; 8 | import "../contracts/ERC1155Mock.sol"; 9 | import "../contracts/ERC3525Mock.sol"; 10 | import "../contracts/X509/X509.sol"; 11 | import "../contracts/Nightfall.sol"; 12 | import "forge-std/console.sol"; 13 | 14 | contract MockDeployer is Script { 15 | function run() external { 16 | uint256 deployerPrivateKey = vm.envUint("NF4_SIGNING_KEY"); 17 | address owner = vm.envAddress("CLIENT_ADDRESS"); 18 | address client2 = vm.envAddress("CLIENT2_ADDRESS"); 19 | // This is the address of the Nightfall contract, which is not used in this script 20 | address nightfallAddress = vm.envAddress("NIGHTFALL_ADDRESS"); 21 | vm.startBroadcast(deployerPrivateKey); 22 | ERC20Mock erc20 = new ERC20Mock(2000, nightfallAddress, owner, client2); 23 | ERC721Mock erc721 = new ERC721Mock(426, owner, nightfallAddress); 24 | ERC1155Mock erc1155 = new ERC1155Mock( 25 | nightfallAddress, 26 | 2, 27 | 100, 28 | 73, 29 | 0, 30 | owner 31 | ); 32 | ERC3525Mock erc3525 = new ERC3525Mock( 33 | nightfallAddress, 34 | 7, 35 | 100, 36 | 8, 37 | 120, 38 | 5, 39 | owner 40 | ); 41 | // Keep this log 42 | console.log("Deployed ERC20Mock to:", address(erc20)); 43 | console.log("Deployed ERC721Mock to:", address(erc721)); 44 | console.log("Deployed ERC1155Mock to:", address(erc1155)); 45 | console.log("Deployed ERC3525Mock to:", address(erc3525)); 46 | vm.stopBroadcast(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /nightfall_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [[bin]] 8 | name = "menu" 9 | path = "bin/menu.rs" 10 | 11 | [dependencies] 12 | tracing = "0.1.41" 13 | dotenv = "0.15.0" 14 | nightfall_client = { path = "../nightfall_client" } 15 | nightfall_bindings = {path = "../nightfall_bindings"} 16 | nightfall_proposer = { path = "../nightfall_proposer" } 17 | configuration = { path = "../configuration" } 18 | lib = { path = "../lib" } 19 | serde = { version = "1.0.219", features = ["derive"] } 20 | reqwest = { version = "0.12.15", features = ["json", "multipart"] } 21 | url = "2.5.4" 22 | ark-ff = { version = "0.4.2", features = ["parallel"] } 23 | serde_json = "1.0.140" 24 | k256 = "0.13.4" 25 | alloy-node-bindings = { version = "1.0.23" } 26 | tokio = { version = "1.45.0", features = ["full"] } 27 | ark-serialize = "0.4.2" 28 | ark-std = { version = "0.4.0", default-features = false } 29 | arkworks-utils = "1.0.1" 30 | ark-bn254 = "0.4.0" 31 | alloy = { version = "1.0.23", features = ["full"] } 32 | hex = "0.4.3" 33 | log = "0.4.27" 34 | sha2 = "0.10.9" 35 | ark-ec = { version = "0.4.2", features = ["parallel"] } 36 | nf-curves = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 37 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 38 | "test-srs", 39 | ] } 40 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 41 | jf-relation = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 42 | serial_test = "3.2.0" 43 | num-bigint = "0.4.6" 44 | figment = { version = "0.10.19", features = ["toml", "env"] } 45 | futures = "0.3.31" 46 | ethers-middleware = "2.0.14" 47 | uuid = { version = "1.16.0", features = ["v4"] } 48 | warp = "0.3.7" 49 | console-menu = "0.3.1" 50 | bip32 = { version = "0.4.0", features = ["bip39"] } 51 | inquire = "0.7.5" 52 | toml = "0.8.22" 53 | 54 | 55 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/commitment.rs: -------------------------------------------------------------------------------- 1 | use warp::{hyper::StatusCode, path, reply, Filter, Reply}; 2 | 3 | use crate::driven::db::mongo::CommitmentEntry; 4 | use crate::initialisation::get_db_connection; 5 | use crate::ports::db::CommitmentDB; 6 | use ark_bn254::Fr as Fr254; 7 | use lib::hex_conversion::HexConvertible; 8 | 9 | /// GET request for a specific commitment by key 10 | pub fn get_commitment( 11 | ) -> impl Filter + Clone { 12 | path!("v1" / "commitment" / String) 13 | .and(warp::get()) 14 | .and_then(handle_get_commitment) 15 | } 16 | 17 | pub async fn handle_get_commitment(key: String) -> Result { 18 | let parsed_key = Fr254::from_hex_string(&key).map_err(|_| { 19 | warp::reject::custom(crate::domain::error::ClientRejection::InvalidCommitmentKey) 20 | })?; 21 | let commitment_db = get_db_connection().await; 22 | if let Some(res) = commitment_db.get_commitment(&parsed_key).await { 23 | Ok(reply::with_status(reply::json(&res), StatusCode::OK)) 24 | } else { 25 | Err(warp::reject::custom( 26 | crate::domain::error::ClientRejection::CommitmentNotFound, 27 | )) 28 | } 29 | } 30 | 31 | /// GET request for all commitments 32 | pub fn get_all_commitments( 33 | ) -> impl Filter + Clone { 34 | path!("v1" / "commitments") 35 | .and(warp::get()) 36 | .and_then(handle_get_all_commitments) 37 | } 38 | 39 | pub async fn handle_get_all_commitments() -> Result { 40 | let commitment_db = get_db_connection().await; 41 | let res = commitment_db 42 | .get_all_commitments() 43 | .await 44 | .map_err(|_| warp::reject::custom(crate::domain::error::ClientRejection::DatabaseError))?; 45 | let values: Vec = res.into_iter().map(|c| c.1).collect(); 46 | Ok(reply::with_status(reply::json(&values), StatusCode::OK)) 47 | } 48 | -------------------------------------------------------------------------------- /nightfall_deployer/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1-labs 2 | FROM ghcr.io/foundry-rs/foundry:v1.1.0 AS foundry 3 | FROM rust:1.88.0 AS builder 4 | 5 | # install additional ca-certificates e.g. zscaler certificate (can be removed if not needed) 6 | COPY ./configuration/trust/* /usr/local/share/ca-certificates/ 7 | RUN chmod 644 /usr/local/share/ca-certificates/* && update-ca-certificates 8 | 9 | WORKDIR /app 10 | RUN mkdir -p bin 11 | COPY --from=foundry /usr/local/bin/forge ./forge 12 | COPY --exclude=configuration/bin/* . . 13 | COPY configuration/bin/*_vk configuration/bin/ 14 | ENV PATH=/app:$PATH 15 | RUN --mount=type=cache,id=cargo-registry-v4,target=/usr/local/cargo/registry,sharing=locked \ 16 | --mount=type=cache,id=cargo-git-v4,target=/usr/local/cargo/git,sharing=locked \ 17 | --mount=type=cache,id=cargo-target-client-v4,target=/app/target,sharing=private \ 18 | cargo build --package nightfall_deployer --release && \ 19 | mv /app/target/release/nightfall_deployer /app/bin/deployer 20 | 21 | FROM debian:bookworm-slim 22 | WORKDIR /app 23 | COPY --from=builder /app/bin/deployer bin/ 24 | COPY configuration/bin/*_vk configuration/bin/ 25 | COPY --from=builder /app/blockchain_assets blockchain_assets/ 26 | COPY .env *.env nightfall.toml foundry.toml ./ 27 | 28 | 29 | # RUN apt-get update && apt-get install -y --no-install-recommends \ 30 | # libssl3 31 | 32 | # Install libssl3 + Node.js (includes npm/npx via NodeSource) 33 | RUN apt-get update && apt-get install -y --no-install-recommends \ 34 | curl ca-certificates gnupg libssl3 \ 35 | && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ 36 | && apt-get install -y --no-install-recommends nodejs \ 37 | && npm i -g @openzeppelin/upgrades-core@^1.37.0 \ 38 | && npm cache clean --force && rm -rf /var/lib/apt/lists/* 39 | 40 | 41 | COPY --from=foundry /usr/local/bin/forge ./forge 42 | ENV PATH=/app:$PATH 43 | RUN mkdir -p nightfall_deployer/blockchain_assets 44 | RUN ln -s /app/blockchain_assets /app/nightfall_deployer/blockchain_assets 45 | 46 | CMD ["/app/bin/deployer"] 47 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/request_status.rs: -------------------------------------------------------------------------------- 1 | use crate::{driven::queue::get_queue, initialisation::get_db_connection, ports::db::RequestDB}; 2 | use log::debug; 3 | use uuid::Uuid; 4 | use warp::{http::StatusCode, path, reply::Reply, Filter}; 5 | 6 | /// This module provides an end point for querying the status of a request 7 | pub fn get_request_status( 8 | ) -> impl Filter + Clone { 9 | path!("v1" / "request" / String) 10 | .and(warp::get()) 11 | .and_then(handle_get_request_status) 12 | } 13 | 14 | pub async fn handle_get_request_status(id: String) -> Result { 15 | // check if the id is a valid uuid 16 | match Uuid::parse_str(&id) { 17 | Ok(_) => {} 18 | Err(_) => { 19 | return Err(warp::reject::custom( 20 | crate::domain::error::ClientRejection::InvalidRequestId, 21 | )); 22 | } 23 | }; 24 | let db = get_db_connection().await; 25 | // get the request 26 | debug! {"Getting request status for {id}"}; 27 | let request = db.get_request(&id).await; 28 | debug! {"Request status: {request:?}"}; 29 | if let Some(request) = request { 30 | Ok(warp::reply::with_status( 31 | serde_json::to_string(&request).unwrap(), 32 | StatusCode::OK, 33 | )) 34 | } else { 35 | Err(warp::reject::custom( 36 | crate::domain::error::ClientRejection::RequestNotFound, 37 | )) 38 | } 39 | } 40 | 41 | /// This endpoint is used to get the length of thr request queue 42 | pub fn get_queue_length( 43 | ) -> impl Filter + Clone { 44 | path!("v1" / "queue") 45 | .and(warp::get()) 46 | .and_then(handle_get_queue_length) 47 | } 48 | pub async fn handle_get_queue_length() -> Result { 49 | let length = get_queue().await.read().await.len(); 50 | Ok(warp::reply::with_status( 51 | serde_json::to_string(&length).unwrap(), 52 | StatusCode::OK, 53 | )) 54 | } 55 | -------------------------------------------------------------------------------- /nightfall_proposer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_proposer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tracing = "0.1.41" 8 | bson = "2.15.0" 9 | ark-bn254 = "0.4.0" 10 | ark-poly = { version = "0.4.2", features = ["parallel"] } 11 | ark-ff = { version = "0.4.2", features = ["parallel"] } 12 | ark-ed-on-bn254 = "0.4.0" 13 | ark-ec = { version = "0.4.2", features = ["parallel"] } 14 | nightfall_bindings = {path = "../nightfall_bindings"} 15 | alloy = { version = "1.0.23", features = ["full"] } 16 | ark-serialize = "0.4.2" 17 | serde = { version = "1.0.219", features = ["derive"] } 18 | tokio-stream = "0.1.17" 19 | serde_json = "1.0.140" 20 | serde_repr = "0.1.20" 21 | serde_bytes = "0.11" 22 | serde_with = "2.3.3" 23 | num-bigint = "0.4.6" 24 | sha3 = "0.10.8" 25 | futures = "0.3.31" 26 | configuration = { path = "../configuration" } 27 | lib = { path = "../lib" } 28 | tokio = { version = "1.45.0", features = ["full"] } 29 | log = "0.4.27" 30 | warp = "0.3.7" 31 | async-trait = "0.1.88" 32 | mongodb = "3.2.3" 33 | rayon = "1.10" 34 | ark-std = { version = "0.4.0", default-features = false, features = [ 35 | "parallel", 36 | ] } 37 | hex = "0.4.3" 38 | either = "1.15.0" 39 | lazy_static = "1.5.0" 40 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 41 | testcontainers = { version = "0.24.0", features = ["blocking"] } 42 | url = "2.5.4" 43 | jf-relation = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 44 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 45 | "test-srs", 46 | ] } 47 | jf-utils = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 48 | "parallel", 49 | ] } 50 | nf-curves = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 51 | itertools = { version = "0.10.5", default-features = false } 52 | sha2 = { version = "0.10.9", default-features = false } 53 | 54 | [dev-dependencies] 55 | criterion = { version = "0.4", features = ["html_reports"] } 56 | 57 | [[bench]] 58 | name = "bench_deposit" 59 | harness = false 60 | 61 | [features] 62 | parallel = ["ark-std/parallel", "ark-ff/parallel", "ark-poly/parallel", "ark-ec/parallel"] 63 | 64 | 65 | -------------------------------------------------------------------------------- /nightfall_proposer/src/main.rs: -------------------------------------------------------------------------------- 1 | use configuration::{logging::init_logging, settings::get_settings}; 2 | use lib::plonk_prover::plonk_proof::{PlonkProof, PlonkProvingEngine}; 3 | use log::{error, info}; 4 | use nightfall_bindings::artifacts::Nightfall; 5 | use nightfall_proposer::drivers::blockchain::event_listener_manager::ensure_running; 6 | use nightfall_proposer::{ 7 | driven::{db::mongo_db::DB, mock_prover::MockProver, rollup_prover::RollupProver}, 8 | drivers::{blockchain::block_assembly::start_block_assembly, rest::routes}, 9 | }; 10 | use std::error::Error; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<(), Box> { 14 | // at some point we have to be specific about the proof we're using 15 | let settings = get_settings(); 16 | type P = PlonkProof; 17 | type E = PlonkProvingEngine; 18 | type N = Nightfall::NightfallCalls; 19 | 20 | init_logging( 21 | settings.nightfall_proposer.log_level.as_str(), 22 | settings.log_app_only, 23 | ); 24 | 25 | // drop any existing database 26 | let db_url = &settings.nightfall_proposer.db_url; 27 | info!("Dropping database: {DB}"); 28 | let _ = lib::utils::drop_database(db_url, DB).await; 29 | 30 | let task_0 = if settings.mock_prover { 31 | info!("Using MockProver"); 32 | tokio::spawn(start_block_assembly::()) 33 | } else { 34 | info!("Using RollupProver"); 35 | tokio::spawn(start_block_assembly::()) 36 | }; 37 | 38 | // start the event listener 39 | // ── start the (owned) event listener once ───────────────────────────────── 40 | ensure_running::().await; 41 | 42 | let routes = routes::(); 43 | let task_2 = tokio::spawn(warp::serve(routes).run(([0, 0, 0, 0], 3000))); 44 | info!("Starting warp server, block assembler and event_handler threads"); 45 | // we'll run the warp server and blockchain listener in parallel in separate threads 46 | // this maybe overkill so look at combining them into a single thread - depending on speed. 47 | let (_r0, _r2) = (task_0.await??, task_2.await?); 48 | error!("Proposer exited unexpectedly. See information above."); 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/db/commitment_tree.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use jf_primitives::{poseidon::PoseidonParams, trees::MembershipProof}; 3 | use lib::merkle_trees::trees::{MerkleTreeError, MutableTree}; 4 | 5 | use crate::ports::trees::CommitmentTree; 6 | 7 | /// implement a commitment tree for storing commitments. Note that this is really just a 8 | /// special case of a MutableTree 9 | #[async_trait::async_trait] 10 | impl CommitmentTree for mongodb::Client 11 | where 12 | F: PrimeField + std::marker::Unpin + PoseidonParams, 13 | ::Err: std::fmt::Debug, 14 | { 15 | const TREE_NAME: &'static str = "commitment_tree"; 16 | type Error = MerkleTreeError; 17 | 18 | async fn new_commitment_tree( 19 | &self, 20 | tree_height: u32, 21 | sub_tree_height: u32, 22 | ) -> Result<(), >::Error> { 23 | >::new_mutable_tree( 24 | self, 25 | tree_height, 26 | sub_tree_height, 27 | >::TREE_NAME, 28 | ) 29 | .await 30 | } 31 | 32 | async fn append_sub_trees( 33 | &self, 34 | sub_tree_roots: &[F], 35 | update_tree: bool, 36 | ) -> Result<(F, u64), >::Error> { 37 | >::append_sub_trees( 38 | self, 39 | sub_tree_roots, 40 | update_tree, 41 | >::TREE_NAME, 42 | ) 43 | .await 44 | } 45 | 46 | async fn get_membership_proof( 47 | &self, 48 | leaf: Option<&F>, 49 | leaf_index: Option, 50 | ) -> Result, >::Error> { 51 | >::get_membership_proof( 52 | self, 53 | leaf, 54 | leaf_index, 55 | >::TREE_NAME, 56 | ) 57 | .await 58 | } 59 | 60 | async fn get_root(&self) -> Result>::Error> { 61 | >::get_root(self, >::TREE_NAME).await 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/root_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 34 | IQ== 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /nightfall_deployer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_deployer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [[bin]] 8 | name = "key_generation" 9 | path = "bin/key_generation.rs" 10 | 11 | 12 | [dependencies] 13 | serde_json = "1.0.143" 14 | jf-utils = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 15 | "parallel", 16 | ] } 17 | anyhow = "1.0.99" 18 | regex = "1.11.2" 19 | itertools = "0.10.5" 20 | dotenv = "0.15.0" 21 | alloy = { version = "1.0.23", features = ["full"] } 22 | alloy-node-bindings = "1.0.23" 23 | tokio = { version = "1.45.0", features = ["full"] } 24 | ethers-solc = "2.0.14" 25 | configuration = { path = "../configuration" } 26 | nightfall_bindings = { path = "../nightfall_bindings" } 27 | lib = { path = "../lib" } 28 | nightfall_test = { path = "../nightfall_test" } 29 | toml = "0.8.22" 30 | serde = "1.0.219" 31 | log = "0.4.27" 32 | warp = "0.3.7" 33 | sha2 = "0.10" 34 | nightfall_client = { path = "../nightfall_client" } 35 | nightfall_proposer = { path = "../nightfall_proposer" } 36 | ark-serialize = "0.4.2" 37 | ark-std = { version = "0.4.0", default-features = false } 38 | ark-bn254 = "0.4.0" 39 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 40 | "test-srs", 41 | ] } 42 | jf-relation = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 43 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 44 | nf-curves = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 45 | ark-ec = { version = "0.4.2", features = ["parallel"] } 46 | ark-ff = { version = "0.4.2", features = ["parallel"] } 47 | ark-poly = { version = "0.4.2", features = ["parallel"] } 48 | num-bigint = "0.4.6" 49 | reqwest = { version = "0.11.27", features = ["json", "blocking"] } 50 | url = "2.5.4" 51 | 52 | [build-dependencies] 53 | configuration = { path = "../configuration" } 54 | log = "0.4.27" 55 | 56 | [dev-dependencies] 57 | url = "2.5.4" 58 | sha3 = "^0.10" 59 | jf-utils = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 60 | "parallel", 61 | ] } 62 | ark-ed-on-bn254 = "0.4.0" 63 | hex = "0.4.3" 64 | [features] 65 | test-srs = ["jf-plonk/test-srs"] 66 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/blockchain/event_listener_manager.rs: -------------------------------------------------------------------------------- 1 | use crate::drivers::blockchain::nightfall_event_listener::start_event_listener; 2 | use crate::ports::contracts::NightfallContract; 3 | use configuration::settings::get_settings; 4 | use lib::nf_client_proof::{Proof, ProvingEngine}; 5 | use log::{info, warn}; 6 | use tokio::{ 7 | sync::{OnceCell, RwLock}, 8 | task::JoinHandle, 9 | time::{sleep, Duration}, 10 | }; 11 | 12 | // The sole place that holds the listener handle. 13 | static LISTENER: OnceCell>>> = OnceCell::const_new(); 14 | async fn listener_lock() -> &'static RwLock>> { 15 | // Tokio's OnceCell requires an async initializer. 16 | LISTENER.get_or_init(|| async { RwLock::new(None) }).await 17 | } 18 | 19 | // Spawns the actual listener; logs errors; returns JoinHandle<()>. 20 | async fn spawn_listener() -> JoinHandle<()> 21 | where 22 | P: Proof, 23 | E: ProvingEngine

, 24 | N: NightfallContract, 25 | { 26 | let s = get_settings(); 27 | let genesis = s.genesis_block; 28 | let max_attempts = s.nightfall_client.max_event_listener_attempts.unwrap_or(10); 29 | 30 | tokio::spawn(async move { 31 | let _ = start_event_listener::(genesis, max_attempts).await; // discard Result 32 | }) 33 | } 34 | 35 | /// Start once if not already running. 36 | pub async fn ensure_running, N: NightfallContract>() { 37 | let lock = listener_lock().await; 38 | let mut guard = lock.write().await; 39 | if guard.is_none() { 40 | *guard = Some(spawn_listener::().await); 41 | info!("Event listener started."); 42 | } 43 | } 44 | 45 | /// Abort current (if any) and respawn. 46 | pub async fn restart, N: NightfallContract>() { 47 | let lock = listener_lock().await; 48 | let mut guard = lock.write().await; 49 | 50 | if let Some(handle) = guard.take() { 51 | warn!("Restarting event listener: aborting current task…"); 52 | handle.abort(); 53 | // small grace to allow sockets/cursors to unwind 54 | sleep(Duration::from_millis(50)).await; 55 | } 56 | 57 | *guard = Some(spawn_listener::().await); 58 | info!("Event listener restarted."); 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod blockchain_client; 2 | pub mod circuit_key_generation; 3 | pub mod constants; 4 | pub mod contract_conversions; 5 | pub mod deposit_circuit; 6 | pub mod entities; 7 | pub mod error; 8 | pub mod health_check; 9 | pub mod hex_conversion; 10 | pub mod merkle_trees; 11 | pub mod models; 12 | pub mod nf_client_proof; 13 | pub mod nf_token_id; 14 | pub mod plonk_prover; 15 | pub mod rollup_circuit_checks; 16 | pub mod serialization; 17 | pub mod shared_entities; 18 | pub mod tests_utils; 19 | pub mod utils; 20 | pub mod validate_certificate; 21 | pub mod validate_keys; 22 | pub mod verify_contract; 23 | pub mod wallets; 24 | 25 | use alloy::dyn_abi::abi::encode; 26 | use alloy::primitives::{keccak256, U256}; 27 | use alloy::sol_types::SolValue; 28 | use ark_bn254::Fr as Fr254; 29 | use configuration::addresses::get_addresses; 30 | use num::BigUint; 31 | 32 | /// This function gets the fee token ID based on the current deployment. 33 | /// Fee token ID is the keccak256 hash of the zero address and zero, right shifted by 4 bits. 34 | pub fn get_fee_token_id() -> Fr254 { 35 | let nf_address = get_addresses().nightfall(); 36 | 37 | let nf_address_token = nf_address.tokenize(); 38 | let u256_zero = U256::ZERO.tokenize(); 39 | let fee_token_id_biguint = 40 | BigUint::from_bytes_be(keccak256(encode(&(nf_address_token, u256_zero))).as_slice()) >> 4; 41 | Fr254::from(fee_token_id_biguint) 42 | } 43 | 44 | pub mod initialisation { 45 | use crate::{blockchain_client::BlockchainClientConnection, wallets::LocalWsClient}; 46 | use configuration::settings::get_settings; 47 | use tokio::sync::{OnceCell, RwLock}; 48 | /// This function is used to provide a singleton blockchain client connection across the entire application. 49 | pub async fn get_blockchain_client_connection() -> &'static RwLock { 50 | static BLOCKCHAIN_CLIENT_CONNECTION: OnceCell> = 51 | OnceCell::const_new(); 52 | BLOCKCHAIN_CLIENT_CONNECTION 53 | .get_or_init(|| async { 54 | RwLock::new( 55 | LocalWsClient::try_from_settings(get_settings()) 56 | .await 57 | .expect("Could not create blockchain client connection"), 58 | ) 59 | }) 60 | .await 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/intermediate_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/trees.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the interfaces for the three Merkle Trees a Proposer works with. 2 | 3 | use ark_bn254::Fr as Fr254; 4 | use ark_ff::PrimeField; 5 | use configuration::settings::get_settings; 6 | use jf_primitives::{poseidon::PoseidonParams, trees::MembershipProof}; 7 | use lib::merkle_trees::trees::{MerkleTreeError, MutableTree}; 8 | use mongodb::Client; 9 | 10 | /// Trait defining the functionality of a commitment tree. 11 | #[async_trait::async_trait] 12 | pub trait CommitmentTree: MutableTree 13 | where 14 | F: PrimeField + PoseidonParams + Unpin, 15 | ::Err: std::fmt::Debug, 16 | { 17 | /// The name of the commitment tree (Nightfall only has one so it can be a constant) 18 | const TREE_NAME: &'static str; 19 | type Error; 20 | /// get a new commitment tree 21 | async fn new_commitment_tree( 22 | &self, 23 | tree_height: u32, 24 | sub_tree_height: u32, 25 | ) -> Result<(), >::Error>; 26 | 27 | async fn append_sub_trees( 28 | &self, 29 | sub_tree_roots: &[F], 30 | update_tree: bool, 31 | ) -> Result<(F, u64), >::Error>; 32 | 33 | async fn get_membership_proof( 34 | &self, 35 | leaf: Option<&F>, 36 | leaf_index: Option, 37 | ) -> Result, >::Error>; 38 | 39 | async fn get_root(&self) -> Result>::Error>; 40 | 41 | /// reset the tree 42 | async fn reset_tree(&self) -> Result<(), >::Error> 43 | where 44 | Self: MutableTree>, 45 | { 46 | let _ = >::reset_mutable_tree(self, Self::TREE_NAME).await; 47 | // select the client to use 48 | let uri = &get_settings().nightfall_client.db_url; 49 | let client = Client::with_uri_str(uri) 50 | .await 51 | .expect("Could not create database connection"); 52 | // it's not enough just to connect to a database, we need to initialise some trees in it 53 | >::new_commitment_tree(&client, 29, 3) 54 | .await 55 | .expect("Could not create commitment tree"); 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /nightfall_deployer/build.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | use std::{os::unix::process::ExitStatusExt, process::Command}; 3 | 4 | fn main() { 5 | info!("Started running nightfall_deployer/build.rs"); 6 | // Check and forge is installed 7 | if !is_foundry_installed() { 8 | panic!("Foundry not installed, needed to continue please install via the guide found at https://book.getfoundry.sh/getting-started/installation"); 9 | } 10 | // Run forge install 11 | forge_command(&["install"]); 12 | forge_command(&["build"]); 13 | } 14 | 15 | fn is_foundry_installed() -> bool { 16 | // Check if foundry is installed 17 | let cmd_output = Command::new("which").arg("forge").output(); 18 | 19 | match cmd_output { 20 | Ok(output) => { 21 | if output.status.success() { 22 | info!( 23 | "'which forge' successful got: {:?}", 24 | std::str::from_utf8(&output.stdout) 25 | ); 26 | true 27 | } else { 28 | info!("'which forge' not found, got: {:?}", output.stdout); 29 | false 30 | } 31 | } 32 | Err(e) => { 33 | info!("Got an error from running 'which forge': {e}"); 34 | false 35 | } 36 | } 37 | } 38 | 39 | /// Function should only be called after we have checked forge is installed by running 'which forge' 40 | fn forge_command(command: &[&str]) { 41 | let output = Command::new("forge").args(command).output(); 42 | 43 | match output { 44 | Ok(o) => { 45 | if o.status.success() { 46 | info!( 47 | "Command 'forge {:?}' executed successfully: {}", 48 | command, 49 | String::from_utf8_lossy(&o.stdout) 50 | ); 51 | } else { 52 | panic!( 53 | "Command 'forge {:?}' executed with failing error code: {:?}\nStandard Output: {}\nStandard Error: {}", 54 | command, 55 | o.status.signal(), 56 | String::from_utf8_lossy(&o.stdout), 57 | String::from_utf8_lossy(&o.stderr) 58 | ); 59 | } 60 | } 61 | Err(e) => { 62 | panic!("Command 'forge {command:?}' ran into an error without executing: {e}"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /nightfall_test/src/main.rs: -------------------------------------------------------------------------------- 1 | use configuration::{logging::init_logging, settings::Settings}; 2 | use log::{error, info}; 3 | use nightfall_test::{ 4 | run_tests::run_tests, 5 | webhook::{poll_queue, run_webhook_server}, 6 | }; 7 | use std::sync::Arc; 8 | use tokio::task::{JoinError, JoinSet}; 9 | use tokio::time::{sleep, Duration}; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<(), JoinError> { 13 | const MINING_INTERVAL: u32 = 5; // seconds 14 | let settings: Settings = Settings::new().unwrap(); 15 | init_logging( 16 | settings.nightfall_test.log_level.as_str(), 17 | settings.log_app_only, 18 | ); 19 | // set up a vec to hold responses from the webhook server 20 | let responses = Arc::new(tokio::sync::Mutex::new(Vec::new())); 21 | let mut tasks = JoinSet::new(); 22 | //start all the tasks 23 | info!("Starting webhook server..."); 24 | tasks.spawn(run_webhook_server(responses.clone())); 25 | info!("Running tests..."); 26 | tasks.spawn(run_tests(responses.clone(), MINING_INTERVAL)); 27 | info!("Waiting for client services to be ready..."); 28 | let client_urls = [ 29 | "http://client:3000/v1/health", 30 | "http://client2:3000/v1/health", 31 | ]; 32 | for url in &client_urls { 33 | let mut ready = false; 34 | for _ in 1..=30 { 35 | if reqwest::get(*url).await.is_ok() { 36 | info!("Service {url} is ready!"); 37 | ready = true; 38 | break; 39 | } 40 | sleep(Duration::from_secs(2)).await; 41 | } 42 | if !ready { 43 | error!("Service {url} is not reachable after 60s"); 44 | panic!("Service not ready: {url}"); 45 | } 46 | } 47 | info!("Starting queue polling..."); 48 | tasks.spawn(poll_queue()); 49 | 50 | let result = tasks.join_next().await; // wait for any task to finish 51 | match result { 52 | Some(Ok(_)) => { 53 | info!("Nightfall tests completed successfully."); 54 | return Ok(()); 55 | }, 56 | Some(Err(e)) => { 57 | error!("Nightfall tests failed with error: {e:?}"); 58 | return Err(e); 59 | } 60 | None => { 61 | error!("No tasks were completed."); 62 | panic!("No tasks were completed, this is unexpected."); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/withdraw.rs: -------------------------------------------------------------------------------- 1 | use super::models::DeEscrowDataReq; 2 | use crate::{ 3 | domain::entities::WithdrawData as NFWithdrawData, ports::contracts::NightfallContract, 4 | }; 5 | use ::nightfall_bindings::artifacts::Nightfall; 6 | use log::{debug, error}; 7 | use reqwest::StatusCode; 8 | use warp::{path, reject, Filter, Reply}; 9 | 10 | /// GET request for a specific commitment by key 11 | pub fn de_escrow() -> impl Filter + Clone { 12 | path!("v1" / "de-escrow") 13 | .and(warp::body::json()) 14 | .and_then(handle_de_escrow) 15 | } 16 | 17 | pub async fn handle_de_escrow(data: DeEscrowDataReq) -> Result { 18 | let token_type = u8::from_str_radix(&data.token_type, 16) 19 | .map_err(|_| { 20 | error!("Could not convert token type"); 21 | reject::custom(crate::domain::error::ClientRejection::FailedDeEscrow) 22 | })? 23 | .into(); 24 | let withdraw_data: NFWithdrawData = NFWithdrawData::try_from(data.clone()).map_err(|e| { 25 | error!("Could not convert Withdraw data request to WithdrawData: {e}"); 26 | reject::custom(crate::domain::error::ClientRejection::FailedDeEscrow) 27 | })?; 28 | let available = Nightfall::NightfallCalls::withdraw_available(withdraw_data).await; 29 | match available { 30 | Ok(b) => { 31 | if b { 32 | debug!("Withdraw is on chain, attempting to de-escrow funds"); 33 | Nightfall::NightfallCalls::de_escrow_funds(withdraw_data, token_type) 34 | .await 35 | .map_err(|e| { 36 | error!("Could not de-escrow funds: {e}"); 37 | reject::custom(crate::domain::error::ClientRejection::FailedDeEscrow) 38 | })?; 39 | 40 | Ok(warp::reply::with_status("OK", StatusCode::OK)) 41 | } else { 42 | debug!("Not yet able to de-escrow funds"); 43 | Err(reject::custom( 44 | crate::domain::error::ClientRejection::FailedDeEscrow, 45 | )) 46 | } 47 | } 48 | Err(e) => { 49 | debug!("Nightfall contract error: {e}"); 50 | Err(reject::custom( 51 | crate::domain::error::ClientRejection::FailedDeEscrow, 52 | )) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuits/verify/verify_encryption_gadgets.rs: -------------------------------------------------------------------------------- 1 | use crate::plonk_prover::circuits::kemdem_gadgets::KEMDEMCircuit; 2 | use jf_relation::{ 3 | errors::CircuitError, gadgets::ecc::PointVariable, BoolVar, Circuit, PlonkCircuit, Variable, 4 | }; 5 | use nf_curves::ed_on_bn254::Fq as Fr254; 6 | 7 | pub trait VerifyEncryptionCircuit { 8 | #[allow(clippy::too_many_arguments)] 9 | fn verify_encryption( 10 | &mut self, 11 | nf_token_id: Variable, 12 | nf_slot_id: Variable, 13 | new_commitments_value: Variable, 14 | shared_secret: &PointVariable, 15 | ephemeral_key: Variable, 16 | withdraw_address: Variable, 17 | withdraw_flag: BoolVar, 18 | ) -> Result, CircuitError>; 19 | } 20 | impl VerifyEncryptionCircuit for PlonkCircuit { 21 | fn verify_encryption( 22 | &mut self, 23 | nf_token_id: Variable, 24 | nf_slot_id: Variable, 25 | new_commitments_value: Variable, 26 | shared_secret: &PointVariable, 27 | ephemeral_key: Variable, 28 | withdraw_address: Variable, 29 | withdraw_flag: BoolVar, 30 | ) -> Result, CircuitError> { 31 | let (epk, mut cipher_text_kem_dem) = self.kemdem( 32 | ephemeral_key, 33 | shared_secret, 34 | &[nf_token_id, nf_slot_id, new_commitments_value], 35 | )?; 36 | 37 | let x = epk.get_x(); 38 | 39 | let neg_x = self.sub(self.zero(), x)?; 40 | 41 | let flag = self.is_lt(neg_x, x)?; 42 | 43 | cipher_text_kem_dem.push(epk.get_y()); 44 | cipher_text_kem_dem.push(flag.0); 45 | 46 | if cipher_text_kem_dem.len() != 5 { 47 | return Err(CircuitError::ParameterError( 48 | "Calculated cipher text has length not equal to 5".to_string(), 49 | )); 50 | } 51 | 52 | let withdraw_cipher_text = [ 53 | nf_token_id, 54 | withdraw_address, 55 | new_commitments_value, 56 | self.zero(), 57 | self.zero(), 58 | ]; 59 | // Output the one of the two calculated cipher texts based on the withdraw flag 60 | cipher_text_kem_dem 61 | .into_iter() 62 | .zip(withdraw_cipher_text) 63 | .map(|(cipher, withdraw)| self.conditional_select(withdraw_flag, cipher, withdraw)) 64 | .collect::, CircuitError>>() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nightfall_proposer/src/domain/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::{Debug, Display, Formatter}, 4 | }; 5 | 6 | /// errors for a merkle tree 7 | #[derive(Debug)] 8 | pub enum MerkleTreeError { 9 | /// The tree is full 10 | TreeIsFull, 11 | IncorrectBatchSize, 12 | NoLeaves, 13 | DatabaseError(E), 14 | TreeNotFound, 15 | TreeAlreadyExists, 16 | SerializationError, 17 | InvalidProof, 18 | } 19 | 20 | impl Error for MerkleTreeError {} 21 | 22 | impl Display for MerkleTreeError { 23 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 24 | match self { 25 | Self::TreeIsFull => write!(f, "The tree is full"), 26 | Self::IncorrectBatchSize => write!(f, "Incorrect batch size"), 27 | Self::NoLeaves => write!(f, "No leaves"), 28 | Self::DatabaseError(e) => write!(f, "Database error {e}"), 29 | Self::TreeNotFound => write!(f, "Tree not found"), 30 | Self::TreeAlreadyExists => write!(f, "Tree already exists"), 31 | Self::SerializationError => write!(f, "Serialization error "), 32 | Self::InvalidProof => write!(f, "Invalid proof"), 33 | } 34 | } 35 | } 36 | 37 | #[derive(Debug)] 38 | pub enum ProposerRejection { 39 | BlockDataUnavailable, 40 | ClientTransactionFailed, 41 | FailedToRotateProposer, 42 | FailedToAddProposer, 43 | FailedToRemoveProposer, 44 | FailedToWithdrawStake, 45 | ProviderError, 46 | } 47 | 48 | impl std::fmt::Display for ProposerRejection { 49 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 50 | match self { 51 | ProposerRejection::BlockDataUnavailable => write!(f, "Block data unavailable"), 52 | ProposerRejection::ClientTransactionFailed => write!(f, "Client transaction failed"), 53 | ProposerRejection::FailedToRotateProposer => write!(f, "Failed to rotate proposer"), 54 | ProposerRejection::FailedToAddProposer => write!(f, "Failed to add proposer"), 55 | ProposerRejection::FailedToRemoveProposer => write!(f, "Failed to remove proposer"), 56 | ProposerRejection::FailedToWithdrawStake => write!(f, "Failed to withdraw stake"), 57 | ProposerRejection::ProviderError => write!(f, "Provider error"), 58 | } 59 | } 60 | } 61 | 62 | impl std::error::Error for ProposerRejection {} 63 | 64 | impl warp::reject::Reject for ProposerRejection {} 65 | -------------------------------------------------------------------------------- /lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio-util = "0.7.16" 10 | anyhow = "1.0.100" 11 | itertools = "0.14.0" 12 | hex-literal = "1.1.0" 13 | eyre = "0.6.12" 14 | serde_json = "1.0.143" 15 | reqwest = { version = "0.11.27", features = ["json", "blocking", "stream"] } 16 | warp = "0.3.7" 17 | async-trait = "0.1.88" 18 | hex = "0.4.3" 19 | dotenv = "0.15.0" 20 | x509-parser = "0.16.0" 21 | openssl = "0.10.72" 22 | k256 = { version = "0.13", features = ["ecdsa", "sha256"] } 23 | alloy = { version = "1.0.23", features = ["full", "json-rpc"] } 24 | tokio = { version = "1.45.0", features = ["full"] } 25 | configuration = { path = "../configuration" } 26 | nightfall_bindings = {path = "../nightfall_bindings"} 27 | toml = "0.8.22" 28 | ark-poly = { version = "0.4.2", features = ["parallel"] } 29 | ark-serialize = "0.4.2" 30 | ark-std = { version = "0.4.0", default-features = false } 31 | ark-bn254 = "0.4.0" 32 | alloy-rlp = "0.3.12" 33 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 34 | "test-srs", 35 | ] } 36 | jf-relation = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 37 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 38 | jf-utils = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 39 | "parallel", 40 | ] } 41 | nf-curves = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 42 | ark-ec = { version = "0.4.2", features = ["parallel"] } 43 | num-bigint = "0.4.6" 44 | num = "0.4.3" 45 | ark-ff = { version = "0.4.2", features = ["parallel"] } 46 | mongodb = "3.2.3" 47 | rand = "0.8" 48 | serde = "1.0.219" 49 | sha2 = "0.10" 50 | sha3 = "0.10.8" 51 | log = "0.4.27" 52 | url = "2.5.4" 53 | testcontainers = { version = "0.24.0", features = ["blocking"] } 54 | futures = "0.3.31" 55 | azure_identity = "0.21.0" 56 | azure_security_keyvault = "0.21.0" 57 | bincode = "1.3.3" 58 | base64 = "0.22.1" 59 | [build-dependencies] 60 | configuration = { path = "../configuration" } 61 | tokio = { version = "1.45.0", features = ["full"] } 62 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 63 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 64 | "test-srs", 65 | ] } 66 | 67 | 68 | [dev-dependencies] 69 | url = "2.5.4" 70 | serde_json = "1.0.140" 71 | -------------------------------------------------------------------------------- /nightfall_bindings/src/artifacts.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol!( 4 | #[sol(rpc)] 5 | #[derive(Debug)] 6 | #[allow(clippy::too_many_arguments)] 7 | Nightfall, 8 | "../blockchain_assets/artifacts/Nightfall.sol/Nightfall.json" 9 | ); 10 | sol!( 11 | #[sol(rpc)] 12 | #[derive(Debug)] 13 | X509, 14 | "../blockchain_assets/artifacts/X509.sol/X509.json" 15 | ); 16 | sol!( 17 | #[sol(rpc)] 18 | #[derive(Debug)] 19 | #[allow(clippy::too_many_arguments)] 20 | RoundRobin, 21 | "../blockchain_assets/artifacts/RoundRobin.sol/RoundRobin.json" 22 | ); 23 | sol!( 24 | #[sol(rpc)] 25 | #[derive(Debug)] 26 | #[allow(clippy::too_many_arguments)] 27 | RollupProofVerifier, 28 | "../blockchain_assets/artifacts/RollupProofVerifier.sol/RollupProofVerifier.json" 29 | ); 30 | 31 | sol!( 32 | #[sol(rpc)] 33 | #[derive(Debug)] 34 | contract VKHashProvider { 35 | function vkHash() external view returns (bytes32); 36 | } 37 | ); 38 | 39 | sol!( 40 | #[sol(rpc)] 41 | #[derive(Debug)] 42 | IERC1155, 43 | "../blockchain_assets/artifacts/IERC1155.sol/IERC1155.json" 44 | ); 45 | 46 | sol!( 47 | #[sol(rpc)] 48 | #[derive(Debug)] 49 | IERC20, 50 | "../blockchain_assets/artifacts/IERC20.sol/IERC20.json" 51 | ); 52 | 53 | sol!( 54 | #[sol(rpc)] 55 | #[derive(Debug)] 56 | IERC3525, 57 | "../blockchain_assets/artifacts/IERC3525.sol/IERC3525.json" 58 | ); 59 | 60 | sol!( 61 | #[sol(rpc)] 62 | #[derive(Debug)] 63 | IERC721, 64 | "../blockchain_assets/artifacts/IERC721.sol/IERC721.json" 65 | ); 66 | 67 | sol!( 68 | #[sol(rpc)] 69 | #[derive(Debug)] 70 | ERC1155Mock, 71 | "../blockchain_assets/artifacts/ERC1155Mock.sol/ERC1155Mock.json" 72 | ); 73 | 74 | sol!( 75 | #[sol(rpc)] 76 | #[derive(Debug)] 77 | ERC20Mock, 78 | "../blockchain_assets/artifacts/ERC20Mock.sol/ERC20Mock.json" 79 | ); 80 | 81 | sol!( 82 | #[sol(rpc)] 83 | #[derive(Debug)] 84 | #[allow(clippy::too_many_arguments)] 85 | ERC3525Mock, 86 | "../blockchain_assets/artifacts/ERC3525Mock.sol/ERC3525Mock.json" 87 | ); 88 | 89 | sol!( 90 | #[sol(rpc)] 91 | #[derive(Debug)] 92 | ERC721Mock, 93 | "../blockchain_assets/artifacts/ERC721Mock.sol/ERC721Mock.json" 94 | ); 95 | 96 | sol!( 97 | #[sol(rpc)] 98 | #[derive(Debug)] 99 | ProposerManager, 100 | "../blockchain_assets/artifacts/ProposerManager.sol/ProposerManager.json" 101 | ); 102 | -------------------------------------------------------------------------------- /nightfall_client/src/ports/contracts.rs: -------------------------------------------------------------------------------- 1 | //! This module defines interfaces for different types of smart contract that NIghtfall 4 deals with. 2 | //! These mainly include token contracts and the "Nightfall" contract. 3 | 4 | use crate::domain::{ 5 | entities::{DepositSecret, TokenData, WithdrawData}, 6 | error::TokenContractError, 7 | }; 8 | use alloy::primitives::{Address, I256}; 9 | use ark_bn254::Fr as Fr254; 10 | use ark_ff::BigInteger256; 11 | use futures::Future; 12 | use lib::{error::NightfallContractError, shared_entities::TokenType}; 13 | use nightfall_bindings::artifacts::Nightfall; 14 | 15 | /// Interface trait for a token contract. 16 | pub trait TokenContract { 17 | /// We need to be able to set approval for transferring of funds 18 | fn set_approval( 19 | erc_address: Fr254, 20 | value: Fr254, 21 | token_id: BigInteger256, 22 | ) -> impl Future> + Send; 23 | } 24 | 25 | /// Interface trait for the Nightfall contract we are using. 26 | pub trait NightfallContract { 27 | /// Function we call when we wish to escrow funds for a deposit. 28 | /// The values returned will be the Nightfall Token Id and the Nightfall Slot Id. 29 | fn escrow_funds( 30 | token_erc_address: Fr254, 31 | value: Fr254, 32 | token_id: BigInteger256, 33 | fee: Fr254, 34 | deposit_fee: Fr254, 35 | secret_preimage: DepositSecret, 36 | token_type: TokenType, 37 | ) -> impl Future> + Send; 38 | 39 | /// Function to retrieve the address of the contract 40 | fn get_address() -> Fr254; 41 | 42 | /// Function to de-escrow funds 43 | fn de_escrow_funds( 44 | withdraw_data: WithdrawData, 45 | token_type: TokenType, 46 | ) -> impl Future> + Send; 47 | 48 | /// Function to see if funds are available to withdraw 49 | fn withdraw_available( 50 | withdraw_data: WithdrawData, 51 | ) -> impl Future> + Send; 52 | 53 | fn get_current_layer2_blocknumber( 54 | ) -> impl Future> + Send; 55 | 56 | /// Function to retrieve the ERC address and token_id given a Nightfall token id. 57 | fn get_token_info( 58 | nf_token_id: Fr254, 59 | ) -> impl Future> + Send; 60 | 61 | fn get_layer2_block_by_number( 62 | block_number: I256, 63 | ) -> impl Future> + Send; 64 | } 65 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/gen-end_user-certificates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | script_dir=$(dirname "$(realpath "$0")") 3 | set -e 4 | 5 | usage() 6 | { 7 | echo "Usage: ./gen-end_user-certificates.sh [user_suffix]" 8 | } 9 | 10 | if [ -z "$1" ]; then 11 | usage 12 | exit 1 13 | fi 14 | 15 | mkdir -p user 16 | 17 | user_name=user-$1 18 | 19 | echo "Generating self-signed certificate for user '$user_name'" 20 | 21 | # Generate the user's private key in DER format 22 | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out user/$user_name.priv_key -outform DER 23 | echo "We have successfully generated the private key for user '$user_name'" 24 | 25 | # Convert the private key to PEM format for use in CSR generation 26 | openssl rsa -inform DER -in user/$user_name.priv_key -out user/$user_name.pem 27 | 28 | # Generate the certification request 29 | openssl req -new -subj "/C=IN/ST=Mumbai/O=User-$1/OU=Nightfall Team/CN=$user_name/emailAddress=$user_name@user.com" -key user/$user_name.pem -out user/$user_name.csr 30 | echo "We have successfully generated the certification request for user '$user_name'" 31 | 32 | # Convert the intermediate CA private key to PEM format if it is in DER format 33 | if [ ! -f intermediate_ca_private_key.pem ]; then 34 | openssl rsa -inform DER -in intermediate_ca.priv_key -out intermediate_ca_private_key.pem 35 | fi 36 | 37 | # Create the serial number file if it does not exist 38 | if [ ! -f intermediate_ca.srl ]; then 39 | echo 01 > intermediate_ca.srl 40 | fi 41 | 42 | # Convert intermediate_ca.der to PEM Format 43 | openssl x509 -inform DER -in intermediate_ca.der -out intermediate_ca_priv_key.pem 44 | 45 | # Dynamically create the extension configuration file 46 | cat > "$script_dir/conf/ca_extfile.conf" < String { 13 | format!("mongodb://{host}:{port}") 14 | } 15 | 16 | pub async fn get_mongo() -> ContainerAsync { 17 | let mongo_image = GenericImage::new("mongo", "8.0") 18 | .with_exposed_port(27017.tcp()) 19 | .with_wait_for(WaitFor::message_on_stdout("Waiting for connections")) 20 | .with_startup_timeout(Duration::from_secs(120)); 21 | 22 | mongo_image.start().await.unwrap() 23 | } 24 | 25 | pub async fn get_db_connection(container: &ContainerAsync) -> mongodb::Client { 26 | use tokio::time::{sleep, Duration}; 27 | 28 | let host = container.get_host().await.unwrap(); 29 | let port = container.get_host_port_ipv4(27017).await.unwrap(); 30 | let uri = get_db_connection_uri(host, port); 31 | 32 | let mut attempts = 0; 33 | let client; 34 | loop { 35 | match mongodb::Client::with_uri_str(&uri).await { 36 | Ok(c) => match c.database("admin").run_command(doc! {"ping": 1}).await { 37 | Ok(_) => { 38 | info!(" Mongo is ready!"); 39 | client = c; 40 | break; 41 | } 42 | Err(e) => { 43 | warn!("Ping failed: {e}, retrying..."); 44 | } 45 | }, 46 | Err(e) => { 47 | warn!("Connection failed: {e}, retrying..."); 48 | } 49 | } 50 | 51 | attempts += 1; 52 | if attempts >= 10 { 53 | panic!(" MongoDB not ready after 10 attempts"); 54 | } 55 | sleep(Duration::from_secs(1)).await; 56 | } 57 | client 58 | } 59 | 60 | #[allow(dead_code)] 61 | /// This function is used to print the stdout of a container for test debugging 62 | pub async fn print_stdout(container: &ContainerAsync) { 63 | let mut reader_stdout = container.stdout(false); 64 | let mut dst = String::new(); 65 | let _ = reader_stdout.read_to_string(&mut dst).await; 66 | println!("{dst}"); 67 | } 68 | 69 | #[allow(dead_code)] 70 | /// This function is used to print the stderr of a container for test debugging 71 | pub async fn print_stderr(container: &ContainerAsync) { 72 | let mut reader_stderr = container.stderr(false); 73 | let mut dst = String::new(); 74 | let _ = reader_stderr.read_to_string(&mut dst).await; 75 | println!("{dst}"); 76 | } 77 | -------------------------------------------------------------------------------- /nightfall_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightfall_client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio-util = "0.7.16" 8 | once_cell = "1" 9 | tracing = "0.1.41" 10 | jf-relation = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 11 | ark-bn254 = "0.4.0" 12 | ark-ff = { version = "0.4.2", features = ["parallel"] } 13 | bip32 = { version = "0.4.0", features = ["bip39"] } 14 | num-bigint = "0.4.6" 15 | num = "0.4.3" 16 | ff_ce = "0.11.0" 17 | ark-ec = { version = "0.4.2", features = ["parallel"] } 18 | rand_core = { version = "0.6.4", features = ["getrandom"] } 19 | ark-serialize = "0.4.2" 20 | sha3 = "0.10.8" 21 | sha2 = "0.10" 22 | alloy = { version = "1.0.23", features = ["transport-ws", "json-rpc", "serde"] } 23 | warp = "0.3.7" 24 | serde = { version = "1.0.219", features = ["derive"] } 25 | tokio = { version = "1.45.0", features = ["full"] } 26 | criterion = "0.4" 27 | serde_json = "1.0.140" 28 | rand = "0.8" 29 | ark-std = { version = "0.4.0", default-features = false } 30 | itertools = { version = "0.10.5", default-features = false } 31 | arkworks-utils = "1.0.1" 32 | mongodb = "3.2.3" 33 | futures = "0.3.31" 34 | async-trait = "0.1.88" 35 | jf-plonk = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 36 | "test-srs", 37 | ] } 38 | jf-utils = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git", features = [ 39 | "parallel", 40 | ] } 41 | jf-primitives = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 42 | num-traits = "0.2.19" 43 | byteorder = "1.5.0" 44 | hex = "0.4.3" 45 | serde_repr = "0.1.20" 46 | serde_bytes = "0.11" 47 | serde_with = "2.3.3" 48 | config = "0.13.4" 49 | configuration = { path = "../configuration" } 50 | nightfall_bindings = { path = "../nightfall_bindings" } 51 | log = "0.4.27" 52 | nf-curves = { git = "https://git@github.com/EYBlockchain/nightfish_CE.git" } 53 | reqwest = "0.11.27" 54 | url = "2.5.4" 55 | rustc-hex = "2.1.0" 56 | uint = "0.9.5" 57 | lazy_static = "1.5.0" 58 | testcontainers = { version = "0.24.0", features = ["blocking"] } 59 | lib = { path = "../lib" } 60 | uuid = { version = "1.16.0", features = ["v4"] } 61 | 62 | [dev-dependencies] 63 | criterion = { version = "0.4", features = ["html_reports"] } 64 | httparse = "1.10.1" 65 | serial_test = "0.5.1" 66 | 67 | [[bench]] 68 | name = "poseidon_hash_gadgets_bench" 69 | harness = false 70 | 71 | [[bench]] 72 | name = "bench_unified_circuit" 73 | harness = false 74 | 75 | [features] 76 | default = ["parallel"] 77 | std = ["ark-std/std", "ark-serialize/std", "ark-ff/std", "ark-ec/std"] 78 | test_apis = [] # exposing apis for testing purpose 79 | parallel = [ 80 | "ark-ff/parallel", 81 | "ark-ec/parallel", 82 | "jf-utils/parallel", 83 | "jf-relation/parallel", 84 | "jf-primitives/parallel", 85 | ] 86 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/RollupProofVerificationKey_V2.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | import "forge-std/Test.sol"; 5 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 6 | 7 | import "../contracts/proof_verification/RollupProofVerificationKey.sol"; 8 | import "../contracts/proof_verification/lib/Types.sol"; 9 | 10 | contract VKProviderTest is Test { 11 | RollupProofVerificationKey vk; 12 | 13 | function setUp() public { 14 | // Build a minimal VK struct 15 | Types.VerificationKey memory initVK; 16 | initVK.domain_size = 0x2000000; 17 | 18 | // Deploy impl 19 | RollupProofVerificationKey impl = new RollupProofVerificationKey(); 20 | 21 | // Encode initializer and deploy proxy 22 | bytes memory initData = abi.encodeCall( 23 | RollupProofVerificationKey.initialize, 24 | (abi.encode(initVK)) 25 | ); 26 | vk = RollupProofVerificationKey( 27 | address(new ERC1967Proxy(address(impl), initData)) 28 | ); 29 | } 30 | 31 | function testGetVK() public view { 32 | Types.VerificationKey memory t = vk.getVerificationKey(); 33 | assertEq(t.domain_size, 0x2000000, "domain_size mismatch after initialize"); 34 | } 35 | 36 | function testReplaceVK_UpdatesAllAndBumpsVersion() public { 37 | // Prepare a new VK with a different domain size 38 | Types.VerificationKey memory newVK; 39 | newVK.domain_size = 0x3000000; 40 | 41 | bytes32 oldHash = vk.vkHash(); 42 | uint64 oldVer = vk.vkVersion(); 43 | 44 | vk.replaceVK(abi.encode(newVK)); 45 | 46 | Types.VerificationKey memory t = vk.getVerificationKey(); 47 | assertEq(t.domain_size, 0x3000000, "domain_size not updated by replaceVK"); 48 | assertEq(vk.vkVersion(), oldVer + 1, "vkVersion did not increment"); 49 | assertTrue(vk.vkHash() != oldHash, "vkHash did not change"); 50 | } 51 | 52 | function testOnlyOwnerCanReplace() public { 53 | address attacker = address(0xBEEF); 54 | vm.prank(attacker); 55 | 56 | Types.VerificationKey memory newVK; 57 | newVK.domain_size = 0x4000000; 58 | 59 | // OZ v5 reverts with OwnableUnauthorizedAccount; a generic expectRevert is fine 60 | vm.expectRevert(); 61 | vk.replaceVK(abi.encode(newVK)); 62 | } 63 | 64 | function testInitializeOnlyOnce() public { 65 | // Re-initializing the *proxy* must revert due to `initializer` 66 | Types.VerificationKey memory again; 67 | again.domain_size = 0x2000000; 68 | 69 | vm.expectRevert(); // "Initializable: contract is already initialized" 70 | vk.initialize(abi.encode(again)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /nightfall_test/src/webhook.rs: -------------------------------------------------------------------------------- 1 | use configuration::settings::get_settings; 2 | use log::{debug, warn}; 3 | use std::sync::Arc; 4 | use tokio::sync::Mutex; 5 | /// Set up a warp server to listen for webhooks from the Nightfall client. 6 | use warp::Filter; 7 | 8 | pub async fn run_webhook_server(responses: Arc>>) { 9 | // Define the webhook route 10 | let webhook = warp::path("webhook") 11 | .and(warp::post()) 12 | .and(with_responses(responses)) 13 | .and(warp::body::json()) 14 | .and_then(handle_webhook); 15 | 16 | // Start the server on port 8080 17 | warp::serve(webhook).run(([0, 0, 0, 0], 8080)).await; 18 | } 19 | 20 | async fn handle_webhook( 21 | responses: Arc>>, 22 | payload: serde_json::Value, 23 | ) -> Result { 24 | debug!("Received webhook payload: {payload:#?}"); 25 | let mut responses = responses.lock().await; 26 | responses.push(payload); 27 | // Respond with a 200 OK status 28 | Ok(warp::http::StatusCode::OK) 29 | } 30 | 31 | fn with_responses( 32 | responses: Arc>>, 33 | ) -> impl Filter>>,), Error = std::convert::Infallible> + Clone 34 | { 35 | warp::any().map(move || responses.clone()) 36 | } 37 | 38 | /// Poll the Nightfall client queue every 5 seconds and print the length of the queue. 39 | pub async fn poll_queue() { 40 | let url = &get_settings().nightfall_client.url; 41 | let url2 = "http://client2:3000"; 42 | let client = reqwest::Client::new(); 43 | loop { 44 | // poll the queue 45 | match client.get(format!("{url}/v1/queue")).send().await { 46 | Ok(response) if response.status().is_success() => match response.text().await { 47 | Ok(body) => debug!("Client 1 Queue length is : {body}"), 48 | Err(err) => warn!("Failed to read response body for client1: {err}"), 49 | }, 50 | Ok(resp) => warn!("Client 1 returned status: {}", resp.status()), 51 | Err(err) => warn!("Failed to poll client1: {err}"), 52 | } 53 | // poll the queue for client 2 54 | match client.get(format!("{url2}/v1/queue")).send().await { 55 | Ok(response) if response.status().is_success() => match response.text().await { 56 | Ok(body2) => debug!("Client 2 Queue length is : {body2}"), 57 | Err(err) => warn!("Failed to read response body for client2: {err}"), 58 | }, 59 | Ok(resp) => warn!("Client 2 returned status: {}", resp.status()), 60 | Err(err) => warn!("Failed to poll client2: {err}"), 61 | } 62 | tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /nightfall_client/src/main.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | use configuration::{logging::init_logging, settings::get_settings}; 3 | use lib::{ 4 | merkle_trees::trees::TreeMetadata, 5 | plonk_prover::plonk_proof::{PlonkProof, PlonkProvingEngine}, 6 | shared_entities::Node, 7 | utils, 8 | }; 9 | use log::{error, info}; 10 | use nightfall_bindings::artifacts::Nightfall; 11 | use nightfall_client::{ 12 | domain::entities::Request, 13 | driven::queue::process_queue, 14 | drivers::{blockchain::event_listener_manager::ensure_running, rest::routes}, 15 | }; 16 | use tokio::task::JoinError; 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<(), JoinError> { 20 | // declare the types of wallet that we're using 21 | type N = Nightfall::NightfallCalls; 22 | init_logging( 23 | get_settings().nightfall_client.log_level.as_str(), 24 | get_settings().log_app_only, 25 | ); 26 | // ── clear desynchronised tree metadata/requests ─────────────────────────── 27 | // drop the commitment merkle tree data because it will be out of date and need resynching. The commitments are retained. 28 | // status reflected in the DB 29 | let url = &get_settings().nightfall_client.db_url; 30 | utils::drop_collection::>( 31 | url.as_str(), 32 | "nightfall", 33 | "commitment_tree_metadata", 34 | ) 35 | .await 36 | .expect("Failed to drop Metadata collection"); 37 | utils::drop_collection::>(url.as_str(), "nightfall", "commitment_tree_nodes") 38 | .await 39 | .expect("Failed to drop Node collection"); 40 | utils::drop_collection::>(url.as_str(), "nightfall", "commitment_tree_cache") 41 | .await 42 | .expect("Failed to drop Cache collection"); 43 | // drop the request-ID tracking collection 44 | utils::drop_collection::(url.as_str(), "nightfall", "requests") 45 | .await 46 | .expect("Failed to drop Requests collection"); 47 | 48 | // ── start the (owned) event listener once ───────────────────────────────── 49 | ensure_running::().await; 50 | 51 | // ── start Warp server and the queue worker as independent tasks ─────────── 52 | let routes = routes::(); 53 | let task_warp = tokio::spawn(warp::serve(routes).run(([0, 0, 0, 0], 3000))); 54 | 55 | let task_queue = tokio::spawn(process_queue::< 56 | PlonkProof, 57 | PlonkProvingEngine, 58 | Nightfall::NightfallCalls, 59 | >()); 60 | 61 | info!("Starting warp server and request queue (event listener managed separately)"); 62 | // Both tasks are long-lived; if either returns, treat as unexpected 63 | let (_r2, _r3) = (task_warp.await?, task_queue.await?); 64 | error!("Client exited unexpectedly."); 65 | 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/X509/Certified.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | pragma solidity ^0.8.20; 3 | 4 | import "./X509Interface.sol"; 5 | import "./SanctionsListInterface.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | 8 | import "forge-std/console.sol"; 9 | 10 | /// @notice Base contract providing X.509 and sanctions gating for upgradeable contracts. 11 | /// @dev No constructor. Call __Certified_init(...) from the child’s initialize(). 12 | abstract contract Certified is Initializable { 13 | /// @custom:oz-upgrades-unsafe-allow constructor 14 | constructor() { 15 | _disableInitializers(); 16 | } 17 | 18 | X509Interface internal x509; 19 | SanctionsListInterface internal sanctionsList; 20 | address public owner; 21 | 22 | event OwnershipTransferred( 23 | address indexed previousOwner, 24 | address indexed newOwner 25 | ); 26 | event AuthoritiesUpdated( 27 | address indexed sanctionsList, 28 | address indexed x509 29 | ); 30 | 31 | modifier onlyOwner() { 32 | require(msg.sender == owner, "Certified: caller is not the owner"); 33 | _; 34 | } 35 | 36 | /// @dev Must be called by inheriting contract inside its `initialize`. 37 | function __Certified_init( 38 | address _owner, 39 | address x509Addr, 40 | address sanctionsAddr 41 | ) internal onlyInitializing { 42 | require(_owner != address(0), "Certified: owner is zero"); 43 | owner = _owner; 44 | x509 = X509Interface(x509Addr); 45 | sanctionsList = SanctionsListInterface(sanctionsAddr); 46 | emit OwnershipTransferred(address(0), _owner); 47 | emit AuthoritiesUpdated(sanctionsAddr, x509Addr); 48 | } 49 | 50 | /// @notice Update the authority contracts. 51 | function setAuthorities( 52 | address sanctionsListAddress, 53 | address x509Address 54 | ) external onlyOwner { 55 | x509 = X509Interface(x509Address); 56 | sanctionsList = SanctionsListInterface(sanctionsListAddress); 57 | emit AuthoritiesUpdated(sanctionsListAddress, x509Address); 58 | } 59 | 60 | /// @notice Transfer ownership of the Certified gate. 61 | function transferOwnership(address newOwner) external onlyOwner { 62 | require(newOwner != address(0), "Certified: new owner is zero"); 63 | emit OwnershipTransferred(owner, newOwner); 64 | owner = newOwner; 65 | } 66 | 67 | /// @notice Gate modifier: requires valid X509 and not sanctioned. 68 | modifier onlyCertified() { 69 | require( 70 | x509.x509Check(msg.sender), 71 | "Certified: not authorised by X509" 72 | ); 73 | require( 74 | !sanctionsList.isSanctioned(msg.sender), 75 | "Certified: address is sanctioned" 76 | ); 77 | _; 78 | } 79 | 80 | // Storage gap for future upgrades 81 | uint256[50] private __gap; 82 | } 83 | -------------------------------------------------------------------------------- /nightfall_bindings/build.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | use std::{env, os::unix::process::ExitStatusExt, path::Path, path::PathBuf, process::Command}; 3 | 4 | fn main() { 5 | // Find repo root: nightfall_bindings/.. is repo root 6 | let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 7 | let repo_root = manifest_dir.parent().unwrap().to_path_buf(); 8 | 9 | // Check if artifacts exist - they should be pre-compiled by deployer or exist already 10 | let nightfall_artifact = 11 | repo_root.join("blockchain_assets/artifacts/Nightfall.sol/Nightfall.json"); 12 | 13 | if !nightfall_artifact.exists() { 14 | // If artifacts don't exist, compile them 15 | info!("Artifacts not found, building with forge"); 16 | forge_command(&["build"]); 17 | } else { 18 | info!("Using existing artifacts from blockchain_assets/artifacts/"); 19 | } 20 | 21 | // read the artifacts.rs and replace the dummy_artifact with artifacts 22 | let artifacts_path = Path::new("../nightfall_bindings/src/artifacts.rs"); 23 | if artifacts_path.exists() { 24 | info!("Artifacts file found at {artifacts_path:?}"); 25 | let content = 26 | std::fs::read_to_string(artifacts_path).expect("Failed to read artifacts.rs file"); 27 | let updated_content = content.replace("dummy_artifacts", "artifacts"); 28 | std::fs::write(artifacts_path, updated_content) 29 | .expect("Failed to write updated artifacts.rs file"); 30 | } else { 31 | panic!("Artifacts file not found at {artifacts_path:?}"); 32 | } 33 | 34 | println!( 35 | "cargo:warning=Contract verification will use runtime hashes from contract_hashes.toml" 36 | ); 37 | } 38 | 39 | /// Function should only be called after we have checked forge is installed by running 'which forge' 40 | fn forge_command(command: &[&str]) { 41 | let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 42 | let repo_root = manifest_dir.parent().unwrap(); 43 | let output = Command::new("forge") 44 | .args(command) 45 | .current_dir(repo_root) 46 | .output(); 47 | 48 | match output { 49 | Ok(o) => { 50 | if o.status.success() { 51 | info!( 52 | "Command 'forge {:?}' executed successfully: {}", 53 | command, 54 | String::from_utf8_lossy(&o.stdout) 55 | ); 56 | } else { 57 | panic!( 58 | "Command 'forge {:?}' executed with failing error code: {:?}\nStandard Output: {}\nStandard Error: {}", 59 | command, 60 | o.status.signal(), 61 | String::from_utf8_lossy(&o.stdout), 62 | String::from_utf8_lossy(&o.stderr) 63 | ); 64 | } 65 | } 66 | Err(e) => { 67 | panic!("Command 'forge {command:?}' ran into an error without executing: {e}"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/src/plonk_prover/circuits/kemdem_gadgets.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | use ark_ec::{twisted_edwards::Affine as TEAffine, AffineRepr}; 3 | use nf_curves::ed_on_bn254::BabyJubjub; 4 | 5 | use jf_relation::{ 6 | errors::CircuitError, 7 | gadgets::ecc::{Point, PointVariable}, 8 | Circuit, PlonkCircuit, Variable, 9 | }; 10 | 11 | use super::{DOMAIN_DEM, DOMAIN_KEM}; 12 | use jf_primitives::circuit::poseidon::PoseidonHashGadget; 13 | 14 | pub trait KEMDEMCircuit { 15 | fn kem(&mut self, shared_secret: &PointVariable) -> Result; 16 | 17 | fn dem( 18 | &mut self, 19 | encryption_key_var: Variable, 20 | plain_text: &[Variable], 21 | ) -> Result, CircuitError>; 22 | 23 | fn kemdem( 24 | &mut self, 25 | ephemeral_key: Variable, 26 | shared_secret: &PointVariable, 27 | plain_text: &[Variable], 28 | ) -> Result<(PointVariable, Vec), CircuitError>; 29 | } 30 | 31 | impl KEMDEMCircuit for PlonkCircuit { 32 | fn kem(&mut self, shared_secret: &PointVariable) -> Result { 33 | let domain_kem_var = self.create_variable(DOMAIN_KEM)?; 34 | 35 | // Compute the encryption key and store the variable index. 36 | let encryption_key_var = 37 | self.poseidon_hash(&[shared_secret.get_x(), shared_secret.get_y(), domain_kem_var])?; 38 | 39 | Ok(encryption_key_var) 40 | } 41 | 42 | fn dem( 43 | &mut self, 44 | encryption_key_var: Variable, 45 | plain_text: &[Variable], 46 | ) -> Result, CircuitError> { 47 | let domain_dem_var = self.create_variable(DOMAIN_DEM)?; 48 | 49 | // Create variables for all the cipher texts, store the indices in the array. 50 | plain_text 51 | .iter() 52 | .enumerate() 53 | .map(|(i, &plain)| { 54 | let tmp_var = self.create_variable(Fr254::from(i as u64))?; 55 | let tmp_hash = self 56 | .poseidon_hash(vec![encryption_key_var, domain_dem_var, tmp_var].as_slice())?; 57 | self.add(tmp_hash, plain) 58 | }) 59 | .collect::, CircuitError>>() 60 | } 61 | 62 | fn kemdem( 63 | &mut self, 64 | ephemeral_key: Variable, 65 | shared_secret: &PointVariable, 66 | plain_text: &[Variable], 67 | ) -> Result<(PointVariable, Vec), CircuitError> { 68 | // Now we run the KEM. 69 | let encryption_key_var = self.kem(shared_secret)?; 70 | 71 | // Now we run the DEM. 72 | let cipher_text = self.dem(encryption_key_var, plain_text)?; 73 | 74 | let pub_point = self 75 | .create_constant_point_variable(&Point::::from( 76 | TEAffine::::generator(), 77 | ))?; 78 | let epk = self.variable_base_scalar_mul::(ephemeral_key, &pub_point)?; 79 | 80 | Ok((epk, cipher_text)) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /nightfall_client/src/driven/plonk_prover_tests/verify_encryption_gadget_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::driven::primitives::kemdem_functions::kemdem_encrypt; 4 | use ark_ec::{twisted_edwards::Affine as TEAffine, AffineRepr, CurveGroup}; 5 | use ark_std::UniformRand; 6 | use jf_relation::{gadgets::ecc::Point, Circuit, PlonkCircuit}; 7 | use jf_utils::{fr_to_fq, test_rng}; 8 | use lib::{ 9 | nf_token_id::to_nf_token_id_from_fr254, 10 | plonk_prover::circuits::verify::verify_encryption_gadgets::VerifyEncryptionCircuit, 11 | }; 12 | use nf_curves::ed_on_bn254::{BabyJubjub as EdwardsConfig, Fq as Fr254, Fr as BJJScalar}; 13 | #[test] 14 | fn test_encryption_circuit() { 15 | let rng = &mut test_rng(); 16 | let recipient_private_key = BJJScalar::rand(rng); 17 | let ephemeral_private_key = BJJScalar::rand(rng); 18 | let public_point = TEAffine::::generator(); 19 | let recipient_public_key = (public_point * recipient_private_key).into(); 20 | 21 | let token_id_fr = Fr254::rand(rng); 22 | let erc_address_fr = Fr254::rand(rng); 23 | 24 | let nf_token_id_fr = to_nf_token_id_from_fr254(erc_address_fr, token_id_fr); 25 | 26 | let plain_text = vec![nf_token_id_fr, Fr254::rand(rng), Fr254::rand(rng)]; 27 | let cipher_text = kemdem_encrypt::( 28 | ephemeral_private_key, 29 | recipient_public_key, 30 | &plain_text, 31 | public_point, 32 | ) 33 | .unwrap(); 34 | 35 | let mut circuit = PlonkCircuit::::new_ultra_plonk(8); 36 | let nf_token_id = circuit.create_variable(plain_text[0]).unwrap(); 37 | let nf_slot_id = circuit.create_variable(plain_text[1]).unwrap(); 38 | let value = circuit.create_variable(plain_text[2]).unwrap(); 39 | 40 | let shared_secret = (recipient_public_key * ephemeral_private_key).into_affine(); 41 | 42 | let shared_secret = circuit 43 | .create_point_variable(&Point::::from(shared_secret)) 44 | .unwrap(); 45 | 46 | let e_private_key = circuit 47 | .create_variable(fr_to_fq::(&ephemeral_private_key)) 48 | .unwrap(); 49 | 50 | let withdraw_address = circuit.zero(); 51 | let withdraw_flag = circuit.create_boolean_variable(false).unwrap(); 52 | 53 | let circuit_cipher_text = circuit 54 | .verify_encryption( 55 | nf_token_id, 56 | nf_slot_id, 57 | value, 58 | &shared_secret, 59 | e_private_key, 60 | withdraw_address, 61 | withdraw_flag, 62 | ) 63 | .unwrap(); 64 | 65 | for (calc, circuit_cipher) in cipher_text.iter().zip(circuit_cipher_text.iter()) { 66 | assert_eq!(*calc, circuit.witness(*circuit_cipher).unwrap()); 67 | } 68 | 69 | circuit.check_circuit_satisfiability(&[]).unwrap(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /nightfall_proposer/src/domain/entities.rs: -------------------------------------------------------------------------------- 1 | use ark_bn254::Fr as Fr254; 2 | use ark_serialize::SerializationError; 3 | use lib::{ 4 | entities::DepositData, 5 | serialization::{ark_de_hex, ark_se_hex}, 6 | shared_entities::{ClientTransaction, OnChainTransaction}, 7 | }; 8 | use log::error; 9 | use serde::{Deserialize, Serialize}; 10 | use sha3::{Digest, Keccak256}; 11 | use std::fmt::Debug; 12 | 13 | /// A Block struct representing NF block 14 | /// NOTE: This is not finalised yet, we may need to change fields to this struct 15 | #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] 16 | pub struct Block { 17 | // The root of the merkle tree of all commitments in this block. 18 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 19 | pub commitments_root: Fr254, 20 | // The root of the merkle tree of all nullifiers in this block. 21 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 22 | pub nullifiers_root: Fr254, 23 | // The new root of the tree of all previous commitments_roots. 24 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 25 | pub commitments_root_root: Fr254, 26 | // The hash of the block. 27 | // The list of transactions in this block. 28 | pub transactions: Vec, 29 | pub rollup_proof: Vec, 30 | } 31 | 32 | /// Struct used to represent deposit data, used in making deposit proofs by the proposer. 33 | #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)] 34 | pub struct DepositDatawithFee { 35 | /// The fee paid to the proposer 36 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 37 | pub fee: Fr254, 38 | /// deposit data 39 | pub deposit_data: DepositData, 40 | } 41 | 42 | impl DepositDatawithFee { 43 | #[allow(dead_code)] 44 | pub fn hash(&self) -> Result, SerializationError> { 45 | // Step 1: Serialize to bytes 46 | let encoding = serde_json::to_vec(self).map_err(|e| { 47 | error!("DepositDatawithFee hash computation error: {e}"); 48 | SerializationError::InvalidData 49 | })?; 50 | 51 | // Step 2: Hash the bytes with Keccak256 52 | let hash = Keccak256::digest(encoding); 53 | 54 | // Step 3: Convert hash bytes to Vec 55 | Ok(hash.iter().map(|&b| b as u32).collect()) 56 | } 57 | } 58 | 59 | /// A struct representing a client transaction with added metadata that tells us about its current state. 60 | #[derive(Serialize, Deserialize, Clone, Debug)] 61 | pub struct ClientTransactionWithMetaData

{ 62 | pub client_transaction: ClientTransaction

, 63 | pub block_l2: Option, 64 | pub in_mempool: bool, 65 | pub hash: Vec, 66 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] 67 | pub historic_roots: Vec, 68 | } 69 | 70 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 71 | pub struct HistoricRoot( 72 | #[serde(serialize_with = "ark_se_hex", deserialize_with = "ark_de_hex")] pub Fr254, 73 | pub u32, 74 | ); 75 | -------------------------------------------------------------------------------- /nightfall_client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod domain; 2 | pub mod driven; 3 | pub mod drivers; 4 | pub mod ports; 5 | pub mod services; 6 | pub mod test_helpers; 7 | 8 | use bip32::DerivationPath; 9 | use bip32::Mnemonic; 10 | use drivers::derive_key::ZKPKeys; 11 | use std::sync::{Mutex, OnceLock}; 12 | 13 | /// This function is used to retrieve the zkp keys 14 | pub fn get_zkp_keys() -> &'static Mutex { 15 | static ZKP_KEYS: OnceLock> = OnceLock::new(); 16 | ZKP_KEYS.get_or_init(|| { 17 | let rng = ark_std::rand::thread_rng(); 18 | let mnemonic = Mnemonic::random(rng, Default::default()); 19 | let path: DerivationPath = "m/44'/60'/0'/0/0".parse().expect("failed to parse path"); 20 | let zkp_keys = ZKPKeys::derive_from_mnemonic(&mnemonic, &path) 21 | .expect("Could not derive ZKP keys from mnemonic"); 22 | Mutex::new(zkp_keys) 23 | }) 24 | } 25 | 26 | pub mod initialisation { 27 | use crate::ports::trees::CommitmentTree; 28 | use ark_bn254::Fr as Fr254; 29 | use configuration::settings::get_settings; 30 | use mongodb::Client as MongoClient; 31 | use reqwest::{Client as HttpClient, ClientBuilder}; 32 | use std::{sync::OnceLock, time::Duration}; 33 | use tokio::sync::OnceCell; 34 | use url::Url; 35 | 36 | /// This function is used to provide a singleton database connection across the entire application. 37 | pub async fn get_db_connection() -> &'static MongoClient { 38 | static DB_CONNECTION: OnceCell = OnceCell::const_new(); 39 | DB_CONNECTION 40 | .get_or_init(|| async { 41 | let client = MongoClient::with_uri_str(&get_settings().nightfall_client.db_url) 42 | .await 43 | .expect("Could not create database connection"); 44 | // Initialize the commitment tree in the database 45 | >::new_commitment_tree(&client, 29, 3) 46 | .await 47 | .expect("Could not create commitment tree"); 48 | client 49 | }) 50 | .await 51 | } 52 | 53 | /// This function is used to provide a singleton proposer http connection across the entire application. 54 | pub fn get_proposer_http_connection() -> &'static (HttpClient, Url) { 55 | static PROPOSER_HTTP_CONNECTION: OnceLock<(HttpClient, Url)> = OnceLock::new(); 56 | PROPOSER_HTTP_CONNECTION.get_or_init(|| { 57 | let base_url = &get_settings().nightfall_proposer.url; 58 | let url = Url::parse(base_url) 59 | .expect("Could not parse proposer url") 60 | .join("/v1/transaction") 61 | .expect("Could not join proposer url with /v1/transaction"); 62 | 63 | // Create a new HTTP client with a timeout 64 | let client = ClientBuilder::new() 65 | .timeout(Duration::from_secs(5)) 66 | .build() 67 | .expect("Could not build HTTP client with timeout"); 68 | (client, url) 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/proof_verification/RollupProofVerificationKey.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | 8 | import "./lib/Types.sol"; 9 | import "./IVKProvider.sol"; 10 | 11 | contract RollupProofVerificationKey is 12 | Initializable, 13 | UUPSUpgradeable, 14 | OwnableUpgradeable, 15 | IVKProvider 16 | { 17 | /// @custom:oz-upgrades-unsafe-allow constructor 18 | constructor() { 19 | _disableInitializers(); 20 | } 21 | 22 | // -------- Storage v1 -------- 23 | Types.VerificationKey private _vk; // the whole VK, canonical source 24 | bytes32 private _vkHash; // keccak256(abi.encode(_vk)) 25 | uint64 private _vkVersion; // increment on every replacement 26 | 27 | // Gap for future upgrades 28 | uint256[50] private __gap; 29 | 30 | // -------- Events -------- 31 | event VKInitialized(bytes32 vkHash, uint64 version); 32 | event VKReplaced(bytes32 oldHash, bytes32 newHash, uint64 newVersion); 33 | 34 | function _decodeAndSanity( 35 | bytes calldata vkBlob 36 | ) private pure returns (Types.VerificationKey memory vk) { 37 | vk = abi.decode(vkBlob, (Types.VerificationKey)); 38 | } 39 | 40 | // -------- Initialize -------- 41 | /// @notice First-time initialization with a full VK. 42 | /// @param vkBlob ABI-encoded Types.VerificationKey (abi.encode(vk)) 43 | function initialize(bytes calldata vkBlob) external initializer { 44 | require(vkBlob.length != 0, "VK: empty"); 45 | 46 | __Ownable_init(msg.sender); 47 | __UUPSUpgradeable_init(); 48 | 49 | Types.VerificationKey memory vk = _decodeAndSanity(vkBlob); 50 | _vk = vk; 51 | _vkHash = keccak256(vkBlob); 52 | _vkVersion = 1; 53 | emit VKInitialized(_vkHash, _vkVersion); 54 | } 55 | 56 | // -------- Whole-VK replacement (atomic) -------- 57 | /// @notice Replace the entire VerificationKey atomically. 58 | /// @param vkBlob ABI-encoded Types.VerificationKey (abi.encode(vk)) 59 | function replaceVK(bytes calldata vkBlob) external onlyOwner { 60 | require(vkBlob.length != 0, "VK: empty"); 61 | 62 | Types.VerificationKey memory vk = _decodeAndSanity(vkBlob); 63 | 64 | bytes32 oldHash = _vkHash; 65 | _vk = vk; 66 | _vkHash = keccak256(vkBlob); 67 | _vkVersion += 1; 68 | 69 | emit VKReplaced(oldHash, _vkHash, _vkVersion); 70 | } 71 | 72 | // -------- IVKProvider -------- 73 | function getVerificationKey() 74 | external 75 | view 76 | returns (Types.VerificationKey memory) 77 | { 78 | return _vk; 79 | } 80 | 81 | function vkHash() external view returns (bytes32) { 82 | return _vkHash; 83 | } 84 | 85 | // operational getters 86 | function vkVersion() external view returns (uint64) { 87 | return _vkVersion; 88 | } 89 | 90 | // -------- UUPS gate -------- 91 | function _authorizeUpgrade(address) internal override onlyOwner {} 92 | } 93 | -------------------------------------------------------------------------------- /blockchain_assets/contracts/Nightfall_V3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0 2 | pragma solidity ^0.8.20; 3 | 4 | import "./Nightfall.sol"; 5 | import "forge-std/console.sol"; 6 | 7 | /// @custom:oz-upgrades-from blockchain_assets/contracts/Nightfall.sol:Nightfall 8 | contract NightfallV3 is Nightfall { 9 | 10 | /// @custom:oz-upgrades-unsafe-allow constructor 11 | constructor() { 12 | _disableInitializers(); 13 | } 14 | 15 | /// @dev V3: do NOT deal with deposit_fee commitment. 16 | function escrow_funds( 17 | uint256 fee, 18 | address ercAddress, 19 | uint256 tokenId, 20 | uint256 value, 21 | uint256 secretHash, 22 | TokenType token_type 23 | ) external payable virtual override onlyCertified { 24 | uint256 nfTokenId = sha256_and_shift(abi.encode(ercAddress, tokenId)); 25 | tokenIdMapping[nfTokenId] = TokenIdValue(ercAddress, tokenId); 26 | 27 | uint256 nfSlotId = (token_type == TokenType.ERC3525) 28 | ? uint256( 29 | keccak256( 30 | abi.encode(ercAddress, IERC3525(ercAddress).slotOf(tokenId)) 31 | ) 32 | ) >> 4 33 | : nfTokenId; 34 | 35 | DepositCommitment memory valueCommitment = DepositCommitment( 36 | nfTokenId, 37 | nfSlotId, 38 | value, 39 | secretHash 40 | ); 41 | uint256 key = sha256_and_shift(abi.encode(valueCommitment)); 42 | 43 | require( 44 | feeBinding[key].escrowed == 0, 45 | "Funds have already been escrowed for this Deposit" 46 | ); 47 | 48 | if (token_type == TokenType.ERC3525) { 49 | ERC3525(ercAddress).transferFrom( 50 | msg.sender, 51 | address(this), 52 | tokenId 53 | ); 54 | } else if (token_type == TokenType.ERC1155) { 55 | IERC1155(ercAddress).safeTransferFrom( 56 | msg.sender, 57 | address(this), 58 | tokenId, 59 | value, 60 | "" 61 | ); 62 | } else if (token_type == TokenType.ERC721) { 63 | require(value == 0, "ERC721 tokens should have a value of zero"); 64 | IERC721(ercAddress).safeTransferFrom( 65 | msg.sender, 66 | address(this), 67 | tokenId, 68 | "" 69 | ); 70 | } else if (token_type == TokenType.ERC20) { 71 | require(tokenId == 0, "ERC20 tokens should have a tokenId of 0"); 72 | require( 73 | IERC20(ercAddress).transferFrom( 74 | msg.sender, 75 | address(this), 76 | value 77 | ), 78 | "ERC20 transfer failed" 79 | ); 80 | } else { 81 | revert escrowFundsError(); 82 | } 83 | 84 | feeBinding[key] = DepositFeeState(fee, 1, 0); 85 | emit DepositEscrowed(nfSlotId, value); 86 | } 87 | // Simple marker for sanity-check behavior after upgrade 88 | function versionMarker() external pure returns (string memory) { 89 | return "NightfallV3-no-deposit-commitment-event"; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /nightfall_proposer/src/drivers/rest/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::error::ProposerRejection; 2 | use crate::drivers::rest::{ 3 | block_data::get_block_data, client_transactions::client_transaction, 4 | proposers::rotate_proposer, synchronisation::synchronisation, 5 | }; 6 | use block_assembly::{pause_block_assembly, resume_block_assembly}; 7 | use lib::{ 8 | health_check::health_route, 9 | nf_client_proof::{Proof, ProvingEngine}, 10 | validate_certificate::certification_validation_request, 11 | validate_keys::keys_validation_request, 12 | }; 13 | use proposers::{add_proposer, remove_proposer, withdraw}; 14 | use warp::{ 15 | reject::Rejection, 16 | reply::{self, Reply}, 17 | Filter, 18 | }; 19 | 20 | pub mod block_assembly; 21 | pub mod block_data; 22 | pub mod client_transactions; 23 | pub mod proposers; 24 | pub mod synchronisation; 25 | 26 | pub fn routes() -> impl Filter + Clone 27 | where 28 | P: Proof, 29 | E: ProvingEngine

+ Sync + Send + 'static, 30 | { 31 | health_route() 32 | .or(client_transaction::()) 33 | .or(rotate_proposer()) 34 | .or(get_block_data()) 35 | .or(add_proposer()) 36 | .or(remove_proposer()) 37 | .or(withdraw()) 38 | .or(certification_validation_request()) 39 | .or(keys_validation_request()) 40 | .or(synchronisation()) 41 | .or(pause_block_assembly()) 42 | .or(resume_block_assembly()) 43 | .recover(handle_rejection) 44 | } 45 | 46 | async fn handle_rejection(err: Rejection) -> Result { 47 | if let Some(e) = err.find::() { 48 | match e { 49 | ProposerRejection::BlockDataUnavailable => Ok(reply::with_status( 50 | "Block data unavailable", 51 | warp::http::StatusCode::SERVICE_UNAVAILABLE, 52 | )), 53 | ProposerRejection::ClientTransactionFailed => Ok(reply::with_status( 54 | "Client transaction failed", 55 | warp::http::StatusCode::BAD_REQUEST, 56 | )), 57 | ProposerRejection::FailedToRotateProposer => Ok(reply::with_status( 58 | "Failed to rotate proposer", 59 | warp::http::StatusCode::LOCKED, 60 | )), 61 | ProposerRejection::FailedToAddProposer => Ok(reply::with_status( 62 | "Failed to add proposer", 63 | warp::http::StatusCode::BAD_REQUEST, 64 | )), 65 | ProposerRejection::FailedToRemoveProposer => Ok(reply::with_status( 66 | "Failed to remove proposer", 67 | warp::http::StatusCode::BAD_REQUEST, 68 | )), 69 | ProposerRejection::FailedToWithdrawStake => Ok(reply::with_status( 70 | "Failed to withdraw stake", 71 | warp::http::StatusCode::BAD_REQUEST, 72 | )), 73 | ProposerRejection::ProviderError => Ok(reply::with_status( 74 | "Provider error", 75 | warp::http::StatusCode::SERVICE_UNAVAILABLE, 76 | )), 77 | } 78 | } else { 79 | Ok(reply::with_status( 80 | "INTERNAL_SERVER_ERROR", 81 | warp::http::StatusCode::INTERNAL_SERVER_ERROR, 82 | )) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/root_ca_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | DOv5Sfy+o4FasCNr6UU6M7cdO6WGBQ== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/root_ca_priv_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQAIBADANBgkqhkiG9w0BAQEFAASCCSowggkmAgEAAoICAQCd6p3KgL+ofCkj 3 | KxjWwAcomJIqfn4iSn/2OPYYUbXzY5Ln+/2/86CuQJdj4qBM3T3GkqbeRHOR/+Zy 4 | JFaVfdfwMbjZp5mVefb0JYSQrm6dYpvECBX2icWAN8A7RlAiQ7/SmwIRZFRFOBDR 5 | YN4djI3dYkswoloBEYXmC8qb9xGB3TJWES8e/b7PGed6+WQO3k24/1GFXmtJBCT8 6 | T1Yx3ZVR1812JCDjr6CyDmtAOgy3H6FoYfjFkbK9e91WTsbVoXqTLjEIdtHWWvPz 7 | 8hPRxJCG8yx8ig9TdQEn34cJ9gNWiOAuYT8cV6Uloh3YP6J9BiL8Dvx2q6EUGUp/ 8 | 2hsIeQE9B5DzuNOHrKI4/DcTX5umuwyHqXIUNWiwELYu6Lpxx4IChYFw8pJZatld 9 | 1PotyOmro1m49RG184lJBvP9CiLKPesuZ7KpfNK4R6xzvij2mZakz1G2/Ye5+TL2 10 | BJ+IasWncldVaThC3wB5Wp0Ax24sREa92l5ZXL6M31HgUGMtsRDRVTQxiKV/JztD 11 | NOXaXsVWrTytwzJyaNwMUo/kH4N6OTtbL3bkds+mSiokunH19wePU2Dr8xbUJ1qy 12 | krAxuc+Hh6ywCdPcXc1QOMBeGyIlkJ5Zbf4uloz64Hf9/1QOP3j8RklmuxnigN40 13 | +BB5udyhEZBM3Hxbb9XNRKIVsLWmqQIDAQABAoICAAm3dHMP3fsffKaB0drw4N/i 14 | F2hO9TLl856yXY4chP4PW+wzSKdFddcS+V23hZaFLHZp8qONgbDzlnnie4NiRWWt 15 | oYqe8efNI+Fc553BZeQAO8Zhmvxa+NCNXaIoDuk00vpB+GwaiMbjjsFbXP9jdR0w 16 | naDF9JTJP7xqcFKNbsHXWvAJyaT2BwYWOXSLINl7WzvtVdZHe4F4JFB0D0EtObFZ 17 | /nXs/isUVOZv1bCTgdHIu/VAxf5eGynmk7VqTni5oWGp/3FeYkX2vbj+ZbLMDfqH 18 | vu6AzjWb5vDNo7D8trLd5DFmYWw8hlhc88opDDX2KTPqNiWVJOmoLrnUMUKhs241 19 | o6JiGm7HYFXAte3Nx0xRBwgd82N8h7XfVVDK3hvn8ysGGzshBOoVBJsVM27UvIua 20 | XOeOJbs8SwINNQYrPEG5Ovlq4uLZ9i8E8ezdoSPYq8Ef1w2Fw6Q3Ugqv6TIEJkTF 21 | /xc3Rn72MIzVUuy2tQmGKAtuSWMS9Q+1eOaDJX1qZpmuVufEeeEStGUTl8VOkwzZ 22 | XH8/vaezf5i9f8jmOJmCCAEbm4Vye6dQo41nPIkvipKvLXn0ZJUwXJvqpSxrPHOD 23 | sNjLkwFgKU4yX/0Ty7ezyhiYxGVkK5FnuGql58hcfsqm6ROZXQARzhweayUXv3Sd 24 | R8zq7Qa3irnd6TfoncZJAoIBAQDRp+ESbh8lSlzG1M7vHtap0GrhUK59kJHHI/aB 25 | zOjA/GDUBD4QuzA5+Sw+9WLkcOr6mksTFTiZdQEq+0XHYa7Ln5lJACKOHcv84/fz 26 | VTXLVa5bMMMDiI8JHDvcK2dMeEbIoQmAD19gXSsIWfNLAppUzK+4w2u+OTeCr1Br 27 | KVGgyToRtJ+DWpWalEsqZk+7I7oVlzMCv/+ItFa59I5FpeSao6Aaxie8wzYV/6DO 28 | I19Y5433yNMR5qHNBdzl6u6CJzYY91JpBPJjdbkCqp+NWQx7/xWtDewabyq1ADqd 29 | Lcpxs2zL2/lGxlQmlMrar7C0emThKMUQ41gEwCHbP8+NOYTtAoIBAQDA0uI6a9Pk 30 | f8B2ifGssiIp2DX8cOpPXlSAB1c2RgPM14cjtnJRmgTp+9WsVRkYH7nnZ/wlxOqr 31 | i7RO78XAG8wHT9OY4pnsg6kV/+KyWogYNNmoVU3x/MGu+Ci3/7Q4mS6/XmvBsNtT 32 | Pud8JSY0accfd2pB7iIRKijbeC/3w2TpJAC2+HggbD0Gm33+UaYbHhvqYEZUl/CY 33 | pyujjmQaVXW1299KnKw4eJCP6L43CCA2+oDp0fyCsI540Q45IN2+YRKrWDw9kDn9 34 | OByMYUMG/Wi4lTWrOjVvQ3RadRrwfETdia9+QPTOd7buco6dyjM0hIZ7etlLbTtg 35 | EuHPFIRtVk0tAoIBAE/TZM34oeE37BVEh7nh+wfTNC6v4pAqkGbLWZtYYrLAJTn0 36 | l5EVRKp+JguesNWb7MwYHUeZx+Ad/aDY5Vuh5MTg8qMp6v5DeBzEy+6ZXg8ag3Vz 37 | Hn1k7Fkip+QPnpguH31aW1yn/b+aGb1nSkwgJJTvDmUB+y8FaA5ZI/x5UfwvQe3l 38 | 4OpJfjbKCciOBgOR+dMoq6J7ahcp2u+YnucD4APFEzu+gPjNNvWvTBHQxAf8ukBC 39 | 9aKWNrryZYYhKkjiFyXwXS9hU8zs0NGkF3XhFvDLUfx/FYuHEjKycJ7SBYe/Mkh4 40 | a/Q3pWi/NHGJfXVqOKjGNipU9XlxkjKBnEwAP40CggEBAKgnP6Iy5lYeEnlNUrSH 41 | o8NaCbpbvfn5CTedLzDlXesFCZTbRlZjOmXSeEQQ8hFXmYdz5zAIyZ1P963e61Tc 42 | 6NB3jM+GzTS0nH/S5p8i1AxICogB3DdrdHxhbSEInOG+tEWu0cnXsWynLrfUrbyH 43 | gl6uhLt6L7tISNkbtrkabKXj/7uQczHTMmTQtM9T5MdBOpbp3mtnFSoKnsPDKQjN 44 | /hjZ5Z/rgUS931OgZEEZlP5c9o7x0IMn9RbnOMAZKUcc4GsMR3xoiimJBVxmdU1j 45 | MC/H70WjPXYKHS3FcfjGZkfMCn7Ppq1nOOA4hqaT7pOd/qkwOM7OMzlVPhaYjePL 46 | nwECgf867LHh0IVfA3F6YqcqfSLGYEHc0+8UK19iFmLA0FHz2Q7LQStp+nYhc+c9 47 | Ot2MO3U0fyD/4FN+Vaz+i1spDnByte9TBOK+2tFFedKHBnzeRl0mAcG9NU2Zf+rd 48 | 4TA4ihyZOwV5gByvRm+U8E9oseHOTVO65bnLr83DH7gcJWk5eVBVRLnQu4psg7wQ 49 | 7RVbhPiTHDqe6Qk/NZ3xFuQ/DCr3UC0gfAgzJl2H7VGta9lbMy8YG+q+POlKek9S 50 | ueM/g664BoCDYVep6JAInMG+s5NKrtTQ2HhpqiGQWpvZKg+jLDHt1w33vcbt4lVQ 51 | KT5HiV6yhsoY1/jtdB6QZewrFP0= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /blockchain_assets/test_contracts/X509/_certificates/gen-root_ca-certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Generating self-signed root CA certificate" 5 | 6 | # Generate the root CA private key in DER format 7 | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out root_ca.priv_key -outform DER 8 | 9 | # Convert the private key to PEM format for use in certificate generation 10 | openssl rsa -inform DER -in root_ca.priv_key -out root_ca_priv_key.pem -outform PEM 11 | 12 | # Create the OpenSSL configuration file for certificate generation 13 | mkdir -p conf 14 | cat > root_ca.cnf < conf/ca_extfile.conf < impl Filter + Clone { 20 | path!("v1" / "deriveKey") 21 | .and(warp::post()) 22 | // make body optional 23 | .and( 24 | warp::body::json() 25 | .map(Some) 26 | .or_else(|_| async { Ok::<(Option,), Rejection>((None,)) }), 27 | ) 28 | .and_then(handle_derive_key) 29 | } 30 | 31 | pub async fn handle_derive_key( 32 | key_request: Option, 33 | ) -> Result { 34 | if let Some(req) = key_request { 35 | // validate mnemonic and path 36 | let valid_mnemonic = Mnemonic::new(req.mnemonic, Default::default()) 37 | .map_err(|_| reject::custom(BadKeyRequest))?; 38 | let valid_derivation_path: DerivationPath = req 39 | .child_path 40 | .parse() 41 | .map_err(|_| reject::custom(BadKeyRequest))?; 42 | 43 | if let Ok(key) = ZKPKeys::derive_from_mnemonic(&valid_mnemonic, &valid_derivation_path) { 44 | // update the static 45 | let mut zkpk = get_zkp_keys().lock().expect("Poisoned lock"); 46 | *zkpk = key.clone(); // store derived key 47 | let pubkey = ZKPPubKey::from(&key); 48 | Ok(reply::with_status(reply::json(&pubkey), StatusCode::OK)) 49 | } else { 50 | Err(reject::not_found()) 51 | } 52 | } else { 53 | // no body -> return existing static keys 54 | let zkpk = get_zkp_keys().lock().expect("Poisoned lock"); 55 | // only serialize a safe view 56 | let pubkey = ZKPPubKey::from(&*zkpk); 57 | Ok(reply::with_status(reply::json(&pubkey), StatusCode::OK)) 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::*; 64 | use bip32::{DerivationPath, Mnemonic}; 65 | use std::str::FromStr; 66 | 67 | #[tokio::test] 68 | async fn test_rest_key_derivation() { 69 | let key_request = KeyRequest{ 70 | mnemonic: "spice split denial symbol resemble knock hunt trial make buzz attitude mom slice define clinic kid crawl guilt frozen there cage light secret work".to_string(), 71 | child_path: "m/44'/60'/0'/0/0".to_string() 72 | }; 73 | let test_key = ZKPKeys::derive_from_mnemonic( 74 | &Mnemonic::new(&key_request.mnemonic, Default::default()).unwrap(), 75 | &DerivationPath::from_str(&key_request.child_path).unwrap(), 76 | ) 77 | .unwrap(); 78 | let filter = derive_key_mnemonic(); 79 | let res = warp::test::request() 80 | .method("POST") 81 | .path("/v1/deriveKey") 82 | .json(&key_request) 83 | .reply(&filter) 84 | .await; 85 | let key = serde_json::from_slice::(res.body()).unwrap(); 86 | assert_eq!(res.status(), StatusCode::OK); 87 | assert_eq!(key.zkp_public_key, test_key.zkp_public_key); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /nightfall_client/src/drivers/rest/balance.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | domain::error::ClientRejection, initialisation::get_db_connection, ports::db::CommitmentDB, 3 | }; 4 | use ark_ff::{BigInteger, PrimeField}; 5 | use lib::{ 6 | blockchain_client::BlockchainClientConnection, get_fee_token_id, 7 | hex_conversion::HexConvertible, initialisation::get_blockchain_client_connection, 8 | nf_token_id::to_nf_token_id_from_str, 9 | }; 10 | use warp::{http::StatusCode, path, reply::Reply, Filter}; 11 | /// Endpoint to get a token balance 12 | /// NB for consistency with the rest of the API, 13 | /// the value is returned as a *hex* string. 14 | pub fn get_balance() -> impl Filter + Clone 15 | { 16 | path!("v1" / "balance" / String / String) 17 | .and(warp::get()) 18 | .and_then(handle_get_balance) 19 | } 20 | 21 | pub async fn handle_get_balance( 22 | erc_address: String, 23 | token_id: String, 24 | ) -> Result { 25 | let nf_token_id = to_nf_token_id_from_str(&erc_address, &token_id); 26 | if let Ok(nf_token_id) = nf_token_id { 27 | let db = get_db_connection().await; 28 | let balance = db.get_balance(&nf_token_id).await; 29 | if let Some(balance) = balance { 30 | Ok(warp::reply::with_status( 31 | hex::encode(balance.into_bigint().to_bytes_be()), 32 | StatusCode::OK, 33 | )) 34 | } else { 35 | Err(warp::reject::custom(ClientRejection::NoSuchToken)) 36 | } 37 | } else { 38 | Err(warp::reject::custom(ClientRejection::InvalidTokenId)) 39 | } 40 | } 41 | 42 | /// Endpoint to get a fee balance 43 | /// the value is returned as a *hex* string. 44 | pub fn get_fee_balance( 45 | ) -> impl Filter + Clone { 46 | path!("v1" / "fee_balance") 47 | .and(warp::get()) 48 | .and_then(handle_get_fee_balance) 49 | } 50 | 51 | pub async fn handle_get_fee_balance() -> Result { 52 | let fee_token_id = get_fee_token_id(); 53 | // search the commitment db for a preimage with the correct nf_token_id 54 | let db = get_db_connection().await; 55 | // get the balance 56 | let balance = db.get_balance(&fee_token_id).await; 57 | if let Some(balance) = balance { 58 | Ok(warp::reply::with_status( 59 | hex::encode(balance.into_bigint().to_bytes_be()), 60 | StatusCode::OK, 61 | )) 62 | } else { 63 | // if we don't find a balance, return a custom rejection 64 | Err(warp::reject::custom(ClientRejection::NoSuchToken)) 65 | } 66 | } 67 | 68 | /// Endpoint to get the L1 balance of the client's wallet 69 | /// Returns the value as a *hex* string. 70 | pub fn get_l1_balance( 71 | ) -> impl Filter + Clone { 72 | path!("v1" / "l1_balance") 73 | .and(warp::get()) 74 | .and_then(handle_get_l1_balance) 75 | } 76 | 77 | pub async fn handle_get_l1_balance() -> Result { 78 | // Get the blockchain client connection (should be initialised elsewhere) 79 | let client = get_blockchain_client_connection().await.read().await; 80 | // get_balance returns Option 81 | match client.get_balance().await { 82 | Some(balance) => Ok(warp::reply::with_status( 83 | balance.to_hex_string(), 84 | StatusCode::OK, 85 | )), 86 | None => Err(warp::reject::custom(ClientRejection::NoSuchToken)), 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /nightfall_client/benches/poseidon_hash_gadgets_bench.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant}; 2 | 3 | use ark_bn254::Bn254; 4 | use ark_ff::UniformRand; 5 | use criterion::{criterion_group, criterion_main, Criterion}; 6 | use jf_plonk::{nightfall::FFTPlonk, proof_system::UniversalSNARK, transcript::StandardTranscript}; 7 | 8 | use jf_relation::{Arithmetization, Circuit, PlonkCircuit}; 9 | use nf_curves::ed_on_bn254::Fq as Fr254; 10 | 11 | use jf_primitives::circuit::poseidon::PoseidonHashGadget; 12 | use jf_primitives::pcs::prelude::UnivariateKzgPCS; 13 | fn bench_poseidon_hash_gadget_criterion( 14 | c: &mut Criterion, 15 | circuit: &mut PlonkCircuit, 16 | i: usize, 17 | ) { 18 | let mut rng = jf_utils::test_rng(); 19 | let mut inputs: Vec = vec![Fr254::from(1)]; 20 | for _ in 1..i { 21 | let rand = Fr254::rand(&mut rng); 22 | inputs.push(rand); 23 | } 24 | 25 | let start = Instant::now(); 26 | let inputs_vars = inputs 27 | .iter() 28 | .map(|&x| circuit.create_variable(x).unwrap()) 29 | .collect::>(); 30 | circuit.poseidon_hash(&inputs_vars).ok(); 31 | // Constraint count 32 | println!( 33 | "Poseidon : {} constraints before padding", 34 | circuit.num_gates() 35 | ); 36 | 37 | circuit.finalize_for_arithmetization().unwrap(); 38 | println!("Poseidon witness time:{} ms", start.elapsed().as_millis()); 39 | 40 | // Constraint count 41 | println!( 42 | "Poseidon : {} constraints after padding", 43 | circuit.num_gates() 44 | ); 45 | 46 | // Srs generation time 47 | c.bench_function("Poseidon Srs generation time:", |b| { 48 | b.iter(|| { 49 | let srs_size = circuit.srs_size(true).unwrap(); 50 | FFTPlonk::>::universal_setup_for_testing(srs_size, &mut rng) 51 | .unwrap(); 52 | }) 53 | }); 54 | 55 | let srs_size = circuit.srs_size(true).unwrap(); 56 | let srs = FFTPlonk::>::universal_setup_for_testing(srs_size, &mut rng) 57 | .unwrap(); 58 | 59 | let (pk, vk) = 60 | FFTPlonk::>::preprocess(&srs, None, circuit, true).unwrap(); 61 | 62 | //Proving time 63 | c.bench_function("Poseidon Proving time:", |b| { 64 | b.iter(|| { 65 | FFTPlonk::>::prove::<_, _, StandardTranscript>( 66 | &mut rng, circuit, &pk, None, true, 67 | ) 68 | .unwrap(); 69 | }) 70 | }); 71 | let proof = FFTPlonk::>::prove::<_, _, StandardTranscript>( 72 | &mut rng, circuit, &pk, None, true, 73 | ) 74 | .unwrap(); 75 | // Verification time 76 | c.bench_function("Poseidon Verification time:", |b| { 77 | b.iter(|| { 78 | FFTPlonk::>::verify::( 79 | &vk, 80 | &[], 81 | &proof, 82 | None, 83 | true, 84 | ) 85 | .unwrap(); 86 | }) 87 | }); 88 | } 89 | 90 | fn bench_poseidon_hash_gadget_criterion_main(c: &mut Criterion) { 91 | for i in 1..7 { 92 | println!("The number of inputs:{}", i + 1); 93 | let mut circuit_turbo: PlonkCircuit = PlonkCircuit::new_turbo_plonk(); 94 | bench_poseidon_hash_gadget_criterion(c, &mut circuit_turbo, i); 95 | } 96 | } 97 | 98 | criterion_group! { 99 | name = benches; 100 | config = Criterion::default().sample_size(10).measurement_time(Duration::from_secs(2)).warm_up_time(Duration::from_secs(1)); 101 | targets = bench_poseidon_hash_gadget_criterion_main 102 | } 103 | criterion_main!(benches); 104 | -------------------------------------------------------------------------------- /blockchain_assets/script/upgrade_X509_V3.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {Script} from "forge-std/Script.sol"; 5 | import {console} from "forge-std/console.sol"; 6 | import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; 7 | 8 | contract UpgradeX509WithLogging is Script { 9 | bytes32 constant _IMPL_SLOT = 10 | 0x360894A13BA1A3210667C828492DB98DCA3E2076CC3735A920A3CA505D382BBC; 11 | bytes32 constant _ADMIN_SLOT = 12 | 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 13 | string constant ARTIFACT = "X509V3.sol:X509V3"; 14 | 15 | function _getImpl(address proxy) internal view returns (address impl) { 16 | bytes32 raw = vm.load(proxy, _IMPL_SLOT); 17 | impl = address(uint160(uint256(raw))); 18 | } 19 | function _getAdmin(address proxy) internal view returns (address admin) { 20 | bytes32 raw = vm.load(proxy, _ADMIN_SLOT); 21 | admin = address(uint160(uint256(raw))); 22 | } 23 | function _codehash(address a) internal view returns (bytes32 h) { 24 | assembly { 25 | h := extcodehash(a) 26 | } 27 | } 28 | function _proxiableUUID( 29 | address proxy 30 | ) internal view returns (bytes32 uuid, bool ok) { 31 | (bool success, bytes memory ret) = proxy.staticcall( 32 | abi.encodeWithSignature("proxiableUUID()") 33 | ); 34 | if (!success || ret.length != 32) return (bytes32(0), false); 35 | return (abi.decode(ret, (bytes32)), true); 36 | } 37 | function _logProxyState(string memory tag, address proxy) internal view { 38 | address impl = _getImpl(proxy); 39 | address admin = _getAdmin(proxy); 40 | (bytes32 uuid, bool hasUUID) = _proxiableUUID(proxy); 41 | 42 | console.log("========== %s ==========", tag); 43 | console.log("Proxy: %s", proxy); 44 | console.log(" code length: %u", proxy.code.length); 45 | console.logBytes32(_codehash(proxy)); 46 | console.log("Implementation: %s", impl); 47 | console.log(" code length: %u", impl.code.length); 48 | console.logBytes32(_codehash(impl)); 49 | console.log("Admin (EIP-1967): %s", admin); 50 | if (hasUUID) { 51 | console.log("proxiableUUID():"); 52 | console.logBytes32(uuid); 53 | } else { 54 | console.log("proxiableUUID(): "); 55 | } 56 | console.log("=============================="); 57 | } 58 | 59 | // Preferred entrypoint: pass the proxy address 60 | function run(address proxy) public { 61 | vm.setEnv("FOUNDRY_OUT", "blockchain_assets/artifacts"); 62 | require(proxy != address(0), "proxy arg is zero"); 63 | require(proxy.code.length > 0, "X509 proxy has no code on RPC_URL"); 64 | 65 | uint256 pk = vm.envUint("NF4_SIGNING_KEY"); // must be proxy owner 66 | 67 | address implBefore = _getImpl(proxy); 68 | _logProxyState("Before upgrade", proxy); 69 | 70 | vm.startBroadcast(pk); 71 | Upgrades.upgradeProxy(proxy, ARTIFACT, bytes("")); 72 | vm.stopBroadcast(); 73 | 74 | address implAfter = _getImpl(proxy); 75 | _logProxyState("After upgrade", proxy); 76 | require(implAfter != address(0), "implAfter is zero"); 77 | require( 78 | _codehash(implAfter) != _codehash(implBefore), 79 | "Implementation did not change" 80 | ); 81 | console.log( 82 | "Upgrade successful: impl changed from %s to %s", 83 | implBefore, 84 | implAfter 85 | ); 86 | } 87 | 88 | // Convenience entrypoint: read proxy from env X509_PROXY 89 | function run() external { 90 | address proxy = vm.envAddress("X509_PROXY"); 91 | run(proxy); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /nightfall_test.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | key_request.mnemonic = "spice split denial symbol resemble knock hunt trial make buzz attitude mom slice define clinic kid crawl guilt frozen there cage light secret work" 3 | key_request.child_path = "m/44'/60'/0'/0/0" 4 | 5 | key_request2.mnemonic = "wink shell monkey fiscal exit great friend motor arrange file coffee leg catch drip amateur simple plastic win seat circle couch differ stomach law" 6 | key_request2.child_path = "m/44'/60'/0'/0/0" 7 | 8 | erc20_deposit_0.value = "0x01" 9 | erc20_deposit_0.fee = "0x00" 10 | erc20_deposit_0.token_id = "0x00" 11 | 12 | erc20_deposit_1.value = "0x03" 13 | erc20_deposit_1.fee = "0x02" 14 | erc20_deposit_1.token_id = "0x00" 15 | 16 | erc20_deposit_2.value = "0x04" 17 | erc20_deposit_2.fee = "0x05" 18 | erc20_deposit_2.token_id = "0x00" 19 | 20 | erc20_deposit_3.value = "0x06" 21 | erc20_deposit_3.fee = "0x01" 22 | erc20_deposit_3.token_id = "0x00" 23 | 24 | erc20_deposit_4.value = "0x01" 25 | erc20_deposit_4.fee = "0x03" 26 | erc20_deposit_4.token_id = "0x00" 27 | 28 | erc20_deposit_large_block.value = "0x04" 29 | erc20_deposit_large_block.fee = "0x05" 30 | erc20_deposit_large_block.token_id = "0x00" 31 | 32 | erc20_transfer_0.value = "0x01" 33 | erc20_transfer_0.fee = "0x00" 34 | erc20_transfer_0.token_id = "0x00" 35 | 36 | erc20_transfer_1.value = "0x02" 37 | erc20_transfer_1.fee = "0x01" 38 | erc20_transfer_1.token_id = "0x00" 39 | 40 | erc20_transfer_2.value = "0x0A" #"0x03" 41 | erc20_transfer_2.fee = "0x02" 42 | erc20_transfer_2.token_id = "0x00" 43 | 44 | erc20_transfer_large_block.value = "0x02" 45 | erc20_transfer_large_block.fee = "0x01" 46 | erc20_transfer_large_block.token_id = "0x00" 47 | 48 | erc20_withdraw_0.value = "0x01" 49 | erc20_withdraw_0.fee = "0x00" 50 | erc20_withdraw_0.token_id = "0x00" 51 | 52 | erc20_withdraw_1.value = "0x01" 53 | erc20_withdraw_1.fee = "0x01" 54 | erc20_withdraw_1.token_id = "0x00" 55 | 56 | erc20_withdraw_2.value = "0x01" 57 | erc20_withdraw_2.fee = "0x02" 58 | erc20_withdraw_2.token_id = "0x00" 59 | 60 | erc721_deposit.value = "0x00" 61 | erc721_deposit.fee = "0x07" 62 | erc721_deposit.token_id = "0x01aa" 63 | 64 | erc721_transfer.value = "0x00" 65 | erc721_transfer.fee = "0x08" 66 | erc721_transfer.token_id = "0x01aa" 67 | 68 | erc721_withdraw.value = "0x00" 69 | erc721_withdraw.fee = "0x03" 70 | erc721_withdraw.token_id = "0x01aa" 71 | 72 | erc3525_deposit_1.value = "0x09" 73 | erc3525_deposit_1.fee = "0x0a" 74 | erc3525_deposit_1.token_id = "0x07" 75 | 76 | erc3525_deposit_2.value = "0x0c" 77 | erc3525_deposit_2.fee = "0x0d" 78 | erc3525_deposit_2.token_id = "0x08" 79 | 80 | erc3525_transfer_1.value = "0x03" 81 | erc3525_transfer_1.fee = "0x0c" 82 | erc3525_transfer_1.token_id = "0x07" 83 | 84 | erc3525_transfer_2.value = "0x04" 85 | erc3525_transfer_2.fee = "0x01" 86 | erc3525_transfer_2.token_id = "0x08" 87 | 88 | erc3525_withdraw.value = "0x03" 89 | erc3525_withdraw.fee = "0x04" 90 | erc3525_withdraw.token_id = "0x07" 91 | 92 | erc1155_deposit_1.value = "0x0f" 93 | erc1155_deposit_1.fee = "0x10" 94 | erc1155_deposit_1.token_id = "0x02" 95 | 96 | erc1155_deposit_2.value = "0x12" 97 | erc1155_deposit_2.fee = "0x13" 98 | erc1155_deposit_2.token_id = "0x02" 99 | 100 | erc1155_deposit_3_nft.value = "0x00" 101 | erc1155_deposit_3_nft.fee = "0x15" 102 | erc1155_deposit_3_nft.token_id = "0x49" 103 | 104 | erc1155_transfer_1.value = "0x20" 105 | erc1155_transfer_1.fee = "0x11" 106 | erc1155_transfer_1.token_id = "0x02" 107 | 108 | erc1155_transfer_2_nft.value = "0x00" 109 | erc1155_transfer_2_nft.fee = "0x01" 110 | erc1155_transfer_2_nft.token_id = "0x49" 111 | 112 | erc1155_withdraw_1.value = "0x1F" 113 | erc1155_withdraw_1.fee = "0x05" 114 | erc1155_withdraw_1.token_id = "0x02" 115 | 116 | erc1155_withdraw_2_nft.value = "0x00" 117 | erc1155_withdraw_2_nft.fee = "0x06" 118 | erc1155_withdraw_2_nft.token_id = "0x49" 119 | --------------------------------------------------------------------------------