Add initial rln sc register in registry_listener (#33)

* Add initial rln sc register in registry_listener
* Generic over alloy provider
* Fix prover mock startup
* Update handle_transfer_event documentation
This commit is contained in:
Sydhds
2025-08-29 10:18:37 +02:00
committed by GitHub
parent eec2ae76e9
commit b2f142b0e5
22 changed files with 379 additions and 405 deletions

View File

@@ -11,7 +11,7 @@ PRIVATE_KEY=__MY_PRIVATE_KEY__ RUST_LOG=debug cargo run -p prover_cli -- --no-co
### Run prover + Mock
RUST_LOG=debug cargo run -p prover_cli -- --ip 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 -- --ip 127.0.0.1 --metrics-ip 127.0.0.1 --mock-sc true --mock-user mock/mock_user_1.json --no-config
### Run prover + opentelemetry
@@ -23,9 +23,9 @@ RUST_LOG=debug cargo run -p prover_cli -- --ip 127.0.0.1 --metrics-ip 127.0.0.1
### Run prover client (for tests)
* RUST_LOG=debug cargo run -p prover_client -- --help
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 42942 register-user
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 42942 send-transaction --tx-hash aa
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 42942 -a 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 get-user-tier-info
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 50051 register-user
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 50051 send-transaction --tx-hash aa
* RUST_LOG=debug cargo run -p prover_client -- -i 127.0.0.1 -p 50051 -a 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 get-user-tier-info
## Debug

View File

@@ -10,7 +10,7 @@ option java_multiple_files = true;
service RlnProver {
rpc SendTransaction (SendTransactionRequest) returns (SendTransactionReply);
rpc RegisterUser (RegisterUserRequest) returns (RegisterUserReply);
// rpc RegisterUser (RegisterUserRequest) returns (RegisterUserReply);
// Server side streaming RPC: 1 request -> X responses (stream)
rpc GetProofs(RlnProofFilter) returns (stream RlnProofReply);
@@ -171,6 +171,7 @@ message RlnProofError {
string error = 2;
}
/*
message RegisterUserRequest {
Address user = 14;
}
@@ -184,6 +185,7 @@ enum RegistrationStatus {
message RegisterUserReply {
RegistrationStatus status = 1;
}
*/
message GetUserTierInfoRequest {
Address user = 1;

View File

@@ -1,6 +1,7 @@
use criterion::Criterion;
use criterion::{BenchmarkId, Throughput};
use criterion::{criterion_group, criterion_main};
use std::io::Write;
// std
use std::net::{IpAddr, Ipv4Addr};
@@ -11,11 +12,12 @@ use std::time::Duration;
use alloy::primitives::{Address, U256};
use futures::FutureExt;
use parking_lot::RwLock;
use tempfile::NamedTempFile;
use tokio::sync::Notify;
use tokio::task::JoinSet;
use tonic::Response;
// internal
use prover::{AppArgs, run_prover};
use prover::{AppArgs, MockUser, run_prover};
// grpc
pub mod prover_proto {
@@ -23,38 +25,17 @@ pub mod prover_proto {
tonic::include_proto!("prover");
}
use prover_proto::{
Address as GrpcAddress, RegisterUserReply, RegisterUserRequest, RegistrationStatus,
RlnProofFilter, RlnProofReply, SendTransactionReply, SendTransactionRequest, U256 as GrpcU256,
Wei as GrpcWei, rln_prover_client::RlnProverClient,
Address as GrpcAddress, RlnProofFilter, RlnProofReply, SendTransactionReply,
SendTransactionRequest, U256 as GrpcU256, Wei as GrpcWei, rln_prover_client::RlnProverClient,
};
async fn register_users(port: u16, addresses: Vec<Address>) {
let url = format!("http://127.0.0.1:{}", port);
let mut client = RlnProverClient::connect(url).await.unwrap();
for address in addresses {
let addr = GrpcAddress {
value: address.to_vec(),
};
let request_0 = RegisterUserRequest { user: Some(addr) };
let request = tonic::Request::new(request_0);
let response: Response<RegisterUserReply> = client.register_user(request).await.unwrap();
assert_eq!(
RegistrationStatus::try_from(response.into_inner().status).unwrap(),
RegistrationStatus::Success
);
}
}
async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
let chain_id = GrpcU256 {
// FIXME: LE or BE?
value: U256::from(1).to_le_bytes::<32>().to_vec(),
};
let url = format!("http://127.0.0.1:{}", port);
let url = format!("http://127.0.0.1:{port}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let addr = GrpcAddress {
@@ -85,7 +66,7 @@ async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
let result = Arc::new(RwLock::new(vec![]));
let url = format!("http://127.0.0.1:{}", port);
let url = format!("http://127.0.0.1:{port}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let request_0 = RlnProofFilter { address: None };
@@ -107,7 +88,6 @@ async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
}
fn proof_generation_bench(c: &mut Criterion) {
let start = std::time::Instant::now();
let rayon_num_threads = std::env::var("RAYON_NUM_THREADS").unwrap_or("".to_string());
let proof_service_count_default = 4;
let proof_service_count = std::env::var("PROOF_SERVICE_COUNT")
@@ -123,6 +103,24 @@ fn proof_generation_bench(c: &mut Criterion) {
.build()
.unwrap();
// Write mock users to tempfile
let mock_users = vec![
MockUser {
address: Address::from_str("0xd8da6bf26964af9d7eed9e03e53415d37aa96045").unwrap(),
tx_count: 0,
},
MockUser {
address: Address::from_str("0xb20a608c624Ca5003905aA834De7156C68b2E1d0").unwrap(),
tx_count: 0,
},
];
let addresses: Vec<Address> = mock_users.iter().map(|u| u.address).collect();
let mock_users_as_str = serde_json::to_string(&mock_users).unwrap();
let mut temp_file = NamedTempFile::new().unwrap();
let temp_file_path = temp_file.path().to_path_buf();
temp_file.write_all(mock_users_as_str.as_bytes()).unwrap();
temp_file.flush().unwrap();
let port = 50051;
let temp_folder = tempfile::tempdir().unwrap();
let temp_folder_tree = tempfile::tempdir().unwrap();
@@ -137,9 +135,9 @@ fn proof_generation_bench(c: &mut Criterion) {
rlnsc_address: None,
tsc_address: None,
mock_sc: Some(true),
mock_user: None,
mock_user: Some(temp_file_path),
config_path: Default::default(),
no_config: Some(true),
no_config: true,
metrics_ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
metrics_port: 30051,
broadcast_channel_size: 100,
@@ -148,10 +146,6 @@ fn proof_generation_bench(c: &mut Criterion) {
proof_sender_channel_size: 100,
};
let addresses = vec![
Address::from_str("0xd8da6bf26964af9d7eed9e03e53415d37aa96045").unwrap(),
Address::from_str("0xb20a608c624Ca5003905aA834De7156C68b2E1d0").unwrap(),
];
// Tokio notify - wait for some time after spawning run_prover then notify it's ready to accept
// connections
let notify_start = Arc::new(Notify::new());
@@ -166,13 +160,12 @@ fn proof_generation_bench(c: &mut Criterion) {
});
let notify_start_2 = notify_start.clone();
let addresses_0 = addresses.clone();
// Wait for proof_collector to be connected and waiting for some proofs
rt.block_on(async move {
notify_start_2.notified().await;
println!("Prover is ready, registering users...");
register_users(port, addresses_0).await;
println!("Prover is ready...");
// register_users(port, addresses_0).await;
});
println!("Starting benchmark...");
@@ -185,6 +178,7 @@ fn proof_generation_bench(c: &mut Criterion) {
let proof_count = proof_count as usize;
group.throughput(Throughput::Elements(proof_count as u64));
#[allow(clippy::uninlined_format_args)]
let benchmark_name = format!(
"prover_proof_{}_proof_service_{}_rt_{}",
proof_count, proof_service_count, rayon_num_threads
@@ -208,14 +202,13 @@ fn proof_generation_bench(c: &mut Criterion) {
);
group.finish();
println!("Benchmark finished in {:?}", start.elapsed());
}
criterion_group!(
name = benches;
config = Criterion::default()
.sample_size(10)
.measurement_time(Duration::from_secs(150))
.measurement_time(Duration::from_secs(500))
;
targets = proof_generation_bench
);

View File

@@ -2,7 +2,6 @@ use std::net::IpAddr;
use std::path::PathBuf;
// third-party
use alloy::primitives::Address;
use clap::ArgAction::SetTrue;
use clap::Parser;
use clap_config::ClapConfig;
use url::Url;
@@ -44,7 +43,6 @@ pub struct AppArgs {
#[arg(
short = 'u',
long = "ws-rpc-url",
default_value = "wss://public.sepolia.rpc.status.network/ws",
help = "Websocket rpc url (e.g. wss://eth-mainnet.g.alchemy.com/v2/your-api-key)"
)]
pub ws_rpc_url: Option<Url>,
@@ -102,11 +100,11 @@ pub struct AppArgs {
#[arg(
long = "no-config",
help = "Dont read a config file",
default_missing_value = "true",
action = SetTrue,
help_heading = "config"
required = false,
action,
help_heading = "Do not try to read config file"
)]
pub no_config: Option<bool>,
pub no_config: bool,
#[arg(
long = "metrics-ip",
default_value = "::1",

View File

@@ -72,10 +72,17 @@ pub enum GetMerkleTreeProofError {
MerkleTree(#[from] UserMerkleTreeIndexError),
}
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct RegisterSCError(#[from] alloy::contract::Error);
#[derive(thiserror::Error, Debug)]
pub enum HandleTransferError {
#[error(transparent)]
Register(#[from] RegisterError),
#[error("Fail to register user in RLN SC: {0}")]
ScRegister(#[from] RegisterSCError),
#[error("Unable to query balance: {0}")]
FetchBalanceOf(#[from] alloy::contract::Error),
}

View File

@@ -5,42 +5,33 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
// third-party
use alloy::primitives::{Address, U256};
use alloy::{primitives::Address, providers::Provider};
use async_channel::Sender;
use bytesize::ByteSize;
use futures::TryFutureExt;
use http::Method;
use metrics::{counter, histogram};
use num_bigint::BigUint;
use smart_contract::RlnScError;
use tokio::sync::{broadcast, mpsc};
use tonic::{
Request, Response, Status, codegen::tokio_stream::wrappers::ReceiverStream, transport::Server,
};
use tonic_web::GrpcWebLayer;
use tower_http::cors::{Any, CorsLayer};
use tracing::{debug, error};
use tracing::{
debug,
// error
};
use url::Url;
use zeroize::Zeroizing;
// internal
use crate::error::{AppError, ProofGenerationStringError};
use crate::metrics::{
GET_PROOFS_LISTENERS, GET_USER_TIER_INFO_REQUESTS, GaugeWrapper,
PROOF_SERVICES_CHANNEL_QUEUE_LEN, SEND_TRANSACTION_REQUESTS, USER_REGISTERED,
USER_REGISTERED_REQUESTS,
PROOF_SERVICES_CHANNEL_QUEUE_LEN, SEND_TRANSACTION_REQUESTS,
};
use crate::proof_generation::{ProofGenerationData, ProofSendingData};
use crate::user_db::{UserDb, UserTierInfo};
use crate::user_db_error::RegisterError;
use rln_proof::RlnIdentifier;
use smart_contract::{
KarmaAmountExt,
KarmaRLNSC::KarmaRLNSCInstance,
KarmaSC::KarmaSCInstance,
MockKarmaRLNSc,
MockKarmaSc,
RLNRegister, // traits
};
use smart_contract::{KarmaAmountExt, KarmaSC::KarmaSCInstance, MockKarmaSc};
pub mod prover_proto {
@@ -53,9 +44,9 @@ pub mod prover_proto {
use prover_proto::{
GetUserTierInfoReply,
GetUserTierInfoRequest,
RegisterUserReply,
RegisterUserRequest,
RegistrationStatus,
// RegisterUserReply,
// RegisterUserRequest,
// RegistrationStatus,
RlnProof,
RlnProofFilter,
RlnProofReply,
@@ -85,7 +76,7 @@ const PROVER_SERVICE_MESSAGE_ENCODING_MAX_SIZE: ByteSize = ByteSize::mib(5);
const PROVER_TX_HASH_BYTESIZE: usize = 32;
#[derive(Debug)]
pub struct ProverService<KSC: KarmaAmountExt, RLNSC: RLNRegister> {
pub struct ProverService<KSC: KarmaAmountExt> {
proof_sender: Sender<ProofGenerationData>,
user_db: UserDb,
rln_identifier: Arc<RlnIdentifier>,
@@ -94,17 +85,15 @@ pub struct ProverService<KSC: KarmaAmountExt, RLNSC: RLNRegister> {
broadcast::Receiver<Result<ProofSendingData, ProofGenerationStringError>>,
),
karma_sc: KSC,
karma_rln_sc: RLNSC,
// karma_rln_sc: RLNSC,
proof_sender_channel_size: usize,
}
#[tonic::async_trait]
impl<KSC, RLNSC> RlnProver for ProverService<KSC, RLNSC>
impl<KSC> RlnProver for ProverService<KSC>
where
KSC: KarmaAmountExt + Send + Sync + 'static,
KSC::Error: std::error::Error + Send + Sync + 'static,
RLNSC: RLNRegister + Send + Sync + 'static,
RLNSC::Error: std::error::Error + Send + Sync + 'static,
{
#[tracing::instrument(skip(self), err, ret)]
async fn send_transaction(
@@ -166,6 +155,7 @@ where
Ok(Response::new(reply))
}
/*
#[tracing::instrument(skip(self), err, ret)]
async fn register_user(
&self,
@@ -215,6 +205,7 @@ where
counter!(USER_REGISTERED.name, "prover" => "grpc").increment(1);
Ok(Response::new(reply))
}
*/
type GetProofsStream = ReceiverStream<Result<RlnProofReply, Status>>;
@@ -292,7 +283,7 @@ where
}
}
pub(crate) struct GrpcProverService {
pub(crate) struct GrpcProverService<P: Provider> {
pub proof_sender: Sender<ProofGenerationData>,
pub broadcast_channel: (
broadcast::Sender<Result<ProofSendingData, ProofGenerationStringError>>,
@@ -302,31 +293,20 @@ pub(crate) struct GrpcProverService {
pub rln_identifier: RlnIdentifier,
pub user_db: UserDb,
pub karma_sc_info: Option<(Url, Address)>,
pub rln_sc_info: Option<(Url, Address)>,
// pub rln_sc_info: Option<(Url, Address)>,
pub provider: Option<P>,
pub proof_sender_channel_size: usize,
}
impl GrpcProverService {
impl<P: Provider + Clone + Send + Sync + 'static> GrpcProverService<P> {
pub(crate) async fn serve(&self) -> Result<(), AppError> {
let karma_sc = if let Some(karma_sc_info) = self.karma_sc_info.as_ref() {
KarmaSCInstance::try_new(karma_sc_info.0.clone(), karma_sc_info.1).await?
let karma_sc = if let Some(karma_sc_info) = self.karma_sc_info.as_ref()
&& let Some(provider) = self.provider.as_ref()
{
KarmaSCInstance::new(karma_sc_info.1, provider.clone())
} else {
panic!("Please provide karma_sc_info or use serve_with_mock");
};
let karma_rln_sc = if let Some(rln_sc_info) = self.rln_sc_info.as_ref() {
let private_key = Zeroizing::new(std::env::var("PRIVATE_KEY").map_err(|_| {
error!("PRIVATE_KEY environment variable is not set");
AppError::RlnScError(RlnScError::EmptyPrivateKey)
})?);
KarmaRLNSCInstance::try_new_with_signer(
rln_sc_info.0.clone(),
rln_sc_info.1,
private_key,
)
.await?
} else {
panic!("Please provide rln_sc_info or use serve_with_mock");
};
let prover_service = ProverService {
proof_sender: self.proof_sender.clone(),
@@ -337,7 +317,6 @@ impl GrpcProverService {
self.broadcast_channel.0.subscribe(),
),
karma_sc,
karma_rln_sc,
proof_sender_channel_size: self.proof_sender_channel_size,
};
@@ -399,7 +378,7 @@ impl GrpcProverService {
self.broadcast_channel.0.subscribe(),
),
karma_sc: MockKarmaSc {},
karma_rln_sc: MockKarmaRLNSc {},
// karma_rln_sc: MockKarmaRLNSc {},
proof_sender_channel_size: self.proof_sender_channel_size,
};

View File

@@ -22,10 +22,14 @@ mod proof_service_tests;
mod user_db_tests;
// std
use alloy::network::EthereumWallet;
use std::net::SocketAddr;
use std::str::FromStr;
use std::time::Duration;
// third-party
use alloy::primitives::U256;
use alloy::providers::{ProviderBuilder, WsConnect};
use alloy::signers::local::PrivateKeySigner;
use chrono::{DateTime, Utc};
use tokio::task::JoinSet;
use tracing::{
@@ -33,10 +37,12 @@ use tracing::{
// error,
// info
};
use zeroize::Zeroizing;
// internal
pub use crate::args::{AppArgs, AppArgsConfig};
use crate::epoch_service::EpochService;
use crate::grpc_service::GrpcProverService;
pub use crate::mock::MockUser;
use crate::mock::read_mock_user;
use crate::proof_service::ProofService;
use crate::registry_listener::RegistryListener;
@@ -47,7 +53,7 @@ use crate::user_db_service::UserDbService;
use crate::user_db_types::RateLimit;
use rln_proof::RlnIdentifier;
use smart_contract::KarmaTiers::KarmaTiersInstance;
use smart_contract::TIER_LIMITS;
use smart_contract::{KarmaTiersError, TIER_LIMITS};
const RLN_IDENTIFIER_NAME: &[u8] = b"test-rln-identifier";
const PROVER_SPAM_LIMIT: RateLimit = RateLimit::new(10_000u64);
@@ -62,11 +68,43 @@ pub async fn run_prover(
let epoch_service = EpochService::try_from((Duration::from_secs(60 * 2), GENESIS))
.expect("Failed to create epoch service");
// Alloy provider (Smart contract provider)
let provider = if app_args.ws_rpc_url.is_some() {
let ws = WsConnect::new(app_args.ws_rpc_url.clone().unwrap().as_str());
let provider = ProviderBuilder::new()
.connect_ws(ws)
.await
.map_err(KarmaTiersError::RpcTransportError)?;
Some(provider)
} else {
None
};
// Alloy provider + signer
let provider_with_signer = if app_args.ws_rpc_url.is_some() {
let pk: Zeroizing<String> =
Zeroizing::new(std::env::var("PRIVATE_KEY").expect("Please provide a private key"));
let pk_signer = PrivateKeySigner::from_str(pk.as_str())?;
let wallet = EthereumWallet::from(pk_signer);
let ws = WsConnect::new(app_args.ws_rpc_url.clone().unwrap().as_str());
let provider = ProviderBuilder::new()
.wallet(wallet)
.connect_ws(ws)
.await
.map_err(KarmaTiersError::RpcTransportError)?;
Some(provider)
} else {
None
};
//
let tier_limits = if app_args.ws_rpc_url.is_some() {
TierLimits::from(
KarmaTiersInstance::get_tiers(
app_args.ws_rpc_url.clone().unwrap(),
app_args.tsc_address.unwrap(),
KarmaTiersInstance::get_tiers_from_provider(
&provider.clone().unwrap(),
&app_args.tsc_address.unwrap(),
)
.await?,
)
@@ -92,7 +130,8 @@ pub async fn run_prover(
if app_args.mock_sc.is_some()
&& let Some(user_filepath) = app_args.mock_user.as_ref()
{
let mock_users = read_mock_user(user_filepath)?;
let mock_users = read_mock_user(user_filepath);
let mock_users = mock_users.unwrap();
debug!("Mock - will register {} users", mock_users.len());
for mock_user in mock_users {
debug!(
@@ -116,12 +155,14 @@ pub async fn run_prover(
}
// Smart contract
// FIXME: use provider
let registry_listener = if app_args.mock_sc.is_some() {
// debug!("No registry listener when mock is enabled");
None
} else {
Some(RegistryListener::new(
app_args.ws_rpc_url.clone().unwrap().as_str(),
app_args.ksc_address.unwrap(),
app_args.rlnsc_address.unwrap(),
user_db_service.get_user_db(),
PROVER_MINIMAL_AMOUNT_FOR_REGISTRATION,
))
@@ -131,7 +172,6 @@ pub async fn run_prover(
None
} else {
Some(TiersListener::new(
app_args.ws_rpc_url.clone().unwrap().as_str(),
app_args.tsc_address.unwrap(),
user_db_service.get_user_db(),
))
@@ -154,14 +194,13 @@ pub async fn run_prover(
rln_identifier,
user_db: user_db_service.get_user_db(),
karma_sc_info: None,
rln_sc_info: None,
provider: provider.clone(),
proof_sender_channel_size: app_args.proof_sender_channel_size,
};
if app_args.ws_rpc_url.is_some() {
let ws_rpc_url = app_args.ws_rpc_url.clone().unwrap();
service.karma_sc_info = Some((ws_rpc_url.clone(), app_args.ksc_address.unwrap()));
service.rln_sc_info = Some((ws_rpc_url, app_args.rlnsc_address.unwrap()));
}
service
};
@@ -187,10 +226,16 @@ pub async fn run_prover(
}
if let Some(registry_listener) = registry_listener {
set.spawn(async move { registry_listener.listen().await });
let p = provider.clone().unwrap();
set.spawn(async move {
registry_listener
.listen(p, provider_with_signer.unwrap())
.await
});
}
if let Some(tiers_listener) = tiers_listener {
set.spawn(async move { tiers_listener.listen().await });
let p = provider.clone().unwrap();
set.spawn(async move { tiers_listener.listen(p).await });
}
set.spawn(async move { epoch_service.listen_for_new_epoch().await });
set.spawn(async move { user_db_service.listen_for_epoch_changes().await });

View File

@@ -218,8 +218,8 @@ mod tests {
debug!("Now recovering secret hash...");
let proof_values_0 = proof_values_store.first().unwrap();
let proof_values_1 = proof_values_store.get(1).unwrap();
println!("proof_values_0: {:?}", proof_values_0);
println!("proof_values_1: {:?}", proof_values_1);
println!("proof_values_0: {proof_values_0:?}");
println!("proof_values_1: {proof_values_1:?}");
let share1 = (proof_values_0.x, proof_values_0.y);
let share2 = (proof_values_1.x, proof_values_1.y);

View File

@@ -2,54 +2,56 @@
use alloy::{
contract::Error as AlloyContractError,
primitives::{Address, U256},
providers::{Provider, ProviderBuilder, WsConnect},
providers::Provider,
sol_types::SolEvent,
transports::{RpcError, TransportError},
};
use num_bigint::BigUint;
use tonic::codegen::tokio_stream::StreamExt;
use tracing::{debug, error, info};
// internal
use crate::error::{AppError, HandleTransferError};
use crate::error::{AppError, HandleTransferError, RegisterSCError};
use crate::user_db::UserDb;
use crate::user_db_error::RegisterError;
use smart_contract::{AlloyWsProvider, KarmaAmountExt, KarmaSC};
use smart_contract::{KarmaAmountExt, KarmaRLNSC, KarmaSC, RLNRegister};
pub(crate) struct RegistryListener {
rpc_url: String,
sc_address: Address,
karma_sc_address: Address,
rln_sc_address: Address,
user_db: UserDb,
minimal_amount: U256,
}
impl RegistryListener {
pub(crate) fn new(
rpc_url: &str,
sc_address: Address,
karma_sc_address: Address,
rln_sc_address: Address,
user_db: UserDb,
minimal_amount: U256,
) -> Self {
Self {
rpc_url: rpc_url.to_string(),
sc_address,
karma_sc_address,
rln_sc_address,
user_db,
minimal_amount,
}
}
/// 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 karma_sc = KarmaSC::new(self.sc_address, provider.clone());
pub(crate) async fn listen<P: Provider + Clone, PS: Provider>(
&self,
provider: P,
provider_with_signer: PS,
) -> Result<(), AppError> {
// let provider = self.setup_provider_ws().await.map_err(AppError::from)?;
let karma_sc = KarmaSC::new(self.karma_sc_address, provider.clone());
// let provider_with_signer = self.setup_provider_with_signer(self.private_key.clone())
// .await
// .map_err(AppError::from)?;
let rln_sc = KarmaRLNSC::new(self.rln_sc_address, provider_with_signer);
let filter = alloy::rpc::types::Filter::new()
.address(self.sc_address)
.address(self.karma_sc_address)
.event(KarmaSC::Transfer::SIGNATURE);
// Subscribe to logs matching the filter.
@@ -60,7 +62,10 @@ impl RegistryListener {
while let Some(log) = stream.next().await {
match KarmaSC::Transfer::decode_log_data(log.data()) {
Ok(transfer_event) => {
match self.handle_transfer_event(&karma_sc, transfer_event).await {
match self
.handle_transfer_event(&karma_sc, &rln_sc, transfer_event)
.await
{
Ok(addr) => {
info!("Registered new user: {}", addr);
}
@@ -93,10 +98,22 @@ impl RegistryListener {
Ok(())
}
// async fn handle_transfer_event(&self, karma_sc: &KarmaSCInstance<AlloyWsProvider>, transfer_event: KarmaSC::Transfer) -> Result<(), HandleTransferError> {
async fn handle_transfer_event<E: Into<AlloyContractError>, KSC: KarmaAmountExt<Error = E>>(
/// Handle transfer event from Karma smart contract
///
/// Handle 'Transfer' event but filter on Transfer event from a _mint call.
/// As we can see here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53bb34057ed97ea9b36d550f1c2c413ef5b6c6bb/contracts/token/ERC20/ERC20.sol#L214
/// _mint function emits a Transfer event (with from_adress set to 0x0). UserDb (on disk) is updated
/// as well as RLN Smart contract.
/// Can panic if RLN Smart contract registration fails, and UserDb remove fails too (but this should
/// never happen)
async fn handle_transfer_event<
E: Into<AlloyContractError>,
KSC: KarmaAmountExt<Error = E>,
RLNSC: RLNRegister<Error = E>,
>(
&self,
karma_sc: &KSC,
rln_sc: &RLNSC,
transfer_event: KarmaSC::Transfer,
) -> Result<Address, HandleTransferError> {
let from_address: Address = transfer_event.from;
@@ -119,9 +136,25 @@ impl RegistryListener {
};
if should_register {
self.user_db
let id_commitment = self
.user_db
.on_new_user(&to_address)
.map_err(HandleTransferError::Register)?;
let id_co =
U256::from_le_slice(BigUint::from(id_commitment).to_bytes_le().as_slice());
if let Err(e) = rln_sc.register_user(&to_address, id_co).await {
// Fail to register user on smart contract
// Remove the user in internal Db
if !self.user_db.remove_user(&to_address, false) {
// Fails if DB & SC are inconsistent
panic!("Unable to register user to SC and to remove it from DB...");
}
let e_ = RegisterSCError::from(e.into());
return Err(HandleTransferError::ScRegister(e_));
}
}
}
@@ -145,6 +178,9 @@ mod tests {
// const ADDR_1: Address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
const ADDR_2: Address = address!("0xb20a608c624Ca5003905aA834De7156C68b2E1d0");
// Mock Karma Sc
struct MockKarmaSc {}
#[async_trait]
@@ -155,6 +191,23 @@ mod tests {
}
}
// Mock RLN Sc
struct MockRLNSc {}
#[async_trait]
impl RLNRegister for MockRLNSc {
type Error = AlloyContractError;
async fn register_user(
&self,
_address: &Address,
_identity_commitment: U256,
) -> Result<(), Self::Error> {
// println!("Registering user: {} with identity commitment: {}...", address, identity_commitment);
Ok(())
}
}
#[tokio::test]
async fn test_handle_transfer_event() {
let epoch = Epoch::from(11);
@@ -177,8 +230,8 @@ mod tests {
let minimal_amount = U256::from(25);
let registry = RegistryListener {
rpc_url: "".to_string(),
sc_address: Default::default(),
rln_sc_address: Default::default(),
karma_sc_address: Default::default(),
user_db,
minimal_amount: U256::from(25),
};
@@ -190,8 +243,9 @@ mod tests {
};
let karma_sc = MockKarmaSc {};
let rln_sc = MockRLNSc {};
registry
.handle_transfer_event(&karma_sc, transfer)
.handle_transfer_event(&karma_sc, &rln_sc, transfer)
.await
.unwrap();

View File

@@ -355,7 +355,7 @@ mod tests {
if let TierMatch::Matched(tier) = result {
assert_eq!(tier.name, "Basic");
} else {
panic!("Expected TierMatch::Matched, got {:?}", result);
panic!("Expected TierMatch::Matched, got {result:?}");
}
// Case 4: Exact match on a tier boundary (start of second tier)
@@ -363,7 +363,7 @@ mod tests {
if let TierMatch::Matched(tier) = result {
assert_eq!(tier.name, "Active");
} else {
panic!("Expected TierMatch::Matched, got {:?}", result);
panic!("Expected TierMatch::Matched, got {result:?}");
}
// Case 5: Karma within a tier range (between third tier)
@@ -371,7 +371,7 @@ mod tests {
if let TierMatch::Matched(tier) = result {
assert_eq!(tier.name, "Regular");
} else {
panic!("Expected TierMatch, got {:?}", result);
panic!("Expected TierMatch, got {result:?}");
}
// Case 6: Exact match on max_karma (end of the third tier)
@@ -379,7 +379,7 @@ mod tests {
if let TierMatch::Matched(tier) = result {
assert_eq!(tier.name, "Regular");
} else {
panic!("Expected TierMatch, got {:?}", result);
panic!("Expected TierMatch, got {result:?}");
}
// Case 7: Karma above all tiers

View File

@@ -1,44 +1,30 @@
// third-party
use alloy::{
primitives::Address,
providers::{Provider, ProviderBuilder, WsConnect},
sol_types::SolEvent,
transports::{RpcError, TransportError},
};
use alloy::{primitives::Address, providers::Provider, sol_types::SolEvent};
use futures::StreamExt;
use tracing::error;
// internal
use crate::error::AppError;
use crate::tier::TierLimits;
use crate::user_db::UserDb;
use smart_contract::KarmaTiers;
use smart_contract::KarmaTiers::KarmaTiersInstance;
use smart_contract::{AlloyWsProvider, KarmaTiers};
pub(crate) struct TiersListener {
rpc_url: String,
sc_address: Address,
user_db: UserDb,
}
impl TiersListener {
pub(crate) fn new(rpc_url: &str, sc_address: Address, user_db: UserDb) -> Self {
pub(crate) fn new(sc_address: Address, user_db: UserDb) -> Self {
Self {
rpc_url: rpc_url.to_string(),
sc_address,
user_db,
}
}
/// 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)?;
pub(crate) async fn listen<P: Provider + Clone>(&self, provider: P) -> Result<(), AppError> {
// let provider = self.setup_provider_ws().await.map_err(AppError::from)?;
let filter = alloy::rpc::types::Filter::new()
.address(self.sc_address)

View File

@@ -1,15 +1,17 @@
use alloy::primitives::{Address, U256};
use futures::FutureExt;
use parking_lot::RwLock;
use prover::{AppArgs, run_prover};
use prover::{AppArgs, MockUser, run_prover};
use std::io::Write;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tempfile::NamedTempFile;
use tokio::task;
use tokio::task::JoinSet;
use tonic::Response;
use tracing::info;
use tracing::{debug, info};
use tracing_test::traced_test;
pub mod prover_proto {
@@ -17,11 +19,12 @@ pub mod prover_proto {
tonic::include_proto!("prover");
}
use crate::prover_proto::{
Address as GrpcAddress, GetUserTierInfoReply, GetUserTierInfoRequest, RegisterUserReply,
RegisterUserRequest, RegistrationStatus, RlnProofFilter, RlnProofReply, SendTransactionReply,
SendTransactionRequest, U256 as GrpcU256, Wei as GrpcWei, rln_prover_client::RlnProverClient,
Address as GrpcAddress, GetUserTierInfoReply, GetUserTierInfoRequest, RlnProofFilter,
RlnProofReply, SendTransactionReply, SendTransactionRequest, U256 as GrpcU256, Wei as GrpcWei,
rln_prover_client::RlnProverClient,
};
/*
async fn register_users(port: u16, addresses: Vec<Address>) {
let url = format!("http://127.0.0.1:{}", port);
let mut client = RlnProverClient::connect(url).await.unwrap();
@@ -41,9 +44,10 @@ async fn register_users(port: u16, addresses: Vec<Address>) {
);
}
}
*/
async fn query_user_info(port: u16, addresses: Vec<Address>) -> Vec<GetUserTierInfoReply> {
let url = format!("http://127.0.0.1:{}", port);
let url = format!("http://127.0.0.1:{port}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let mut result = vec![];
@@ -62,6 +66,7 @@ async fn query_user_info(port: u16, addresses: Vec<Address>) -> Vec<GetUserTierI
result
}
/*
#[tokio::test]
#[traced_test]
async fn test_grpc_register_users() {
@@ -109,6 +114,7 @@ async fn test_grpc_register_users() {
prover_handle.abort();
tokio::time::sleep(Duration::from_secs(1)).await;
}
*/
async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
let start = std::time::Instant::now();
@@ -118,7 +124,7 @@ async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
value: U256::from(1).to_le_bytes::<32>().to_vec(),
};
let url = format!("http://127.0.0.1:{}", port);
let url = format!("http://127.0.0.1:{port}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let addr = GrpcAddress {
@@ -152,27 +158,13 @@ async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
count,
start.elapsed().as_secs_f64()
);
/*
let tx_hash = U256::from(42).to_le_bytes::<32>().to_vec();
let request_0 = SendTransactionRequest {
gas_price: Some(wei),
sender: Some(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();
assert_eq!(response.into_inner().result, true);
*/
}
async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
let start = std::time::Instant::now();
let result = Arc::new(RwLock::new(vec![]));
let url = format!("http://127.0.0.1:{}", port);
let url = format!("http://127.0.0.1:{port}");
let mut client = RlnProverClient::connect(url).await.unwrap();
let request_0 = RlnProofFilter { address: None };
@@ -201,7 +193,7 @@ async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
};
let _res = tokio::time::timeout(Duration::from_secs(500), receiver).await;
println!("_res: {:?}", _res);
println!("_res: {_res:?}");
let res = std::mem::take(&mut *result.write());
println!(
"[proof_collector] elapsed: {} secs",
@@ -213,10 +205,29 @@ async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
#[tokio::test]
#[traced_test]
async fn test_grpc_gen_proof() {
let addresses = vec![
Address::from_str("0xd8da6bf26964af9d7eed9e03e53415d37aa96045").unwrap(),
Address::from_str("0xb20a608c624Ca5003905aA834De7156C68b2E1d0").unwrap(),
let mock_users = vec![
MockUser {
address: Address::from_str("0xd8da6bf26964af9d7eed9e03e53415d37aa96045").unwrap(),
tx_count: 0,
},
MockUser {
address: Address::from_str("0xb20a608c624Ca5003905aA834De7156C68b2E1d0").unwrap(),
tx_count: 0,
},
];
let addresses: Vec<Address> = mock_users.iter().map(|u| u.address).collect();
// Write mock users to tempfile
let mock_users_as_str = serde_json::to_string(&mock_users).unwrap();
let mut temp_file = NamedTempFile::new().unwrap();
let temp_file_path = temp_file.path().to_path_buf();
temp_file.write_all(mock_users_as_str.as_bytes()).unwrap();
temp_file.flush().unwrap();
debug!(
"Mock user temp file path: {}",
temp_file_path.to_str().unwrap()
);
//
let temp_folder = tempfile::tempdir().unwrap();
let temp_folder_tree = tempfile::tempdir().unwrap();
@@ -232,9 +243,9 @@ async fn test_grpc_gen_proof() {
rlnsc_address: None,
tsc_address: None,
mock_sc: Some(true),
mock_user: None,
mock_user: Some(temp_file_path),
config_path: Default::default(),
no_config: Some(true),
no_config: true,
metrics_ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
metrics_port: 30031,
broadcast_channel_size: 500,
@@ -243,13 +254,16 @@ async fn test_grpc_gen_proof() {
proof_sender_channel_size: 500,
};
info!("Starting prover...");
info!("Starting prover with args: {:?}", app_args);
let prover_handle = task::spawn(run_prover(app_args));
// Wait for the prover to be ready
// Note: if unit test is failing - maybe add an optional notification when service is ready
tokio::time::sleep(Duration::from_secs(5)).await;
info!("Registering some users...");
register_users(port, addresses.clone()).await;
// info!("Registering some users...");
// register_users(port, addresses.clone()).await;
info!("Query info for these new users...");
let res = query_user_info(port, addresses.clone()).await;
assert_eq!(res.len(), addresses.len());
info!("Sending tx and collecting proofs...");
let proof_count = 10;

View File

@@ -9,17 +9,10 @@ pub mod prover_proto {
tonic::include_proto!("prover");
}
use crate::prover_proto::{GetUserTierInfoReply, GetUserTierInfoRequest, RegistrationStatus};
use crate::prover_proto::{GetUserTierInfoReply, GetUserTierInfoRequest};
use prover_proto::{
Address as GrpcAddress,
RegisterUserReply,
RegisterUserRequest,
SendTransactionReply,
SendTransactionRequest,
U256 as GrpcU256,
Wei as GrpcWei,
// RegistrationStatus,
rln_prover_client::RlnProverClient,
Address as GrpcAddress, SendTransactionReply, SendTransactionRequest, U256 as GrpcU256,
Wei as GrpcWei, rln_prover_client::RlnProverClient,
};
#[derive(Debug, Clone, Parser)]
@@ -47,8 +40,6 @@ pub struct AppArgs {
#[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")]
@@ -83,19 +74,6 @@ async fn main() {
};
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?

View File

@@ -1,8 +1,12 @@
use alloy::providers::{
Identity, RootProvider,
fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller},
};
/*
use alloy::network::{EthereumWallet};
use alloy::providers::{Identity, RootProvider, fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller}, Provider, WsConnect, ProviderBuilder};
use alloy::providers::fillers::WalletFiller;
*/
use alloy::providers::{Provider, ProviderBuilder, WsConnect};
use alloy::transports::TransportError;
/*
pub type AlloyWsProvider = FillProvider<
JoinFill<
Identity,
@@ -10,3 +14,22 @@ pub type AlloyWsProvider = FillProvider<
>,
RootProvider,
>;
pub type AlloyWsProviderWithSigner = FillProvider<
JoinFill<
JoinFill<
Identity,
JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>
>,
WalletFiller<EthereumWallet>
>,
RootProvider
>;
*/
#[allow(clippy::let_and_return)]
pub async fn ws_provider(rpc_url: String) -> Result<impl Provider, TransportError> {
let ws = WsConnect::new(rpc_url);
let provider = ProviderBuilder::new().connect_ws(ws).await;
provider
}

View File

@@ -1,15 +1,11 @@
// third-party
use alloy::providers::Provider;
use alloy::{
primitives::{Address, U256},
providers::{ProviderBuilder, WsConnect},
providers::Provider,
sol,
transports::{RpcError, TransportErrorKind},
};
use async_trait::async_trait;
use url::Url;
// internal
use crate::AlloyWsProvider;
#[derive(thiserror::Error, Debug)]
pub enum KarmaScError {
@@ -73,19 +69,8 @@ sol! {
}
}
impl KarmaSC::KarmaSCInstance<AlloyWsProvider> {
pub async fn try_new(rpc_url: Url, address: Address) -> Result<Self, KarmaScError> {
let ws = WsConnect::new(rpc_url.as_str());
let provider = ProviderBuilder::new()
.connect_ws(ws)
.await
.map_err(KarmaScError::RpcTransportError)?;
Ok(KarmaSC::new(address, provider))
}
}
#[async_trait]
impl<T: Provider> KarmaAmountExt for KarmaSC::KarmaSCInstance<T> {
impl<P: Provider> KarmaAmountExt for KarmaSC::KarmaSCInstance<P> {
type Error = alloy::contract::Error;
async fn karma_amount(&self, address: &Address) -> Result<U256, Self::Error> {
self.balanceOf(*address).call().await
@@ -97,9 +82,11 @@ impl<T: Provider> KarmaAmountExt for KarmaSC::KarmaSCInstance<T> {
pub(crate) mod tests {
use super::*;
// third-party
use alloy::primitives::U256;
use alloy::primitives::address;
use alloy::sol_types::SolCall;
use alloy::{
primitives::{U256, address},
providers::ProviderBuilder,
sol_types::SolCall,
};
use claims::assert_gt;
sol! {
@@ -144,52 +131,6 @@ pub(crate) mod tests {
}
/*
#[tokio::test]
async fn test_balance_of() {
let provider = ProviderBuilder::new()
.connect_anvil_with_wallet()
;
let contract_distributor = KarmaDistributorMock::deploy(&provider).await.unwrap();
// Deploy the KarmaTiers contract.
let contract = KarmaSC::deploy(&provider).await.unwrap();
// getTierCount call
let addr = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let call_1 = contract.balanceOf(addr);
let result_1 = call_1.call().await.unwrap();
println!("result_1: {:?}", result_1);
assert_eq!(result_1, U256::from(0));
// let call_2_0 = contract.initialize(addr);
// let tx_hash_2_0 = call_2_0.send().await.unwrap().watch().await.unwrap();
// println!("tx_hash_2_0: {:?}", tx_hash_2_0);
// let call_2 = contract.mint(addr, U256::from(100));
// let tx_hash_2 = call_2.send().await.unwrap().watch().await.unwrap();
// println!("tx_hash_2: {:?}", tx_hash_2);
let call_2 = contract.totalSupply();
let result_2 = call_2.call().await.unwrap();
println!("result_2: {:?}", result_2);
// assert_eq!(result_2, U256::from(0));
let call_2_2 = contract.name();
let result_2_2 = call_2_2.call().await.unwrap();
println!("result_2_2: {:?}", result_2_2);
let call_3_0 = contract.mint(addr, U256::from(1));
let tx_hash_3 = call_3_0.send().await.unwrap().watch().await.unwrap();
println!("tx_hash_3: {:?}", tx_hash_3);
let call_3 = contract.balanceOf(addr);
let result_3 = call_3.call().await.unwrap();
println!("result_3: {:?}", result_3);
}
*/
#[tokio::test]
async fn test_karma_amount() {
let provider = ProviderBuilder::new().connect_anvil_with_wallet();

View File

@@ -1,7 +1,8 @@
use alloy::primitives::Address;
use alloy::providers::Provider;
use clap::Parser;
use rustls::crypto::aws_lc_rs;
use smart_contract::{KarmaSC, KarmaScError};
use smart_contract::{KarmaSC, KarmaScError, ws_provider};
use std::str::FromStr;
use url::Url;
@@ -45,7 +46,8 @@ async fn main() -> Result<(), KarmaScError> {
.map_err(|e| KarmaScError::SignerConnectionError(format!("Invalid URL: {e}")))?;
// Connect to Karma contract
let karma_contract = KarmaSC::KarmaSCInstance::try_new(url, karma_contract_addr).await?;
let provider = ws_provider(url.to_string()).await?.erased();
let karma_contract = KarmaSC::KarmaSCInstance::new(karma_contract_addr, provider);
println!("Successfully connected to Karma contract at {karma_contract_addr}",);

View File

@@ -1,17 +1,13 @@
use std::{fmt::Formatter, str::FromStr};
use std::fmt::Formatter;
// third-party
use alloy::providers::Provider;
use alloy::{
network::Ethereum,
primitives::{Address, U256},
providers::{ProviderBuilder, WsConnect},
signers::local::PrivateKeySigner,
sol,
transports::{RpcError, TransportErrorKind},
};
use url::Url;
// internal
use crate::common::AlloyWsProvider;
// use crate::common::AlloyWsProvider;
#[derive(thiserror::Error, Debug)]
pub enum KarmaTiersError {
@@ -29,30 +25,6 @@ pub enum KarmaTiersError {
TierCountTooHigh,
}
/*
sol! {
// https://github.com/vacp2p/staking-reward-streamer/pull/224
#[sol(rpc)]
contract KarmaTiersSC {
event TiersUpdated();
struct Tier {
uint256 minKarma;
uint256 maxKarma;
string name;
uint32 txPerEpoch;
}
// mapping(uint8 id => Tier tier) public tiers;
// uint8 public currentTierId;
Tier[] public tiers;
function getTierCount() external view returns (uint256 count);
}
}
*/
sol!(
// https://github.com/vacp2p/staking-reward-streamer/pull/224
// Compile bytecode using:
@@ -161,7 +133,8 @@ sol!(
}
);
impl KarmaTiers::KarmaTiersInstance<AlloyWsProvider> {
impl<P: Provider> KarmaTiers::KarmaTiersInstance<P> {
/*
/// Try to create a new instance with a signer
pub async fn try_new_with_signer(
rpc_url: Url,
@@ -186,6 +159,9 @@ impl KarmaTiers::KarmaTiersInstance<AlloyWsProvider> {
Ok(KarmaTiers::new(address, provider))
}
*/
/*
/// Read smart contract `tiers` mapping
pub async fn get_tiers(
ws_rpc_url: Url,
@@ -199,9 +175,10 @@ impl KarmaTiers::KarmaTiersInstance<AlloyWsProvider> {
Self::get_tiers_from_provider(&provider, &sc_address).await
}
*/
pub async fn get_tiers_from_provider<T: Provider>(
provider: &T,
pub async fn get_tiers_from_provider(
provider: &P,
sc_address: &Address,
) -> Result<Vec<Tier>, KarmaTiersError> {
let karma_tiers_sc = KarmaTiers::new(*sc_address, provider);
@@ -258,32 +235,12 @@ impl KarmaTiers::KarmaTiersInstance<AlloyWsProvider> {
}
}
/*
#[derive(Debug, Clone, Default, Copy, From, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TierIndex(u8);
impl From<&TierIndex> for u8 {
fn from(value: &TierIndex) -> u8 {
value.0
}
}
impl Add<u8> for TierIndex {
type Output = TierIndex;
fn add(self, rhs: u8) -> Self::Output {
Self(self.0 + rhs)
}
}
*/
#[derive(Debug, Clone, PartialEq)]
pub struct Tier {
pub min_karma: U256,
pub max_karma: U256,
pub name: String,
pub tx_per_epoch: u32,
// pub active: bool,
}
impl From<KarmaTiers::Tier> for Tier {
@@ -297,34 +254,6 @@ impl From<KarmaTiers::Tier> for Tier {
}
}
/*
impl From<KarmaTiersSC::TierAdded> for Tier {
fn from(tier_added: KarmaTiersSC::TierAdded) -> Self {
Self {
min_karma: tier_added.minKarma,
max_karma: tier_added.maxKarma,
name: tier_added.name,
tx_per_epoch: tier_added.txPerEpoch,
// active: true,
}
}
}
*/
/*
impl From<KarmaTiersSC::TierUpdated> for Tier {
fn from(tier_updated: KarmaTiersSC::TierUpdated) -> Self {
Self {
min_karma: tier_updated.minKarma,
max_karma: tier_updated.maxKarma,
name: tier_updated.name,
tx_per_epoch: tier_updated.txPerEpoch,
// active: true,
}
}
}
*/
impl From<KarmaTiers::tiersReturn> for Tier {
fn from(tiers_return: KarmaTiers::tiersReturn) -> Self {
Self {
@@ -352,6 +281,7 @@ impl std::fmt::Debug for KarmaTiers::Tier {
mod tests {
use super::*;
use crate::KarmaTiers::KarmaTiersInstance;
use alloy::providers::ProviderBuilder;
impl PartialEq<KarmaTiers::Tier> for Tier {
fn eq(&self, other: &KarmaTiers::Tier) -> bool {

View File

@@ -1,9 +1,13 @@
use alloy::network::EthereumWallet;
use alloy::providers::{ProviderBuilder, WsConnect};
use alloy::signers::local::PrivateKeySigner;
use alloy::{
hex,
primitives::{Address, U256},
};
use clap::Parser;
use rustls::crypto::aws_lc_rs;
use smart_contract::KarmaTiers::KarmaTiersInstance;
use smart_contract::{KarmaTiers, KarmaTiersError};
use std::str::FromStr;
use url::Url;
@@ -47,13 +51,22 @@ async fn main() -> Result<(), KarmaTiersError> {
return Err(KarmaTiersError::EmptyPrivateKey);
}
// Alloy provider + signer
let provider_with_signer = {
let pk_signer = PrivateKeySigner::from_str(args.private_key.as_str()).unwrap();
let wallet = EthereumWallet::from(pk_signer);
let ws = WsConnect::new(url.clone().as_str());
ProviderBuilder::new()
.wallet(wallet)
.connect_ws(ws)
.await
.map_err(KarmaTiersError::RpcTransportError)?
};
// Connect to KarmaTiers contract
let karma_tiers_contract = KarmaTiers::KarmaTiersInstance::try_new_with_signer(
url.clone(),
contract_addr,
args.private_key,
)
.await?;
let karma_tiers_contract = KarmaTiersInstance::new(contract_addr, provider_with_signer.clone());
println!("Successfully connected to KarmaTiers contract for reading at {contract_addr}",);
@@ -90,7 +103,8 @@ async fn main() -> Result<(), KarmaTiersError> {
// Use the get_tiers function from karma_tiers.rs instead of duplicating code
let current_tiers =
KarmaTiers::KarmaTiersInstance::get_tiers(url.clone(), contract_addr).await?;
KarmaTiersInstance::get_tiers_from_provider(&provider_with_signer, &contract_addr)
.await?;
for (i, tier) in current_tiers.iter().enumerate() {
println!(
@@ -186,8 +200,11 @@ async fn main() -> Result<(), KarmaTiersError> {
println!("============================");
// Use the get_tiers function from karma_tiers.rs instead of duplicating code
let updated_tiers =
KarmaTiers::KarmaTiersInstance::get_tiers(url.clone(), contract_addr).await?;
let updated_tiers = KarmaTiersInstance::get_tiers_from_provider(
&provider_with_signer,
&contract_addr,
)
.await?;
for (i, tier) in updated_tiers.iter().enumerate() {
println!(

View File

@@ -4,7 +4,11 @@ mod karma_tiers;
mod mock;
mod rln_sc;
pub use common::AlloyWsProvider;
pub use common::{
// AlloyWsProvider,
// AlloyWsProviderWithSigner,
ws_provider,
};
pub use karma_sc::{KarmaAmountExt, KarmaSC, KarmaScError};
pub use karma_tiers::{KarmaTiers, KarmaTiersError, Tier};
pub use rln_sc::{KarmaRLNSC, RLNRegister, RlnScError};

View File

@@ -27,10 +27,7 @@ impl RLNRegister for MockKarmaRLNSc {
address: &Address,
identity_commitment: U256,
) -> Result<(), Self::Error> {
debug!(
"Register user ({}) with identity_commitment: {:?}",
address, identity_commitment
);
debug!("Register user ({address}) with identity_commitment: {identity_commitment:?}");
Ok(())
}
}

View File

@@ -2,19 +2,13 @@
use alloy::primitives::U256;
use alloy::providers::Provider;
use alloy::{
network::Ethereum,
primitives::Address,
providers::{ProviderBuilder, WsConnect},
signers::local::PrivateKeySigner,
sol,
transports::{RpcError, TransportErrorKind},
};
use async_trait::async_trait;
use std::str::FromStr;
use url::Url;
use zeroize::Zeroizing;
// internal
use crate::common::AlloyWsProvider;
// use crate::common::AlloyWsProvider;
#[derive(thiserror::Error, Debug)]
pub enum RlnScError {
@@ -70,6 +64,7 @@ sol! {
}
}
/*
impl KarmaRLNSC::KarmaRLNSCInstance<AlloyWsProvider> {
pub async fn try_new_with_signer(
rpc_url: Url,
@@ -94,6 +89,7 @@ impl KarmaRLNSC::KarmaRLNSCInstance<AlloyWsProvider> {
Ok(KarmaRLNSC::new(address, provider))
}
}
*/
#[async_trait]
impl<T: Provider> RLNRegister for KarmaRLNSC::KarmaRLNSCInstance<T> {
@@ -120,6 +116,7 @@ mod tests {
// third-party
use crate::KarmaSC;
use alloy::primitives::address;
use alloy::providers::ProviderBuilder;
use alloy::sol_types::SolCall;
#[tokio::test]

View File

@@ -1,6 +1,9 @@
// std
use std::str::FromStr;
// third-party
use alloy::network::EthereumWallet;
use alloy::providers::{ProviderBuilder, WsConnect};
use alloy::signers::local::PrivateKeySigner;
use alloy::{
hex,
primitives::{Address, U256},
@@ -8,9 +11,8 @@ use alloy::{
use clap::Parser;
use rustls::crypto::aws_lc_rs;
use url::Url;
use zeroize::Zeroizing;
// internal
use smart_contract::{KarmaRLNSC, RlnScError};
use smart_contract::{KarmaRLNSC::KarmaRLNSCInstance, RlnScError};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
@@ -49,9 +51,8 @@ async fn main() -> Result<(), RlnScError> {
println!("Connecting to RPC: {}", args.ws_rpc_url);
let contract_addr = Address::from_str(&args.contract_address).map_err(|e| {
RlnScError::SignerConnectionError(format!("Invalid contract address: {}", e))
})?;
let contract_addr = Address::from_str(&args.contract_address)
.map_err(|e| RlnScError::SignerConnectionError(format!("Invalid contract address: {e}")))?;
let test_identity_commitment = U256::from(args.test_identity_commitment);
let test_user_address = Address::from_str(&args.test_user_address)
@@ -65,12 +66,18 @@ async fn main() -> Result<(), RlnScError> {
}
// Connect to KarmaRLN contract with signer
let rln_contract = KarmaRLNSC::KarmaRLNSCInstance::try_new_with_signer(
url,
contract_addr,
Zeroizing::new(args.private_key),
)
.await?;
let provider_with_signer = {
let pk_signer = PrivateKeySigner::from_str(args.private_key.as_str()).unwrap();
let wallet = EthereumWallet::from(pk_signer);
let ws = WsConnect::new(url.clone().as_str());
ProviderBuilder::new()
.wallet(wallet)
.connect_ws(ws)
.await
.map_err(RlnScError::RpcTransportError)?
};
let rln_contract = KarmaRLNSCInstance::new(contract_addr, provider_with_signer);
println!("Successfully connected to RLN contract with signer at {contract_addr}",);
@@ -92,11 +99,11 @@ async fn main() -> Result<(), RlnScError> {
};
println!("Registry Info:");
println!(" Set size: {}", set_size);
println!(" Current index: {}", current_index);
println!(" Karma address: {}", karma_address);
println!(" Is full: {}", is_full);
println!(" Available slots: {}", available_slots);
println!(" Set size: {set_size}");
println!(" Current index: {current_index}");
println!(" Karma address: {karma_address}");
println!(" Is full: {is_full}");
println!(" Available slots: {available_slots}");
}
_ => {
eprintln!("Failed to get registry info");
@@ -107,7 +114,7 @@ async fn main() -> Result<(), RlnScError> {
match rln_contract.members(test_identity_commitment).call().await {
Ok(member) => {
if member.userAddress != Address::ZERO {
println!("Member {} is registered:", test_identity_commitment);
println!("Member {test_identity_commitment} is registered:");
println!(" User address: {}", member.userAddress);
println!(" Index: {}", member.index);
} else {