mirror of
https://github.com/vacp2p/status-rln-prover.git
synced 2026-01-08 21:18:05 -05:00
Initial code
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
4811
Cargo.lock
generated
Normal file
4811
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "status_rln_prover"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.37", features = ["derive"] }
|
||||
tonic = "0.13"
|
||||
prost = "0.13"
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
tonic-reflection = "0.13"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-test = "0.2.5"
|
||||
alloy = { version = "0.15", features = ["full"] }
|
||||
thiserror = "2.0"
|
||||
futures = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "*"
|
||||
10
Readme.md
Normal file
10
Readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Status L2 Rln Prover
|
||||
|
||||
## Run
|
||||
|
||||
RUST_LOG=debug cargo run -- -i 127.0.0.1 -r "wss://eth-mainnet.g.alchemy.com/v2/__MY_TOKEN__"
|
||||
|
||||
## Debug
|
||||
|
||||
* grpcurl -plaintext -d '{"sender": "Alice", "tx_id": "42"}' '[::1]:50051' prover.RlnProver/SendTransaction
|
||||
* grpcurl -plaintext '[::1]:50051' prover.RlnProver/GetProofs
|
||||
12
build.rs
Normal file
12
build.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
tonic_build::configure()
|
||||
.file_descriptor_set_path(out_dir.join("prover_descriptor.bin"))
|
||||
.compile_protos(&["proto/prover.proto"], &["proto"])
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
29
proto/prover.proto
Normal file
29
proto/prover.proto
Normal file
@@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
package prover;
|
||||
|
||||
service RlnProver {
|
||||
rpc SendTransaction (SendTransactionRequest) returns (SendTransactionReply);
|
||||
// Server side streaming RPC: 1 request -> X responses (stream)
|
||||
rpc GetProofs(RlnProofFilter) returns (stream RlnProof);
|
||||
}
|
||||
|
||||
message SendTransactionRequest {
|
||||
string sender = 1;
|
||||
string tx_id = 2;
|
||||
}
|
||||
|
||||
message SendTransactionReply {
|
||||
bool result = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message RlnProofFilter {
|
||||
optional string address = 1;
|
||||
}
|
||||
|
||||
message RlnProof {
|
||||
string sender = 1;
|
||||
string id_commitment = 2;
|
||||
string proof = 3;
|
||||
}
|
||||
|
||||
23
src/args.rs
Normal file
23
src/args.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[command(about = "RLN prover service", long_about = None)]
|
||||
pub struct AppArgs {
|
||||
#[arg(short = 'i', long = "ip", default_value = "::1", help = "Service ip")]
|
||||
pub(crate) ip: IpAddr,
|
||||
#[arg(
|
||||
short = 'p',
|
||||
long = "port",
|
||||
default_value = "50051",
|
||||
help = "Service port"
|
||||
)]
|
||||
pub(crate) port: u16,
|
||||
#[arg(
|
||||
short = 'r',
|
||||
long = "rpc_url",
|
||||
help = "Websocket rpc url (e.g. wss://eth-mainnet.g.alchemy.com/v2/your-api-key)"
|
||||
)]
|
||||
pub(crate) rpc_url: String,
|
||||
}
|
||||
13
src/error.rs
Normal file
13
src/error.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use alloy::transports::{RpcError, TransportErrorKind};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum AppError {
|
||||
#[error("Tonic (grpc) error: {0}")]
|
||||
Tonic(#[from] tonic::transport::Error),
|
||||
#[error("Tonic reflection (grpc) error: {0}")]
|
||||
TonicReflection(#[from] tonic_reflection::server::Error),
|
||||
#[error("SC error 1: {0}")]
|
||||
Alloy(#[from] RpcError<RpcError<TransportErrorKind>>),
|
||||
#[error("SC error 2: {0}")]
|
||||
Alloy2(#[from] RpcError<TransportErrorKind>),
|
||||
}
|
||||
106
src/grpc_service.rs
Normal file
106
src/grpc_service.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use futures::TryFutureExt;
|
||||
use tokio::sync::mpsc;
|
||||
use tonic::{
|
||||
Request, Response, Status, codegen::tokio_stream::wrappers::ReceiverStream, transport::Server,
|
||||
};
|
||||
use tracing::{
|
||||
debug,
|
||||
// error,
|
||||
// info
|
||||
};
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
pub mod prover_proto {
|
||||
|
||||
// Include generated code (see build.rs)
|
||||
tonic::include_proto!("prover");
|
||||
// for reflection service
|
||||
pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =
|
||||
tonic::include_file_descriptor_set!("prover_descriptor");
|
||||
}
|
||||
use prover_proto::{
|
||||
RlnProof, RlnProofFilter, SendTransactionReply, SendTransactionRequest,
|
||||
rln_prover_server::{RlnProver, RlnProverServer},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ProverService {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl RlnProver for ProverService {
|
||||
async fn send_transaction(
|
||||
&self,
|
||||
request: Request<SendTransactionRequest>,
|
||||
) -> Result<Response<SendTransactionReply>, Status> {
|
||||
debug!("send_transaction request: {:?}", request);
|
||||
let req = request.into_inner();
|
||||
let sender = req.sender;
|
||||
// let tx_id = req.tx_id;
|
||||
|
||||
let reply = SendTransactionReply {
|
||||
result: false,
|
||||
message: format!("User address: {} not registered", sender),
|
||||
};
|
||||
|
||||
Ok(Response::new(reply))
|
||||
}
|
||||
|
||||
type GetProofsStream = ReceiverStream<Result<RlnProof, Status>>;
|
||||
|
||||
async fn get_proofs(
|
||||
&self,
|
||||
request: Request<RlnProofFilter>,
|
||||
) -> Result<Response<Self::GetProofsStream>, Status> {
|
||||
debug!("get_proofs request: {:?}", request);
|
||||
// FIXME: channel size or unbounded channel?
|
||||
let (tx, rx) = mpsc::channel(100);
|
||||
|
||||
tokio::spawn(async move {
|
||||
// TODO: real proof
|
||||
debug!("Sending dummy rln proofs...");
|
||||
loop {
|
||||
let rln_proof = RlnProof {
|
||||
sender: "0xAA".to_string(),
|
||||
id_commitment: "1".to_string(),
|
||||
proof: "__bytes__".to_string(),
|
||||
};
|
||||
// FIXME: no unwrap
|
||||
if let Err(e) = tx.send(Ok(rln_proof)).await {
|
||||
debug!("Done: sending dummy rln proofs: {}", e);
|
||||
break;
|
||||
};
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Response::new(ReceiverStream::new(rx)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct GrpcProverService {
|
||||
addr: SocketAddr,
|
||||
// reflection_descriptor_set: &'a [u8],
|
||||
}
|
||||
|
||||
impl GrpcProverService {
|
||||
pub(crate) fn new(addr: SocketAddr) -> Self {
|
||||
Self { addr }
|
||||
}
|
||||
|
||||
pub(crate) async fn serve(&self) -> Result<(), AppError> {
|
||||
let prover_service = ProverService::default();
|
||||
let reflection_service = tonic_reflection::server::Builder::configure()
|
||||
.register_encoded_file_descriptor_set(prover_proto::FILE_DESCRIPTOR_SET)
|
||||
.build_v1()?;
|
||||
|
||||
Server::builder()
|
||||
.add_service(reflection_service)
|
||||
.add_service(RlnProverServer::new(prover_service))
|
||||
.serve(self.addr)
|
||||
.map_err(AppError::from)
|
||||
.await
|
||||
}
|
||||
}
|
||||
63
src/main.rs
Normal file
63
src/main.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
mod args;
|
||||
mod error;
|
||||
mod grpc_service;
|
||||
mod registry_listener;
|
||||
|
||||
// std
|
||||
use std::net::SocketAddr;
|
||||
// third-party
|
||||
use alloy::primitives::address;
|
||||
use clap::Parser;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing::{
|
||||
debug,
|
||||
error,
|
||||
// info
|
||||
};
|
||||
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
// internal
|
||||
use crate::args::AppArgs;
|
||||
use crate::grpc_service::GrpcProverService;
|
||||
use crate::registry_listener::RegistryListener;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let filter = EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy();
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(filter)
|
||||
.init();
|
||||
|
||||
let app_args = AppArgs::parse();
|
||||
debug!("Arguments: {:?}", app_args);
|
||||
|
||||
// Smart contract
|
||||
|
||||
let uniswap_token_address = address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984");
|
||||
let event = "Transfer(address,address,uint256)";
|
||||
let registry_listener =
|
||||
RegistryListener::new(app_args.rpc_url.as_str(), uniswap_token_address, event);
|
||||
|
||||
// grpc
|
||||
|
||||
let addr = SocketAddr::new(app_args.ip, app_args.port);
|
||||
debug!("Listening on: {}", addr);
|
||||
let prover_service = GrpcProverService::new(addr);
|
||||
|
||||
let res = tokio::try_join!(prover_service.serve(), registry_listener.listen());
|
||||
|
||||
match res {
|
||||
Ok((first, second)) => {
|
||||
debug!("{:?}", first);
|
||||
debug!("{:?}", second);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
64
src/registry_listener.rs
Normal file
64
src/registry_listener.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use crate::error::AppError;
|
||||
use alloy::eips::BlockNumberOrTag;
|
||||
use alloy::primitives::Address;
|
||||
use alloy::providers::fillers::{
|
||||
BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller,
|
||||
};
|
||||
use alloy::providers::{Identity, Provider, ProviderBuilder, RootProvider, WsConnect};
|
||||
use alloy::rpc::types::Filter;
|
||||
use alloy::transports::{RpcError, TransportError};
|
||||
use tonic::codegen::tokio_stream::StreamExt;
|
||||
|
||||
type AlloyWsProvider = FillProvider<
|
||||
JoinFill<
|
||||
Identity,
|
||||
JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
|
||||
>,
|
||||
RootProvider,
|
||||
>;
|
||||
|
||||
pub(crate) struct RegistryListener {
|
||||
rpc_url: String,
|
||||
sc_address: Address,
|
||||
event: String,
|
||||
}
|
||||
|
||||
impl RegistryListener {
|
||||
pub(crate) fn new(rpc_url: &str, sc_address: Address, event: &str) -> Self {
|
||||
Self {
|
||||
rpc_url: rpc_url.to_string(),
|
||||
sc_address,
|
||||
event: event.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a provider (aka connect to websocket url)
|
||||
async fn setup_provider_ws(&self) -> Result<AlloyWsProvider, RpcError<TransportError>> {
|
||||
let ws = WsConnect::new(self.rpc_url.as_str());
|
||||
let provider = ProviderBuilder::new().connect_ws(ws).await?;
|
||||
Ok(provider)
|
||||
}
|
||||
|
||||
/// Listen to Smart Contract specified events
|
||||
pub(crate) async fn listen(&self) -> Result<(), AppError> {
|
||||
let provider = self.setup_provider_ws().await.map_err(AppError::from)?;
|
||||
|
||||
let filter = Filter::new()
|
||||
.address(self.sc_address)
|
||||
.event(self.event.as_str())
|
||||
.from_block(BlockNumberOrTag::Latest);
|
||||
|
||||
// Subscribe to logs.
|
||||
let sub = provider
|
||||
.subscribe_logs(&filter)
|
||||
.await
|
||||
.map_err(AppError::from)?;
|
||||
let mut stream = sub.into_stream();
|
||||
|
||||
while let Some(log) = stream.next().await {
|
||||
println!("Uniswap token logs: {log:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user