Initial code for tracing + oltp (#19)

* Initial code for tracing + oltp
* Add tracing for all grpc endpoints + proof services + epoch service
* Trace result and errors for grpc endpoints
This commit is contained in:
Sydhds
2025-07-16 11:33:43 +02:00
committed by GitHub
parent 9cdac9c184
commit 1e813c6710
12 changed files with 455 additions and 58 deletions

209
Cargo.lock generated
View File

@@ -1493,6 +1493,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
version = "0.4.41"
@@ -2326,8 +2332,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -2337,9 +2345,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasm-bindgen",
]
[[package]]
@@ -3021,6 +3031,12 @@ dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "lz4-sys"
version = "1.11.1+lz4-1.10.0"
@@ -3371,6 +3387,82 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "opentelemetry"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 2.0.12",
"tracing",
]
[[package]]
name = "opentelemetry-http"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d"
dependencies = [
"async-trait",
"bytes",
"http",
"opentelemetry",
"reqwest",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b"
dependencies = [
"http",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost",
"reqwest",
"thiserror 2.0.12",
"tokio",
"tonic",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
"prost",
"tonic",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"opentelemetry",
"percent-encoding",
"rand 0.9.1",
"serde_json",
"thiserror 2.0.12",
"tokio",
"tokio-stream",
]
[[package]]
name = "ordered-float"
version = "4.6.0"
@@ -3796,10 +3888,14 @@ name = "prover_cli"
version = "0.1.0"
dependencies = [
"clap",
"opentelemetry",
"opentelemetry-otlp",
"opentelemetry_sdk",
"prover",
"tokio",
"toml",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber 0.3.19",
"tracing-test",
]
@@ -3809,7 +3905,9 @@ name = "prover_client"
version = "0.1.0"
dependencies = [
"alloy",
"clap",
"prost",
"sha2",
"tokio",
"tonic",
"tonic-build",
@@ -3836,6 +3934,61 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quinn"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash 2.1.1",
"rustls",
"socket2",
"thiserror 2.0.12",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
dependencies = [
"bytes",
"getrandom 0.3.2",
"lru-slab",
"rand 0.9.1",
"ring",
"rustc-hash 2.1.1",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "quote"
version = "1.0.40"
@@ -4046,12 +4199,14 @@ checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [
"base64",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"ipnet",
@@ -4062,13 +4217,18 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tower",
"tower-service",
"url",
@@ -4272,6 +4432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -4306,6 +4467,9 @@ name = "rustls-pki-types"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
dependencies = [
"web-time",
]
[[package]]
name = "rustls-webpki"
@@ -4948,6 +5112,21 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.46.0"
@@ -5093,8 +5272,10 @@ dependencies = [
"percent-encoding",
"pin-project",
"prost",
"rustls-native-certs",
"socket2",
"tokio",
"tokio-rustls",
"tokio-stream",
"tower",
"tower-layer",
@@ -5247,6 +5428,24 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-opentelemetry"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddcf5959f39507d0d04d6413119c04f33b623f4f951ebcbdddddfad2d0623a9c"
dependencies = [
"js-sys",
"once_cell",
"opentelemetry",
"opentelemetry_sdk",
"smallvec",
"tracing",
"tracing-core",
"tracing-log",
"tracing-subscriber 0.3.19",
"web-time",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.25"
@@ -5569,6 +5768,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"

View File

@@ -3,17 +3,24 @@
## Docker
* docker build --progress=plain --no-cache -t prover .
* sudo docker run -p 50051:50051 prover --mock-sc true --mock-user mock/mock_user_1.json
* docker run -p 50051:50051 prover --mock-sc true --mock-user mock/mock_user_1.json
## Run prover
RUST_LOG=debug cargo run -p prover_cli -- -i 127.0.0.1 -r "wss://eth-mainnet.g.alchemy.com/v2/__MY_TOKEN__"
## Run prover + Mock
### Run prover + Mock
RUST_LOG=debug cargo run -p prover_cli -- -i 127.0.0.1 --metrics-ip 127.0.0.1 --mock-sc true --mock-user mock/mock_user_1.json
RUST_LOG=debug cargo run -p prover_cli -- -i 127.0.0.1 --metrics-ip 127.0.0.1 --mock-sc true --mock-user mock/mock_user_1.json
## Run prover client (for tests)
### Run prover + opentelemetry
* Run jaeger (locally, port 16686 -> Web ui, port 4317 -> otlp/grpc, port 4318 -> otlp/http)
* docker run -d --name jaeger -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest
* Run prover:
* OTEL_EXPORTER_OTLP_PROTOCOL=grpc RUST_LOG=debug cargo run -p prover_cli -- -i 127.0.0.1 --metrics-ip 127.0.0.1 --mock-sc true --mock-user mock/mock_user_1.json
### Run prover client (for tests)
RUST_LOG=debug cargo run -p prover_client

View File

@@ -10,7 +10,9 @@ use tokio::sync::Notify;
use tracing::{debug, error};
// internal
use crate::error::AppError;
use crate::metrics::{EPOCH_SERVICE_CURRENT_EPOCH, EPOCH_SERVICE_CURRENT_EPOCH_SLICE, EPOCH_SERVICE_DRIFT_MILLIS};
use crate::metrics::{
EPOCH_SERVICE_CURRENT_EPOCH, EPOCH_SERVICE_CURRENT_EPOCH_SLICE, EPOCH_SERVICE_DRIFT_MILLIS,
};
/// Duration of an epoch (1 day)
const EPOCH_DURATION: Duration = Duration::from_secs(TimeDelta::days(1).num_seconds() as u64);
@@ -39,6 +41,9 @@ pub struct EpochService {
}
impl EpochService {
// Note: listen_for_new_epoch never ends so no log will happen with #[instrument]
// + metrics already tracks the current epoch / epoch_slice
// #[instrument(skip(self), fields(self.epoch_slice_duration, self.genesis, self.current_epoch))]
pub(crate) async fn listen_for_new_epoch(&self) -> Result<(), AppError> {
let epoch_slice_count =
Self::compute_epoch_slice_count(EPOCH_DURATION, self.epoch_slice_duration);
@@ -91,7 +96,8 @@ impl EpochService {
{
let now_ = tokio::time::Instant::now();
debug!("awake at: {:?}, drift by: {:?}", now_, now_ - wait_until);
histogram!(EPOCH_SERVICE_DRIFT_MILLIS.name, "prover" => "epoch service").record(now_ - wait_until);
histogram!(EPOCH_SERVICE_DRIFT_MILLIS.name, "prover" => "epoch service")
.record(now_ - wait_until);
}
// Note: could use checked_add() here, but it's quite impossible to have an overflow here
@@ -112,8 +118,10 @@ impl EpochService {
// Note: based on this link https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
// "Casting from an integer to float will produce the closest possible float *"
gauge!(EPOCH_SERVICE_CURRENT_EPOCH.name, "prover" => "epoch service").set(i64::from(current_epoch) as f64);
gauge!(EPOCH_SERVICE_CURRENT_EPOCH_SLICE.name, "prover" => "epoch service").set(i64::from(current_epoch_slice) as f64);
gauge!(EPOCH_SERVICE_CURRENT_EPOCH.name, "prover" => "epoch service")
.set(current_epoch as f64);
gauge!(EPOCH_SERVICE_CURRENT_EPOCH_SLICE.name, "prover" => "epoch service")
.set(current_epoch_slice as f64);
// println!("Epoch changed: {}", current_epoch);
self.epoch_changes.notify_one();

View File

@@ -20,9 +20,12 @@ use tracing::debug;
use url::Url;
// internal
use crate::error::{AppError, ProofGenerationStringError};
use crate::metrics::{
GET_PROOFS_LISTENERS, GET_USER_TIER_INFO_REQUESTS, GaugeWrapper, SEND_TRANSACTION_REQUESTS,
USER_REGISTERED, USER_REGISTERED_REQUESTS,
};
use crate::proof_generation::{ProofGenerationData, ProofSendingData};
use crate::user_db::{UserDb, UserTierInfo};
use crate::metrics::{USER_REGISTERED, USER_REGISTERED_REQUESTS, SEND_TRANSACTION_REQUESTS, GET_USER_TIER_INFO_REQUESTS, GET_PROOFS_LISTENERS, GaugeWrapper};
use crate::user_db_error::RegisterError;
use rln_proof::RlnIdentifier;
use smart_contract::{
@@ -98,11 +101,11 @@ where
RLNSC: RLNRegister + Send + Sync + 'static,
RLNSC::Error: std::error::Error + Send + Sync + 'static,
{
#[tracing::instrument(skip(self), err, ret)]
async fn send_transaction(
&self,
request: Request<SendTransactionRequest>,
) -> Result<Response<SendTransactionReply>, Status> {
counter!(SEND_TRANSACTION_REQUESTS.name, "service" => "grpc").increment(1);
debug!("send_transaction request: {:?}", request);
let req = request.into_inner();
@@ -153,6 +156,7 @@ where
Ok(Response::new(reply))
}
#[tracing::instrument(skip(self), err, ret)]
async fn register_user(
&self,
request: Request<RegisterUserRequest>,
@@ -204,11 +208,11 @@ where
type GetProofsStream = ReceiverStream<Result<RlnProofReply, Status>>;
#[tracing::instrument(skip(self), err, ret)]
async fn get_proofs(
&self,
request: Request<RlnProofFilter>,
) -> Result<Response<Self::GetProofsStream>, Status> {
debug!("get_proofs request: {:?}", request);
let gauge = GaugeWrapper::new(GET_PROOFS_LISTENERS.name, "service", "grpc");
@@ -217,7 +221,6 @@ where
// Channel to receive a RLN proof (from one proof service)
let mut rx2 = self.broadcast_channel.0.subscribe();
tokio::spawn(async move {
// FIXME: Should we send the error here?
let gauge_ = gauge;
@@ -246,11 +249,11 @@ where
Ok(Response::new(ReceiverStream::new(rx)))
}
#[tracing::instrument(skip(self), err, ret)]
async fn get_user_tier_info(
&self,
request: Request<GetUserTierInfoRequest>,
) -> Result<Response<GetUserTierInfoReply>, Status> {
debug!("request: {:?}", request);
counter!(GET_USER_TIER_INFO_REQUESTS.name, "service" => "grpc").increment(1);

View File

@@ -38,12 +38,12 @@ use crate::proof_service::ProofService;
use crate::registry_listener::RegistryListener;
use crate::tier::TierLimits;
use crate::tiers_listener::TiersListener;
use crate::user_db_error::RegisterError;
use crate::user_db_service::UserDbService;
use crate::user_db_types::RateLimit;
use rln_proof::RlnIdentifier;
use smart_contract::KarmaTiersSC::KarmaTiersSCInstance;
use smart_contract::TIER_LIMITS;
use crate::user_db_error::RegisterError;
const RLN_IDENTIFIER_NAME: &[u8] = b"test-rln-identifier";
const PROVER_SPAM_LIMIT: RateLimit = RateLimit::new(10_000u64);
@@ -54,7 +54,6 @@ const PROVER_MINIMAL_AMOUNT_FOR_REGISTRATION: U256 =
pub async fn run_prover(
app_args: AppArgs,
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
// Epoch
let epoch_service = EpochService::try_from((Duration::from_secs(60 * 2), GENESIS))
.expect("Failed to create epoch service");
@@ -92,7 +91,6 @@ pub async fn run_prover(
let mock_users = read_mock_user(user_filepath)?;
debug!("Mock - will register {} users", mock_users.len());
for mock_user in mock_users {
debug!(
"Registering user address: {} - tx count: {}",
mock_user.address, mock_user.tx_count
@@ -103,14 +101,13 @@ pub async fn run_prover(
match e {
RegisterError::AlreadyRegistered(_) => {
debug!("User {} already registered", mock_user.address);
},
}
_ => {
return Err(Box::new(e));
}
}
}
user_db
.on_new_tx(&mock_user.address, Some(mock_user.tx_count))?;
user_db.on_new_tx(&mock_user.address, Some(mock_user.tx_count))?;
}
}
}

View File

@@ -1,5 +1,5 @@
use std::net::{IpAddr, SocketAddr};
use metrics::gauge;
use std::net::{IpAddr, SocketAddr};
// third-party
use metrics_exporter_prometheus::PrometheusBuilder;
// use metrics_util::MetricKindMask;
@@ -49,7 +49,7 @@ pub const EPOCH_SERVICE_DRIFT_MILLIS: Metric = Metric {
pub const PROOF_SERVICE_GEN_PROOF_TIME: Metric = Metric {
name: "proof_service_gen_proof_time",
description: "Generation time of a proof in milliseconds",
description: "Generation time of a proof in seconds",
};
pub const GET_PROOFS_LISTENERS: Metric = Metric {
@@ -61,7 +61,7 @@ pub const COUNTERS: [Metric; 4] = [
USER_REGISTERED,
USER_REGISTERED_REQUESTS,
SEND_TRANSACTION_REQUESTS,
GET_USER_TIER_INFO_REQUESTS
GET_USER_TIER_INFO_REQUESTS,
];
pub const GAUGES: [Metric; 3] = [
EPOCH_SERVICE_CURRENT_EPOCH,
@@ -118,8 +118,8 @@ fn register_histogram(metric: Metric) {
let _histogram = ::metrics::histogram!(metric.name);
}
/// A Wrapper around a metric gauge
///
/// A Wrapper around a metric gauge
///
/// Increment the given metric gauge on a new and decrement on drop
/// Useful in a closure (or an async closure)
pub struct GaugeWrapper {
@@ -129,7 +129,11 @@ pub struct GaugeWrapper {
}
impl GaugeWrapper {
pub fn new(gauge_name: &'static str, gauge_app: &'static str, gauge_label: &'static str) -> Self {
pub fn new(
gauge_name: &'static str,
gauge_app: &'static str,
gauge_label: &'static str,
) -> Self {
gauge!(gauge_name, gauge_app => gauge_label).increment(1.0);
Self {
gauge_name,
@@ -143,4 +147,4 @@ impl Drop for GaugeWrapper {
fn drop(&mut self) {
gauge!(self.gauge_name, self.gauge_app => self.gauge_label).decrement(1.0);
}
}
}

View File

@@ -8,15 +8,15 @@ use metrics::histogram;
use parking_lot::RwLock;
use rln::hashers::hash_to_field;
use rln::protocol::serialize_proof_values;
use tracing::{debug, info};
use tracing::{Instrument, debug, debug_span, info};
// internal
use crate::epoch_service::{Epoch, EpochSlice};
use crate::error::{AppError, ProofGenerationError, ProofGenerationStringError};
use crate::metrics::PROOF_SERVICE_GEN_PROOF_TIME;
use crate::proof_generation::{ProofGenerationData, ProofSendingData};
use crate::user_db::UserDb;
use crate::user_db_types::RateLimit;
use rln_proof::{RlnData, compute_rln_proof_and_values};
use crate::metrics::PROOF_SERVICE_GEN_PROOF_TIME;
const PROOF_SIZE: usize = 512;
@@ -51,7 +51,6 @@ impl ProofService {
}
pub(crate) async fn serve(&self) -> Result<(), AppError> {
loop {
let received = self.receiver.recv().await;
@@ -69,7 +68,6 @@ impl ProofService {
// Move to a task (as generating the proof can take quite some time)
let blocking_task = tokio::task::spawn_blocking(move || {
let proof_generation_start = std::time::Instant::now();
let message_id = {
@@ -124,7 +122,7 @@ impl ProofService {
Ok::<Vec<u8>, ProofGenerationError>(output_buffer.into_inner())
});
let result = blocking_task.await;
let result = blocking_task.instrument(debug_span!("compute proof")).await;
// Result (1st) is a JoinError (and should not happen)
// Result (2nd) is a ProofGenerationError
let result = result.unwrap(); // Should never happen (but should panic if it does)

View File

@@ -1,5 +1,5 @@
use crate::epoch_service::{Epoch, EpochSlice};
use claims::debug_assert_ge;
// use claims::debug_assert_ge;
use nom::{
IResult,
error::ContextError,

View File

@@ -11,5 +11,11 @@ tracing = "0.1.41"
toml = "0.8"
prover = { path = "../prover" }
opentelemetry = { version = "0.30", default-features = false, features = ["trace"]}
opentelemetry_sdk = { version = "0.30", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.30", features = ["grpc-tonic", "reqwest-client", "reqwest-rustls", "http-proto", "tls", "tls-roots"]}
tracing-opentelemetry = "0.31"
[dev-dependencies]
tracing-test = "0.2.5"

View File

@@ -8,20 +8,41 @@ use tracing::{
// error,
// info
};
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
use opentelemetry::trace::TracerProvider;
use opentelemetry_otlp::WithTonicConfig;
use opentelemetry_sdk::Resource;
// internal
use prover::{AppArgs, AppArgsConfig, metrics::init_metrics, run_prover};
const APP_NAME: &str = "prover-cli";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
// debug!("Args: {:?}", std::env::args());
// tracing
let filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
.from_env_lossy()
// TODO: Add a way to disable this for maximum log?
.add_directive("h2=error".parse()?)
.add_directive("sled::pagecache=error".parse()?)
.add_directive("opentelemetry_sdk=error".parse()?);
let fmt_layer = tracing_subscriber::fmt::layer();
let telemetry_layer = match create_otlp_tracer_provider() {
Some(tracer_provider) => {
let tracer = tracer_provider.tracer(APP_NAME);
Some(tracing_opentelemetry::OpenTelemetryLayer::new(tracer))
}
None => None,
};
tracing_subscriber::registry()
.with(fmt::layer())
.with(fmt_layer)
.with(filter)
.with(telemetry_layer)
.init();
// let app_args = AppArgs::parse();
@@ -60,3 +81,46 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>
run_prover(app_args).await
}
fn create_otlp_tracer_provider() -> Option<opentelemetry_sdk::trace::SdkTracerProvider> {
if !std::env::vars().any(|(name, _)| name.starts_with("OTEL_")) {
return None;
}
let protocol = std::env::var("OTEL_EXPORTER_OTLP_PROTOCOL").unwrap_or("grpc".to_string());
let exporter = match protocol.as_str() {
"grpc" => {
// Note - Performance:
// https://docs.rs/opentelemetry-otlp/latest/opentelemetry_otlp/index.html#performance
let mut exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
//.with_endpoint(...)
;
// Check if we need TLS
if let Ok(endpoint) = std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT") {
if endpoint.starts_with("https") {
exporter = exporter.with_tls_config(
opentelemetry_otlp::tonic_types::transport::ClientTlsConfig::default()
.with_enabled_roots(),
);
}
}
exporter.build().expect("Failed to create tonic exporter")
}
"http/protobuf" => opentelemetry_otlp::SpanExporter::builder()
.with_http()
.build()
.expect("Failed to create http/protobuf exporter"),
p => panic!("Unsupported protocol {p}"),
};
let resource = Resource::builder().with_service_name(APP_NAME).build();
Some(
opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_resource(resource)
.with_batch_exporter(exporter)
.build(),
)
}

View File

@@ -9,7 +9,9 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
# TODO: workspace?
tonic = "0.13.1"
prost = "0.13"
clap = { version = "4.5.40", features = ["derive", "wrap_help"] }
alloy.workspace = true
sha2 = "0.10"
[build-dependencies]
tonic-build = "*"

View File

@@ -1,40 +1,139 @@
use alloy::primitives::Address;
use std::str::FromStr;
use alloy::primitives::{Address, U256};
use clap::{Args, Parser, Subcommand};
use sha2::Digest;
use std::net::IpAddr;
use tonic::Response;
pub mod prover_proto {
// Include generated code (see build.rs)
tonic::include_proto!("prover");
}
use crate::prover_proto::{GetUserTierInfoReply, GetUserTierInfoRequest, RegistrationStatus};
use prover_proto::{
Address as GrpcAddress, RegisterUserReply, RegisterUserRequest,
Address as GrpcAddress,
RegisterUserReply,
RegisterUserRequest,
SendTransactionReply,
SendTransactionRequest,
U256 as GrpcU256,
Wei as GrpcWei,
// RegistrationStatus,
rln_prover_client::RlnProverClient,
};
#[derive(Debug, Clone, Parser)]
#[command(about = "RLN prover client", long_about = None)]
pub struct AppArgs {
#[arg(short = 'i', long = "ip", default_value = "::1", help = "Service ip")]
pub ip: IpAddr,
#[arg(
short = 'p',
long = "port",
default_value = "50051",
help = "Service port"
)]
pub port: u16,
#[arg(
short = 'a',
long = "address",
default_value = "0xb20a608c624Ca5003905aA834De7156C68b2E1d0",
help = "User address"
)]
pub address: Address,
#[command(subcommand)]
command: Commands,
}
#[derive(Debug, Clone, PartialEq, Subcommand)]
pub(crate) enum Commands {
#[command(about = "Register a new user")]
RegisterUser, // (RegisterUserArgs),
#[command(about = "Send a transaction")]
SendTransaction(SendTransactionArgs),
#[command(about = "Get user tier info")]
GetUserTierInfo,
}
#[derive(Debug, Clone, PartialEq, Args)]
pub struct SendTransactionArgs {
#[arg(short = 'c', long = "chain-id", help = "Chain id", default_value = "1")]
chain_id: U256,
#[arg(short = 'w', long = "wei", help = "Tx fee", default_value = "1000")]
wei: U256,
#[arg(short = 't', long = "tx-hash", help = "Tx hash", default_value = "foo")]
tx_hash: String,
#[arg(
long = "invalid-tx-hash",
default_missing_value = "false",
help = "Send an invalid tx hash"
)]
invalid_hash: bool,
}
#[tokio::main]
async fn main() {
// FIXME: clap
let url = "http://127.0.0.1:42942";
let addr = "0xb20a608c624Ca5003905aA834De7156C68b2E1d0";
let addr = Address::from_str(addr).unwrap();
let grpc_addr = GrpcAddress {
value: addr.to_vec(),
};
let app_args = AppArgs::parse();
let url = format!("http://{}:{}", app_args.ip, app_args.port);
println!("url: {url}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let request_0 = RegisterUserRequest {
user: Some(grpc_addr),
let grpc_addr = GrpcAddress {
value: app_args.address.to_vec(),
};
let request = tonic::Request::new(request_0);
let response: Response<RegisterUserReply> = client.register_user(request).await.unwrap();
println!(
"RegisterUSerReply status: {:?}",
response.into_inner().status
);
match app_args.command {
Commands::RegisterUser => {
let request_0 = RegisterUserRequest {
user: Some(grpc_addr),
};
let request = tonic::Request::new(request_0);
let response: Response<RegisterUserReply> =
client.register_user(request).await.unwrap();
println!(
"RegisterUSerReply status: {:?}",
RegistrationStatus::try_from(response.into_inner().status)
);
}
Commands::SendTransaction(send_transaction_args) => {
let chain_id = GrpcU256 {
// FIXME: LE or BE?
value: send_transaction_args.chain_id.to_le_bytes::<32>().to_vec(),
};
let wei = GrpcWei {
// FIXME: LE or BE?
value: send_transaction_args.wei.to_le_bytes::<32>().to_vec(),
};
let tx_hash = if send_transaction_args.invalid_hash {
vec![]
} else {
// U256::from(42).to_le_bytes::<32>().to_vec()
sha2::Sha256::digest(send_transaction_args.tx_hash).to_vec()
};
let request_0 = SendTransactionRequest {
gas_price: Some(wei),
sender: Some(grpc_addr),
chain_id: Some(chain_id),
transaction_hash: tx_hash,
};
let request = tonic::Request::new(request_0);
let response: Response<SendTransactionReply> =
client.send_transaction(request).await.unwrap();
println!("SendTransactionReply status: {:?}", response.into_inner());
}
Commands::GetUserTierInfo => {
let request_0 = GetUserTierInfoRequest {
user: Some(grpc_addr),
};
let request = tonic::Request::new(request_0);
let response: Response<GetUserTierInfoReply> =
client.get_user_tier_info(request).await.unwrap();
println!("GetUserTierInfoReply: {:?}", response.into_inner());
}
}
}