Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
02b1389
fix!: enforce merkle payment amount verification against quoted prices
mickvandijke Apr 1, 2026
9411613
feat!: adapt to evmlib PaymentVault API and simplify pricing
mickvandijke Apr 1, 2026
08bb16f
chore: switch evmlib dependency to Git-based source on refactor/unify…
mickvandijke Apr 1, 2026
7998ab4
fix: verify merkle payment amounts against contract formula
mickvandijke Apr 1, 2026
b224ad3
fix: add backticks in doc comment to fix clippy::doc_markdown warning
mickvandijke Apr 1, 2026
f283b9a
fix: use SingleNodePayment to reconstruct paid amounts for verification
mickvandijke Apr 1, 2026
3900c3c
refactor!: remove max_chunks storage limit cap
mickvandijke Apr 2, 2026
f320a04
refactor!: remove max_records and NODE_STORAGE_LIMIT_BYTES
mickvandijke Apr 2, 2026
2e48d9e
chore: update evmlib to 779b996 (removes max_records from QuotingMetr…
mickvandijke Apr 2, 2026
19b0d11
fix: verify median quote payment from all nodes, not just the median
mickvandijke Apr 2, 2026
26d1fe6
feat: auto-scale LMDB map size to available disk space
mickvandijke Apr 2, 2026
109f184
refactor: simplify single-node payment verification to median-only check
mickvandijke Apr 2, 2026
1ca840c
fix: check all tied median quotes during single-node payment verifica…
mickvandijke Apr 2, 2026
2eb6fb2
fix: suppress clippy::cast_possible_truncation lint in single-node re…
mickvandijke Apr 2, 2026
347a5ac
fix: use zero disk_reserve in test configs to avoid CI flakes on cons…
mickvandijke Apr 2, 2026
41511ab
chore: update evmlib to 0.7.0 from crates.io for payment verification
mickvandijke Apr 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 10 additions & 62 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ saorsa-core = "0.21.0"
saorsa-pqc = "0.5"

# Payment verification - autonomi network lookup + EVM payment
evmlib = "0.5.0"
evmlib = "0.7"
xor_name = "5"

# Caching - LRU cache for verified XorNames
Expand Down Expand Up @@ -93,6 +93,9 @@ sha2 = "0.10"
# Cross-platform file locking for upgrade caches
fs2 = "0.4"

# System page size (for LMDB map alignment during resize)
page_size = "0.6"

# Protocol serialization
postcard = { version = "1.1.3", features = ["use-std"] }

Expand Down
3 changes: 0 additions & 3 deletions config/production.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ metrics_port = 9100
[storage]
enabled = true

# Maximum number of chunks to store (0 = unlimited)
max_chunks = 0

# Verify content hash on read
verify_on_read = true

Expand Down
10 changes: 3 additions & 7 deletions src/bin/ant-devnet/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,11 @@ async fn main() -> color_eyre::Result<()> {
.default_wallet_private_key()
.map_err(|e| color_eyre::eyre::eyre!("Failed to get wallet key: {e}"))?;

let (rpc_url, token_addr, payments_addr, merkle_addr) = match &network {
let (rpc_url, token_addr, vault_addr) = match &network {
evmlib::Network::Custom(custom) => (
custom.rpc_url_http.to_string(),
format!("{:?}", custom.payment_token_address),
format!("{:?}", custom.data_payments_address),
custom
.merkle_payments_address
.map(|addr| format!("{addr:?}")),
format!("{:?}", custom.payment_vault_address),
),
_ => {
return Err(color_eyre::eyre::eyre!(
Expand All @@ -93,8 +90,7 @@ async fn main() -> color_eyre::Result<()> {
rpc_url,
wallet_private_key: wallet_key,
payment_token_address: token_addr,
data_payments_address: payments_addr,
merkle_payments_address: merkle_addr,
payment_vault_address: vault_addr,
})
} else {
None
Expand Down
28 changes: 18 additions & 10 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,43 +430,51 @@ const fn default_bootstrap_stale_days() -> u64 {
///
/// Controls how chunks are stored, including:
/// - Whether storage is enabled
/// - Maximum chunks to store (for capacity management)
/// - Content verification on read
/// - Database size limits (auto-scales with available disk by default)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
/// Enable chunk storage.
/// Default: true
#[serde(default = "default_storage_enabled")]
pub enabled: bool,

/// Maximum number of chunks to store (0 = unlimited).
/// Default: 0 (unlimited)
#[serde(default)]
pub max_chunks: usize,

/// Verify content hash matches address on read.
/// Default: true
#[serde(default = "default_storage_verify_on_read")]
pub verify_on_read: bool,

/// Maximum LMDB database size in GiB (0 = use default of 32 GiB).
/// On Unix the mmap is a lazy reservation and costs nothing until pages
/// are faulted in.
/// Explicit LMDB database size cap in GiB.
///
/// When set to 0 (default), the map size is computed automatically from
/// available disk space at startup and grows on demand when the operator
/// adds storage. Set a non-zero value to impose a hard cap.
#[serde(default)]
pub db_size_gb: usize,

/// Minimum free disk space (in GiB) to preserve on the storage partition.
///
/// Writes are refused when available space drops below this threshold,
/// preventing the node from filling the disk completely. Default: 1 GiB.
#[serde(default = "default_disk_reserve_gb")]
pub disk_reserve_gb: u64,
}

impl Default for StorageConfig {
fn default() -> Self {
Self {
enabled: default_storage_enabled(),
max_chunks: 0,
verify_on_read: default_storage_verify_on_read(),
db_size_gb: 0,
disk_reserve_gb: default_disk_reserve_gb(),
}
}
}

const fn default_disk_reserve_gb() -> u64 {
1
}

const fn default_storage_enabled() -> bool {
true
}
Expand Down
16 changes: 4 additions & 12 deletions src/devnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ const DEVNET_PAYMENT_CACHE_CAPACITY: usize = 1000;
/// Devnet rewards address (20 bytes, all 0x01).
const DEVNET_REWARDS_ADDRESS: [u8; 20] = [0x01; 20];

/// Max records for quoting metrics (devnet value).
const DEVNET_MAX_RECORDS: usize = 100_000;

/// Initial records for quoting metrics (devnet value).
const DEVNET_INITIAL_RECORDS: usize = 1000;

Expand Down Expand Up @@ -244,11 +241,8 @@ pub struct DevnetEvmInfo {
pub wallet_private_key: String,
/// Payment token contract address.
pub payment_token_address: String,
/// Data payments contract address.
pub data_payments_address: String,
/// Merkle payments contract address (for batch payments).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub merkle_payments_address: Option<String>,
/// Unified payment vault contract address (handles both single-node and merkle payments).
pub payment_vault_address: String,
}

/// Network state for devnet startup lifecycle.
Expand Down Expand Up @@ -569,8 +563,7 @@ impl Devnet {
let storage_config = LmdbStorageConfig {
root_dir: data_dir.to_path_buf(),
verify_on_read: true,
max_chunks: 0,
max_map_size: 0,
..LmdbStorageConfig::default()
};
let storage = LmdbStorage::new(storage_config)
.await
Expand All @@ -590,8 +583,7 @@ impl Devnet {
local_rewards_address: rewards_address,
};
let payment_verifier = PaymentVerifier::new(payment_config);
let metrics_tracker =
QuotingMetricsTracker::new(DEVNET_MAX_RECORDS, DEVNET_INITIAL_RECORDS);
let metrics_tracker = QuotingMetricsTracker::new(DEVNET_INITIAL_RECORDS);
let mut quote_generator = QuoteGenerator::new(rewards_address, metrics_tracker);

// Wire ML-DSA-65 signing from the devnet node's identity
Expand Down
22 changes: 7 additions & 15 deletions src/node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Node implementation - thin wrapper around saorsa-core's `P2PNode`.

use crate::ant_protocol::{CHUNK_PROTOCOL_ID, MAX_CHUNK_SIZE};
use crate::ant_protocol::CHUNK_PROTOCOL_ID;
use crate::config::{
default_nodes_dir, default_root_dir, EvmNetworkConfig, NetworkMode, NodeConfig,
NODE_IDENTITY_FILENAME,
Expand Down Expand Up @@ -30,14 +30,6 @@ use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, info, warn};

/// Node storage capacity limit (5 GB).
///
/// Used to derive `max_records` for the quoting metrics pricing curve.
/// A node advertises `NODE_STORAGE_LIMIT_BYTES / MAX_CHUNK_SIZE` as
/// its maximum record count, giving the pricing algorithm a meaningful
/// fullness ratio instead of a hardcoded constant.
pub const NODE_STORAGE_LIMIT_BYTES: u64 = 5 * 1024 * 1024 * 1024;

#[cfg(unix)]
use tokio::signal::unix::{signal, SignalKind};

Expand Down Expand Up @@ -330,8 +322,11 @@ impl NodeBuilder {
let storage_config = LmdbStorageConfig {
root_dir: config.root_dir.clone(),
verify_on_read: config.storage.verify_on_read,
max_chunks: config.storage.max_chunks,
max_map_size: config.storage.db_size_gb.saturating_mul(1_073_741_824),
max_map_size: config.storage.db_size_gb.saturating_mul(1024 * 1024 * 1024),
disk_reserve: config
.storage
.disk_reserve_gb
.saturating_mul(1024 * 1024 * 1024),
};
let storage = LmdbStorage::new(storage_config)
.await
Expand Down Expand Up @@ -360,10 +355,7 @@ impl NodeBuilder {
local_rewards_address: rewards_address,
};
let payment_verifier = PaymentVerifier::new(payment_config);
// Safe: 5GB fits in usize on all supported 64-bit platforms.
#[allow(clippy::cast_possible_truncation)]
let max_records = (NODE_STORAGE_LIMIT_BYTES as usize) / MAX_CHUNK_SIZE;
let metrics_tracker = QuotingMetricsTracker::new(max_records, 0);
let metrics_tracker = QuotingMetricsTracker::new(0);
let mut quote_generator = QuoteGenerator::new(rewards_address, metrics_tracker);

// Wire ML-DSA-65 signing from node identity.
Expand Down
Loading
Loading