Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(grpc): get_results #8255

Merged
merged 3 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion zebra-grpc/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Compile proto files

fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/scanner.proto")?;
tonic_build::configure()
.btree_map(["."])
.compile(&["proto/scanner.proto"], &[""])?;
arya2 marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}
29 changes: 28 additions & 1 deletion zebra-grpc/proto/scanner.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ service Scanner {
// Deletes a set of keys and their results from the scanner.
// This request stop the scanner from scanning blocks for the these keys.
rpc DeleteKeys(DeleteKeysRequest) returns (Empty);

// Get all data we have stored for the given keys.
rpc GetResults(GetResultsRequest) returns (GetResultsResponse);
}

// A response to a GetInfo call.
Expand All @@ -34,4 +37,28 @@ message ClearResultsRequest {
message DeleteKeysRequest {
// Keys to delete from scanner.
repeated string keys = 1;
}
}

// A request for getting results for a set of keys.
message GetResultsRequest {
// Keys for which to get results.
repeated string keys = 1;
}

// A set of responses for each provided key of a GetResults call.
message GetResultsResponse {
// Results for each key.
map<string, Results> results = 1;
}

// A result for a single key.
message Results {
// A height, transaction id map
map<uint32, TransactionHash> transactions = 1;
}

// A vector of transaction hashes
message TransactionHash {
// A transaction id hash
repeated string hash = 1;
}
56 changes: 54 additions & 2 deletions zebra-grpc/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The gRPC server implementation

use std::net::SocketAddr;
use std::{collections::BTreeMap, net::SocketAddr};

use futures_util::future::TryFutureExt;
use tonic::{transport::Server, Response, Status};
Expand All @@ -12,7 +12,8 @@ use zebra_node_services::scan_service::{

use crate::scanner::{
scanner_server::{Scanner, ScannerServer},
ClearResultsRequest, DeleteKeysRequest, Empty, InfoReply,
ClearResultsRequest, DeleteKeysRequest, Empty, GetResultsRequest, GetResultsResponse,
InfoReply, Results, TransactionHash,
};

type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
Expand Down Expand Up @@ -120,6 +121,57 @@ where

Ok(Response::new(Empty {}))
}

async fn get_results(
&self,
request: tonic::Request<GetResultsRequest>,
) -> Result<Response<GetResultsResponse>, Status> {
let keys = request.into_inner().keys;

if keys.is_empty() {
let msg = "must provide at least 1 key to get results";
return Err(Status::invalid_argument(msg));
}

let ScanServiceResponse::Results(response) = self
.scan_service
.clone()
.ready()
.and_then(|service| service.call(ScanServiceRequest::Results(keys.clone())))
.await
.map_err(|err| Status::unknown(format!("scan service returned error: {err}")))?
else {
return Err(Status::unknown(
"scan service returned an unexpected response",
));
};

// If there are no results for a key, we still want to return it with empty results.
let empty_map = BTreeMap::new();

let results = keys
.into_iter()
.map(|key| {
let values = response.get(&key).unwrap_or(&empty_map);

// Skip heights with no transactions, they are scanner markers and should not be returned.
let transactions = Results {
transactions: values
.iter()
.filter(|(_, transactions)| !transactions.is_empty())
.map(|(height, transactions)| {
let txs = transactions.iter().map(ToString::to_string).collect();
(height.0, TransactionHash { hash: txs })
})
.collect(),
};

(key, transactions)
})
.collect::<BTreeMap<_, _>>();

Ok(Response::new(GetResultsResponse { results }))
}
}

/// Initializes the zebra-scan gRPC server
Expand Down
Loading