Skip to content

Commit c2843ac

Browse files
dev-jodeegithub-actions
andauthored
feat: (PRO-262) Implement usage limit feature with Redis support (#215)
* feat: (PRO-262) Implement usage limit feature with Redis support - Removed `redis-test` dependency from Cargo files. - Updated Docker configuration to use a custom Redis configuration file. - Introduced `UsageLimitConfig` to manage per-wallet transaction limits. - Added `UsageTracker` for enforcing usage limits based on transactions. - Implemented `UsageStore` trait with Redis and in-memory implementations for tracking usage. - Enhanced RPC methods to check usage limits before processing transactions. - Added tests for usage limit functionality and configuration parsing. - Updated configuration validation to include usage limit settings. - Config validation - Better fallback handling * Update coverage badge [skip ci] --------- Co-authored-by: github-actions <github-actions@github.com>
1 parent ad909f3 commit c2843ac

31 files changed

+1097
-89
lines changed

.github/badges/coverage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"schemaVersion": 1, "label": "coverage", "message": "86.3%", "color": "green"}
1+
{"schemaVersion": 1, "label": "coverage", "message": "85.8%", "color": "green"}

Cargo.lock

Lines changed: 0 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ FROM rust:1.80 as builder
22

33
WORKDIR /usr/src/app
44
COPY . .
5-
RUN cargo build --release --bin kora-rpc
5+
RUN cargo build --release --bin kora
66

77
FROM debian:bookworm-slim
88
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
9-
COPY --from=builder /usr/src/app/target/release/kora-rpc /usr/local/bin/
9+
COPY --from=builder /usr/src/app/target/release/kora /usr/local/bin/
1010

1111
EXPOSE 8080
12-
CMD ["kora-rpc"]
12+
CMD ["kora"]

crates/lib/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,3 @@ tempfile = "3.2"
8585
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
8686
mockito = "1.2.0"
8787
serial_test = "3.2.0"
88-
redis-test = "0.12.0"

crates/lib/src/admin/token_util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use {crate::cache::CacheUtil, crate::state::get_config};
2222

2323
#[cfg(test)]
2424
use {
25-
crate::config::SplTokenConfig, crate::tests::config_mock::mock_state::get_config,
26-
crate::tests::redis_cache_mock::MockCacheUtil as CacheUtil,
25+
crate::config::SplTokenConfig, crate::tests::cache_mock::MockCacheUtil as CacheUtil,
26+
crate::tests::config_mock::mock_state::get_config,
2727
};
2828

2929
/*

crates/lib/src/config.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
DEFAULT_CACHE_ACCOUNT_TTL, DEFAULT_CACHE_DEFAULT_TTL,
1111
DEFAULT_FEE_PAYER_BALANCE_METRICS_EXPIRY_SECONDS, DEFAULT_MAX_TIMESTAMP_AGE,
1212
DEFAULT_METRICS_ENDPOINT, DEFAULT_METRICS_PORT, DEFAULT_METRICS_SCRAPE_INTERVAL,
13+
DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE, DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS,
1314
},
1415
error::KoraError,
1516
fee::price::{PriceConfig, PriceModel},
@@ -349,6 +350,8 @@ pub struct KoraConfig {
349350
pub payment_address: Option<String>,
350351
#[serde(default)]
351352
pub cache: CacheConfig,
353+
#[serde(default)]
354+
pub usage_limit: UsageLimitConfig,
352355
}
353356

354357
impl Default for KoraConfig {
@@ -359,6 +362,30 @@ impl Default for KoraConfig {
359362
auth: AuthConfig::default(),
360363
payment_address: None,
361364
cache: CacheConfig::default(),
365+
usage_limit: UsageLimitConfig::default(),
366+
}
367+
}
368+
}
369+
370+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
371+
pub struct UsageLimitConfig {
372+
/// Enable per-wallet usage limiting
373+
pub enabled: bool,
374+
/// Cache URL for shared usage limiting across multiple Kora instances
375+
pub cache_url: Option<String>,
376+
/// Default maximum transactions per wallet (0 = unlimited)
377+
pub max_transactions: u64,
378+
/// Fallback behavior when cache is unavailable
379+
pub fallback_if_unavailable: bool,
380+
}
381+
382+
impl Default for UsageLimitConfig {
383+
fn default() -> Self {
384+
Self {
385+
enabled: false,
386+
cache_url: None,
387+
max_transactions: DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS,
388+
fallback_if_unavailable: DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE,
362389
}
363390
}
364391
}
@@ -671,4 +698,41 @@ mod tests {
671698
assert_eq!(config.kora.cache.default_ttl, 300);
672699
assert_eq!(config.kora.cache.account_ttl, 60);
673700
}
701+
702+
#[test]
703+
fn test_usage_limit_config_parsing() {
704+
let config = ConfigBuilder::new()
705+
.with_usage_limit_config(true, Some("redis://localhost:6379"), 10, false)
706+
.build_config()
707+
.unwrap();
708+
709+
assert!(config.kora.usage_limit.enabled);
710+
assert_eq!(config.kora.usage_limit.cache_url, Some("redis://localhost:6379".to_string()));
711+
assert_eq!(config.kora.usage_limit.max_transactions, 10);
712+
assert!(!config.kora.usage_limit.fallback_if_unavailable);
713+
}
714+
715+
#[test]
716+
fn test_usage_limit_config_default() {
717+
let config = ConfigBuilder::new().build_config().unwrap();
718+
719+
assert!(!config.kora.usage_limit.enabled);
720+
assert_eq!(config.kora.usage_limit.cache_url, None);
721+
assert_eq!(config.kora.usage_limit.max_transactions, DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS);
722+
assert_eq!(
723+
config.kora.usage_limit.fallback_if_unavailable,
724+
DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE
725+
);
726+
}
727+
728+
#[test]
729+
fn test_usage_limit_config_unlimited() {
730+
let config = ConfigBuilder::new()
731+
.with_usage_limit_config(true, None, 0, true)
732+
.build_config()
733+
.unwrap();
734+
735+
assert!(config.kora.usage_limit.enabled);
736+
assert_eq!(config.kora.usage_limit.max_transactions, 0); // 0 = unlimited
737+
}
674738
}

crates/lib/src/constant.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub const DEFAULT_CACHE_DEFAULT_TTL: u64 = 300; // 5 minutes
2525
pub const DEFAULT_CACHE_ACCOUNT_TTL: u64 = 60; // 1 minute for account data
2626
pub const DEFAULT_FEE_PAYER_BALANCE_METRICS_EXPIRY_SECONDS: u64 = 30; // 30 seconds
2727

28+
pub const DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS: u64 = 0; // 0 = unlimited
29+
pub const DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE: bool = false;
30+
2831
// Account Indexes within instructions
2932
// Instruction indexes for the instructions that we support to parse from the transaction
3033
pub mod instruction_indexes {

crates/lib/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub enum KoraError {
5858

5959
#[error("Rate limit exceeded")]
6060
RateLimitExceeded,
61+
62+
#[error("Usage limit exceeded: {0}")]
63+
UsageLimitExceeded(String),
6164
}
6265

6366
impl From<ClientError> for KoraError {

crates/lib/src/fee/fee.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ use solana_message::Message;
2121
use {crate::cache::CacheUtil, crate::state::get_config};
2222

2323
#[cfg(test)]
24-
use crate::tests::{
25-
config_mock::mock_state::get_config, redis_cache_mock::MockCacheUtil as CacheUtil,
26-
};
24+
use crate::tests::{cache_mock::MockCacheUtil as CacheUtil, config_mock::mock_state::get_config};
2725
use solana_client::nonblocking::rpc_client::RpcClient;
2826
use solana_message::VersionedMessage;
2927
use solana_program::program_pack::Pack;

crates/lib/src/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod signer;
1717
pub mod state;
1818
pub mod token;
1919
pub mod transaction;
20+
pub mod usage_limit;
2021
pub mod validator;
2122
pub use cache::CacheUtil;
2223
pub use config::Config;

0 commit comments

Comments
 (0)