Skip to content

Commit

Permalink
Add /debug/stale-keys endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
slowli committed Oct 24, 2024
1 parent 5f3904f commit 0b9f3ad
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 0 deletions.
7 changes: 7 additions & 0 deletions core/lib/merkle_tree/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
ValueHash, TREE_DEPTH,
},
BlockOutput, HashTree, MerkleTree, MerkleTreePruner, MerkleTreePrunerHandle, NoVersionError,
PruneDatabase,
};

impl TreeInstruction<StorageKey> {
Expand Down Expand Up @@ -460,6 +461,12 @@ impl ZkSyncTreeReader {
.collect()
}

/// Returns raw stale keys obsoleted in the specified version of the tree.
pub fn raw_stale_keys(&self, l1_batch_number: L1BatchNumber) -> Vec<NodeKey> {
let version = u64::from(l1_batch_number.0);
self.0.db.stale_keys(version)
}

/// Verifies consistency of the tree at the specified L1 batch number.
///
/// # Errors
Expand Down
1 change: 1 addition & 0 deletions core/node/metadata_calculator/src/api_server/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(super) enum MerkleTreeApiMethod {
Info,
GetProofs,
GetNodes,
GetStaleKeys,
}

/// Metrics for Merkle tree API.
Expand Down
25 changes: 25 additions & 0 deletions core/node/metadata_calculator/src/api_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ struct TreeNodesResponse {
nodes: HashMap<HexNodeKey, ApiRawNode>,
}

#[derive(Debug, Deserialize)]
struct StaleKeysRequest {
l1_batch_number: L1BatchNumber,
}

#[derive(Debug, Serialize)]
struct StaleKeysResponse {
stale_keys: Vec<HexNodeKey>,
}

/// Server-side tree API error.
#[derive(Debug)]
enum TreeApiServerError {
Expand Down Expand Up @@ -466,6 +476,17 @@ impl AsyncTreeReader {
Json(response)
}

async fn get_stale_keys_handler(
State(this): State<Self>,
Json(request): Json<StaleKeysRequest>,
) -> Json<StaleKeysResponse> {
let latency = API_METRICS.latency[&MerkleTreeApiMethod::GetStaleKeys].start();
let stale_keys = this.clone().raw_stale_keys(request.l1_batch_number).await;
let stale_keys = stale_keys.into_iter().map(HexNodeKey).collect();
latency.observe();
Json(StaleKeysResponse { stale_keys })
}

async fn create_api_server(
self,
bind_address: &SocketAddr,
Expand All @@ -477,6 +498,10 @@ impl AsyncTreeReader {
.route("/", routing::get(Self::info_handler))
.route("/proofs", routing::post(Self::get_proofs_handler))
.route("/debug/nodes", routing::post(Self::get_nodes_handler))
.route(
"/debug/stale-keys",
routing::post(Self::get_stale_keys_handler),
)
.with_state(self);

let listener = tokio::net::TcpListener::bind(bind_address)
Expand Down
22 changes: 22 additions & 0 deletions core/node/metadata_calculator/src/api_server/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ async fn merkle_tree_api() {
let raw_nodes_response: serde_json::Value = raw_nodes_response.json().await.unwrap();
assert_raw_nodes_response(&raw_nodes_response);

let raw_stale_keys_response = api_client
.inner
.post(format!("http://{local_addr}/debug/stale-keys"))
.json(&serde_json::json!({ "l1_batch_number": 1 }))
.send()
.await
.unwrap()
.error_for_status()
.unwrap();
let raw_stale_keys_response: serde_json::Value = raw_stale_keys_response.json().await.unwrap();
assert_raw_stale_keys_response(&raw_stale_keys_response);

// Stop the calculator and the tree API server.
stop_sender.send_replace(true);
api_server_task.await.unwrap().unwrap();
Expand Down Expand Up @@ -111,6 +123,16 @@ fn assert_raw_nodes_response(response: &serde_json::Value) {
);
}

fn assert_raw_stale_keys_response(response: &serde_json::Value) {
let response = response.as_object().expect("not an object");
let stale_keys = response["stale_keys"].as_array().expect("not an array");
assert!(!stale_keys.is_empty()); // At least the root is always obsoleted
for stale_key in stale_keys {
let stale_key = stale_key.as_str().expect("not a string");
stale_key.parse::<NodeKey>().unwrap();
}
}

#[tokio::test]
async fn api_client_connection_error() {
// Use an address that will definitely fail on a timeout.
Expand Down
6 changes: 6 additions & 0 deletions core/node/metadata_calculator/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ impl AsyncTreeReader {
.await
.unwrap()
}

pub(crate) async fn raw_stale_keys(self, l1_batch_number: L1BatchNumber) -> Vec<NodeKey> {
tokio::task::spawn_blocking(move || self.inner.raw_stale_keys(l1_batch_number))
.await
.unwrap()
}
}

/// Version of async tree reader that holds a weak reference to RocksDB. Used in [`MerkleTreeHealthCheck`].
Expand Down

0 comments on commit 0b9f3ad

Please sign in to comment.