mirror of
https://github.com/vacp2p/status-rln-prover.git
synced 2026-01-08 05:03:54 -05:00
Update for new KarmaTiers smart contract (#23)
* Update for new KarmaTiers smart contract * Add Anvil unit test for KarmaTiers SC * Add unit test for KarmaSC * Add rln sc unit test
This commit is contained in:
60
Cargo.lock
generated
60
Cargo.lock
generated
@@ -62,6 +62,7 @@ dependencies = [
|
||||
"alloy-eips",
|
||||
"alloy-genesis",
|
||||
"alloy-network",
|
||||
"alloy-node-bindings",
|
||||
"alloy-provider",
|
||||
"alloy-pubsub",
|
||||
"alloy-rpc-client",
|
||||
@@ -244,6 +245,19 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-hardforks"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819a3620fe125e0fff365363315ee5e24c23169173b19747dfd6deba33db8990"
|
||||
dependencies = [
|
||||
"alloy-chains",
|
||||
"alloy-eip2124",
|
||||
"alloy-primitives",
|
||||
"auto_impl",
|
||||
"dyn-clone",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-json-abi"
|
||||
version = "1.2.0"
|
||||
@@ -309,6 +323,27 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-node-bindings"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f70cdf0ba711fb6f1a427fd026e149bd6d64c9da4c020c1c96c69245c4721ac1"
|
||||
dependencies = [
|
||||
"alloy-genesis",
|
||||
"alloy-hardforks",
|
||||
"alloy-network",
|
||||
"alloy-primitives",
|
||||
"alloy-signer",
|
||||
"alloy-signer-local",
|
||||
"k256",
|
||||
"rand 0.8.5",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-primitives"
|
||||
version = "1.2.0"
|
||||
@@ -349,9 +384,11 @@ dependencies = [
|
||||
"alloy-json-rpc",
|
||||
"alloy-network",
|
||||
"alloy-network-primitives",
|
||||
"alloy-node-bindings",
|
||||
"alloy-primitives",
|
||||
"alloy-pubsub",
|
||||
"alloy-rpc-client",
|
||||
"alloy-rpc-types-anvil",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-signer",
|
||||
"alloy-sol-types",
|
||||
@@ -453,6 +490,19 @@ name = "alloy-rpc-types"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c000cab4ec26a4b3e29d144e999e1c539c2fa0abed871bf90311eb3466187ca8"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-types-anvil",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-serde",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types-anvil"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ad84b1927e3c5985afca4a3c3a3a5bdce433de30cf8b2f7e52b642758d235d9"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-types-eth",
|
||||
@@ -1967,6 +2017,12 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
@@ -4840,8 +4896,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"async-trait",
|
||||
"derive_more",
|
||||
"claims",
|
||||
"log",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@ ark-serialize = "0.5"
|
||||
ark-groth16 = "0.5"
|
||||
ark-ff = "0.5"
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
alloy = { version = "1.0", features = ["getrandom", "sol-types", "contract", "provider-ws"] }
|
||||
alloy = { version = "1.0", features = ["getrandom", "sol-types", "contract", "provider-ws", "provider-anvil-node"] }
|
||||
async-trait = "0.1"
|
||||
derive_more = "2.0.1"
|
||||
thiserror = "2.0"
|
||||
# dev
|
||||
criterion = { version = "0.6", features = ["async_tokio"] }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tracing = "0.1.41"
|
||||
alloy.workspace = true
|
||||
thiserror = "2.0"
|
||||
thiserror.workspace = true
|
||||
futures = "0.3"
|
||||
ark-bn254.workspace = true
|
||||
ark-serialize.workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use criterion::{BenchmarkId, Throughput};
|
||||
use criterion::Criterion;
|
||||
use criterion::{BenchmarkId, Throughput};
|
||||
use criterion::{criterion_group, criterion_main};
|
||||
|
||||
// std
|
||||
@@ -175,19 +175,23 @@ fn proof_generation_bench(c: &mut Criterion) {
|
||||
let proof_count = 5;
|
||||
|
||||
group.throughput(Throughput::Elements(proof_count as u64));
|
||||
group.bench_with_input(BenchmarkId::new("proof generation", proof_count), &proof_count, |b, &_s| {
|
||||
b.to_async(&rt).iter(|| {
|
||||
async {
|
||||
let mut set = JoinSet::new();
|
||||
set.spawn(proof_collector(port, proof_count));
|
||||
set.spawn(proof_sender(port, addresses.clone(), proof_count).map(|_r| vec![]));
|
||||
// Wait for proof_sender + proof_collector to complete
|
||||
let res = set.join_all().await;
|
||||
// Check we receive enough proof
|
||||
assert_eq!(res[1].len(), proof_count);
|
||||
}
|
||||
});
|
||||
});
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("proof generation", proof_count),
|
||||
&proof_count,
|
||||
|b, &_s| {
|
||||
b.to_async(&rt).iter(|| {
|
||||
async {
|
||||
let mut set = JoinSet::new();
|
||||
set.spawn(proof_collector(port, proof_count));
|
||||
set.spawn(proof_sender(port, addresses.clone(), proof_count).map(|_r| vec![]));
|
||||
// Wait for proof_sender + proof_collector to complete
|
||||
let res = set.join_all().await;
|
||||
// Check we receive enough proof
|
||||
assert_eq!(res[1].len(), proof_count);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use alloy::transports::{RpcError, TransportErrorKind};
|
||||
use ark_serialize::SerializationError;
|
||||
use rln::error::ProofError;
|
||||
use smart_contract::GetScTiersError;
|
||||
// internal
|
||||
use crate::epoch_service::WaitUntilError;
|
||||
use crate::user_db_error::{RegisterError, UserMerkleTreeIndexError};
|
||||
@@ -20,7 +21,7 @@ pub enum AppError {
|
||||
#[error(transparent)]
|
||||
RegistryError(#[from] HandleTransferError),
|
||||
#[error(transparent)]
|
||||
ContractError(#[from] alloy::contract::Error),
|
||||
TierLimitsError(#[from] GetScTiersError),
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
||||
@@ -188,7 +188,7 @@ where
|
||||
let id_co =
|
||||
U256::from_le_slice(BigUint::from(id_commitment).to_bytes_le().as_slice());
|
||||
|
||||
if let Err(e) = self.karma_rln_sc.register(id_co).await {
|
||||
if let Err(e) = self.karma_rln_sc.register_user(&user, id_co).await {
|
||||
// Fail to register user on smart contract
|
||||
// Remove the user in internal Db
|
||||
if !self.user_db.remove_user(&user, false) {
|
||||
|
||||
@@ -47,7 +47,7 @@ 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::KarmaTiers::KarmaTiersInstance;
|
||||
use smart_contract::TIER_LIMITS;
|
||||
|
||||
const RLN_IDENTIFIER_NAME: &[u8] = b"test-rln-identifier";
|
||||
@@ -63,9 +63,9 @@ pub async fn run_prover(
|
||||
let epoch_service = EpochService::try_from((Duration::from_secs(60 * 2), GENESIS))
|
||||
.expect("Failed to create epoch service");
|
||||
|
||||
let mut tier_limits = if app_args.ws_rpc_url.is_some() {
|
||||
let tier_limits = if app_args.ws_rpc_url.is_some() {
|
||||
TierLimits::from(
|
||||
KarmaTiersSCInstance::get_tiers(
|
||||
KarmaTiersInstance::get_tiers(
|
||||
app_args.ws_rpc_url.clone().unwrap(),
|
||||
app_args.tsc_address.unwrap(),
|
||||
)
|
||||
@@ -78,7 +78,6 @@ pub async fn run_prover(
|
||||
tl
|
||||
};
|
||||
|
||||
tier_limits.filter_inactive();
|
||||
tier_limits.validate()?;
|
||||
|
||||
// User db service
|
||||
|
||||
@@ -8,8 +8,10 @@ use metrics::{counter, histogram};
|
||||
use parking_lot::RwLock;
|
||||
use rln::hashers::hash_to_field;
|
||||
use rln::protocol::serialize_proof_values;
|
||||
use tracing::{Instrument, // debug,
|
||||
debug_span, info
|
||||
use tracing::{
|
||||
Instrument, // debug,
|
||||
debug_span,
|
||||
info,
|
||||
};
|
||||
// internal
|
||||
use crate::epoch_service::{Epoch, EpochSlice};
|
||||
@@ -84,7 +86,6 @@ impl ProofService {
|
||||
// see https://ryhl.io/blog/async-what-is-blocking/
|
||||
|
||||
rayon::spawn(move || {
|
||||
|
||||
let proof_generation_start = std::time::Instant::now();
|
||||
|
||||
let message_id = {
|
||||
@@ -110,7 +111,8 @@ impl ProofService {
|
||||
};
|
||||
let epoch = hash_to_field(epoch_bytes.as_slice());
|
||||
|
||||
let merkle_proof = match user_db.get_merkle_proof(&proof_generation_data.tx_sender) {
|
||||
let merkle_proof = match user_db.get_merkle_proof(&proof_generation_data.tx_sender)
|
||||
{
|
||||
Ok(merkle_proof) => merkle_proof,
|
||||
Err(e) => {
|
||||
let _ = send.send(Err(ProofGenerationError::MerkleProofError(e)));
|
||||
@@ -138,28 +140,25 @@ impl ProofService {
|
||||
|
||||
// Serialize proof
|
||||
let mut output_buffer = Cursor::new(Vec::with_capacity(PROOF_SIZE));
|
||||
if let Err(e) = proof
|
||||
.serialize_compressed(&mut output_buffer)
|
||||
{
|
||||
if let Err(e) = proof.serialize_compressed(&mut output_buffer) {
|
||||
let _ = send.send(Err(ProofGenerationError::Serialization(e)));
|
||||
return;
|
||||
}
|
||||
if let Err(e) = output_buffer
|
||||
.write_all(&serialize_proof_values(&proof_values)) {
|
||||
if let Err(e) = output_buffer.write_all(&serialize_proof_values(&proof_values)) {
|
||||
let _ = send.send(Err(ProofGenerationError::SerializationWrite(e)));
|
||||
return;
|
||||
}
|
||||
|
||||
histogram!(PROOF_SERVICE_GEN_PROOF_TIME.name, "prover" => "proof service")
|
||||
.record(proof_generation_start.elapsed().as_secs_f64());
|
||||
.record(proof_generation_start.elapsed().as_secs_f64());
|
||||
// println!("[proof service {counter_id}] proof generation time: {:?} secs", proof_generation_start.elapsed().as_secs_f64());
|
||||
let labels = [("prover", format!("proof service id: {counter_id}"))];
|
||||
counter!(PROOF_SERVICE_PROOF_COMPUTED.name, &labels).increment(1);
|
||||
|
||||
// Send the result back to Tokio.
|
||||
let _ = send.send(
|
||||
Ok::<Vec<u8>, ProofGenerationError>(output_buffer.into_inner())
|
||||
);
|
||||
let _ = send.send(Ok::<Vec<u8>, ProofGenerationError>(
|
||||
output_buffer.into_inner(),
|
||||
));
|
||||
});
|
||||
|
||||
// Wait for the rayon task.
|
||||
@@ -205,7 +204,7 @@ mod tests {
|
||||
use claims::assert_matches;
|
||||
use futures::TryFutureExt;
|
||||
use tokio::sync::broadcast;
|
||||
use tracing::{info, debug};
|
||||
use tracing::{debug, info};
|
||||
// third-party: zerokit
|
||||
use rln::{
|
||||
circuit::{Curve, zkey_from_folder},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::ops::ControlFlow;
|
||||
// third-party
|
||||
use alloy::primitives::U256;
|
||||
use derive_more::{Deref, DerefMut, From, Into};
|
||||
// internal
|
||||
use smart_contract::{Tier, TierIndex};
|
||||
use smart_contract::Tier;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, From, Into)]
|
||||
pub struct TierLimit(u32);
|
||||
@@ -19,11 +19,11 @@ impl From<&str> for TierName {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, From, Into, Deref, DerefMut, PartialEq)]
|
||||
pub struct TierLimits(BTreeMap<TierIndex, Tier>);
|
||||
pub struct TierLimits(Vec<Tier>);
|
||||
|
||||
impl<const N: usize> From<[(TierIndex, Tier); N]> for TierLimits {
|
||||
fn from(value: [(TierIndex, Tier); N]) -> Self {
|
||||
Self(BTreeMap::from(value))
|
||||
impl<const N: usize> From<[Tier; N]> for TierLimits {
|
||||
fn from(value: [Tier; N]) -> Self {
|
||||
Self(Vec::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,17 +34,10 @@ pub enum TierMatch {
|
||||
/// Karma is above the highest tier
|
||||
AboveHighest,
|
||||
/// Karma is in the range of a defined tier.
|
||||
Matched(TierIndex, Tier),
|
||||
Matched(Tier),
|
||||
}
|
||||
|
||||
impl TierLimits {
|
||||
/// Filter inactive Tier (rejected by function validate)
|
||||
pub(crate) fn filter_inactive(&mut self) -> Self {
|
||||
let map = std::mem::take(&mut self.0);
|
||||
let map_filtered = map.into_iter().filter(|(_k, v)| v.active).collect();
|
||||
Self(map_filtered)
|
||||
}
|
||||
|
||||
/// Validate tier limits (unique names, increasing min & max karma ...)
|
||||
pub(crate) fn validate(&self) -> Result<(), ValidateTierLimitsError> {
|
||||
#[derive(Debug, Default)]
|
||||
@@ -53,49 +46,38 @@ impl TierLimits {
|
||||
prev_min: Option<&'a U256>,
|
||||
prev_max: Option<&'a U256>,
|
||||
prev_tx_per_epoch: Option<&'a u32>,
|
||||
prev_index: Option<&'a TierIndex>,
|
||||
}
|
||||
|
||||
let _context =
|
||||
self.0
|
||||
.iter()
|
||||
.try_fold(Context::default(), |mut state, (tier_index, tier)| {
|
||||
if !tier.active {
|
||||
return Err(ValidateTierLimitsError::InactiveTier);
|
||||
}
|
||||
if *tier_index != (*state.prev_index.unwrap_or(&TierIndex::default()) + 1) {
|
||||
// Tier index must be increasing and consecutive
|
||||
// cf function `_validateNoOverlap` in KarmaTiers.sol
|
||||
return Err(ValidateTierLimitsError::InvalidTierIndex);
|
||||
}
|
||||
let _context = self
|
||||
.0
|
||||
.iter()
|
||||
.try_fold(Context::default(), |mut state, tier| {
|
||||
if tier.min_karma <= *state.prev_min.unwrap_or(&U256::ZERO) {
|
||||
return Err(ValidateTierLimitsError::InvalidMinKarmaAmount);
|
||||
}
|
||||
|
||||
if tier.min_karma <= *state.prev_min.unwrap_or(&U256::ZERO) {
|
||||
return Err(ValidateTierLimitsError::InvalidMinKarmaAmount);
|
||||
}
|
||||
if tier.min_karma <= *state.prev_max.unwrap_or(&U256::ZERO) {
|
||||
return Err(ValidateTierLimitsError::InvalidMinKarmaAmount);
|
||||
}
|
||||
|
||||
if tier.min_karma <= *state.prev_max.unwrap_or(&U256::ZERO) {
|
||||
return Err(ValidateTierLimitsError::InvalidMinKarmaAmount);
|
||||
}
|
||||
if tier.min_karma >= tier.max_karma {
|
||||
return Err(ValidateTierLimitsError::InvalidMaxKarmaAmount);
|
||||
}
|
||||
|
||||
if tier.min_karma >= tier.max_karma {
|
||||
return Err(ValidateTierLimitsError::InvalidMaxKarmaAmount);
|
||||
}
|
||||
if tier.tx_per_epoch <= *state.prev_tx_per_epoch.unwrap_or(&0) {
|
||||
return Err(ValidateTierLimitsError::InvalidTierLimit);
|
||||
}
|
||||
|
||||
if tier.tx_per_epoch <= *state.prev_tx_per_epoch.unwrap_or(&0) {
|
||||
return Err(ValidateTierLimitsError::InvalidTierLimit);
|
||||
}
|
||||
if state.tier_names.contains(&tier.name) {
|
||||
return Err(ValidateTierLimitsError::NonUniqueTierName);
|
||||
}
|
||||
|
||||
if state.tier_names.contains(&tier.name) {
|
||||
return Err(ValidateTierLimitsError::NonUniqueTierName);
|
||||
}
|
||||
|
||||
state.prev_min = Some(&tier.min_karma);
|
||||
state.prev_max = Some(&tier.max_karma);
|
||||
state.prev_tx_per_epoch = Some(&tier.tx_per_epoch);
|
||||
state.tier_names.insert(tier.name.clone());
|
||||
state.prev_index = Some(tier_index);
|
||||
Ok(state)
|
||||
})?;
|
||||
state.prev_min = Some(&tier.min_karma);
|
||||
state.prev_max = Some(&tier.max_karma);
|
||||
state.prev_tx_per_epoch = Some(&tier.tx_per_epoch);
|
||||
state.tier_names.insert(tier.name.clone());
|
||||
Ok(state)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -103,33 +85,27 @@ impl TierLimits {
|
||||
/// Given some karma amount, find the matching Tier. Assume all tiers are active.
|
||||
pub(crate) fn get_tier_by_karma(&self, karma_amount: &U256) -> TierMatch {
|
||||
struct Context<'a> {
|
||||
current: Option<(&'a TierIndex, &'a Tier)>,
|
||||
current: Option<&'a Tier>,
|
||||
}
|
||||
|
||||
let ctx_initial = Context { current: None };
|
||||
let ctx = self
|
||||
.0
|
||||
.iter()
|
||||
.try_fold(ctx_initial, |mut state, (tier_index, tier)| {
|
||||
// Assume all the tier are active but checks it at dev time
|
||||
debug_assert!(tier.active, "Find a non active tier");
|
||||
|
||||
if karma_amount < &tier.min_karma {
|
||||
// Early break - above lowest tier (< lowest_tier.min_karma)
|
||||
ControlFlow::Break(state)
|
||||
} else if karma_amount >= &tier.min_karma && karma_amount <= &tier.max_karma {
|
||||
// Found a match - update ctx and break
|
||||
state.current = Some((tier_index, tier));
|
||||
ControlFlow::Break(state)
|
||||
} else {
|
||||
ControlFlow::Continue(state)
|
||||
}
|
||||
});
|
||||
let ctx = self.0.iter().try_fold(ctx_initial, |mut state, tier| {
|
||||
if karma_amount < &tier.min_karma {
|
||||
// Early break - above lowest tier (< lowest_tier.min_karma)
|
||||
ControlFlow::Break(state)
|
||||
} else if karma_amount >= &tier.min_karma && karma_amount <= &tier.max_karma {
|
||||
// Found a match - update ctx and break
|
||||
state.current = Some(tier);
|
||||
ControlFlow::Break(state)
|
||||
} else {
|
||||
ControlFlow::Continue(state)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(ctx) = ctx.break_value() {
|
||||
// ControlFlow::Break
|
||||
if let Some((tier_index, tier)) = ctx.current {
|
||||
TierMatch::Matched(*tier_index, tier.clone())
|
||||
if let Some(tier) = ctx.current {
|
||||
TierMatch::Matched(tier.clone())
|
||||
} else {
|
||||
TierMatch::UnderLowest
|
||||
}
|
||||
@@ -148,12 +124,8 @@ pub enum ValidateTierLimitsError {
|
||||
InvalidMaxKarmaAmount,
|
||||
#[error("Invalid Tier limit (must be increasing)")]
|
||||
InvalidTierLimit,
|
||||
#[error("Invalid Tier index (must be increasing & consecutive)")]
|
||||
InvalidTierIndex,
|
||||
#[error("Non unique Tier name")]
|
||||
NonUniqueTierName,
|
||||
#[error("Non active Tier")]
|
||||
InactiveTier,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -162,98 +134,21 @@ mod tests {
|
||||
use alloy::primitives::U256;
|
||||
use claims::assert_matches;
|
||||
|
||||
#[test]
|
||||
fn test_filter_inactive() {
|
||||
let mut tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(0),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Power User".to_string(),
|
||||
min_karma: U256::from(500),
|
||||
max_karma: U256::from(999),
|
||||
tx_per_epoch: 86400,
|
||||
active: false,
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
let filtered = tier_limits.filter_inactive();
|
||||
assert_eq!(filtered.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_fails_with_inactive_tier() {
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: false,
|
||||
},
|
||||
),
|
||||
]);
|
||||
assert_matches!(
|
||||
tier_limits.validate(),
|
||||
Err(ValidateTierLimitsError::InactiveTier)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_failed_when_karma_overlapping_between_tier() {
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(150),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(150),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -264,32 +159,24 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_validate_fails_when_min_karma_equal_or_greater_max_karma() {
|
||||
let tier_limits = TierLimits::from([(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
)]);
|
||||
let tier_limits = TierLimits::from([Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
}]);
|
||||
|
||||
assert_matches!(
|
||||
tier_limits.validate(),
|
||||
Err(ValidateTierLimitsError::InvalidMaxKarmaAmount)
|
||||
);
|
||||
|
||||
let tier_limits = TierLimits::from([(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(500),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
)]);
|
||||
let tier_limits = TierLimits::from([Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(500),
|
||||
max_karma: U256::from(100),
|
||||
tx_per_epoch: 6,
|
||||
}]);
|
||||
|
||||
assert_matches!(
|
||||
tier_limits.validate(),
|
||||
@@ -302,26 +189,18 @@ mod tests {
|
||||
// Case 1: Duplicate min_karma values
|
||||
{
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -333,26 +212,18 @@ mod tests {
|
||||
// Case 2: Decreasing min_karma values
|
||||
{
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -367,26 +238,18 @@ mod tests {
|
||||
// Case 1: Duplicate tx_per_epoch values
|
||||
{
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -398,26 +261,18 @@ mod tests {
|
||||
// Case 2: Decreasing tx_per_epoch values
|
||||
{
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -430,26 +285,18 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_fails_with_duplicate_tier_names() {
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
@@ -458,40 +305,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_fails_tier_index() {
|
||||
// Non-consecutive tier index
|
||||
{
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(3),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
assert_matches!(
|
||||
tier_limits.validate(),
|
||||
Err(ValidateTierLimitsError::InvalidTierIndex)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_and_get_tier_by_karma_with_empty_tier_limits() {
|
||||
let tier_limits = TierLimits::default();
|
||||
@@ -505,36 +318,24 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_tier_by_karma_bounds_and_ranges() {
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(3),
|
||||
Tier {
|
||||
name: "Regular".to_string(),
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(499),
|
||||
tx_per_epoch: 720,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
Tier {
|
||||
name: "Regular".to_string(),
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(499),
|
||||
tx_per_epoch: 720,
|
||||
},
|
||||
]);
|
||||
|
||||
// Case 1: Zero karma
|
||||
@@ -547,8 +348,7 @@ mod tests {
|
||||
|
||||
// Case 3: Exact match on min_karma (start of first tier)
|
||||
let result = tier_limits.get_tier_by_karma(&U256::from(10));
|
||||
if let TierMatch::Matched(index, tier) = result {
|
||||
assert_eq!(index, TierIndex::from(1));
|
||||
if let TierMatch::Matched(tier) = result {
|
||||
assert_eq!(tier.name, "Basic");
|
||||
} else {
|
||||
panic!("Expected TierMatch::Matched, got {:?}", result);
|
||||
@@ -556,8 +356,7 @@ mod tests {
|
||||
|
||||
// Case 4: Exact match on a tier boundary (start of second tier)
|
||||
let result = tier_limits.get_tier_by_karma(&U256::from(50));
|
||||
if let TierMatch::Matched(index, tier) = result {
|
||||
assert_eq!(index, TierIndex::from(2));
|
||||
if let TierMatch::Matched(tier) = result {
|
||||
assert_eq!(tier.name, "Active");
|
||||
} else {
|
||||
panic!("Expected TierMatch::Matched, got {:?}", result);
|
||||
@@ -565,8 +364,7 @@ mod tests {
|
||||
|
||||
// Case 5: Karma within a tier range (between third tier)
|
||||
let result = tier_limits.get_tier_by_karma(&U256::from(250));
|
||||
if let TierMatch::Matched(index, tier) = result {
|
||||
assert_eq!(index, TierIndex::from(3));
|
||||
if let TierMatch::Matched(tier) = result {
|
||||
assert_eq!(tier.name, "Regular");
|
||||
} else {
|
||||
panic!("Expected TierMatch, got {:?}", result);
|
||||
@@ -574,8 +372,7 @@ mod tests {
|
||||
|
||||
// Case 6: Exact match on max_karma (end of the third tier)
|
||||
let result = tier_limits.get_tier_by_karma(&U256::from(499));
|
||||
if let TierMatch::Matched(index, tier) = result {
|
||||
assert_eq!(index, TierIndex::from(3));
|
||||
if let TierMatch::Matched(tier) = result {
|
||||
assert_eq!(tier.name, "Regular");
|
||||
} else {
|
||||
panic!("Expected TierMatch, got {:?}", result);
|
||||
@@ -585,33 +382,4 @@ mod tests {
|
||||
let result = tier_limits.get_tier_by_karma(&U256::from(1000));
|
||||
assert_eq!(result, TierMatch::AboveHighest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Find a non active tier")]
|
||||
fn test_get_tier_by_karma_ignores_inactive_tiers() {
|
||||
let tier_limits = TierLimits::from([
|
||||
(
|
||||
TierIndex::from(0),
|
||||
Tier {
|
||||
name: "Basic".to_string(),
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
tx_per_epoch: 6,
|
||||
active: false,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
name: "Active".to_string(),
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
let _result = tier_limits.get_tier_by_karma(&U256::from(25));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ use futures::StreamExt;
|
||||
use tracing::error;
|
||||
// internal
|
||||
use crate::error::AppError;
|
||||
use crate::tier::TierLimits;
|
||||
use crate::user_db::UserDb;
|
||||
use smart_contract::{AlloyWsProvider, KarmaTiersSC, Tier, TierIndex};
|
||||
use smart_contract::KarmaTiers::KarmaTiersInstance;
|
||||
use smart_contract::{AlloyWsProvider, KarmaTiers};
|
||||
|
||||
pub(crate) struct TiersListener {
|
||||
rpc_url: String,
|
||||
@@ -40,42 +42,43 @@ impl TiersListener {
|
||||
|
||||
let filter = alloy::rpc::types::Filter::new()
|
||||
.address(self.sc_address)
|
||||
.event(KarmaTiersSC::TierAdded::SIGNATURE)
|
||||
.event(KarmaTiersSC::TierUpdated::SIGNATURE);
|
||||
.event(KarmaTiers::TiersUpdated::SIGNATURE);
|
||||
|
||||
// Subscribe to logs matching the filter.
|
||||
let subscription = provider.subscribe_logs(&filter).await?;
|
||||
let subscription = provider.clone().subscribe_logs(&filter).await?;
|
||||
let mut stream = subscription.into_stream();
|
||||
|
||||
// Loop through the incoming event logs
|
||||
while let Some(log) = stream.next().await {
|
||||
if let Ok(tier_added) = KarmaTiersSC::TierAdded::decode_log_data(log.data()) {
|
||||
let tier_id: TierIndex = tier_added.tierId.into();
|
||||
if let Err(e) = self.user_db.on_new_tier(tier_id, Tier::from(tier_added)) {
|
||||
if let Ok(_tu) = KarmaTiers::TiersUpdated::decode_log_data(log.data()) {
|
||||
let tier_limits =
|
||||
match KarmaTiersInstance::get_tiers_from_provider(&provider, &self.sc_address)
|
||||
.await
|
||||
{
|
||||
Ok(tier_limits) => tier_limits,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Error while getting tiers limits from smart contract: {}",
|
||||
e
|
||||
);
|
||||
return Err(AppError::TierLimitsError(e));
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self
|
||||
.user_db
|
||||
.on_tier_limits_updated(TierLimits::from(tier_limits))
|
||||
{
|
||||
// If there is an error here, we assume this is an error by the user
|
||||
// updating the Tier limits (and thus we don't want to shut down the prover)
|
||||
error!("Error while adding tier (index: {:?}): {}", tier_id, e);
|
||||
error!("Error while updating tier limits: {}", e);
|
||||
}
|
||||
} else {
|
||||
match KarmaTiersSC::TierUpdated::decode_log_data(log.data()) {
|
||||
Ok(tier_updated) => {
|
||||
let tier_id: TierIndex = tier_updated.tierId.into();
|
||||
if let Err(e) = self
|
||||
.user_db
|
||||
.on_tier_updated(tier_updated.tierId.into(), Tier::from(tier_updated))
|
||||
{
|
||||
// If there is an error here, we assume this is an error by the user
|
||||
// updating the Tier limits (and thus we don't want to shut down the prover)
|
||||
error!("Error while updating tier (index: {:?}): {}", tier_id, 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());
|
||||
}
|
||||
}
|
||||
// Should never happen as TiersUpdated is empty
|
||||
eprintln!("Error decoding log data");
|
||||
// It's also useful to print the raw log data for debugging
|
||||
eprintln!("Raw log topics: {:?}", log.topics());
|
||||
eprintln!("Raw log data: {:?}", log.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::user_db_serialization::{
|
||||
};
|
||||
use crate::user_db_types::{EpochCounter, EpochSliceCounter, MerkleTreeIndex, RateLimit};
|
||||
use rln_proof::{RlnUserIdentity, ZerokitMerkleTree};
|
||||
use smart_contract::{KarmaAmountExt, Tier, TierIndex};
|
||||
use smart_contract::KarmaAmountExt;
|
||||
|
||||
const MERKLE_TREE_HEIGHT: usize = 20;
|
||||
pub const USER_CF: &str = "user";
|
||||
@@ -534,41 +534,10 @@ impl UserDb {
|
||||
Ok(tier_limits)
|
||||
}
|
||||
|
||||
pub(crate) fn on_new_tier(
|
||||
pub(crate) fn on_tier_limits_updated(
|
||||
&self,
|
||||
tier_index: TierIndex,
|
||||
tier: Tier,
|
||||
tier_limits: TierLimits,
|
||||
) -> Result<(), SetTierLimitsError> {
|
||||
let mut tier_limits = self.get_tier_limits()?;
|
||||
tier_limits.insert(tier_index, tier);
|
||||
tier_limits.validate()?;
|
||||
|
||||
// Serialize
|
||||
let tier_limits_serializer = TierLimitsSerializer::default();
|
||||
let mut buffer = Vec::with_capacity(tier_limits_serializer.size_hint(tier_limits.len()));
|
||||
// Unwrap safe - already validated - should always serialize
|
||||
tier_limits_serializer
|
||||
.serialize(&tier_limits, &mut buffer)
|
||||
.unwrap();
|
||||
|
||||
// Write
|
||||
let cf = self.get_tier_limits_cf();
|
||||
self.db
|
||||
.put_cf(cf, TIER_LIMITS_NEXT_KEY.as_slice(), buffer)
|
||||
.map_err(SetTierLimitsError::Db)
|
||||
}
|
||||
|
||||
pub(crate) fn on_tier_updated(
|
||||
&self,
|
||||
tier_index: TierIndex,
|
||||
tier: Tier,
|
||||
) -> Result<(), SetTierLimitsError> {
|
||||
let mut tier_limits = self.get_tier_limits()?;
|
||||
if !tier_limits.contains_key(&tier_index) {
|
||||
return Err(SetTierLimitsError::InvalidUpdateTierIndex);
|
||||
}
|
||||
|
||||
tier_limits.entry(tier_index).and_modify(|e| *e = tier);
|
||||
tier_limits.validate()?;
|
||||
|
||||
// Serialize
|
||||
@@ -622,7 +591,7 @@ impl UserDb {
|
||||
tier_limit: None,
|
||||
};
|
||||
|
||||
if let TierMatch::Matched(_tier_index, tier) = tier_match {
|
||||
if let TierMatch::Matched(tier) = tier_match {
|
||||
t.tier_name = Some(tier.name.into());
|
||||
t.tier_limit = Some(TierLimit::from(tier.tx_per_epoch));
|
||||
}
|
||||
@@ -661,25 +630,6 @@ mod tests {
|
||||
const ADDR_1: Address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
|
||||
const ADDR_2: Address = address!("0xb20a608c624Ca5003905aA834De7156C68b2E1d0");
|
||||
|
||||
/*
|
||||
struct MockKarmaSc2 {}
|
||||
|
||||
#[async_trait]
|
||||
impl KarmaAmountExt for MockKarmaSc2 {
|
||||
type Error = DummyError;
|
||||
|
||||
async fn karma_amount(&self, address: &Address) -> Result<U256, Self::Error> {
|
||||
if address == &ADDR_1 {
|
||||
Ok(U256::from(10))
|
||||
} else if address == &ADDR_2 {
|
||||
Ok(U256::from(2000))
|
||||
} else {
|
||||
Ok(U256::ZERO)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_user_register() {
|
||||
let temp_folder = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -59,8 +59,6 @@ pub enum UserMerkleTreeIndexError {
|
||||
pub enum SetTierLimitsError {
|
||||
#[error(transparent)]
|
||||
Validate(#[from] ValidateTierLimitsError),
|
||||
#[error("Updating an invalid tier index")]
|
||||
InvalidUpdateTierIndex,
|
||||
#[error(transparent)]
|
||||
Db(#[from] rocksdb::Error),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::num::TryFromIntError;
|
||||
use std::string::FromUtf8Error;
|
||||
// third-party
|
||||
@@ -17,7 +16,7 @@ use rln_proof::RlnUserIdentity;
|
||||
// internal
|
||||
use crate::tier::TierLimits;
|
||||
use crate::user_db_types::MerkleTreeIndex;
|
||||
use smart_contract::{Tier, TierIndex};
|
||||
use smart_contract::Tier;
|
||||
|
||||
pub(crate) struct RlnUserIdentitySerializer {}
|
||||
|
||||
@@ -106,7 +105,6 @@ impl TierSerializer {
|
||||
buffer.extend(name_len.to_le_bytes());
|
||||
buffer.extend(value.name.as_bytes());
|
||||
buffer.extend(value.tx_per_epoch.to_le_bytes().as_slice());
|
||||
buffer.push(u8::from(value.active));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -155,8 +153,6 @@ impl TierDeserializer {
|
||||
let name = String::from_utf8(name.to_vec())
|
||||
.map_err(|e| nom::Err::Error(TierDeserializeError::Utf8Error(e)))?;
|
||||
let (input, tx_per_epoch) = le_u32(input)?;
|
||||
let (input, active) = take(1usize)(input)?;
|
||||
let active = active[0] != 0;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
@@ -165,7 +161,6 @@ impl TierDeserializer {
|
||||
max_karma,
|
||||
name,
|
||||
tx_per_epoch,
|
||||
active,
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -185,9 +180,8 @@ impl TierLimitsSerializer {
|
||||
let len = value.len() as u32;
|
||||
buffer.extend(len.to_le_bytes());
|
||||
let mut tier_buffer = Vec::with_capacity(self.tier_serializer.size_hint());
|
||||
value.iter().try_for_each(|(k, v)| {
|
||||
buffer.push(k.into());
|
||||
self.tier_serializer.serialize(v, &mut tier_buffer)?;
|
||||
value.iter().try_for_each(|t| {
|
||||
self.tier_serializer.serialize(t, &mut tier_buffer)?;
|
||||
buffer.extend_from_slice(&tier_buffer);
|
||||
tier_buffer.clear();
|
||||
Ok(())
|
||||
@@ -209,16 +203,14 @@ impl TierLimitsDeserializer {
|
||||
&self,
|
||||
buffer: &'a [u8],
|
||||
) -> IResult<&'a [u8], TierLimits, TierDeserializeError<&'a [u8]>> {
|
||||
let (input, tiers): (&[u8], BTreeMap<TierIndex, Tier>) = length_count(
|
||||
let (input, tiers): (&[u8], Vec<Tier>) = length_count(
|
||||
le_u32,
|
||||
context("Tier index & Tier deser", |input: &'a [u8]| {
|
||||
let (input, tier_index) = take(1usize)(input)?;
|
||||
let tier_index = TierIndex::from(tier_index[0]);
|
||||
let (input, tier) = self.tier_deserializer.deserialize(input)?;
|
||||
Ok((input, (tier_index, tier)))
|
||||
Ok((input, tier))
|
||||
}),
|
||||
)
|
||||
.map(BTreeMap::from_iter)
|
||||
.map(Vec::from_iter)
|
||||
.parse(buffer)?;
|
||||
|
||||
Ok((input, TierLimits::from(tiers)))
|
||||
@@ -270,7 +262,6 @@ mod tests {
|
||||
max_karma: U256::from(u64::MAX),
|
||||
name: "All".to_string(),
|
||||
tx_per_epoch: 10_000_000,
|
||||
active: false,
|
||||
};
|
||||
|
||||
let serializer = TierSerializer {};
|
||||
@@ -290,20 +281,15 @@ mod tests {
|
||||
max_karma: U256::from(4),
|
||||
name: "Basic".to_string(),
|
||||
tx_per_epoch: 10_000,
|
||||
active: false,
|
||||
};
|
||||
let tier_2 = Tier {
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(u64::MAX),
|
||||
name: "Premium".to_string(),
|
||||
tx_per_epoch: 1_000_000_000,
|
||||
active: true,
|
||||
};
|
||||
|
||||
let tier_limits = TierLimits::from(BTreeMap::from([
|
||||
(TierIndex::from(1), tier_1),
|
||||
(TierIndex::from(2), tier_2),
|
||||
]));
|
||||
let tier_limits = TierLimits::from([tier_1, tier_2]);
|
||||
|
||||
let serializer = TierLimitsSerializer::default();
|
||||
let mut buffer = Vec::with_capacity(serializer.size_hint(tier_limits.len()));
|
||||
|
||||
@@ -4,10 +4,9 @@ mod user_db_tests {
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
// third-party
|
||||
use alloy::primitives::{Address, address};
|
||||
use claims::assert_matches;
|
||||
use parking_lot::RwLock;
|
||||
use crate::epoch_service::{Epoch, EpochSlice};
|
||||
use alloy::primitives::{Address, address};
|
||||
use parking_lot::RwLock;
|
||||
// internal
|
||||
use crate::user_db::UserDb;
|
||||
use crate::user_db_types::{EpochCounter, EpochSliceCounter, MerkleTreeIndex};
|
||||
@@ -17,7 +16,6 @@ mod user_db_tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_incr_tx_counter_2() {
|
||||
|
||||
// Same as test_incr_tx_counter but multi users AND multi incr
|
||||
|
||||
let temp_folder = tempfile::tempdir().unwrap();
|
||||
@@ -35,7 +33,7 @@ mod user_db_tests {
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
|
||||
// Register users
|
||||
user_db.register(ADDR_1).unwrap();
|
||||
@@ -83,7 +81,6 @@ mod user_db_tests {
|
||||
user_db.get_tx_counter(&ADDR_2),
|
||||
Ok((EpochCounter::from(2), EpochSliceCounter::from(2)))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -111,7 +111,6 @@ async fn test_grpc_register_users() {
|
||||
}
|
||||
|
||||
async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let chain_id = GrpcU256 {
|
||||
@@ -148,7 +147,11 @@ async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
println!("[proof_sender] sent {} tx - elapsed: {} secs", count, start.elapsed().as_secs_f64());
|
||||
println!(
|
||||
"[proof_sender] sent {} tx - elapsed: {} secs",
|
||||
count,
|
||||
start.elapsed().as_secs_f64()
|
||||
);
|
||||
|
||||
/*
|
||||
let tx_hash = U256::from(42).to_le_bytes::<32>().to_vec();
|
||||
@@ -166,7 +169,6 @@ async fn proof_sender(port: u16, addresses: Vec<Address>, proof_count: usize) {
|
||||
}
|
||||
|
||||
async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let result = Arc::new(RwLock::new(vec![]));
|
||||
|
||||
@@ -190,7 +192,10 @@ async fn proof_collector(port: u16, proof_count: usize) -> Vec<RlnProofReply> {
|
||||
if count >= proof_count {
|
||||
break;
|
||||
}
|
||||
println!("count {count} - elapsed: {} secs", start_per_message.elapsed().as_secs_f64());
|
||||
println!(
|
||||
"count {count} - elapsed: {} secs",
|
||||
start_per_message.elapsed().as_secs_f64()
|
||||
);
|
||||
start_per_message = std::time::Instant::now();
|
||||
}
|
||||
};
|
||||
@@ -198,7 +203,10 @@ 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);
|
||||
let res = std::mem::take(&mut *result.write());
|
||||
println!("[proof_collector] elapsed: {} secs", start.elapsed().as_secs_f64());
|
||||
println!(
|
||||
"[proof_collector] elapsed: {} secs",
|
||||
start.elapsed().as_secs_f64()
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
@@ -7,5 +7,10 @@ edition = "2024"
|
||||
alloy.workspace = true
|
||||
url.workspace = true
|
||||
async-trait.workspace = true
|
||||
derive_more.workspace = true
|
||||
log = "0.4.27"
|
||||
thiserror.workspace = true
|
||||
log = "0.4.27"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
claims = "0.8"
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ mod rln_sc;
|
||||
|
||||
pub use common::AlloyWsProvider;
|
||||
pub use karma_sc::{KarmaAmountExt, KarmaSC};
|
||||
pub use karma_tiers::{KarmaTiersSC, Tier, TierIndex};
|
||||
pub use karma_tiers::{GetScTiersError, KarmaTiers, Tier};
|
||||
pub use rln_sc::{KarmaRLNSC, RLNRegister};
|
||||
|
||||
pub use mock::{MockKarmaRLNSc, MockKarmaSc, TIER_LIMITS};
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::karma_tiers::{Tier, TierIndex};
|
||||
use crate::karma_tiers::Tier;
|
||||
use crate::{KarmaAmountExt, RLNRegister};
|
||||
use alloy::primitives::{Address, U256};
|
||||
use async_trait::async_trait;
|
||||
use log::debug;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
pub struct MockKarmaSc {}
|
||||
@@ -23,66 +22,50 @@ pub struct MockKarmaRLNSc {}
|
||||
impl RLNRegister for MockKarmaRLNSc {
|
||||
type Error = alloy::contract::Error;
|
||||
|
||||
async fn register(&self, identity_commitment: U256) -> Result<(), Self::Error> {
|
||||
async fn register_user(
|
||||
&self,
|
||||
address: &Address,
|
||||
identity_commitment: U256,
|
||||
) -> Result<(), Self::Error> {
|
||||
debug!(
|
||||
"Register user with identity_commitment: {:?}",
|
||||
identity_commitment
|
||||
"Register user ({}) with identity_commitment: {:?}",
|
||||
address, identity_commitment
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub static TIER_LIMITS: LazyLock<BTreeMap<TierIndex, Tier>> = LazyLock::new(|| {
|
||||
BTreeMap::from([
|
||||
(
|
||||
TierIndex::from(0),
|
||||
Tier {
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
name: "Basic".to_string(),
|
||||
tx_per_epoch: 6,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(1),
|
||||
Tier {
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
name: "Active".to_string(),
|
||||
tx_per_epoch: 120,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(2),
|
||||
Tier {
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(499),
|
||||
name: "Regular".to_string(),
|
||||
tx_per_epoch: 720,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(3),
|
||||
Tier {
|
||||
min_karma: U256::from(500),
|
||||
max_karma: U256::from(999),
|
||||
name: "Power User".to_string(),
|
||||
tx_per_epoch: 86400,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
(
|
||||
TierIndex::from(4),
|
||||
Tier {
|
||||
min_karma: U256::from(1000),
|
||||
max_karma: U256::from(4999),
|
||||
name: "S-Tier".to_string(),
|
||||
tx_per_epoch: 432000,
|
||||
active: true,
|
||||
},
|
||||
),
|
||||
])
|
||||
pub static TIER_LIMITS: LazyLock<Vec<Tier>> = LazyLock::new(|| {
|
||||
vec![
|
||||
Tier {
|
||||
min_karma: U256::from(10),
|
||||
max_karma: U256::from(49),
|
||||
name: "Basic".to_string(),
|
||||
tx_per_epoch: 6,
|
||||
},
|
||||
Tier {
|
||||
min_karma: U256::from(50),
|
||||
max_karma: U256::from(99),
|
||||
name: "Active".to_string(),
|
||||
tx_per_epoch: 120,
|
||||
},
|
||||
Tier {
|
||||
min_karma: U256::from(100),
|
||||
max_karma: U256::from(499),
|
||||
name: "Regular".to_string(),
|
||||
tx_per_epoch: 720,
|
||||
},
|
||||
Tier {
|
||||
min_karma: U256::from(500),
|
||||
max_karma: U256::from(999),
|
||||
name: "Power User".to_string(),
|
||||
tx_per_epoch: 86400,
|
||||
},
|
||||
Tier {
|
||||
min_karma: U256::from(1000),
|
||||
max_karma: U256::from(4999),
|
||||
name: "S-Tier".to_string(),
|
||||
tx_per_epoch: 432000,
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user