From d36abdae4e13cb3abc851c755e58b041c9236315 Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 16:31:03 +0200 Subject: [PATCH 1/6] fix: correct workspace edition from non-existent 2024 to 2021 The workspace Cargo.toml declared edition = "2024" which does not exist. All individual crates already override to 2021, so this was silently ignored but would break if any crate removed its local edition field. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1a0738ff..49846510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ resolver = "2" [workspace.package] version = "0.1.0" -edition = "2024" +edition = "2021" From 4d64372337a9cc4cf6ede7d7054b21843f57b160 Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 16:31:10 +0200 Subject: [PATCH 2/6] fix: replace unwrap() calls with proper error handling in client handlers Convert panicking unwrap/expect calls in production request handlers to proper error propagation. Fixes hash().unwrap(), try_into().unwrap(), and .first().unwrap() patterns. Also replaces a dbg!() macro with debug!() logging and resolves two stale TODO comments. --- .../src/drivers/rest/client_nf_3.rs | 90 +++++++++++++------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/nightfall_client/src/drivers/rest/client_nf_3.rs b/nightfall_client/src/drivers/rest/client_nf_3.rs index 15225f6c..f488ff9a 100644 --- a/nightfall_client/src/drivers/rest/client_nf_3.rs +++ b/nightfall_client/src/drivers/rest/client_nf_3.rs @@ -132,12 +132,14 @@ async fn queue_request( request_id: String, ) -> Result { let settings = get_settings(); - let max_queue_size = settings + let max_queue_size: usize = settings .nightfall_client .max_queue_size .unwrap_or(1000) .try_into() - .unwrap(); + .map_err(|_| { + warp::reject::custom(crate::domain::error::ClientRejection::DatabaseError) + })?; // check if the id is a valid uuid if Uuid::parse_str(&request_id).is_err() { @@ -340,12 +342,17 @@ pub async fn handle_deposit( .map_err(TransactionHandlerError::DepositError)?; // Insert the preimage into the commitments DB as pending creation - // TODO remove the blocknumber let ZKPKeys { nullifier_key, .. } = *get_zkp_keys().lock().expect("Poisoned Mutex lock"); let nullifier = preimage_value .nullifier_hash(&nullifier_key) - .expect("Could not hash commitment {}"); - let commitment_hash = preimage_value.hash().expect("Could not hash commitment"); + .map_err(|e| { + error!("{id} Could not compute nullifier hash: {e}"); + TransactionHandlerError::CustomError(format!("Could not compute nullifier hash: {e}")) + })?; + let commitment_hash = preimage_value.hash().map_err(|e| { + error!("{id} Could not hash commitment: {e}"); + TransactionHandlerError::CustomError(format!("Could not hash commitment: {e}")) + })?; let commitment_entry = CommitmentEntry::new( preimage_value, nullifier, @@ -372,8 +379,16 @@ pub async fn handle_deposit( if let Some(preimage_fee) = preimage_fee_option { let nullifier = preimage_fee .nullifier_hash(&nullifier_key) - .expect("Could not hash commitment"); - let commitment_hash = preimage_fee.hash().expect("Could not hash commitment"); + .map_err(|e| { + error!("{id} Could not compute fee nullifier hash: {e}"); + TransactionHandlerError::CustomError(format!( + "Could not compute fee nullifier hash: {e}" + )) + })?; + let commitment_hash = preimage_fee.hash().map_err(|e| { + error!("{id} Could not hash fee commitment: {e}"); + TransactionHandlerError::CustomError(format!("Could not hash fee commitment: {e}")) + })?; // Add the mapping for fee commitment as well let commitment_hex = commitment_hash.to_hex_string(); @@ -402,16 +417,31 @@ pub async fn handle_deposit( Some(preimage_fee) => vec![ preimage_value .hash() - .expect("Preimage must be hashable - this should not happen") + .map_err(|e| { + error!("{id} Could not hash preimage value: {e}"); + TransactionHandlerError::CustomError(format!( + "Could not hash preimage value: {e}" + )) + })? .to_hex_string(), preimage_fee .hash() - .expect("Preimage must be hashable - this should not happen") + .map_err(|e| { + error!("{id} Could not hash preimage fee: {e}"); + TransactionHandlerError::CustomError(format!( + "Could not hash preimage fee: {e}" + )) + })? .to_hex_string(), ], None => vec![preimage_value .hash() - .expect("Preimage must be hashable - this should not happen") + .map_err(|e| { + error!("{id} Could not hash preimage value: {e}"); + TransactionHandlerError::CustomError(format!( + "Could not hash preimage value: {e}" + )) + })? .to_hex_string()], }; debug!("{id} Deposit request completed successfully - returning reply to caller"); @@ -456,11 +486,14 @@ where })?; let keys = get_zkp_keys().lock().expect("Poisoned Mutex lock").clone(); - let value = - Fr254::from_hex_string(recipient_data.values.first().unwrap().as_str()).map_err(|e| { - error!("{id} Error when reading value: {e}"); - TransactionHandlerError::CustomError(e.to_string()) - })?; + let first_value = recipient_data.values.first().ok_or_else(|| { + error!("{id} No value provided in recipient data"); + TransactionHandlerError::CustomError("No value provided in recipient data".into()) + })?; + let value = Fr254::from_hex_string(first_value.as_str()).map_err(|e| { + error!("{id} Error when reading value: {e}"); + TransactionHandlerError::CustomError(e.to_string()) + })?; let fee: Fr254 = Fr254::from_hex_string(fee.as_str()).map_err(|e| { error!("{id} Error when reading fee: {e}"); @@ -499,7 +532,8 @@ where } let ephemeral_private_key = { - let mut rng = ark_std::rand::thread_rng(); // TODO initialise in main and pass around as a rwlock + // thread_rng() is already thread-local and cheap to create; no need to share via RwLock. + let mut rng = ark_std::rand::thread_rng(); BJJScalar::rand(&mut rng) }; let shared_secret: Affine = (recipient_public_key * ephemeral_private_key).into(); @@ -631,10 +665,13 @@ where new_commitment_four, ]; - dbg!(new_commitments - .iter() - .map(|c| c.hash().unwrap().to_hex_string()) - .collect::>()); + debug!( + "{id} New commitment hashes: {:?}", + new_commitments + .iter() + .filter_map(|c| c.hash().ok().map(|h| h.to_hex_string())) + .collect::>() + ); let secret_preimages = [ spend_commitments[0].get_secret_preimage(), @@ -665,7 +702,7 @@ where // Rollback the spend commitments to unspent let commitment_ids = spend_commitments .iter() - .map(|c| c.hash().unwrap()) + .filter_map(|c| c.hash().ok()) .collect::>(); info!( @@ -687,7 +724,7 @@ where // Delete new commitments let new_commitment_ids = new_commitments .iter() - .map(|c| c.hash().unwrap()) + .filter_map(|c| c.hash().ok()) .collect::>(); info!("{id} Deleting {} new commitments", new_commitment_ids.len()); @@ -870,7 +907,10 @@ where }; let withdraw_fund_salt = spend_commitments[0] .nullifier_hash(&keys.nullifier_key) - .expect("Failed to compute nullifier hash"); + .map_err(|e| { + error!("{id} Failed to compute nullifier hash: {e}"); + TransactionHandlerError::CustomError(format!("Failed to compute nullifier hash: {e}")) + })?; match handle_client_operation::( op, spend_commitments, @@ -916,7 +956,7 @@ where // Rollback spend commitments let commitment_ids = spend_commitments .iter() - .map(|c| c.hash().unwrap()) + .filter_map(|c| c.hash().ok()) .collect::>(); info!( @@ -938,7 +978,7 @@ where // Delete new commitments let new_commitment_ids = new_commitments .iter() - .map(|c| c.hash().unwrap()) + .filter_map(|c| c.hash().ok()) .collect::>(); info!("{id} Deleting {} new commitments", new_commitment_ids.len()); From 4c07a02c83c7cdc7c4f3b5dd89c4d8cdd33bd25e Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 16:31:16 +0200 Subject: [PATCH 3/6] feat: add pagination support to /v1/commitments endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add limit and offset query parameters (default: limit=100, offset=0) to prevent unbounded response bodies. Backwards-compatible — existing clients without query params receive up to 100 results. --- nightfall_client/src/driven/db/mongo.rs | 16 ++++++++++---- .../src/drivers/rest/commitment.rs | 22 ++++++++++++++++--- nightfall_client/src/ports/db.rs | 6 ++++- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/nightfall_client/src/driven/db/mongo.rs b/nightfall_client/src/driven/db/mongo.rs index f269b10e..6fde0774 100644 --- a/nightfall_client/src/driven/db/mongo.rs +++ b/nightfall_client/src/driven/db/mongo.rs @@ -431,12 +431,20 @@ impl RequestCommitmentMappingDB for Client { impl CommitmentDB for Client { async fn get_all_commitments( &self, + limit: Option, + offset: Option, ) -> Result, mongodb::error::Error> { - let mut cursor = self + let mut find = self .database(DB) .collection::("commitments") - .find(doc! {}) - .await?; + .find(doc! {}); + if let Some(offset) = offset { + find = find.skip(offset); + } + if let Some(limit) = limit { + find = find.limit(limit as i64); + } + let mut cursor = find.await?; let mut result: Vec<(Fr254, CommitmentEntry)> = Vec::new(); while cursor.advance().await? { let v = cursor.deserialize_current()?; @@ -553,7 +561,7 @@ impl CommitmentDB for Client { let k_string = k.to_hex_string(); debug!("Getting commitment with key: {k_string}"); let commitment_1 = self - .get_all_commitments() + .get_all_commitments(None, None) .await .expect("Database error") .into_iter() diff --git a/nightfall_client/src/drivers/rest/commitment.rs b/nightfall_client/src/drivers/rest/commitment.rs index 680c2ffb..532c9dd2 100644 --- a/nightfall_client/src/drivers/rest/commitment.rs +++ b/nightfall_client/src/drivers/rest/commitment.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use warp::{hyper::StatusCode, path, reply, Filter, Reply}; use crate::driven::db::mongo::CommitmentEntry; @@ -7,6 +8,18 @@ use ark_bn254::Fr as Fr254; use ark_ff::{BigInteger, One, PrimeField, Zero}; use lib::{hex_conversion::HexConvertible, shared_entities::TokenType}; +#[derive(Debug, Deserialize)] +struct PaginationParams { + #[serde(default = "default_limit")] + limit: u64, + #[serde(default)] + offset: u64, +} + +fn default_limit() -> u64 { + 100 +} + /// GET request for a specific commitment by key pub fn get_commitment( ) -> impl Filter + Clone { @@ -29,18 +42,21 @@ pub async fn handle_get_commitment(key: String) -> Result impl Filter + Clone { path!("v1" / "commitments") .and(warp::get()) + .and(warp::query::()) .and_then(handle_get_all_commitments) } -pub async fn handle_get_all_commitments() -> Result { +pub async fn handle_get_all_commitments( + params: PaginationParams, +) -> Result { let commitment_db = get_db_connection().await; let res = commitment_db - .get_all_commitments() + .get_all_commitments(Some(params.limit), Some(params.offset)) .await .map_err(|_| warp::reject::custom(crate::domain::error::ClientRejection::DatabaseError))?; let values: Vec = res.into_iter().map(|c| c.1).collect(); diff --git a/nightfall_client/src/ports/db.rs b/nightfall_client/src/ports/db.rs index a3c3797e..a6f96b4e 100644 --- a/nightfall_client/src/ports/db.rs +++ b/nightfall_client/src/ports/db.rs @@ -26,7 +26,11 @@ where async fn store_commitment(&self, commitment_entry: V) -> Option<()>; async fn store_commitments(&self, commitment_entries: &[V], dup_key_check: bool) -> Option<()>; async fn delete_commitments(&self, commitment_ids: Vec) -> Option<()>; - async fn get_all_commitments(&self) -> Result, mongodb::error::Error>; + async fn get_all_commitments( + &self, + limit: Option, + offset: Option, + ) -> Result, mongodb::error::Error>; async fn get_commitments_by_token_type( &self, token_type: &str, From 16644d7fdea5cdb3647ca9b99837f56d70d841b6 Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 16:31:21 +0200 Subject: [PATCH 4/6] chore: add Makefile for common development operations Wraps docker-compose, cargo, and forge commands into simple make targets. Run 'make help' to see all available targets. --- Makefile | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..5a469baa --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +.PHONY: help dev dev-build dev-down test-unit test-sync test-forge fmt fmt-check clippy build build-release build-contracts key-gen clean docker-clean + +# Default target: show help +help: + @echo "Nightfall 4 CE - Development Commands" + @echo "" + @echo " make dev Start the full development stack" + @echo " make dev-build Build and start the development stack" + @echo " make dev-down Stop all development services" + @echo "" + @echo " make build Build all Rust crates" + @echo " make build-release Build all Rust crates (release mode)" + @echo " make build-contracts Build Solidity contracts with Foundry" + @echo "" + @echo " make test-unit Run Rust unit tests" + @echo " make test-sync Run synchronization tests via Docker" + @echo " make test-forge Run Solidity contract tests" + @echo "" + @echo " make fmt Format Rust code" + @echo " make fmt-check Check Rust code formatting" + @echo " make clippy Run clippy linter" + @echo "" + @echo " make key-gen Generate ZK proving keys (heavy)" + @echo " make clean Clean Rust and Solidity build artifacts" + @echo " make docker-clean Remove Docker containers, volumes, and images" + +# Start the full development stack +dev: + docker compose --profile development --env-file .env up + +# Build and start the development stack +dev-build: + docker compose --profile development --env-file .env up --build + +# Stop all development services +dev-down: + docker compose --profile development down + +# Run Rust unit tests +test-unit: + cargo test + +# Run synchronization tests via Docker +test-sync: + docker compose --profile sync_test --env-file .env up + +# Run Solidity contract tests +test-forge: + forge test + +# Format Rust code (requires nightly) +fmt: + cargo +nightly fmt + +# Check Rust code formatting +fmt-check: + cargo +nightly fmt -- --check + +# Run clippy linter (strict mode) +clippy: + cargo clippy --all-targets -- -D warnings + +# Build all Rust crates +build: + cargo build + +# Build all Rust crates in release mode +build-release: + cargo build --release + +# Build Solidity contracts with Foundry +build-contracts: + forge clean && forge build + +# Generate ZK proving keys (resource-intensive) +key-gen: + NF4_MOCK_PROVER=false cargo run --release --bin key_generation + +# Clean Rust and Solidity build artifacts +clean: + cargo clean && forge clean + +# Remove Docker containers, volumes, and dangling images +docker-clean: + docker compose down -v + docker system prune -f From ecb41ec3b513c0f0cca228bc842149160dde722f Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 16:31:27 +0200 Subject: [PATCH 5/6] docs: expand README with architecture, quick start, and API reference Replace the minimal 9-line README with comprehensive documentation including architecture overview, prerequisites, quick start guide, development commands, API endpoint tables, and documentation links. --- README.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 83491834..c77f65c2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,108 @@ -# nightfall_4_CE -Community edition of Nightfall_4 +# Nightfall 4 CE + +Community Edition of Nightfall\_4 + +Nightfall\_4 is a Zero-Knowledge Proof (ZKP)-based Layer 2 ZK-ZK rollup for transferring ERC20, ERC721, ERC1155, and ERC3525 tokens privately on Ethereum. Unlike Nightfall\_3's optimistic rollup, Nightfall\_4 uses cryptographic proofs for near-instant finality. A private transfer typically costs around 6000 gas. _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._ -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. +**This software is experimental. It should not be used to make significant value transactions.** + +## Architecture + +The project is a Rust workspace with the following crates: + +| Crate | Description | +|---|---| +| `nightfall_client` | Client service for creating private transactions (deposit, transfer, withdraw) | +| `nightfall_proposer` | Block proposer that assembles L2 blocks and generates rollup proofs | +| `nightfall_deployer` | Smart contract deployment and ZK proving key generation | +| `nightfall_bindings` | Auto-generated Rust bindings for Solidity contracts | +| `lib` | Shared cryptographic and blockchain utilities (Merkle trees, Poseidon hashing, PLONK proofs) | +| `configuration` | Configuration management via TOML files and environment variables | + +Smart contracts are in `blockchain_assets/contracts/` and use the UUPS upgradeable proxy pattern. + +## Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose +- [Rust](https://rustup.rs/) 1.88.0+ (pinned via `rust-toolchain.toml`) +- [Foundry](https://book.getfoundry.sh/getting-started/installation) (forge, anvil) + +## Quick Start + +Start the full local development stack (Anvil chain, deployer, proposer, two clients, and MongoDB): + +```bash +make dev +``` + +This uses the `development` profile with a local Anvil chain and mock provers, so no heavy ZK key generation is required. + +Once running: +- Client 1 API: `http://localhost:3000` +- Client 2 API: `http://localhost:3002` +- Proposer API: `http://localhost:3001` +- Anvil RPC: `http://localhost:8545` + +## Development Commands + +Run `make help` to see all available targets: + +| Command | Description | +|---|---| +| `make dev` | Start the full development stack | +| `make dev-build` | Build and start the development stack | +| `make dev-down` | Stop all development services | +| `make build` | Build all Rust crates | +| `make build-contracts` | Build Solidity contracts with Foundry | +| `make test-unit` | Run Rust unit tests | +| `make test-sync` | Run synchronization tests via Docker | +| `make test-forge` | Run Solidity contract tests | +| `make fmt` | Format Rust code (requires nightly) | +| `make clippy` | Run clippy linter (strict mode) | +| `make key-gen` | Generate ZK proving keys (resource-intensive) | +| `make clean` | Clean build artifacts | + +## API Endpoints + +### Client (port 3000) + +| Method | Path | Description | +|---|---|---| +| POST | `/v1/deposit` | Deposit tokens into Nightfall | +| POST | `/v1/transfer` | Private token transfer | +| POST | `/v1/withdraw` | Withdraw tokens from Nightfall | +| GET | `/v1/commitments` | List commitments (supports `?limit=&offset=`) | +| GET | `/v1/commitment/{key}` | Get a single commitment | +| GET | `/v1/balance/{token}/{owner}` | Get ERC token balance | +| GET | `/v1/fee_balance` | Get fee balance | +| GET | `/v1/l1_balance` | Get Layer 1 balance | +| GET | `/v1/synchronisation` | Check sync status | +| POST | `/v1/certification` | Submit X.509 certificate | +| GET | `/v1/health` | Health check | + +### Proposer (port 3001) + +| Method | Path | Description | +|---|---|---| +| POST | `/v1/transaction` | Submit client transaction | +| POST | `/v1/register` | Register as a proposer | +| POST | `/v1/deregister` | Deregister a proposer | +| GET | `/v1/rotate` | Rotate active proposer | +| POST | `/v1/pause` | Pause block assembly | +| POST | `/v1/resume` | Resume block assembly | +| GET | `/v1/blockdata` | Get current block data | +| POST | `/v1/certification` | Submit X.509 certificate | +| GET | `/v1/health` | Health check | + +## Documentation + +- [Architecture and API Reference](doc/nf_4.md) - Full documentation +- [Testnet Setup Guide](doc/Setup%20Testnet%20Guide.md) - Deploy to a host chain +- [Upgradable Contracts Guide](doc/Upgradable%20Contracts%20Guide.md) - Contract upgrade procedures +- [Changelog](doc/CHANGELOG.md) - Version history -Please note that this software should be treated as experimental. It should not be used to make significant value transactions. +## License +See [LICENSE](LICENSE). From ba58a9a7cb7eabd3dbdb16168770db0e3c30b81b Mon Sep 17 00:00:00 2001 From: Moshe Malawach Date: Tue, 31 Mar 2026 17:49:44 +0200 Subject: [PATCH 6/6] fix: address review findings - Log orphaned commitments in rollback paths instead of silently dropping - Clamp pagination limit to i64::MAX before casting to prevent overflow - Scope docker-clean to project images only (--rmi local) --- Makefile | 5 +- nightfall_client/src/driven/db/mongo.rs | 2 +- .../src/drivers/rest/client_nf_3.rs | 58 ++++++++++++++++--- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 5a469baa..a5df5d01 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,6 @@ key-gen: clean: cargo clean && forge clean -# Remove Docker containers, volumes, and dangling images +# Remove Docker containers, volumes, and locally-built images docker-clean: - docker compose down -v - docker system prune -f + docker compose down -v --rmi local diff --git a/nightfall_client/src/driven/db/mongo.rs b/nightfall_client/src/driven/db/mongo.rs index 6fde0774..62ad91dd 100644 --- a/nightfall_client/src/driven/db/mongo.rs +++ b/nightfall_client/src/driven/db/mongo.rs @@ -442,7 +442,7 @@ impl CommitmentDB for Client { find = find.skip(offset); } if let Some(limit) = limit { - find = find.limit(limit as i64); + find = find.limit(limit.min(i64::MAX as u64) as i64); } let mut cursor = find.await?; let mut result: Vec<(Fr254, CommitmentEntry)> = Vec::new(); diff --git a/nightfall_client/src/drivers/rest/client_nf_3.rs b/nightfall_client/src/drivers/rest/client_nf_3.rs index f488ff9a..7531bbcf 100644 --- a/nightfall_client/src/drivers/rest/client_nf_3.rs +++ b/nightfall_client/src/drivers/rest/client_nf_3.rs @@ -40,7 +40,7 @@ use lib::{ serialization::ark_de_hex, shared_entities::{DepositSecret, Preimage, Salt, TokenType}, }; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use nf_curves::ed_on_bn254::{BJJTEAffine as JubJub, BabyJubjub, Fr as BJJScalar}; use nightfall_bindings::artifacts::{Nightfall, IERC1155, IERC20, IERC3525, IERC721}; use serde::{Deserialize, Serialize}; @@ -559,7 +559,13 @@ where // rollback the value commitments to unspent if fails to find fee commitments let value_commitment_ids = spend_value_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); for commitment_id in &value_commitment_ids { @@ -669,7 +675,13 @@ where "{id} New commitment hashes: {:?}", new_commitments .iter() - .filter_map(|c| c.hash().ok().map(|h| h.to_hex_string())) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h.to_hex_string()), + Err(e) => { + warn!("{id} Failed to hash commitment for debug output: {e}"); + None + } + }) .collect::>() ); @@ -702,7 +714,13 @@ where // Rollback the spend commitments to unspent let commitment_ids = spend_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); info!( @@ -724,7 +742,13 @@ where // Delete new commitments let new_commitment_ids = new_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); info!("{id} Deleting {} new commitments", new_commitment_ids.len()); @@ -800,7 +824,13 @@ where // rollback the value commitments to unspent if fails to find fee commitments let value_commitment_ids = spend_value_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); for commitment_id in &value_commitment_ids { if let Some(existing) = db.get_commitment(commitment_id).await { @@ -956,7 +986,13 @@ where // Rollback spend commitments let commitment_ids = spend_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); info!( @@ -978,7 +1014,13 @@ where // Delete new commitments let new_commitment_ids = new_commitments .iter() - .filter_map(|c| c.hash().ok()) + .filter_map(|c| match c.hash() { + Ok(h) => Some(h), + Err(e) => { + error!("{id} Failed to hash commitment during rollback, commitment may be orphaned: {e}"); + None + } + }) .collect::>(); info!("{id} Deleting {} new commitments", new_commitment_ids.len());