Update RegistryListener to interact with Karma smart contract (#4)

* Update RegistryListener to interact with Karma smart contract
* Add karma sc instance in grpc service
This commit is contained in:
Sydhds
2025-06-06 09:22:19 +02:00
committed by GitHub
parent 7525c8c226
commit 632cf67142
11 changed files with 283 additions and 94 deletions

34
Cargo.lock generated
View File

@@ -247,9 +247,9 @@ dependencies = [
[[package]]
name = "alloy-json-abi"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5189fa9a8797e92396bc4b4454c5f2073a4945f7c2b366af9af60f9536558f7a"
checksum = "8b26fdd571915bafe857fccba4ee1a4f352965800e46a53e4a5f50187b7776fa"
dependencies = [
"alloy-primitives",
"alloy-sol-type-parser",
@@ -312,9 +312,9 @@ dependencies = [
[[package]]
name = "alloy-primitives"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70b98b99c1dcfbe74d7f0b31433ff215e7d1555e367d90e62db904f3c9d4ff53"
checksum = "a326d47106039f38b811057215a92139f46eef7983a4b77b10930a0ea5685b1e"
dependencies = [
"alloy-rlp",
"bytes",
@@ -611,9 +611,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60fcfa26956bcb22f66ab13407115197f26ef23abca5b48d39a1946897382d74"
checksum = "d4be1ce1274ddd7fdfac86e5ece1b225e9bba1f2327e20fbb30ee6b9cc1423fe"
dependencies = [
"alloy-sol-macro-expander",
"alloy-sol-macro-input",
@@ -625,9 +625,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro-expander"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72a9b402f0013f1ff8c24066eeafc2207a8e52810a2b18b77776ce7fead5af41"
checksum = "01e92f3708ea4e0d9139001c86c051c538af0146944a2a9c7181753bd944bf57"
dependencies = [
"alloy-json-abi",
"alloy-sol-macro-input",
@@ -644,9 +644,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro-input"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d02d61741337bb6b3f4899c2e3173fe17ffa2810e143d3b28acd953197c8dd79"
checksum = "9afe1bd348a41f8c9b4b54dfb314886786d6201235b0b3f47198b9d910c86bb2"
dependencies = [
"alloy-json-abi",
"const-hex",
@@ -662,9 +662,9 @@ dependencies = [
[[package]]
name = "alloy-sol-type-parser"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b5f5f9f561c29f78ea521ebe2e5ac1633f1b1442dae582f68ecd57c6350042"
checksum = "d6195df2acd42df92a380a8db6205a5c7b41282d0ce3f4c665ecf7911ac292f1"
dependencies = [
"serde",
"winnow",
@@ -672,14 +672,13 @@ dependencies = [
[[package]]
name = "alloy-sol-types"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02635bce18205ff8149fb752c753b0a91ea3f3c8ee04c58846448be4811a640"
checksum = "6185e98a79cf19010722f48a74b5a65d153631d2f038cabd250f4b9e9813b8ad"
dependencies = [
"alloy-json-abi",
"alloy-primitives",
"alloy-sol-macro",
"const-hex",
"serde",
]
@@ -4435,6 +4434,7 @@ dependencies = [
"tracing",
"tracing-subscriber 0.3.19",
"tracing-test",
"url",
]
[[package]]
@@ -4495,9 +4495,9 @@ dependencies = [
[[package]]
name = "syn-solidity"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34c9c96de1f835488c1501092847b522be88c9ac6fb0d4c0fbea92992324c8f4"
checksum = "14c8c8f496c33dc6343dac05b4be8d9e0bca180a4caa81d7b8416b10cc2273cd"
dependencies = [
"paste",
"proc-macro2",

View File

@@ -13,7 +13,8 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing = "0.1.41"
tracing-test = "0.2.5"
alloy = { version = "0.15", features = ["full", "getrandom"] }
alloy = { version = "0.15", features = ["full", "getrandom", "sol-types", "contract"] }
# alloy-sol-types = "1.2.0"
thiserror = "2.0"
futures = "0.3"
rln = { git = "https://github.com/vacp2p/zerokit" }
@@ -30,6 +31,7 @@ http = "*"
async-channel = "2.3.1"
rand = "0.8.5"
derive_more = "2.0.1"
url = "2.5"
[build-dependencies]
tonic-build = "*"

View File

@@ -1,6 +1,8 @@
use std::net::IpAddr;
// third-party
use clap::Parser;
use url::Url;
use alloy::primitives::Address;
#[derive(Debug, Clone, Parser)]
#[command(about = "RLN prover service", long_about = None)]
@@ -16,8 +18,15 @@ pub struct AppArgs {
pub(crate) port: u16,
#[arg(
short = 'r',
long = "rpc_url",
long = "ws_rpc_url",
help = "Websocket rpc url (e.g. wss://eth-mainnet.g.alchemy.com/v2/your-api-key)"
)]
pub(crate) rpc_url: String,
pub(crate) ws_rpc_url: Url,
#[arg(
short = 'k',
long = "ksc",
help = "Karma smart contract address"
)]
pub(crate) ksc_address: Address,
}

View File

@@ -494,7 +494,7 @@ mod tests {
debug!("[Notified] Epoch update...");
let _v = counter.fetch_add(1, Ordering::SeqCst);
}
Ok::<(), AppErrorExt>(())
// Ok::<(), AppErrorExt>(())
}
)
.map_err(|_e| AppErrorExt::Elapsed)

View File

@@ -18,6 +18,8 @@ pub enum AppError {
Alloy2(#[from] RpcError<TransportErrorKind>),
#[error("Epoch service error: {0}")]
EpochError(#[from] WaitUntilError),
#[error(transparent)]
RegistryError(#[from] HandleTransferError),
}
/*
@@ -89,3 +91,11 @@ pub enum GetMerkleTreeProofError {
#[error("Merkle tree error: {0}")]
TreeError(String),
}
#[derive(thiserror::Error, Debug)]
pub enum HandleTransferError {
#[error(transparent)]
Register(#[from] RegisterError),
#[error("Unable to query balance: {0}")]
BalanceOf(#[from] alloy::contract::Error)
}

View File

@@ -16,11 +16,12 @@ use tonic::{
use tonic_web::GrpcWebLayer;
use tower_http::cors::{Any, CorsLayer};
use tracing::debug;
use url::Url;
// internal
use crate::error::{AppError, ProofGenerationStringError, RegisterError};
use crate::proof_generation::{ProofGenerationData, ProofSendingData};
use crate::tier::{KarmaAmount, TierLimit, TierName};
use crate::user_db_service::{KarmaAmountExt, UserDb, UserTierInfo};
use crate::user_db_service::{UserDb, UserTierInfo};
use rln_proof::RlnIdentifier;
pub mod prover_proto {
@@ -40,6 +41,8 @@ use prover_proto::{
rln_proof_reply::Resp as GetProofsResp,
rln_prover_server::{RlnProver, RlnProverServer},
};
use crate::registry_listener::{AlloyWsProvider};
use crate::registry_listener::KarmaSC::KarmaSCInstance;
const PROVER_SERVICE_LIMIT_PER_CONNECTION: usize = 16;
// Timeout for all handlers of a request
@@ -62,6 +65,7 @@ pub struct ProverService {
broadcast::Sender<Result<ProofSendingData, ProofGenerationStringError>>,
broadcast::Receiver<Result<ProofSendingData, ProofGenerationStringError>>,
),
karma_sc: KarmaSCInstance<AlloyWsProvider>
}
#[tonic::async_trait]
@@ -200,16 +204,11 @@ impl RlnProver for ProverService {
} else {
return Err(Status::invalid_argument("No user address"));
};
// TODO: SC call
struct MockKarmaSc {}
impl KarmaAmountExt for MockKarmaSc {
async fn karma_amount(&self, _address: &Address) -> U256 {
U256::from(10)
}
}
let tier_info = self.user_db.user_tier_info(&user, MockKarmaSc {}).await;
let tier_info = self.user_db.user_tier_info::<alloy::contract::Error, KarmaSCInstance<AlloyWsProvider>>(
&user,
&self.karma_sc
).await;
match tier_info {
Ok(tier_info) => Ok(Response::new(GetUserTierInfoReply {
@@ -271,10 +270,16 @@ pub(crate) struct GrpcProverService {
pub addr: SocketAddr,
pub rln_identifier: RlnIdentifier,
pub user_db: UserDb,
pub karma_sc_info: (Url, Address),
}
impl GrpcProverService {
pub(crate) async fn serve(&self) -> Result<(), AppError> {
let karma_sc = KarmaSCInstance::try_new(
self.karma_sc_info.0.clone(), self.karma_sc_info.1)
.await?;
let prover_service = ProverService {
proof_sender: self.proof_sender.clone(),
user_db: self.user_db.clone(),
@@ -283,6 +288,7 @@ impl GrpcProverService {
self.broadcast_channel.0.clone(),
self.broadcast_channel.0.subscribe(),
),
karma_sc,
};
let reflection_service = tonic_reflection::server::Builder::configure()
@@ -356,8 +362,8 @@ impl From<UserTierInfo> for UserTierInfoResult {
}
/// UserTierInfoError to UserTierInfoError (Grpc message) conversion
impl From<crate::user_db_service::UserTierInfoError> for UserTierInfoError {
fn from(value: crate::user_db_service::UserTierInfoError) -> Self {
impl<E> From<crate::user_db_service::UserTierInfoError<E>> for UserTierInfoError where E: std::error::Error {
fn from(value: crate::user_db_service::UserTierInfoError<E>) -> Self {
UserTierInfoError {
message: value.to_string(),
}

View File

@@ -15,6 +15,7 @@ use std::time::Duration;
// third-party
use chrono::{DateTime, Utc};
use clap::Parser;
use alloy::primitives::U256;
use rln_proof::RlnIdentifier;
use tokio::task::JoinSet;
use tracing::level_filters::LevelFilter;
@@ -29,15 +30,19 @@ use crate::args::AppArgs;
use crate::epoch_service::EpochService;
use crate::grpc_service::GrpcProverService;
use crate::proof_service::ProofService;
use crate::registry_listener::{RegistryListener};
use crate::user_db_service::{RateLimit, UserDbService};
const RLN_IDENTIFIER_NAME: &[u8] = b"test-rln-identifier";
const PROVER_SPAM_LIMIT: RateLimit = RateLimit::new(10_000u64);
const PROOF_SERVICE_COUNT: u8 = 8;
const GENESIS: DateTime<Utc> = DateTime::from_timestamp(1431648000, 0).unwrap();
const PROVER_MINIMAL_AMOUNT_FOR_REGISTRATION: U256 = U256::from_le_slice(10u64.to_le_bytes().as_slice());
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
@@ -49,13 +54,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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);
// Epoch
let epoch_service = EpochService::try_from((Duration::from_secs(60 * 2), GENESIS))
.expect("Failed to create epoch service");
@@ -67,6 +65,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
PROVER_SPAM_LIMIT,
);
// Smart contract
// let karma_sc_address = address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984");
let registry_listener =
RegistryListener::new(app_args.ws_rpc_url.as_str(), app_args.ksc_address, user_db_service.get_user_db(), PROVER_MINIMAL_AMOUNT_FOR_REGISTRATION);
// proof service
// FIXME: bound
let (tx, rx) = tokio::sync::broadcast::channel(2);
@@ -85,6 +89,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
addr,
rln_identifier,
user_db: user_db_service.get_user_db(),
karma_sc_info: (app_args.ws_rpc_url, app_args.ksc_address),
};
let mut set = JoinSet::new();
@@ -105,7 +110,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
proof_service.serve().await
});
}
// set.spawn(async move { registry_listener.listen().await });
set.spawn(async move { registry_listener.listen().await });
set.spawn(async move { epoch_service.listen_for_new_epoch().await });
set.spawn(async move { user_db_service.listen_for_epoch_changes().await });
set.spawn(async move { prover_grpc_service.serve().await });

View File

@@ -150,7 +150,7 @@ mod tests {
use tokio::sync::broadcast;
use tracing::info;
// third-party: zerokit
use rln::protocol::{compute_id_secret, deserialize_proof_values, keygen, verify_proof};
use rln::protocol::{compute_id_secret, deserialize_proof_values, verify_proof};
// internal
use crate::user_db_service::UserDbService;
use rln_proof::RlnIdentifier;
@@ -343,7 +343,7 @@ mod tests {
broadcast_receiver: &mut broadcast::Receiver<
Result<ProofSendingData, ProofGenerationStringError>,
>,
verifying_key: &VerifyingKey<Curve>,
// verifying_key: &VerifyingKey<Curve>,
) -> Result<(), AppErrorExt> {
// used by test_user_spamming unit test
@@ -361,7 +361,7 @@ mod tests {
let res = res.unwrap();
let res = res?;
let mut proof_cursor = Cursor::new(&res.proof);
let proof: Proof<Curve> = ArkProof::deserialize_compressed(&mut proof_cursor).unwrap();
let _proof: Proof<Curve> = ArkProof::deserialize_compressed(&mut proof_cursor).unwrap();
let position = proof_cursor.position() as usize;
let proof_cursor_2 = &proof_cursor.get_ref().as_slice()[position..];
let (proof_values, _) = deserialize_proof_values(proof_cursor_2);
@@ -471,14 +471,10 @@ mod tests {
rate_limit,
);
// Verification
let proving_key = zkey_from_folder();
let verification_key = &proving_key.0.vk;
info!("Starting...");
let res = tokio::try_join!(
proof_service.serve().map_err(AppErrorExt::AppError),
proof_reveal_secret(&mut broadcast_receiver, verification_key),
proof_reveal_secret(&mut broadcast_receiver),
proof_sender_2(
&mut proof_tx,
rln_identifier.clone(),
@@ -537,14 +533,10 @@ mod tests {
rate_limit,
);
// Verification
let proving_key = zkey_from_folder();
let verification_key = &proving_key.0.vk;
info!("Starting...");
let res = tokio::try_join!(
let _res = tokio::try_join!(
proof_service.serve().map_err(AppErrorExt::AppError),
proof_reveal_secret(&mut broadcast_receiver, verification_key),
proof_reveal_secret(&mut broadcast_receiver),
proof_sender_2(
&mut proof_tx,
rln_identifier.clone(),

View File

@@ -1,15 +1,18 @@
use crate::error::AppError;
use alloy::eips::BlockNumberOrTag;
use alloy::primitives::Address;
use crate::error::{AppError, HandleTransferError, RegisterError};
use alloy::primitives::{Address, U256};
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 alloy::{sol, sol_types::SolEvent, contract::Error as AlloyContractError};
use alloy::transports::http::reqwest::Url;
use tonic::codegen::tokio_stream::StreamExt;
use tracing::{debug, error, info};
use crate::registry_listener::KarmaSC::KarmaSCInstance;
use crate::user_db_service::{KarmaAmountExt, UserDb};
type AlloyWsProvider = FillProvider<
pub type AlloyWsProvider = FillProvider<
JoinFill<
Identity,
JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
@@ -17,18 +20,46 @@ type AlloyWsProvider = FillProvider<
RootProvider,
>;
sol! {
#[sol(rpc)]
contract KarmaSC {
// From: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L16
event Transfer(address indexed from, address indexed to, uint256 value);
function balanceOf(address account) public view override returns (uint256);
}
}
impl KarmaSCInstance<AlloyWsProvider> {
pub(crate) async fn try_new(rpc_url: Url, address: Address) -> Result<Self, RpcError<TransportError>> {
let ws = WsConnect::new(rpc_url.as_str());
let provider = ProviderBuilder::new().connect_ws(ws).await?;
Ok(KarmaSC::new(address, provider))
}
}
impl KarmaAmountExt for KarmaSCInstance<AlloyWsProvider> {
type Error = alloy::contract::Error;
async fn karma_amount(&self, address: &Address) -> Result<U256, Self::Error> {
self.balanceOf(*address).call().await
}
}
pub(crate) struct RegistryListener {
rpc_url: String,
sc_address: Address,
event: String,
user_db: UserDb,
minimal_amount: U256,
}
impl RegistryListener {
pub(crate) fn new(rpc_url: &str, sc_address: Address, event: &str) -> Self {
pub(crate) fn new(rpc_url: &str, sc_address: Address, user_db: UserDb, minimal_amount: U256) -> Self {
Self {
rpc_url: rpc_url.to_string(),
sc_address,
event: event.to_string(),
user_db,
minimal_amount,
}
}
@@ -41,24 +72,136 @@ impl RegistryListener {
/// 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());
let filter = Filter::new()
let filter = alloy::rpc::types::Filter::new()
.address(self.sc_address)
.event(self.event.as_str())
.from_block(BlockNumberOrTag::Latest);
.event(KarmaSC::Transfer::SIGNATURE);
// Subscribe to logs.
let sub = provider
.subscribe_logs(&filter)
.await
.map_err(AppError::from)?;
let mut stream = sub.into_stream();
// Subscribe to logs matching the filter.
let subscription = provider.subscribe_logs(&filter).await?;
let mut stream = subscription.into_stream();
// Loop through the incoming event logs
while let Some(log) = stream.next().await {
println!("Uniswap token logs: {log:?}");
match KarmaSC::Transfer::decode_log_data(log.data()) {
Ok(transfer_event) => {
match self.handle_transfer_event(&karma_sc, transfer_event).await {
Ok(addr) => {
info!("Registered new user: {}", addr);
}
Err(HandleTransferError::Register(RegisterError::AlreadyRegistered(address))) => {
debug!("Already registered: {}", address);
}
Err(e) => {
error!("Unexpected error: {}", e);
// FIXME: return / continue?
return Err(AppError::RegistryError(e));
}
};
},
Err(e) => {
eprintln!("Error decoding log data: {:?}", e);
// It's also useful to print the raw log data for debugging
eprintln!("Raw log topics: {:?}", log.topics());
eprintln!("Raw log data: {:?}", log.data());
}
}
}
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>>(&self, karma_sc: &KSC, transfer_event: KarmaSC::Transfer) -> Result<Address, HandleTransferError> {
let from_address: Address = transfer_event.from;
let to_address: Address = transfer_event.to;
let amount: U256 = transfer_event.value;
// This is a mint event if from_address is the zero address
if from_address == Address::default() {
let should_register = {
if amount >= self.minimal_amount {
true
} else {
let balance = karma_sc.karma_amount(&to_address)
.await
.map_err(|e| HandleTransferError::BalanceOf(e.into()))?;
balance >= self.minimal_amount
}
};
if should_register {
self.user_db.on_new_user(to_address).map_err(HandleTransferError::Register)?;
}
}
Ok(to_address)
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use alloy::primitives::address;
use parking_lot::RwLock;
use crate::epoch_service::{Epoch, EpochSlice};
// use crate::tier::TIER_LIMITS;
use crate::user_db_service::{UserDbService};
use super::*;
// const ADDR_1: Address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
const ADDR_2: Address = address!("0xb20a608c624Ca5003905aA834De7156C68b2E1d0");
struct MockKarmaSc {}
impl KarmaAmountExt for MockKarmaSc {
type Error = AlloyContractError;
async fn karma_amount(&self, _address: &Address) -> Result<U256, Self::Error> {
Ok(U256::from(10))
}
}
#[tokio::test]
async fn test_handle_transfer_event() {
let epoch = Epoch::from(11);
let epoch_slice = EpochSlice::from(42);
let epoch_store = Arc::new(RwLock::new((epoch, epoch_slice)));
let user_db_service = UserDbService::new(Default::default(), epoch_store, 10.into());
let user_db = user_db_service.get_user_db();
assert!(
user_db_service.get_user_db().get_user(&ADDR_2).is_none()
);
let minimal_amount = U256::from(25);
let registry = RegistryListener {
rpc_url: "".to_string(),
sc_address: Default::default(),
user_db,
minimal_amount: U256::from(25),
};
let transfer = KarmaSC::Transfer {
from: Address::default(),
to: ADDR_2,
value: minimal_amount,
};
let karma_sc = MockKarmaSc {};
registry.handle_transfer_event(&karma_sc, transfer)
.await
.unwrap();
assert!(
user_db_service.get_user_db().get_user(&ADDR_2).is_some()
);
}
}

View File

@@ -177,13 +177,18 @@ pub struct UserTierInfo {
}
#[derive(Debug, thiserror::Error)]
pub enum UserTierInfoError {
pub enum UserTierInfoError<E: std::error::Error> {
#[error("User {0} not registered")]
NotRegistered(Address),
#[error(transparent)]
Contract(E)
}
pub trait KarmaAmountExt {
async fn karma_amount(&self, address: &Address) -> U256;
type Error;
async fn karma_amount(&self, address: &Address) -> Result<U256, Self::Error>;
}
/// User registration + tx counters + tier limits storage
@@ -280,11 +285,11 @@ impl UserDb {
}
/// Get user tier info
pub(crate) async fn user_tier_info<KSC: KarmaAmountExt>(
pub(crate) async fn user_tier_info<E: std::error::Error, KSC: KarmaAmountExt<Error = E>>(
&self,
address: &Address,
karma_sc: KSC,
) -> Result<UserTierInfo, UserTierInfoError> {
karma_sc: &KSC,
) -> Result<UserTierInfo, UserTierInfoError<E>> {
if self.user_registry.has_user(address) {
let (epoch_tx_count, epoch_slice_tx_count) = self
.tx_registry
@@ -292,7 +297,8 @@ impl UserDb {
.map(|ref_v| (ref_v.0, ref_v.1))
.unwrap_or_default();
let karma_amount = karma_sc.karma_amount(address).await;
let karma_amount = karma_sc.karma_amount(address).await
.map_err(|e| UserTierInfoError::Contract(e))?;
let guard = self.tier_limits.read();
let range_res = guard.range((
Included(&KarmaAmount::ZERO),
@@ -409,12 +415,19 @@ mod tests {
use super::*;
use alloy::primitives::address;
use claims::{assert_err, assert_matches};
use derive_more::Display;
#[derive(Debug, Display, thiserror::Error)]
struct DummyError();
struct MockKarmaSc {}
impl KarmaAmountExt for MockKarmaSc {
async fn karma_amount(&self, _address: &Address) -> U256 {
U256::from(10)
type Error = DummyError;
async fn karma_amount(&self, _address: &Address) -> Result<U256, Self::Error> {
Ok(U256::from(10))
}
}
@@ -424,13 +437,16 @@ mod tests {
struct MockKarmaSc2 {}
impl KarmaAmountExt for MockKarmaSc2 {
async fn karma_amount(&self, address: &Address) -> U256 {
type Error = DummyError;
async fn karma_amount(&self, address: &Address) -> Result<U256, Self::Error> {
if address == &ADDR_1 {
U256::from(10)
Ok(U256::from(10))
} else if address == &ADDR_2 {
U256::from(2000)
Ok(U256::from(2000))
} else {
U256::ZERO
Ok(U256::ZERO)
}
}
}
@@ -465,7 +481,7 @@ mod tests {
// Try to update tx counter without registering first
assert_eq!(user_db.on_new_tx(&addr), None);
let tier_info = user_db.user_tier_info(&addr, MockKarmaSc {}).await;
let tier_info = user_db.user_tier_info(&addr, &MockKarmaSc {}).await;
// User is not registered -> no tier info
assert!(matches!(
tier_info,
@@ -475,7 +491,7 @@ mod tests {
user_db.user_registry.register(addr).unwrap();
// Now update user tx counter
assert_eq!(user_db.on_new_tx(&addr), Some(EpochSliceCounter(1)));
let tier_info = user_db.user_tier_info(&addr, MockKarmaSc {}).await.unwrap();
let tier_info = user_db.user_tier_info(&addr, &MockKarmaSc {}).await.unwrap();
assert_eq!(tier_info.epoch_tx_count, 1);
assert_eq!(tier_info.epoch_slice_tx_count, 1);
}
@@ -510,7 +526,7 @@ mod tests {
new_epoch_slice,
);
let addr_1_tier_info = user_db
.user_tier_info(&ADDR_1, MockKarmaSc2 {})
.user_tier_info(&ADDR_1, &MockKarmaSc2 {})
.await
.unwrap();
assert_eq!(addr_1_tier_info.epoch_tx_count, addr_1_tx_count);
@@ -518,7 +534,7 @@ mod tests {
assert_eq!(addr_1_tier_info.tier_name, Some(TierName::from("Basic")));
let addr_2_tier_info = user_db
.user_tier_info(&ADDR_2, MockKarmaSc2 {})
.user_tier_info(&ADDR_2, &MockKarmaSc2 {})
.await
.unwrap();
assert_eq!(addr_2_tier_info.epoch_tx_count, addr_2_tx_count);
@@ -540,7 +556,7 @@ mod tests {
new_epoch_slice,
);
let addr_1_tier_info = user_db
.user_tier_info(&ADDR_1, MockKarmaSc2 {})
.user_tier_info(&ADDR_1, &MockKarmaSc2 {})
.await
.unwrap();
assert_eq!(addr_1_tier_info.epoch_tx_count, 0);
@@ -548,7 +564,7 @@ mod tests {
assert_eq!(addr_1_tier_info.tier_name, Some(TierName::from("Basic")));
let addr_2_tier_info = user_db
.user_tier_info(&ADDR_2, MockKarmaSc2 {})
.user_tier_info(&ADDR_2, &MockKarmaSc2 {})
.await
.unwrap();
assert_eq!(addr_2_tier_info.epoch_tx_count, 0);

View File

@@ -7,9 +7,8 @@ use ark_relations::r1cs::ConstraintMatrices;
use rln::circuit::ZKEY_BYTES;
use rln::circuit::zkey::read_zkey;
use rln::hashers::{hash_to_field, poseidon_hash};
use rln::pm_tree_adapter::PmTree;
use rln::protocol::{
ProofError, RLNProofValues, compute_id_secret, generate_proof, keygen,
ProofError, RLNProofValues, generate_proof,
proof_values_from_witness, rln_witness_from_values,
};
@@ -96,6 +95,13 @@ pub fn compute_rln_proof_and_values(
mod tests {
use super::*;
use zerokit_utils::ZerokitMerkleTree;
use rln::{
protocol::{
keygen,
compute_id_secret
},
pm_tree_adapter::PmTree,
};
#[test]
fn test_recover_secret_hash() {