Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
89bb56f
Always enforce provider auth; remove --disable-auth flag
RafalMirowski1 Jun 26, 2026
9954629
Finalize bucket creation before provider uploads in PAPI demos
RafalMirowski1 Jun 26, 2026
42e03d0
Fix provider membership decoding of nested BoundedVec/AccountId32 shape
RafalMirowski1 Jun 26, 2026
1059930
Sign the remaining provider HTTP requests under enforced auth
RafalMirowski1 Jun 28, 2026
a3069bc
add --lib to coverage job
RafalMirowski1 Jun 28, 2026
bcbac12
Merge remote-tracking branch 'origin/dev' into remove-disable-auth-flag
RafalMirowski1 Jul 2, 2026
acf9a06
fix(demo): bump full-flow agreement duration to 30 to outlast the che…
RafalMirowski1 Jul 2, 2026
93344d9
Merge branch 'dev' into remove-disable-auth-flag
RafalMirowski1 Jul 3, 2026
ee3ac16
Revert 'add --lib to coverage job'
RafalMirowski1 Jul 3, 2026
93fd661
Update provider-node/tests/common/mod.rs
bkontur Jul 3, 2026
845c4de
Merge remote-tracking branch 'origin/remove-disable-auth-flag' into r…
RafalMirowski1 Jul 7, 2026
a18d910
Merge remote-tracking branch 'origin/dev' into remove-disable-auth-flag
RafalMirowski1 Jul 7, 2026
339315b
test(provider): restore #![allow(dead_code)] in shared tests/common
RafalMirowski1 Jul 7, 2026
bdd167b
refactor(s3-client): drop redundant signer_keypair(), clone from sign…
RafalMirowski1 Jul 7, 2026
f8cea48
refactor(client): unify signing on a mandatory Signer type
RafalMirowski1 Jul 7, 2026
010f9f0
docs
RafalMirowski1 Jul 7, 2026
c046d11
refactor(sdk): make ChainSigner.keypair mandatory; fail fast on unsig…
RafalMirowski1 Jul 7, 2026
95f0333
Merge branch 'dev' into remove-disable-auth-flag
bkontur Jul 8, 2026
f8ee79f
Merge branch 'dev' into remove-disable-auth-flag
bkontur Jul 9, 2026
2123768
Merge branch 'dev' into remove-disable-auth-flag
bkontur Jul 13, 2026
05a67ba
Merge branch 'dev' into remove-disable-auth-flag
bkontur Jul 14, 2026
70d4369
Merge branch 'dev' into remove-disable-auth-flag
bkontur Jul 14, 2026
043582e
refactor(examples/papi): rename uploadAndVerify signer param from cli…
RafalMirowski1 Jul 16, 2026
2bd85da
refactor(client): require an explicit Signer — drop Default (dev Alic…
RafalMirowski1 Jul 16, 2026
03e4542
refactor(primitives): move provider HTTP auth helpers into provider-n…
RafalMirowski1 Jul 16, 2026
86577d0
refactor(client): AdminClient takes its signer at construction
RafalMirowski1 Jul 16, 2026
4b62a56
refactor(layer1): extract shared Layer1Client base from fs/s3 clients
RafalMirowski1 Jul 16, 2026
1461964
refactor(ui): single setSigner entry point for account switching in d…
RafalMirowski1 Jul 16, 2026
8b2dd49
refactor(provider-node): construct ProviderState from ProviderDeps
RafalMirowski1 Jul 16, 2026
ddc2d18
Merge remote-tracking branch 'origin/dev' into remove-disable-auth-flag
RafalMirowski1 Jul 17, 2026
404c5a0
naming
RafalMirowski1 Jul 17, 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
12 changes: 4 additions & 8 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ jobs:
nohup ./target/release/storage-provider-node \
--keyfile /tmp/alice-key --storage-mode inmemory \
--bind-addr 0.0.0.0:3333 --chain-rpc ws://127.0.0.1:2222 \
--enable-checkpoint-coordinator \
--disable-auth-i-know-what-i-am-doing > /tmp/provider-inmemory.log 2>&1 &
--enable-checkpoint-coordinator > /tmp/provider-inmemory.log 2>&1 &

- name: Wait for provider health
uses: ./.github/actions/wait-for-provider-health
Expand Down Expand Up @@ -320,8 +319,7 @@ jobs:
nohup ./target/release/storage-provider-node \
--keyfile /tmp/alice-key --storage-mode inmemory \
--bind-addr 0.0.0.0:3333 --chain-rpc ws://127.0.0.1:2222 \
--enable-checkpoint-coordinator \
--disable-auth-i-know-what-i-am-doing > /tmp/provider.log 2>&1 &
--enable-checkpoint-coordinator > /tmp/provider.log 2>&1 &

- name: Wait for provider health
uses: ./.github/actions/wait-for-provider-health
Expand Down Expand Up @@ -552,8 +550,7 @@ jobs:
nohup ./target/release/storage-provider-node \
--keyfile /tmp/alice-key --storage-mode inmemory \
--bind-addr 0.0.0.0:3333 --chain-rpc ws://127.0.0.1:2222 \
--enable-checkpoint-coordinator \
--disable-auth-i-know-what-i-am-doing > /tmp/provider.log 2>&1 &
--enable-checkpoint-coordinator > /tmp/provider.log 2>&1 &

- name: Wait for provider health
uses: ./.github/actions/wait-for-provider-health
Expand Down Expand Up @@ -684,8 +681,7 @@ jobs:
nohup ./target/release/storage-provider-node \
--keyfile /tmp/alice-key --storage-mode inmemory \
--bind-addr 0.0.0.0:3333 --chain-rpc ws://127.0.0.1:2222 \
--enable-checkpoint-coordinator \
--disable-auth-i-know-what-i-am-doing > /tmp/provider.log 2>&1 &
--enable-checkpoint-coordinator > /tmp/provider.log 2>&1 &

- name: Wait for provider health
uses: ./.github/actions/wait-for-provider-health
Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

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

5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,10 @@ The provider node uses environment variables for configuration:
## Example: Basic Upload Flow

```rust
use storage_client::StorageUserClient;
use storage_client::{Signer, StorageUserClient};

// Connect to provider
let mut client = StorageUserClient::new(config);
client.connect_chain().await?;
let client = StorageUserClient::new(config, Signer::dev("alice")?)?;

// Upload data (off-chain)
let data = b"Hello, decentralized storage!";
Expand Down
34 changes: 16 additions & 18 deletions client/INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Each specialized client (ProviderClient, AdminClient, etc.) uses the base client
### Basic Setup

```rust
use storage_client::{AdminClient, ClientConfig};
use storage_client::{AdminClient, ClientConfig, Signer};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -46,12 +46,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
enable_retries: true,
};

// Create client and connect to chain
let mut client = AdminClient::new(config, "5GrwvaEF...".to_string())?;
client.base.connect_chain().await?;

// Set signer (for testing)
client.base = client.base.with_dev_signer("alice")?;
// Create client (dev signer for testing) and connect to chain
let mut client = AdminClient::new(config, Signer::dev("alice")?)?;
client.connect().await?;

// Now you can make on-chain calls
let bucket_id = client.create_bucket(2).await?;
Expand All @@ -66,16 +63,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
For production, use actual keypairs instead of dev accounts:

```rust
use storage_client::{AdminClient, Signer};
use subxt_signer::sr25519::Keypair;

// Load from seed phrase or keystore
let keypair = Keypair::from_uri("//Alice")?;

// Set signer
if let Some(chain_client) = client.base.chain_client.as_mut() {
let client = Arc::make_mut(chain_client);
*client = client.clone().with_signer(keypair);
}
// Any subxt sr25519 keypair converts into a Signer
let mut client = AdminClient::new(config, Signer::from(keypair))?;
client.connect().await?;
```

### Dynamic Extrinsics
Expand Down Expand Up @@ -222,8 +218,8 @@ mod tests {
};

let mut client = ProviderClient::new(config, "5FHne...".to_string())?;
client.base.connect_chain().await?;
client.base = client.base.with_dev_signer("bob")?;
client.connect().await?;
client.set_signer(Signer::dev("bob")?)?;

let result = client.register(
"/ip4/127.0.0.1/tcp/3333".to_string(),
Expand Down Expand Up @@ -254,18 +250,20 @@ cargo test --features integration-tests

### "Not connected to chain" Error

Ensure you call `connect_chain()` before making on-chain calls:
Ensure you call `connect()` before making on-chain calls:

```rust
client.base.connect_chain().await?;
client.connect().await?;
```

### "No signer configured" Error

Set a signer before submitting extrinsics:
Set a signer before submitting extrinsics. Only `ProviderClient` and
`ChallengerClient` need this step — `AdminClient` and `StorageUserClient`
take their signer at construction:

```rust
client.base = client.base.with_dev_signer("alice")?;
client.set_signer(Signer::dev("alice")?)?;
```

### Transaction Fails
Expand Down
34 changes: 15 additions & 19 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ tokio = { version = "1", features = ["full"] }

### Setup

All clients that need on-chain access must connect to the chain and set a signer:
All clients must connect to the chain before on-chain operations. `AdminClient`
and `StorageUserClient` take their signer at construction; `ProviderClient` and
`ChallengerClient` set it after connecting via `set_signer`:

```rust
use storage_client::{AdminClient, ClientConfig};
use storage_client::{AdminClient, ClientConfig, Signer};

let config = ClientConfig::default(); // ws://localhost:2222
let mut client = AdminClient::new(config, "5GrwvaEF...".to_string())?;
// Dev account for testing — use a real keypair in production!
let mut client = AdminClient::new(config, Signer::dev("alice")?)?;

// Connect to chain
client.base.connect_chain().await?;

// Set signer (for testing - use proper keypairs in production!)
client.base = client.base.with_dev_signer("alice")?;
client.connect().await?;

// Now ready for on-chain operations
```
Expand All @@ -56,16 +56,12 @@ See [INTEGRATION.md](INTEGRATION.md) for detailed substrate integration guide.
Upload, download, and verify data:

```rust
use storage_client::{StorageUserClient, ChunkingStrategy};
use storage_client::{ChunkingStrategy, ClientConfig, Signer, StorageUserClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client
let mut client = StorageUserClient::with_defaults()?;

// Connect to chain for commit operations
client.base.connect_chain().await?;
client.base = client.base.with_dev_signer("alice")?;
// Create client (default config, dev Alice signer)
let client = StorageUserClient::new(ClientConfig::default(), Signer::dev("alice")?)?;

// Upload data
let data = b"My important data";
Expand Down Expand Up @@ -123,11 +119,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Create and manage buckets:

```rust
use storage_client::AdminClient;
use storage_client::{AdminClient, Signer};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = AdminClient::with_defaults("5GrwvaEF...".to_string())?;
let client = AdminClient::with_defaults(Signer::dev("alice")?)?;

// Create bucket
let bucket_id = client.create_bucket(2).await?; // min 2 providers
Expand Down Expand Up @@ -253,7 +249,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
All clients can be configured with custom settings:

```rust
use storage_client::ClientConfig;
use storage_client::{ClientConfig, Signer};

let config = ClientConfig {
chain_ws_url: "ws://localhost:2222".to_string(),
Expand All @@ -262,7 +258,7 @@ let config = ClientConfig {
enable_retries: true,
};

let client = StorageUserClient::new(config)?;
let client = StorageUserClient::new(config, Signer::from_seed("//Alice")?)?;
```

### Error Handling
Expand Down Expand Up @@ -359,7 +355,7 @@ cargo run --example complete_workflow
### Automated Spot-Checking

```rust
let mut client = StorageUserClient::with_defaults()?;
let mut client = StorageUserClient::new(ClientConfig::default(), Signer::dev("alice")?)?;

// Perform 10 random spot-checks
let (passed, failed) = client.spot_check_batch(
Expand Down
7 changes: 3 additions & 4 deletions client/examples/complete_workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

// 2. Redeem the signed terms on-chain to open a bucket + primary agreement.
println!("Establishing storage agreement on-chain...");
let mut admin = AdminClient::new(chain_config.clone(), user_ss58.clone())?;
let mut admin = AdminClient::new(chain_config.clone(), user_keypair.clone().into())?;
admin.connect().await?;
admin.set_signer(user_keypair)?;
let bucket_id = admin
.establish_storage_agreement(provider_ss58, signed)
.await?;
println!(" Bucket #{bucket_id} created");

// 3. Upload + download against the provider node, verifying integrity.
println!("Uploading and downloading data...");
let user_config = ClientConfig {
let user_client_config = ClientConfig {
chain_ws_url: chain_ws.to_string(),
provider_urls: vec![provider_url.to_string()],
..Default::default()
};
let user = StorageUserClient::new(user_config)?;
let user = StorageUserClient::new(user_client_config, user_keypair.into())?;
let data = b"hello e2e".to_vec();
let data_root = user
.upload(bucket_id, &data, ChunkingStrategy::default())
Expand Down
2 changes: 1 addition & 1 deletion client/examples/register_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let mut provider_client = ProviderClient::new(config, ss58_address.clone())?;
provider_client.connect().await?;
provider_client.set_signer(keypair.clone())?;
provider_client.set_signer(keypair.clone().into())?;

// Step 1: Register (idempotent — skip if already registered).
const STAKE: u128 = 1_000_000_000_000_000; // 1000 tokens (MinProviderStake, 12 decimals)
Expand Down
47 changes: 22 additions & 25 deletions client/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,43 @@ use crate::agreement::SignedTerms;
use crate::base::{BaseClient, ClientConfig, ClientError, ClientResult};
use crate::event_subscription::{EventParser, StorageEvent, StorageProviderEventParser};
use crate::substrate::{extrinsics, storage, SubstrateClient};
use crate::Signer;
use sp_core::H256;
use sp_runtime::AccountId32;
use storage_primitives::{BucketId, Commitment, EndAction, Role};
use subxt::ext::scale_value::{Composite, ValueDef, Variant};

/// Client for bucket administrators.
pub struct AdminClient {
base: BaseClient,
admin_account: String, // Substrate account ID
signer: Signer,
}

impl AdminClient {
/// Create a new admin client.
pub fn new(config: ClientConfig, admin_account: String) -> ClientResult<Self> {
/// Create a new admin client. `signer` submits every extrinsic and
/// identifies the admin account.
pub fn new(config: ClientConfig, signer: Signer) -> ClientResult<Self> {
Ok(Self {
base: BaseClient::new(config)?,
admin_account,
signer,
})
}

/// Create with default configuration.
pub fn with_defaults(admin_account: String) -> ClientResult<Self> {
Self::new(ClientConfig::default(), admin_account)
}

/// Connect to the blockchain. Must be called before any on-chain operations.
pub async fn connect(&mut self) -> ClientResult<()> {
self.base.connect_chain().await
/// The admin account: the signer's public key.
fn admin_account(&self) -> AccountId32 {
AccountId32::new(self.signer.keypair().public_key().0)
}

/// Set a development signer (alice, bob, charlie, dave, eve, ferdie).
/// Must be called after connect().
pub fn set_dev_signer(&mut self, name: &str) -> ClientResult<()> {
self.base.set_dev_signer(name)
/// Create with default configuration.
pub fn with_defaults(signer: Signer) -> ClientResult<Self> {
Self::new(ClientConfig::default(), signer)
}

/// Set a custom keypair signer loaded from a keyfile or seed.
/// Must be called after connect().
pub fn set_signer(&mut self, signer: subxt_signer::sr25519::Keypair) -> ClientResult<()> {
self.base.set_signer(signer)
/// Connect to the blockchain and install the signer. Must be called before
/// any on-chain operations.
pub async fn connect(&mut self) -> ClientResult<()> {
self.base.connect_chain().await?;
self.base.set_signer(self.signer.clone())
}

// ═════════════════════════════════════════════════════════════════════════
Expand All @@ -67,9 +65,9 @@ impl AdminClient {
///
/// # Example
/// ```no_run
/// # use storage_client::{AdminClient, NegotiateRequest, ProviderClient};
/// # use storage_client::{AdminClient, NegotiateRequest, ProviderClient, Signer};
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let client = AdminClient::with_defaults("5GrwvaEF...".to_string())?;
/// let client = AdminClient::with_defaults(Signer::dev("alice")?)?;
/// let signed = ProviderClient::negotiate_terms(
/// "http://provider.example:3333",
/// &NegotiateRequest {
Expand Down Expand Up @@ -101,7 +99,7 @@ impl AdminClient {
tracing::info!(
"Establishing storage agreement with provider {} for owner {} (max_bytes={}, duration={}, nonce={})",
provider,
self.admin_account,
self.admin_account(),
terms.max_bytes,
terms.duration,
terms.nonce,
Expand Down Expand Up @@ -701,14 +699,13 @@ impl AdminClient {
/// Get the buckets this admin account is a member of.
pub async fn list_my_buckets(&self) -> ClientResult<Vec<BucketId>> {
let chain = self.base.chain()?;
let admin_account = SubstrateClient::parse_account(&self.admin_account)?;

let at = chain
.api()
.at_current_block()
.await
.map_err(|e| ClientError::Chain(format!("Failed to get storage: {e}")))?;
let (addr, keys) = storage::member_buckets(&admin_account);
let (addr, keys) = storage::member_buckets(&self.admin_account());
let thunk = at
.storage()
.try_fetch(addr, keys)
Expand Down
Loading
Loading