btlightning 0.2.6

QUIC transport layer for Bittensor
Documentation

Python

pip install btlightning
from btlightning import Lightning

client = Lightning(wallet_hotkey="5GrwvaEF...")
client.set_python_signer(my_signer_callback)
client.initialize_connections([
    {"hotkey": "5FHneW46...", "ip": "192.168.1.1", "port": 8443}
])
response = client.query_axon(
    {"hotkey": "5FHneW46...", "ip": "192.168.1.1", "port": 8443},
    {"synapse_type": "MyQuery", "data": {"key": "value"}}
)

Rust

[dependencies]
btlightning = { version = "0.1", features = ["subtensor"] }

With the subtensor feature, the client discovers miners from the metagraph and keeps connections in sync automatically:

use btlightning::{LightningClient, LightningClientConfig, Sr25519Signer, MetagraphMonitorConfig};

let config = LightningClientConfig {
    metagraph: Some(MetagraphMonitorConfig::finney(YOUR_NETUID)),
    ..Default::default()
};
let mut client = LightningClient::with_config("5GrwvaEF...".into(), config)?;
client.set_signer(Box::new(Sr25519Signer::from_seed(seed)));
client.initialize_connections(vec![]).await?;

Without subtensor, pass miner addresses directly:

use btlightning::{LightningClient, Sr25519Signer, QuicAxonInfo};

let mut client = LightningClient::new("5GrwvaEF...".into());
client.set_signer(Box::new(Sr25519Signer::from_seed(seed)));
client.initialize_connections(vec![
    QuicAxonInfo::new("5FHneW46...".into(), "192.168.1.1".into(), 8443, 4)
]).await?;

Query a single miner by axon info and deserialize the response:

use btlightning::QuicRequest;
use serde::{Serialize, Deserialize};

#[derive(Serialize)]
struct MyQuery { prompt: String }

#[derive(Deserialize)]
struct MyResult { answer: String }

let request = QuicRequest::from_typed("MyQuery", &MyQuery { prompt: "hello".into() })?;
let response = client.query_axon(axon_info, request).await?.into_result()?;
let result: MyResult = response.deserialize_data()?;

Fan out to every QUIC miner on the subnet concurrently:

use std::sync::Arc;
use tokio::task::JoinSet;

let client = Arc::new(client);
let miners = metagraph.quic_miners();
let mut tasks = JoinSet::new();
for miner in miners {
    let req = QuicRequest::from_typed("MyQuery", &MyQuery { prompt: "hello".into() })?;
    let client = Arc::clone(&client);
    tasks.spawn(async move {
        (miner.hotkey.clone(), client.query_axon(miner, req).await)
    });
}
while let Some(Ok((hotkey, result))) = tasks.join_next().await {
    match result {
        Ok(resp) => { /* handle response from hotkey */ }
        Err(e) => { /* miner failed */ }
    }
}

Build from source

cargo build -p btlightning
maturin develop --manifest-path crates/btlightning-py/Cargo.toml

Performance

Benchmarked on Apple Silicon (M-series), macOS, loopback networking. Echo handler returns input unchanged. Connection setup includes first request-response round trip. Lightning authenticates once at connection time and amortizes over all subsequent requests; bittensor signs and verifies every request. Bittensor payloads are base64-encoded (JSON has no binary type), adding ~33% wire overhead beyond the nominal payload size. Source: benchmarks/.

bittensor (dendrite/axon) lightning
Protocol HTTP/1.1 QUIC
Serialization JSON MessagePack
Transport encryption None TLS 1.3
Auth model Per-request Per-connection
Metric bittensor lightning (Python) lightning (Rust)
Connection setup (p50) 114.46 ms 2.68 ms 0.71 ms
Latency p50 (1KB) 24.01 ms 0.08 ms 0.05 ms
Latency p99 (1KB) 28.80 ms 0.09 ms 0.07 ms
Throughput (1KB) 41 req/s 26,539 req/s 68,650 req/s
Wire size (1KB payload) 2,032 bytes 1,052 bytes 1,052 bytes
Color System
bittensor bittensor
lightning-py lightning (Python)
lightning lightning (Rust)

[!NOTE] Charts use log10 scale. Refer to the tables below for raw values.

Latency p50

---
config:
    xyChart:
        plotReservedSpacePercent: 60
    themeVariables:
        xyChart:
            backgroundColor: "#000000"
            plotColorPalette: "#808080, #FF8C00, #FFE000"
            titleColor: "#ffffff"
            xAxisLabelColor: "#aaaaaa"
            xAxisTitleColor: "#cccccc"
            xAxisTickColor: "#666666"
            xAxisLineColor: "#444444"
            yAxisLabelColor: "#aaaaaa"
            yAxisTitleColor: "#cccccc"
            yAxisTickColor: "#666666"
            yAxisLineColor: "#444444"
---
xychart-beta
    x-axis ["256B", "1KB", "10KB", "100KB", "1MB"]
    y-axis "log10(us)" 0 --> 5
    bar "bittensor" [4.39, 4.38, 4.39, 4.42, 4.61]
    bar "lightning (Python)" [1.90, 1.90, 2.36, 3.27, 4.31]
    bar "lightning (Rust)" [1.70, 1.70, 2.04, 2.84, 3.87]

Throughput (req/s)

---
config:
    xyChart:
        plotReservedSpacePercent: 60
    themeVariables:
        xyChart:
            backgroundColor: "#000000"
            plotColorPalette: "#FFE000, #FF8C00, #808080"
            titleColor: "#ffffff"
            xAxisLabelColor: "#aaaaaa"
            xAxisTitleColor: "#cccccc"
            xAxisTickColor: "#666666"
            xAxisLineColor: "#444444"
            yAxisLabelColor: "#aaaaaa"
            yAxisTitleColor: "#cccccc"
            yAxisTickColor: "#666666"
            yAxisLineColor: "#444444"
---
xychart-beta
    x-axis ["256B", "1KB", "10KB", "100KB", "1MB"]
    y-axis "log10(req/s)" 0 --> 5
    bar "lightning (Rust)" [4.92, 4.84, 4.27, 3.26, 2.26]
    bar "lightning (Python)" [4.55, 4.42, 3.83, 2.94, 1.94]
    bar "bittensor" [1.61, 1.61, 1.60, 1.57, 1.40]

Latency (ms)

Payload System p50 p95 p99
256B bittensor 24.50 28.04 35.05
lightning (py) 0.08 0.19 0.23
lightning (rust) 0.05 0.06 0.07
1KB bittensor 24.01 25.57 28.80
lightning (py) 0.08 0.09 0.09
lightning (rust) 0.05 0.06 0.07
10KB bittensor 24.70 25.70 26.59
lightning (py) 0.23 0.24 0.26
lightning (rust) 0.11 0.12 0.13
100KB bittensor 26.13 27.31 29.42
lightning (py) 1.88 2.25 2.37
lightning (rust) 0.69 0.73 0.75
1MB bittensor 40.72 43.11 48.00
lightning (py) 20.55 22.48 23.31
lightning (rust) 7.40 12.36 17.02

Throughput (req/s)

Payload bittensor lightning (Python) lightning (Rust)
256B 41 35,181 83,460
1KB 41 26,539 68,650
10KB 40 6,729 18,686
100KB 38 875 1,819
1MB 25 88 181

Wire overhead (bytes)

Payload bittensor lightning
256B 1,008 284
1KB 2,032 1,052
10KB 14,321 10,268
100KB 137,202 102,430
1MB 1,398,771 1,048,606