From 4d54344b8ba37d5c3358acf6b65e596c542f63dc Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Wed, 19 Feb 2025 14:48:37 -0300 Subject: [PATCH 1/9] feat: add block_num parameter to check_nullifiers_by_prefix --- crates/proto/src/generated/requests.rs | 3 +++ crates/rpc-proto/proto/requests.proto | 2 ++ crates/store/src/db/mod.rs | 3 ++- crates/store/src/db/sql/mod.rs | 29 +++++++++++++++++++++----- crates/store/src/db/tests.rs | 26 ++++++++++++++++++++++- crates/store/src/server/api.rs | 3 ++- crates/store/src/state.rs | 5 ++++- proto/requests.proto | 2 ++ 8 files changed, 64 insertions(+), 9 deletions(-) diff --git a/crates/proto/src/generated/requests.rs b/crates/proto/src/generated/requests.rs index c8e19bb29..fb09041bc 100644 --- a/crates/proto/src/generated/requests.rs +++ b/crates/proto/src/generated/requests.rs @@ -17,6 +17,9 @@ pub struct CheckNullifiersByPrefixRequest { /// to `prefix_len`. #[prost(uint32, repeated, tag = "2")] pub nullifiers: ::prost::alloc::vec::Vec, + /// Block number from which the nullifiers are requested (exclusive). + #[prost(fixed32, optional, tag = "3")] + pub block_num: ::core::option::Option, } /// Returns a nullifier proof for each of the requested nullifiers. #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/rpc-proto/proto/requests.proto b/crates/rpc-proto/proto/requests.proto index f2323c56c..19b5004ef 100644 --- a/crates/rpc-proto/proto/requests.proto +++ b/crates/rpc-proto/proto/requests.proto @@ -18,6 +18,8 @@ message CheckNullifiersByPrefixRequest { // List of nullifiers to check. Each nullifier is specified by its prefix with length equal // to `prefix_len`. repeated uint32 nullifiers = 2; + // Block number from which the nullifiers are requested (exclusive). + optional fixed32 block_num = 3; } // Returns a nullifier proof for each of the requested nullifiers. diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 51a678e22..3a6d240a2 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -215,12 +215,13 @@ impl Db { &self, prefix_len: u32, nullifier_prefixes: Vec, + block_num: Option, ) -> Result> { self.pool .get() .await? .interact(move |conn| { - sql::select_nullifiers_by_prefix(conn, prefix_len, &nullifier_prefixes) + sql::select_nullifiers_by_prefix(conn, prefix_len, &nullifier_prefixes, block_num) }) .await .map_err(|err| { diff --git a/crates/store/src/db/sql/mod.rs b/crates/store/src/db/sql/mod.rs index 7ade42670..f5d1182f0 100644 --- a/crates/store/src/db/sql/mod.rs +++ b/crates/store/src/db/sql/mod.rs @@ -680,14 +680,33 @@ pub fn select_nullifiers_by_prefix( conn: &mut Connection, prefix_len: u32, nullifier_prefixes: &[u32], + block_num: Option, ) -> Result> { assert_eq!(prefix_len, 16, "Only 16-bit prefixes are supported"); let nullifier_prefixes: Vec = nullifier_prefixes.iter().copied().map(Into::into).collect(); - let mut stmt = conn.prepare_cached( - " + let mut stmt; + let mut rows = if let Some(block_number) = block_num { + stmt = conn.prepare_cached( + " + SELECT + nullifier, + block_num + FROM + nullifiers + WHERE + nullifier_prefix IN rarray(?1) AND + block_num > ?2 + ORDER BY + block_num ASC + ", + )?; + stmt.query(params![Rc::new(nullifier_prefixes), block_number.as_u32()])? + } else { + stmt = conn.prepare_cached( + " SELECT nullifier, block_num @@ -698,9 +717,9 @@ pub fn select_nullifiers_by_prefix( ORDER BY block_num ASC ", - )?; - - let mut rows = stmt.query(params![Rc::new(nullifier_prefixes)])?; + )?; + stmt.query(params![Rc::new(nullifier_prefixes)])? + }; let mut result = Vec::new(); while let Some(row) = rows.next()? { diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index fe8d1684c..e313f459e 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -619,7 +619,7 @@ fn select_nullifiers_by_prefix() { const PREFIX_LEN: u32 = 16; let mut conn = create_db(); // test empty table - let nullifiers = sql::select_nullifiers_by_prefix(&mut conn, PREFIX_LEN, &[]).unwrap(); + let nullifiers = sql::select_nullifiers_by_prefix(&mut conn, PREFIX_LEN, &[], None).unwrap(); assert!(nullifiers.is_empty()); // test single item @@ -635,6 +635,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier1)], + None, ) .unwrap(); assert_eq!( @@ -662,6 +663,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier1)], + None, ) .unwrap(); assert_eq!( @@ -675,6 +677,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier2)], + None, ) .unwrap(); assert_eq!( @@ -693,6 +696,7 @@ fn select_nullifiers_by_prefix() { sql::utils::get_nullifier_prefix(&nullifier1), sql::utils::get_nullifier_prefix(&nullifier2), ], + None, ) .unwrap(); assert_eq!( @@ -714,9 +718,29 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&num_to_nullifier(3 << 48))], + None, ) .unwrap(); assert!(nullifiers.is_empty()); + + // If a block number is provided, only matching nullifiers created after that block are returned + let nullifiers = sql::select_nullifiers_by_prefix( + &mut conn, + PREFIX_LEN, + &[ + sql::utils::get_nullifier_prefix(&nullifier1), + sql::utils::get_nullifier_prefix(&nullifier2), + ], + Some(block_number1), + ) + .unwrap(); + assert_eq!( + nullifiers, + vec![NullifierInfo { + nullifier: nullifier2, + block_num: block_number2 + }] + ); } #[test] diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index 24cde86a5..24db0a164 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -127,9 +127,10 @@ impl api_server::Api for StoreApi { return Err(Status::invalid_argument("Only 16-bit prefixes are supported")); } + let block_num = request.block_num.map(BlockNumber::from); let nullifiers = self .state - .check_nullifiers_by_prefix(request.prefix_len, request.nullifiers) + .check_nullifiers_by_prefix(request.prefix_len, request.nullifiers, block_num) .await? .into_iter() .map(|nullifier_info| NullifierUpdate { diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 8b8bcf695..c52de7cc5 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -411,8 +411,11 @@ impl State { &self, prefix_len: u32, nullifier_prefixes: Vec, + block_num: Option, ) -> Result, DatabaseError> { - self.db.select_nullifiers_by_prefix(prefix_len, nullifier_prefixes).await + self.db + .select_nullifiers_by_prefix(prefix_len, nullifier_prefixes, block_num) + .await } /// Generates membership proofs for each one of the `nullifiers` against the latest nullifier diff --git a/proto/requests.proto b/proto/requests.proto index f2323c56c..19b5004ef 100644 --- a/proto/requests.proto +++ b/proto/requests.proto @@ -18,6 +18,8 @@ message CheckNullifiersByPrefixRequest { // List of nullifiers to check. Each nullifier is specified by its prefix with length equal // to `prefix_len`. repeated uint32 nullifiers = 2; + // Block number from which the nullifiers are requested (exclusive). + optional fixed32 block_num = 3; } // Returns a nullifier proof for each of the requested nullifiers. From 261232ae56ea4ca4aeb145881a58b7a2d0d8c8f9 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Wed, 19 Feb 2025 14:50:21 -0300 Subject: [PATCH 2/9] chore: update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a3e87b2a..4c0391bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [BREAKING] Updated minimum Rust version to 1.84. - [BREAKING] `Endpoint` configuration simplified to a single string (#654). +- Added `block_num` parameter to `CheckNullifiersByPrefix` endpoint. ### Enhancements From 7ac13cd80838c29632db8e405544384de2fe276f Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Wed, 19 Feb 2025 15:27:15 -0300 Subject: [PATCH 3/9] review: add `block_num` parameter as required --- crates/proto/src/generated/requests.rs | 6 ++--- crates/rpc-proto/proto/requests.proto | 4 ++-- crates/store/src/db/mod.rs | 2 +- crates/store/src/db/sql/mod.rs | 33 +++++++------------------- crates/store/src/db/tests.rs | 19 ++++++++------- crates/store/src/server/api.rs | 7 ++++-- crates/store/src/state.rs | 2 +- proto/requests.proto | 4 ++-- 8 files changed, 34 insertions(+), 43 deletions(-) diff --git a/crates/proto/src/generated/requests.rs b/crates/proto/src/generated/requests.rs index fb09041bc..dc1bd3e7b 100644 --- a/crates/proto/src/generated/requests.rs +++ b/crates/proto/src/generated/requests.rs @@ -17,9 +17,9 @@ pub struct CheckNullifiersByPrefixRequest { /// to `prefix_len`. #[prost(uint32, repeated, tag = "2")] pub nullifiers: ::prost::alloc::vec::Vec, - /// Block number from which the nullifiers are requested (exclusive). - #[prost(fixed32, optional, tag = "3")] - pub block_num: ::core::option::Option, + /// Block number from which the nullifiers are requested (inclusive). + #[prost(fixed32, tag = "3")] + pub block_num: u32, } /// Returns a nullifier proof for each of the requested nullifiers. #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/rpc-proto/proto/requests.proto b/crates/rpc-proto/proto/requests.proto index 19b5004ef..cd0bb960d 100644 --- a/crates/rpc-proto/proto/requests.proto +++ b/crates/rpc-proto/proto/requests.proto @@ -18,8 +18,8 @@ message CheckNullifiersByPrefixRequest { // List of nullifiers to check. Each nullifier is specified by its prefix with length equal // to `prefix_len`. repeated uint32 nullifiers = 2; - // Block number from which the nullifiers are requested (exclusive). - optional fixed32 block_num = 3; + // Block number from which the nullifiers are requested (inclusive). + fixed32 block_num = 3; } // Returns a nullifier proof for each of the requested nullifiers. diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 3a6d240a2..fc6bbc0ec 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -215,7 +215,7 @@ impl Db { &self, prefix_len: u32, nullifier_prefixes: Vec, - block_num: Option, + block_num: BlockNumber, ) -> Result> { self.pool .get() diff --git a/crates/store/src/db/sql/mod.rs b/crates/store/src/db/sql/mod.rs index f5d1182f0..298e7a6c2 100644 --- a/crates/store/src/db/sql/mod.rs +++ b/crates/store/src/db/sql/mod.rs @@ -672,6 +672,9 @@ pub fn select_nullifiers_by_block_range( /// of the nullifier of interest to the client. This hides the details of the specific /// nullifier being requested. Currently the only supported prefix length is 16 bits. /// +/// Additionally, the nullifiers are filtered by the block number, so only nullifiers created +/// at or after the given block number are returned. +/// /// # Returns /// /// A vector of [`NullifierInfo`] with the nullifiers and the block height at which they were @@ -680,17 +683,15 @@ pub fn select_nullifiers_by_prefix( conn: &mut Connection, prefix_len: u32, nullifier_prefixes: &[u32], - block_num: Option, + block_num: BlockNumber, ) -> Result> { assert_eq!(prefix_len, 16, "Only 16-bit prefixes are supported"); let nullifier_prefixes: Vec = nullifier_prefixes.iter().copied().map(Into::into).collect(); - let mut stmt; - let mut rows = if let Some(block_number) = block_num { - stmt = conn.prepare_cached( - " + let mut stmt = conn.prepare_cached( + " SELECT nullifier, block_num @@ -698,28 +699,12 @@ pub fn select_nullifiers_by_prefix( nullifiers WHERE nullifier_prefix IN rarray(?1) AND - block_num > ?2 + block_num >= ?2 ORDER BY block_num ASC ", - )?; - stmt.query(params![Rc::new(nullifier_prefixes), block_number.as_u32()])? - } else { - stmt = conn.prepare_cached( - " - SELECT - nullifier, - block_num - FROM - nullifiers - WHERE - nullifier_prefix IN rarray(?1) - ORDER BY - block_num ASC - ", - )?; - stmt.query(params![Rc::new(nullifier_prefixes)])? - }; + )?; + let mut rows = stmt.query(params![Rc::new(nullifier_prefixes), block_num.as_u32()])?; let mut result = Vec::new(); while let Some(row) = rows.next()? { diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index e313f459e..4d89e1cf9 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -619,7 +619,9 @@ fn select_nullifiers_by_prefix() { const PREFIX_LEN: u32 = 16; let mut conn = create_db(); // test empty table - let nullifiers = sql::select_nullifiers_by_prefix(&mut conn, PREFIX_LEN, &[], None).unwrap(); + let block_number0 = 0.into(); + let nullifiers = + sql::select_nullifiers_by_prefix(&mut conn, PREFIX_LEN, &[], block_number0).unwrap(); assert!(nullifiers.is_empty()); // test single item @@ -635,7 +637,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier1)], - None, + block_number0, ) .unwrap(); assert_eq!( @@ -663,7 +665,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier1)], - None, + block_number0, ) .unwrap(); assert_eq!( @@ -677,7 +679,7 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&nullifier2)], - None, + block_number0, ) .unwrap(); assert_eq!( @@ -696,7 +698,7 @@ fn select_nullifiers_by_prefix() { sql::utils::get_nullifier_prefix(&nullifier1), sql::utils::get_nullifier_prefix(&nullifier2), ], - None, + block_number0, ) .unwrap(); assert_eq!( @@ -718,12 +720,13 @@ fn select_nullifiers_by_prefix() { &mut conn, PREFIX_LEN, &[sql::utils::get_nullifier_prefix(&num_to_nullifier(3 << 48))], - None, + block_number0, ) .unwrap(); assert!(nullifiers.is_empty()); - // If a block number is provided, only matching nullifiers created after that block are returned + // If a block number is provided, only matching nullifiers created at or after that block are + // returned let nullifiers = sql::select_nullifiers_by_prefix( &mut conn, PREFIX_LEN, @@ -731,7 +734,7 @@ fn select_nullifiers_by_prefix() { sql::utils::get_nullifier_prefix(&nullifier1), sql::utils::get_nullifier_prefix(&nullifier2), ], - Some(block_number1), + block_number2, ) .unwrap(); assert_eq!( diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index aa1b92e9f..a3a000fbb 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -132,10 +132,13 @@ impl api_server::Api for StoreApi { return Err(Status::invalid_argument("Only 16-bit prefixes are supported")); } - let block_num = request.block_num.map(BlockNumber::from); let nullifiers = self .state - .check_nullifiers_by_prefix(request.prefix_len, request.nullifiers, block_num) + .check_nullifiers_by_prefix( + request.prefix_len, + request.nullifiers, + BlockNumber::from(request.block_num), + ) .await? .into_iter() .map(|nullifier_info| NullifierUpdate { diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index c52de7cc5..5df4e94a3 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -411,7 +411,7 @@ impl State { &self, prefix_len: u32, nullifier_prefixes: Vec, - block_num: Option, + block_num: BlockNumber, ) -> Result, DatabaseError> { self.db .select_nullifiers_by_prefix(prefix_len, nullifier_prefixes, block_num) diff --git a/proto/requests.proto b/proto/requests.proto index 19b5004ef..cd0bb960d 100644 --- a/proto/requests.proto +++ b/proto/requests.proto @@ -18,8 +18,8 @@ message CheckNullifiersByPrefixRequest { // List of nullifiers to check. Each nullifier is specified by its prefix with length equal // to `prefix_len`. repeated uint32 nullifiers = 2; - // Block number from which the nullifiers are requested (exclusive). - optional fixed32 block_num = 3; + // Block number from which the nullifiers are requested (inclusive). + fixed32 block_num = 3; } // Returns a nullifier proof for each of the requested nullifiers. From 9acd08921f8376572f4e8b4b64cea8dd684863a3 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 20 Feb 2025 11:27:40 -0300 Subject: [PATCH 4/9] review: update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3136d4f0b..8e53515b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - [BREAKING] Updated minimum Rust version to 1.84. - [BREAKING] `Endpoint` configuration simplified to a single string (#654). -- Added `block_num` parameter to `CheckNullifiersByPrefix` endpoint (#707). +- [BREAKING] `CheckNullifiersByPrefix` now takes a starting block number (#707). - [BREAKING] Changed sync state endpoint to stream the response (#685). ### Enhancements From a3b032ea4939bf53850e5c3a32727984082785c5 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 20 Feb 2025 11:28:04 -0300 Subject: [PATCH 5/9] review: update doc comment --- crates/store/src/db/sql/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/store/src/db/sql/mod.rs b/crates/store/src/db/sql/mod.rs index 298e7a6c2..1a6742f04 100644 --- a/crates/store/src/db/sql/mod.rs +++ b/crates/store/src/db/sql/mod.rs @@ -665,16 +665,12 @@ pub fn select_nullifiers_by_block_range( Ok(result) } -/// Select nullifiers created that match the `nullifier_prefixes` filter using the given -/// [Connection]. +/// Returns nullifiers filtered by prefix and block creation height. /// /// Each value of the `nullifier_prefixes` is only the `prefix_len` most significant bits /// of the nullifier of interest to the client. This hides the details of the specific /// nullifier being requested. Currently the only supported prefix length is 16 bits. /// -/// Additionally, the nullifiers are filtered by the block number, so only nullifiers created -/// at or after the given block number are returned. -/// /// # Returns /// /// A vector of [`NullifierInfo`] with the nullifiers and the block height at which they were From ff4343a6fbe40668aa74f11515f336c2b4661d46 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Wed, 19 Feb 2025 15:49:44 -0300 Subject: [PATCH 6/9] feat: remove nullifiers from `SyncState` endpoint --- crates/proto/src/generated/requests.rs | 6 +- crates/proto/src/generated/responses.rs | 3 - crates/rpc-proto/proto/requests.proto | 6 +- crates/rpc-proto/proto/responses.proto | 3 - crates/store/src/db/mod.rs | 6 +- crates/store/src/db/sql/mod.rs | 57 ----------- crates/store/src/db/tests.rs | 128 ------------------------ crates/store/src/server/api.rs | 17 +--- crates/store/src/state.rs | 8 +- proto/requests.proto | 6 +- proto/responses.proto | 3 - 11 files changed, 6 insertions(+), 237 deletions(-) diff --git a/crates/proto/src/generated/requests.rs b/crates/proto/src/generated/requests.rs index dc1bd3e7b..9194e084f 100644 --- a/crates/proto/src/generated/requests.rs +++ b/crates/proto/src/generated/requests.rs @@ -45,7 +45,7 @@ pub struct GetBlockHeaderByNumberRequest { /// /// Specifies state updates the client is interested in. The server will return the first block which /// contains a note matching `note_tags` or the chain tip. And the corresponding updates to -/// `nullifiers` and `account_ids` for that block range. +/// `account_ids` for that block range. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncStateRequest { /// Last block known by the client. The response will contain data starting from the next block, @@ -63,10 +63,6 @@ pub struct SyncStateRequest { /// Specifies the tags which the client is interested in. #[prost(fixed32, repeated, tag = "3")] pub note_tags: ::prost::alloc::vec::Vec, - /// Determines the nullifiers the client is interested in by specifying the 16high bits of the - /// target nullifier. - #[prost(uint32, repeated, tag = "4")] - pub nullifiers: ::prost::alloc::vec::Vec, } /// Note synchronization request. /// diff --git a/crates/proto/src/generated/responses.rs b/crates/proto/src/generated/responses.rs index 3f9969446..b5de6e749 100644 --- a/crates/proto/src/generated/responses.rs +++ b/crates/proto/src/generated/responses.rs @@ -58,9 +58,6 @@ pub struct SyncStateResponse { /// List of all notes together with the Merkle paths from `response.block_header.note_root`. #[prost(message, repeated, tag = "5")] pub notes: ::prost::alloc::vec::Vec, - /// List of nullifiers created between `request.block_num + 1` and `response.block_header.block_num`. - #[prost(message, repeated, tag = "6")] - pub nullifiers: ::prost::alloc::vec::Vec, } /// Represents the result of syncing notes request. #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/rpc-proto/proto/requests.proto b/crates/rpc-proto/proto/requests.proto index cd0bb960d..bf9fd557a 100644 --- a/crates/rpc-proto/proto/requests.proto +++ b/crates/rpc-proto/proto/requests.proto @@ -43,7 +43,7 @@ message GetBlockHeaderByNumberRequest { // // Specifies state updates the client is interested in. The server will return the first block which // contains a note matching `note_tags` or the chain tip. And the corresponding updates to -// `nullifiers` and `account_ids` for that block range. +// `account_ids` for that block range. message SyncStateRequest { // Last block known by the client. The response will contain data starting from the next block, // until the first block which contains a note of matching the requested tag, or the chain tip @@ -59,10 +59,6 @@ message SyncStateRequest { // Specifies the tags which the client is interested in. repeated fixed32 note_tags = 3; - - // Determines the nullifiers the client is interested in by specifying the 16high bits of the - // target nullifier. - repeated uint32 nullifiers = 4; } // Note synchronization request. diff --git a/crates/rpc-proto/proto/responses.proto b/crates/rpc-proto/proto/responses.proto index e2829a832..e651d9d35 100644 --- a/crates/rpc-proto/proto/responses.proto +++ b/crates/rpc-proto/proto/responses.proto @@ -63,9 +63,6 @@ message SyncStateResponse { // List of all notes together with the Merkle paths from `response.block_header.note_root`. repeated note.NoteSyncRecord notes = 5; - - // List of nullifiers created between `request.block_num + 1` and `response.block_header.block_num`. - repeated NullifierUpdate nullifiers = 6; } // Represents the result of syncing notes request. diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index fc6bbc0ec..a00094f54 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -85,7 +85,6 @@ pub struct StateSyncUpdate { pub block_header: BlockHeader, pub account_updates: Vec, pub transactions: Vec, - pub nullifiers: Vec, } #[derive(Debug, PartialEq)] @@ -328,15 +327,12 @@ impl Db { block_num: BlockNumber, account_ids: Vec, note_tags: Vec, - nullifier_prefixes: Vec, ) -> Result { self.pool .get() .await .map_err(DatabaseError::MissingDbConnection)? - .interact(move |conn| { - sql::get_state_sync(conn, block_num, &account_ids, ¬e_tags, &nullifier_prefixes) - }) + .interact(move |conn| sql::get_state_sync(conn, block_num, &account_ids, ¬e_tags)) .await .map_err(|err| { DatabaseError::InteractError(format!("Get state sync task failed: {err}")) diff --git a/crates/store/src/db/sql/mod.rs b/crates/store/src/db/sql/mod.rs index 1a6742f04..87ff16c3d 100644 --- a/crates/store/src/db/sql/mod.rs +++ b/crates/store/src/db/sql/mod.rs @@ -617,54 +617,6 @@ pub fn select_all_nullifiers(conn: &mut Connection) -> Result Result> { - let nullifier_prefixes: Vec = - nullifier_prefixes.iter().copied().map(Into::into).collect(); - - let mut stmt = conn.prepare_cached( - " - SELECT - nullifier, - block_num - FROM - nullifiers - WHERE - block_num > ?1 AND - block_num <= ?2 AND - nullifier_prefix IN rarray(?3) - ORDER BY - block_num ASC - ", - )?; - - let mut rows = - stmt.query(params![block_start.as_u32(), block_end.as_u32(), Rc::new(nullifier_prefixes)])?; - - let mut result = Vec::new(); - while let Some(row) = rows.next()? { - let nullifier_data = row.get_ref(0)?.as_blob()?; - let nullifier = Nullifier::read_from_bytes(nullifier_data)?; - let block_num: u32 = row.get(1)?; - result.push(NullifierInfo { nullifier, block_num: block_num.into() }); - } - Ok(result) -} - /// Returns nullifiers filtered by prefix and block creation height. /// /// Each value of the `nullifier_prefixes` is only the `prefix_len` most significant bits @@ -1195,7 +1147,6 @@ pub fn get_state_sync( block_num: BlockNumber, account_ids: &[AccountId], note_tag_prefixes: &[u32], - nullifier_prefixes: &[u32], ) -> Result { let notes = select_notes_since_block_by_tag_and_sender( conn, @@ -1218,19 +1169,11 @@ pub fn get_state_sync( account_ids, )?; - let nullifiers = select_nullifiers_by_block_range( - conn, - block_num, - block_header.block_num(), - nullifier_prefixes, - )?; - Ok(StateSyncUpdate { notes, block_header, account_updates, transactions, - nullifiers, }) } diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index 4d89e1cf9..768d6b2ff 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -486,134 +486,6 @@ fn sql_public_account_details() { assert_eq!(read_delta, Some(delta2)); } -#[test] -fn sql_select_nullifiers_by_block_range() { - let mut conn = create_db(); - - // test empty table - let nullifiers = - sql::select_nullifiers_by_block_range(&mut conn, 0.into(), u32::MAX.into(), &[]).unwrap(); - assert!(nullifiers.is_empty()); - - // test single item - let nullifier1 = num_to_nullifier(1 << 48); - let block_number1 = 1.into(); - create_block(&mut conn, block_number1); - - let transaction = conn.transaction().unwrap(); - sql::insert_nullifiers_for_block(&transaction, &[nullifier1], block_number1).unwrap(); - transaction.commit().unwrap(); - - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 0.into(), - u32::MAX.into(), - &[sql::utils::get_nullifier_prefix(&nullifier1)], - ) - .unwrap(); - assert_eq!( - nullifiers, - vec![NullifierInfo { - nullifier: nullifier1, - block_num: block_number1 - }] - ); - - // test two elements - let nullifier2 = num_to_nullifier(2 << 48); - let block_number2 = 2.into(); - create_block(&mut conn, block_number2); - - let transaction = conn.transaction().unwrap(); - sql::insert_nullifiers_for_block(&transaction, &[nullifier2], block_number2).unwrap(); - transaction.commit().unwrap(); - - let nullifiers = sql::select_all_nullifiers(&mut conn).unwrap(); - assert_eq!(nullifiers, vec![(nullifier1, block_number1), (nullifier2, block_number2)]); - - // only the nullifiers matching the prefix are included - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 0.into(), - u32::MAX.into(), - &[sql::utils::get_nullifier_prefix(&nullifier1)], - ) - .unwrap(); - assert_eq!( - nullifiers, - vec![NullifierInfo { - nullifier: nullifier1, - block_num: block_number1 - }] - ); - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 0.into(), - u32::MAX.into(), - &[sql::utils::get_nullifier_prefix(&nullifier2)], - ) - .unwrap(); - assert_eq!( - nullifiers, - vec![NullifierInfo { - nullifier: nullifier2, - block_num: block_number2 - }] - ); - - // Nullifiers created at block_end are included - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 0.into(), - 1.into(), - &[ - sql::utils::get_nullifier_prefix(&nullifier1), - sql::utils::get_nullifier_prefix(&nullifier2), - ], - ) - .unwrap(); - assert_eq!( - nullifiers, - vec![NullifierInfo { - nullifier: nullifier1, - block_num: block_number1 - }] - ); - - // Nullifiers created at block_start are not included - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 1.into(), - u32::MAX.into(), - &[ - sql::utils::get_nullifier_prefix(&nullifier1), - sql::utils::get_nullifier_prefix(&nullifier2), - ], - ) - .unwrap(); - assert_eq!( - nullifiers, - vec![NullifierInfo { - nullifier: nullifier2, - block_num: block_number2 - }] - ); - - // When block start and end are the same, no nullifiers should be returned. This case happens - // when the client requests a sync update, and it is already tracking the chain tip. - let nullifiers = sql::select_nullifiers_by_block_range( - &mut conn, - 2.into(), - 2.into(), - &[ - sql::utils::get_nullifier_prefix(&nullifier1), - sql::utils::get_nullifier_prefix(&nullifier2), - ], - ) - .unwrap(); - assert!(nullifiers.is_empty()); -} - #[test] fn select_nullifiers_by_prefix() { const PREFIX_LEN: u32 = 16; diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index a3a000fbb..3a10af9de 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -180,12 +180,7 @@ impl api_server::Api for StoreApi { let account_ids: Vec = read_account_ids(&request.account_ids)?; let (state, delta) = state - .sync_state( - last_block_num.into(), - account_ids, - request.note_tags.clone(), - request.nullifiers.clone(), - ) + .sync_state(last_block_num.into(), account_ids, request.note_tags.clone()) .await .map_err(internal_error)?; @@ -211,22 +206,12 @@ impl api_server::Api for StoreApi { let notes = state.notes.into_iter().map(Into::into).collect(); - let nullifiers = state - .nullifiers - .into_iter() - .map(|nullifier_info| NullifierUpdate { - nullifier: Some(nullifier_info.nullifier.into()), - block_num: nullifier_info.block_num.as_u32(), - }) - .collect(); - let response = SyncStateResponse { block_header: Some(state.block_header.into()), mmr_delta: Some(delta.into()), accounts, transactions, notes, - nullifiers, }; Ok(response) } diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 5df4e94a3..e3f95d34a 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -646,22 +646,16 @@ impl State { /// range. /// - `note_tags`: The tags the client is interested in, result is restricted to the first block /// with any matches tags. - /// - `nullifier_prefixes`: Only the 16 high bits of the nullifiers the client is interested in, - /// results will include nullifiers matching prefixes produced in the given block range. #[instrument(target = COMPONENT, skip_all, ret(level = "debug"), err)] pub async fn sync_state( &self, block_num: BlockNumber, account_ids: Vec, note_tags: Vec, - nullifier_prefixes: Vec, ) -> Result<(StateSyncUpdate, MmrDelta), StateSyncError> { let inner = self.inner.read().await; - let state_sync = self - .db - .get_state_sync(block_num, account_ids, note_tags, nullifier_prefixes) - .await?; + let state_sync = self.db.get_state_sync(block_num, account_ids, note_tags).await?; let delta = if block_num == state_sync.block_header.block_num() { // The client is in sync with the chain tip. diff --git a/proto/requests.proto b/proto/requests.proto index cd0bb960d..bf9fd557a 100644 --- a/proto/requests.proto +++ b/proto/requests.proto @@ -43,7 +43,7 @@ message GetBlockHeaderByNumberRequest { // // Specifies state updates the client is interested in. The server will return the first block which // contains a note matching `note_tags` or the chain tip. And the corresponding updates to -// `nullifiers` and `account_ids` for that block range. +// `account_ids` for that block range. message SyncStateRequest { // Last block known by the client. The response will contain data starting from the next block, // until the first block which contains a note of matching the requested tag, or the chain tip @@ -59,10 +59,6 @@ message SyncStateRequest { // Specifies the tags which the client is interested in. repeated fixed32 note_tags = 3; - - // Determines the nullifiers the client is interested in by specifying the 16high bits of the - // target nullifier. - repeated uint32 nullifiers = 4; } // Note synchronization request. diff --git a/proto/responses.proto b/proto/responses.proto index e2829a832..e651d9d35 100644 --- a/proto/responses.proto +++ b/proto/responses.proto @@ -63,9 +63,6 @@ message SyncStateResponse { // List of all notes together with the Merkle paths from `response.block_header.note_root`. repeated note.NoteSyncRecord notes = 5; - - // List of nullifiers created between `request.block_num + 1` and `response.block_header.block_num`. - repeated NullifierUpdate nullifiers = 6; } // Represents the result of syncing notes request. From f96401798707911aa45b6a93a3a5168ef77346b4 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Wed, 19 Feb 2025 15:58:59 -0300 Subject: [PATCH 7/9] chore: update doc comments --- crates/proto/src/generated/rpc.rs | 16 ++++++++-------- crates/rpc-proto/proto/rpc.proto | 8 ++++---- proto/rpc.proto | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/proto/src/generated/rpc.rs b/crates/proto/src/generated/rpc.rs index de205e75b..74767ee1f 100644 --- a/crates/proto/src/generated/rpc.rs +++ b/crates/proto/src/generated/rpc.rs @@ -353,18 +353,18 @@ pub mod api_client { self.inner.unary(req, path, codec).await } /// Returns info which can be used by the client to sync up to the latest state of the chain - /// for the objects (accounts, notes, nullifiers) the client is interested in. + /// for the objects (accounts and notes) the client is interested in. /// /// This request returns a stream where multiple update responses will be pushed in order. /// Client is expected to read the updates from the stream and apply them, and then it will be /// fully synchronized with the chain. /// - /// Each update response also contains info about new notes, nullifiers etc. created. It also returns + /// Each update response also contains info about new notes, accounts etc. created. It also returns /// Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain /// MMR peaks and chain MMR nodes. /// - /// For preserving some degree of privacy, note tags and nullifiers filters contain only high - /// part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + /// For preserving some degree of privacy, note tags contain only high + /// part of hashes. Thus, returned data contains excessive notes, client can make /// additional filtering of that data on its side. pub async fn sync_state( &mut self, @@ -512,18 +512,18 @@ pub mod api_server { + std::marker::Send + 'static; /// Returns info which can be used by the client to sync up to the latest state of the chain - /// for the objects (accounts, notes, nullifiers) the client is interested in. + /// for the objects (accounts and notes) the client is interested in. /// /// This request returns a stream where multiple update responses will be pushed in order. /// Client is expected to read the updates from the stream and apply them, and then it will be /// fully synchronized with the chain. /// - /// Each update response also contains info about new notes, nullifiers etc. created. It also returns + /// Each update response also contains info about new notes, accounts etc. created. It also returns /// Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain /// MMR peaks and chain MMR nodes. /// - /// For preserving some degree of privacy, note tags and nullifiers filters contain only high - /// part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + /// For preserving some degree of privacy, note tags contain only high + /// part of hashes. Thus, returned data contains excessive notes, client can make /// additional filtering of that data on its side. async fn sync_state( &self, diff --git a/crates/rpc-proto/proto/rpc.proto b/crates/rpc-proto/proto/rpc.proto index ccce57d23..247c51ede 100644 --- a/crates/rpc-proto/proto/rpc.proto +++ b/crates/rpc-proto/proto/rpc.proto @@ -49,18 +49,18 @@ service Api { rpc SyncNotes(requests.SyncNoteRequest) returns (responses.SyncNoteResponse) {} // Returns info which can be used by the client to sync up to the latest state of the chain - // for the objects (accounts, notes, nullifiers) the client is interested in. + // for the objects (accounts and notes) the client is interested in. // // This request returns a stream where multiple update responses will be pushed in order. // Client is expected to read the updates from the stream and apply them, and then it will be // fully synchronized with the chain. // - // Each update response also contains info about new notes, nullifiers etc. created. It also returns + // Each update response also contains info about new notes, accounts etc. created. It also returns // Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain // MMR peaks and chain MMR nodes. // - // For preserving some degree of privacy, note tags and nullifiers filters contain only high - // part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + // For preserving some degree of privacy, note tags contain only high + // part of hashes. Thus, returned data contains excessive notes, client can make // additional filtering of that data on its side. rpc SyncState(requests.SyncStateRequest) returns (stream responses.SyncStateResponse) {} } diff --git a/proto/rpc.proto b/proto/rpc.proto index ccce57d23..247c51ede 100644 --- a/proto/rpc.proto +++ b/proto/rpc.proto @@ -49,18 +49,18 @@ service Api { rpc SyncNotes(requests.SyncNoteRequest) returns (responses.SyncNoteResponse) {} // Returns info which can be used by the client to sync up to the latest state of the chain - // for the objects (accounts, notes, nullifiers) the client is interested in. + // for the objects (accounts and notes) the client is interested in. // // This request returns a stream where multiple update responses will be pushed in order. // Client is expected to read the updates from the stream and apply them, and then it will be // fully synchronized with the chain. // - // Each update response also contains info about new notes, nullifiers etc. created. It also returns + // Each update response also contains info about new notes, accounts etc. created. It also returns // Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain // MMR peaks and chain MMR nodes. // - // For preserving some degree of privacy, note tags and nullifiers filters contain only high - // part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + // For preserving some degree of privacy, note tags contain only high + // part of hashes. Thus, returned data contains excessive notes, client can make // additional filtering of that data on its side. rpc SyncState(requests.SyncStateRequest) returns (stream responses.SyncStateResponse) {} } From 7953aab81a6f0209e705ea2846f9995aa518c9a0 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 20 Feb 2025 11:32:09 -0300 Subject: [PATCH 8/9] chore: update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e53515b8..8c6045682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - [BREAKING] Updated minimum Rust version to 1.84. - [BREAKING] `Endpoint` configuration simplified to a single string (#654). - [BREAKING] `CheckNullifiersByPrefix` now takes a starting block number (#707). +- [BREAKING] Removed nullifiers from `SyncState` endpoint (#708). - [BREAKING] Changed sync state endpoint to stream the response (#685). ### Enhancements From 73759ef85f22ad637a0f1ffb2adc9be6c49845e0 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 20 Feb 2025 15:52:23 -0300 Subject: [PATCH 9/9] chore: update rpc README --- crates/rpc/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/rpc/README.md b/crates/rpc/README.md index e8e7e6492..d82326139 100644 --- a/crates/rpc/README.md +++ b/crates/rpc/README.md @@ -102,18 +102,17 @@ the chain. ### SyncState -Returns info which can be used by the client to sync up to the latest state of the chain for the objects (accounts, -notes, nullifiers) the client is interested in. +Returns info which can be used by the client to sync up to the latest state of the chain for the objects (accounts +and notes) the client is interested in. -This request returns the next block containing requested data. It also returns `chain_tip` which is the latest block -number in the chain. Client is expected to repeat these requests in a loop until -`response.block_header.block_num == response.chain_tip`, at which point the client is fully synchronized with the chain. +This request returns a stream where multiple update responses will be pushed in order. Client is expected to read +the updates from the stream and apply them, and then it will be fully synchronized with the chain. -Each request also returns info about new notes, nullifiers etc. created. It also returns Chain MMR delta that can be -used to update the state of Chain MMR. This includes both chain MMR peaks and chain MMR nodes. +Each update response also contains info about new notes, accounts etc. created. It also returns Chain MMR delta +that can be used to update the state of Chain MMR. This includes both chain MMR peaks and chain MMR nodes. -For preserving some degree of privacy, note tags and nullifiers filters contain only high part of hashes. Thus, returned -data contains excessive notes and nullifiers, client can make additional filtering of that data on its side. +For preserving some degree of privacy, note tags contain only high part of hashes. Thus, returned data contains +excessive notes, client can make additional filtering of that data on its side. ---