Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
073a2e9
fix(collateral): resolve alpha reclaim accounting bugs
itzlambda Feb 16, 2026
5abfe7b
fix(collateral): prevent nodeToMiner premature clearing with pending …
itzlambda Feb 16, 2026
f7e7721
docs(collateral): add CLAUDE.md with Bittensor EVM and contract docs
itzlambda Feb 16, 2026
1f3b205
docs(collateral): expand CLAUDE.md with alpha flows, key model, and s…
itzlambda Feb 16, 2026
95152c4
fix(collateral): update testnet RPC URL to correct domain
itzlambda Feb 16, 2026
ed348a9
fix(collateral): add storage gap and reject zero-value deposits
itzlambda Feb 16, 2026
ec43c3f
fix(collateral): clear nodeToMiner in denyReclaimRequest when balance…
itzlambda Feb 16, 2026
3931232
Derive contract coldkey at init and remove mutable setter
itzlambda Feb 18, 2026
26e65c5
refactor(collateral): rename CONTRACT_HOTKEY to VALIDATOR_HOTKEY
itzlambda Feb 18, 2026
aac31ce
docs(collateral): fix misleading alpha reclaim and slash descriptions
itzlambda Feb 18, 2026
e7d935b
sync dual-collateral event state and reclaim lifecycle
itzlambda Feb 18, 2026
dde9cd3
Standardize collateral evidence checksums on full SHA-256
itzlambda Feb 18, 2026
6ba1721
Align collateral to alpha-primary dual-state policy
itzlambda Feb 18, 2026
00ac9ff
Disable EVM deployment whitelist in localnet init scripts
itzlambda Feb 19, 2026
649c585
Harden collateral reclaim/slash invariants and align reclaim ABI
itzlambda Feb 19, 2026
f022816
refactor(collateral-contract): remove legacy contracts and use upgrad…
itzlambda Feb 19, 2026
67a2369
Rename collateral query APIs to tao_collaterals
itzlambda Feb 19, 2026
3a9894c
collateral-contract: switch to solc 0.8.24 cancun
itzlambda Feb 19, 2026
7387e4f
chore(collateral-contract): resolve forge lint issues
itzlambda Feb 19, 2026
837e39f
fix(collateral-contract): harden reentry and slash semantics
itzlambda Feb 19, 2026
1dd442f
style(collateral-contract): apply forge fmt formatting
itzlambda Feb 19, 2026
a50b72a
fix(collateral-contract): require explicit deploy env vars
itzlambda Feb 20, 2026
57c5d67
fix(collateral): sync ABI artifacts and getter calls
itzlambda Feb 20, 2026
a773ddd
fix(collateral-contract): harden trustee auth and OZ safety checks
itzlambda Feb 20, 2026
37a9033
feat(collateral): add localnet setup scripts and deploy flow
itzlambda Feb 20, 2026
0ded5f5
fix(collateral-cli): default network and contract from env
itzlambda Feb 20, 2026
506c2b4
fix(collateral-cli): default event scan range and drop block env
itzlambda Feb 20, 2026
6c74149
fix(collateral): drop .env.local backup on localnet setup
itzlambda Feb 20, 2026
754fa1d
feat(collateral): add TAO and alpha deposit type toggles
itzlambda Feb 23, 2026
c83f7ad
fix(collateral): correct storage gap and update init calldata for dep…
itzlambda Feb 23, 2026
e5ed770
feat(collateral): add rpc_url override for Docker connectivity
itzlambda Feb 23, 2026
0ccf20c
fix(localnet): update btcli commands for latest bittensor-cli
itzlambda Feb 23, 2026
c39c56a
feat(localnet): integrate collateral contract deploy into startup flow
itzlambda Feb 23, 2026
3b90ed5
feat(localnet): add subnet start to init and document precompile RAO …
itzlambda Feb 23, 2026
e66634c
fix(collateral): send slashed TAO to trustee instead of burning
itzlambda Feb 24, 2026
bfc6fea
feat(collateral): add integration test and seed localnet AMM pool
itzlambda Feb 25, 2026
880a45b
fix(collateral): use fixed alpha amounts in integration tests
itzlambda Feb 25, 2026
96377ef
feat(collateral): add e2e validator test and improve deploy idempotency
itzlambda Feb 25, 2026
40cbd6e
fix(collateral): correct NatSpec and internal comments in CollateralU…
itzlambda Feb 25, 2026
16b9489
docs(collateral): fix inaccuracies in CLAUDE.md
itzlambda Feb 25, 2026
992302c
fix(collateral): send slashed alpha to trustee address consistently
itzlambda Feb 25, 2026
784dbc4
fix(collateral): use percentage precision for slash fraction calculation
itzlambda Feb 25, 2026
f7641aa
feat(collateral): add minAlphaCollateralIncrease and fix alpha RAO units
itzlambda Feb 25, 2026
8defaf0
fix lints
itzlambda Feb 25, 2026
4a47f3f
docs(collateral): document staking guarantees and update deploy defaults
itzlambda Feb 26, 2026
19c4a34
refactor(rental): centralize node release into single catch-all in st…
itzlambda Mar 2, 2026
17a631f
refactor(collateral): remove grace period and exclusion mechanism
itzlambda Mar 2, 2026
f4b050e
feat(collateral): add background reconciliation service
itzlambda Mar 2, 2026
fa52f24
feat(collateral): add combined getCollaterals query to halve RPC roun…
itzlambda Mar 2, 2026
596cb66
fix(collateral): prevent sub-1% slash_fraction from triggering full s…
itzlambda Mar 2, 2026
653f1d1
refactor(collateral): add graceful shutdown to scan and reconcile loops
itzlambda Mar 2, 2026
bc93801
fix(collateral): use GRANDPA-finalized block for event scanning
itzlambda Mar 2, 2026
3e3cc4e
feat(collateral): add persistent event log for contract events
itzlambda Mar 2, 2026
72b72d7
fix(collateral): normalize hex encoding to use 0x prefix consistently
itzlambda Mar 2, 2026
5225409
refactor(collateral): remove redundant get_collateral_amount wrapper
itzlambda Mar 2, 2026
3b9a6da
refactor(collateral): extract event handling and enforce idempotency
itzlambda Mar 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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Thumbs.db

# Service wallets (auto-generated for containers)
scripts/*/service-wallet/
# Localnet EVM deployer wallet (generated by setup-localnet-env.sh)
scripts/localnet/wallets/contract_deployer_evm.env

# Logs
*.log
Expand All @@ -46,6 +48,7 @@ node_modules/
# Environment variables
.env*
!.env.example
!scripts/collateral/.env.local.example

*.db
*.db-shm
Expand Down Expand Up @@ -113,6 +116,7 @@ scripts/localnet/configs/miner.toml
terraform.tfstate
*.tfstate
CLAUDE.md
!crates/collateral-contract/CLAUDE.md

scripts/web/basilica-linux-amd64
scripts/web/basilica-macos-arm64
Expand Down
98 changes: 98 additions & 0 deletions collateral-todo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## Collateral Contract TODOs (feat/incentive_revamp)

### Security Audit (2026-02-06): Critical/High Collateral-Loss Findings

1) ~~Critical - Alpha pending-reclaim counter is never decremented on finalize~~ **DONE**
- Problem: `reclaimCollateral` increments `alphaCollateralUnderPendingReclaims`, but `finalizeReclaim` only decrements `collateralUnderPendingReclaims` and never clears the alpha pending counter. After one successful alpha reclaim, later alpha deposits on the same `(hotkey, nodeId)` can become non-reclaimable due to stale pending alpha accounting.
- Impact: legitimate miner alpha collateral can become permanently locked.
- Fix:
- In `finalizeReclaim`, decrement `alphaCollateralUnderPendingReclaims[hotkey][nodeId]` by `alphaAmount` before external calls.
- Enforce invariant checks that pending alpha never exceeds tracked alpha.
- Regression test:
- alpha deposit -> reclaim -> finalize -> alpha deposit again -> reclaim/finalize must still succeed.
- Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`

2) ~~High - Pending reclaim plus slash can deadlock collateral (non-finalizable and non-deniable)~~ **DONE**
- Problem: `slashCollateral` mutates live balances while pending reclaim keeps snapshot amounts. Later `finalizeReclaim` can revert when snapshot reclaim amount exceeds remaining balance. For alpha-only requests, `denyReclaimRequest` currently treats `reclaim.amount == 0` as "not found", preventing trustee cleanup.
- Impact: remaining collateral can become permanently stuck (cannot be finalized by miner and cannot be denied by trustee).
- Fix:
- Change reclaim existence check to: `if (reclaim.amount == 0 && reclaim.alphaAmount == 0) revert ReclaimNotFound();`
- Implemented partial finalize: `finalizeReclaim` caps transfer to available balance instead of reverting. Slash and reclaim are now fully independent operations.
- Regression tests:
- pending alpha reclaim + partial alpha slash must not leave unrecoverable remainder.
- alpha-only reclaim must be deny-able before timeout.
- Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`, `crates/collateral-contract/src/lib.rs`, `crates/basilica-validator/src/collateral/slash_executor.rs`

### ~~Upgrade Safety (Critical)~~ **DONE**
- Problem: Storage layout is not append‑only. New fields (`CONTRACT_COLDKEY`, `VALIDATOR_HOTKEY`, `alphaCollaterals`, `alphaCollateralUnderPendingReclaims`, plus new fields in `Reclaim`) are inserted before existing mappings/struct fields, which will corrupt state on upgrade for any already‑deployed proxy.
Fix:
- Contract is not yet deployed, so current layout becomes canonical V1 (no reordering needed).
- Added `uint256[50] private __gap` storage gap after `nextReclaimId` for future upgrade safety.
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`

### ~~Reclaim Deny Logic (High)~~ **DONE**
- Problem: `denyReclaimRequest` treats `reclaim.amount == 0` as "not found". Alpha‑only reclaims have `amount == 0`, so they cannot be denied.
Fix:
- Change existence check to `if (reclaim.amount == 0 && reclaim.alphaAmount == 0) revert ReclaimNotFound();`
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`

### ~~Pending Alpha Accounting + Underflow (High)~~ **DONE**
- Problem: `finalizeReclaim` never decrements `alphaCollateralUnderPendingReclaims`. Also no alpha‑side sufficiency check is performed before subtracting `alphaAmount`, so slashes during the pending window can cause underflow/revert.
Fix:
- Decrement `alphaCollateralUnderPendingReclaims[hotkey][nodeId]` in `finalizeReclaim`.
- Instead of reverting on insufficiency, `finalizeReclaim` now caps the transfer to the available balance (partial finalize), preventing both underflow and deadlock.
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`

### ~~Zero‑Value Deposit Ownership (Medium)~~ **DONE**
- Problem: `deposit` allows `msg.value == 0` and `alphaAmount == 0`, yet still claims `nodeToMiner`, enabling ownership griefing without collateral.
Fix:
- Added `if (msg.value == 0 && alphaAmount == 0) revert AmountZero()` guard at top of `deposit()`.
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`

### ~~Contract Coldkey Mutability (Medium)~~ **DONE**
- Problem: `setContractColdkey` can change the coldkey even if the contract already holds stake, potentially orphaning existing alpha collateral.
Fix:
- Removed `setContractColdkey` entirely.
- `CONTRACT_COLDKEY` is now derived once in `initialize()` via AddressMapping precompile (`0x...080C`) using `address(this)` and is never externally mutable.
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`, `crates/collateral-contract/src/lib.rs`, `crates/collateral-contract/src/main.rs`

### ~~Partial Slash Persistence Desync (Medium)~~ **DONE**
- Problem: Validator persistence set `miner=0` on every slash, but on‑chain clears `nodeToMiner` only when collateral ownership is truly exhausted, causing desync after partial slashes.
Fix:
- Ownership clearing is now rule-based: only set `miner=0` when live and pending collateral are all zero (`tao_collateral == 0 && alpha_collateral == 0 && pending_tao_reclaim == 0 && pending_alpha_reclaim == 0`).
- Added regression coverage for full slash while pending reclaim exists; miner remains set until pending is resolved.
Files: `crates/basilica-validator/src/persistence/collateral_persistence.rs`

### ~~Evidence Checksum Consistency (Integration)~~ **DONE**
- Problem: On-chain checksum fields previously used MD5 naming while validator slash execution submitted a truncated SHA-256 value.
Fix:
- Standardized all checksum interfaces on full SHA-256 (`bytes32` / `[u8; 32]`) and renamed fields to `urlContentSha256`.
- Updated contract ABI/events, CLI parsing/flags (`--url-content-sha256`), validator slash checksum computation, and persistence column naming to `url_content_sha256`.
- Added migration `018_rename_md5_checksum_to_sha256.sql` to rename the persistence column and null out non-64-hex legacy values.
Verification:
- `forge test` (collateral-contract) passed.
- `cargo test -p collateral-contract --lib` passed.
- `cargo test -p collateral-contract --bin collateral-cli` passed.
- `cargo test -p basilica-validator --test collateral_e2e` passed.
- `cargo test -p basilica-validator --test collateral_slash_flow` passed.
- `cargo test -p basilica-validator test_handle_slashed_with_url_data` passed.
Completed: 2026-02-18
Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`, `crates/collateral-contract/src/main.rs`, `crates/collateral-contract/src/lib.rs`, `crates/basilica-validator/src/collateral/slash_executor.rs`, `crates/basilica-validator/src/persistence/collateral_persistence.rs`

### ~~Alpha-Primary, Dual-State Alignment (Integration)~~ **DONE**
- Decision:
- Keep **dual-state** contract/event sync for TAO + alpha.
- Keep **alpha-only policy** for validator eligibility/limits/slash sizing.
- Keep **alpha-only tx surface** in CLI/Rust bindings (`msg.value=0`, `slashAmount=0` in tx helpers).
- Require **non-zero alpha** to claim ownership on first deposit (prevents TAO-only ownership claims).
- Fix:
- Added `AlphaRequiredForOwnership` guard in `deposit()` when `nodeToMiner` is empty.
- Preserved TAO write paths for already-owned nodes (deposits/reclaims/slashes remain contract-supported).
- Added/updated tests for:
- TAO-only first claim reverts.
- Alpha-backed first claim succeeds.
- TAO top-up remains valid after alpha-backed ownership claim.
- Clarified alpha-primary policy in CLI/lib comments and operator docs.
- Added validator regression proving TAO is non-authoritative for eligibility (high TAO + zero alpha remains undercollateralized).
- Completed: 2026-02-18
- Files: `crates/collateral-contract/src/CollateralUpgradeable.sol`, `crates/collateral-contract/test/CollateralBasic.t.sol`, `crates/collateral-contract/test/CollateralUpgradeable.t.sol`, `crates/collateral-contract/src/lib.rs`, `crates/collateral-contract/src/main.rs`, `docs/collateral-contract.md`, `crates/collateral-contract/README.md`, `crates/collateral-contract/flow.sh`, `crates/basilica-validator/src/collateral/manager.rs`, `crates/basilica-validator/src/collateral/evaluator.rs`, `crates/basilica-validator/src/persistence/collateral_persistence.rs`
45 changes: 37 additions & 8 deletions crates/basilica-miner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,9 @@ async fn log_collateral_status(miner_hotkey: &str, nodes: &[RegisteredNode]) ->

for node in nodes {
let node_uuid = NodeId::new(&node.config.host)?.uuid;
let amount = collaterals(hotkey_bytes, node_uuid.into_bytes(), &network_config).await?;
let alpha_amount = alpha_from_wei(amount);
let (_tao_collateral, alpha_collateral_rao) =
collaterals(hotkey_bytes, node_uuid.into_bytes(), &network_config).await?;
let alpha_amount = alpha_from_rao(alpha_collateral_rao);
// Parse to GpuCategory for consistent handling (validation done at config load time)
let gpu_cat: GpuCategory = node.config.gpu_category.parse().unwrap();
let min_usd = minimum_usd_per_gpu(&gpu_cat) * node.config.gpu_count as f64;
Expand Down Expand Up @@ -381,10 +382,11 @@ fn format_alpha(value: f64) -> String {
format!("{:.2}", value)
}

fn alpha_from_wei(wei: alloy_primitives::U256) -> f64 {
fn alpha_from_rao(rao: alloy_primitives::U256) -> f64 {
// alphaCollaterals stores RAO (1e9 = 1 alpha), not wei (1e18).
// TODO: Switch to fixed-point decimal to avoid precision loss for large values.
let val = wei.to_string().parse::<f64>().unwrap_or(0.0);
val / 1e18_f64
let val = rao.to_string().parse::<f64>().unwrap_or(0.0);
val / 1e9_f64
}

fn build_table(headers: &[&str], rows: &[CollateralRow]) -> String {
Expand Down Expand Up @@ -563,9 +565,36 @@ mod tests {
}

#[test]
fn test_alpha_from_wei() {
let amount = alloy_primitives::U256::from(1_000_000_000_000_000_000u128);
let alpha = alpha_from_wei(amount);
fn test_alpha_from_rao_zero() {
let alpha = alpha_from_rao(alloy_primitives::U256::ZERO);
assert!((alpha - 0.0).abs() < 1e-12);
}

#[test]
fn test_alpha_from_rao_one_alpha() {
// 1e9 RAO = 1 alpha
let alpha = alpha_from_rao(alloy_primitives::U256::from(1_000_000_000u128));
assert!((alpha - 1.0).abs() < 1e-6);
}

#[test]
fn test_alpha_from_rao_fractional() {
// 500_000_000 RAO = 0.5 alpha
let alpha = alpha_from_rao(alloy_primitives::U256::from(500_000_000u128));
assert!((alpha - 0.5).abs() < 1e-6);
}

#[test]
fn test_alpha_from_rao_large_amount() {
// 100e9 RAO = 100 alpha
let alpha = alpha_from_rao(alloy_primitives::U256::from(100_000_000_000u128));
assert!((alpha - 100.0).abs() < 1e-6);
}

#[test]
fn test_alpha_from_rao_is_not_wei() {
// Regression: old code divided by 1e18 (wei). 1e9 RAO must yield 1.0, NOT 1e-9.
let alpha = alpha_from_rao(alloy_primitives::U256::from(1_000_000_000u128));
assert!(alpha > 0.999, "1e9 RAO must be ~1 alpha, not ~1e-9");
}
}
12 changes: 4 additions & 8 deletions crates/basilica-miner/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ mod tests {
// cargo test --package basilica-miner --test mod -- tests::test_deposit --exact --nocapture
async fn test_deposit() -> anyhow::Result<()> {
let contract = get_contract().await?;
println!("trustee: {:?}", contract.TRUSTEE().call().await.unwrap());
println!("netuid: {:?}", contract.NETUID().call().await.unwrap());
println!("trustee: {:?}", contract.trustee().call().await.unwrap());
println!("netuid: {:?}", contract.netuid().call().await.unwrap());
println!(
"decision_timeout: {:?}",
contract.DECISION_TIMEOUT().call().await.unwrap()
contract.decisionTimeout().call().await.unwrap()
);
println!(
"min_collateral_increase: {:?}",
contract.MIN_COLLATERAL_INCREASE().call().await.unwrap()
contract.minCollateralIncrease().call().await.unwrap()
);

let node_id: u128 = rand::thread_rng().gen_range(0..10000000000);
Expand Down Expand Up @@ -102,7 +102,6 @@ mod tests {
let hotkey: [u8; 32] = [1u8; 32];
let amount = U256::from(10);
let alpha_hotkey: [u8; 32] = [2u8; 32];
let alpha_coldkey: [u8; 32] = [3u8; 32];
let deposit_tx = contract
.deposit(
FixedBytes::from_slice(&hotkey),
Expand All @@ -119,7 +118,6 @@ mod tests {
let reclaim_tx = contract.reclaimCollateral(
FixedBytes::from_slice(&hotkey),
FixedBytes::from_slice(&node_id.to_be_bytes()),
FixedBytes::from_slice(&alpha_coldkey),
url.to_owned(),
FixedBytes::from_slice(&url_checksum.to_be_bytes()),
);
Expand Down Expand Up @@ -161,7 +159,6 @@ mod tests {
let hotkey: [u8; 32] = [1u8; 32];
let amount = U256::from(10);
let alpha_hotkey: [u8; 32] = [2u8; 32];
let alpha_coldkey: [u8; 32] = [3u8; 32];
let deposit_tx = contract
.deposit(
FixedBytes::from_slice(&hotkey),
Expand All @@ -178,7 +175,6 @@ mod tests {
let reclaim_tx = contract.reclaimCollateral(
FixedBytes::from_slice(&hotkey),
FixedBytes::from_slice(&node_id.to_be_bytes()),
FixedBytes::from_slice(&alpha_coldkey),
url.to_owned(),
FixedBytes::from_slice(&url_checksum.to_be_bytes()),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- Switch collateral persistence to dual-collateral state with reclaim request tracking.
-- Collateral is not enabled yet, so we intentionally rebuild these tables instead of
-- preserving legacy rows.

ALTER TABLE collateral_status RENAME TO collateral_status_legacy;

CREATE TABLE IF NOT EXISTS collateral_status (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hotkey TEXT NOT NULL,
node_id TEXT NOT NULL,
miner TEXT NOT NULL,
tao_collateral TEXT NOT NULL DEFAULT '0',
alpha_collateral TEXT NOT NULL DEFAULT '0',
pending_tao_reclaim TEXT NOT NULL DEFAULT '0',
pending_alpha_reclaim TEXT NOT NULL DEFAULT '0',
url TEXT,
url_content_md5_checksum TEXT,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(hotkey, node_id)
);

CREATE TABLE IF NOT EXISTS collateral_reclaims (
reclaim_request_id TEXT PRIMARY KEY,
hotkey TEXT NOT NULL,
node_id TEXT NOT NULL,
miner TEXT NOT NULL,
requested_tao_amount TEXT NOT NULL,
requested_alpha_amount TEXT NOT NULL,
deny_timeout TEXT NOT NULL,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);

DROP TABLE collateral_status_legacy;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Rename checksum column to SHA-256 semantics and drop legacy-sized values.
ALTER TABLE collateral_status
RENAME COLUMN url_content_md5_checksum TO url_content_sha256;

UPDATE collateral_status
SET url_content_sha256 = NULL
WHERE url_content_sha256 IS NOT NULL
AND length(url_content_sha256) != 64;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Clear stale organic grace period entries so only future force_exclude (slash)
-- entries remain. Without this, old organic entries with expired grace periods
-- would incorrectly appear as slash-excluded nodes.
DELETE FROM collateral_grace_periods;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS collateral_grace_periods;
17 changes: 17 additions & 0 deletions crates/basilica-validator/migrations/021_collateral_event_log.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS collateral_event_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_type TEXT NOT NULL, -- 'Deposit', 'ReclaimProcessStarted', 'Denied', 'Reclaimed', 'Slashed'
block_number INTEGER NOT NULL,
tx_hash TEXT NOT NULL,
log_index INTEGER NOT NULL,
hotkey TEXT, -- common field, extracted for querying
node_id TEXT, -- common field, extracted for querying
event_data TEXT NOT NULL, -- JSON blob with all decoded event fields
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(tx_hash, log_index) -- natural dedup key
);

CREATE INDEX IF NOT EXISTS idx_event_log_block ON collateral_event_log(block_number);
CREATE INDEX IF NOT EXISTS idx_event_log_hotkey ON collateral_event_log(hotkey);
CREATE INDEX IF NOT EXISTS idx_event_log_node_id ON collateral_event_log(node_id);
CREATE INDEX IF NOT EXISTS idx_event_log_type ON collateral_event_log(event_type);
25 changes: 2 additions & 23 deletions crates/basilica-validator/src/bittensor_core/weight_setter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use basilica_protocol::billing::MinerDelivery;
use bittensor::{Metagraph, NormalizedWeight, Service as BittensorService};
use chrono::{DateTime, Utc};
use sqlx::Row;
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::interval;
Expand Down Expand Up @@ -64,7 +64,6 @@ pub struct WeightSetter {
api_client: Arc<BasilicaApiClient>,
gpu_profile_repo: Arc<GpuProfileRepository>,
metrics: Option<Arc<ValidatorMetrics>>,
collateral_grace_period: Option<chrono::Duration>,
}

impl WeightSetter {
Expand All @@ -81,7 +80,6 @@ impl WeightSetter {
api_client: Arc<BasilicaApiClient>,
gpu_profile_repo: Arc<GpuProfileRepository>,
metrics: Option<Arc<ValidatorMetrics>>,
collateral_grace_period: Option<chrono::Duration>,
) -> Result<Self> {
// Create weight allocation engine
let weight_allocation_engine =
Expand All @@ -104,7 +102,6 @@ impl WeightSetter {
api_client,
gpu_profile_repo,
metrics,
collateral_grace_period,
})
}

Expand Down Expand Up @@ -245,9 +242,7 @@ impl WeightSetter {
.get_deliveries_for_window(epoch.period_start, epoch.period_end, None)
.await?;
let hotkey_to_uid = self.build_hotkey_to_uid_map(&metagraph);
let excluded_nodes = self.fetch_excluded_nodes().await?;
let miners_by_category =
self.group_deliveries_by_category(deliveries, &hotkey_to_uid, &excluded_nodes);
let miners_by_category = self.group_deliveries_by_category(deliveries, &hotkey_to_uid);

self.log_tao_price().await;

Expand Down Expand Up @@ -356,18 +351,9 @@ impl WeightSetter {
&self,
deliveries: Vec<MinerDelivery>,
hotkey_to_uid: &HashMap<String, u16>,
excluded_nodes: &HashSet<(String, String)>,
) -> HashMap<String, Vec<(MinerUid, f64)>> {
let mut miners_by_category: HashMap<String, Vec<(MinerUid, f64)>> = HashMap::new();
for delivery in deliveries {
if excluded_nodes.contains(&(delivery.miner_hotkey.clone(), delivery.node_id.clone())) {
debug!(
miner_hotkey = %delivery.miner_hotkey,
node_id = %delivery.node_id,
"Skipping delivery for collateral-excluded node"
);
continue;
}
let current_uid = match hotkey_to_uid.get(&delivery.miner_hotkey) {
Some(&uid) => uid,
None => {
Expand Down Expand Up @@ -436,13 +422,6 @@ impl WeightSetter {
miners_by_category
}

async fn fetch_excluded_nodes(&self) -> Result<HashSet<(String, String)>> {
let Some(grace_period) = self.collateral_grace_period else {
return Ok(HashSet::new());
};
self.persistence.get_excluded_nodes(grace_period).await
}

async fn log_tao_price(&self) {
match self.api_client.get_tao_price_usd(self.config.netuid).await {
Ok(tao_price) => {
Expand Down
Loading