This guide walks you through the basic_usage.rs example, demonstrating the complete Layer 1 file system workflow with real blockchain integration.
The example demonstrates:
- Blockchain Connection - Connect using
subxtwith proper signer setup - Drive Creation - Create a drive with automatic infrastructure setup
- Directory Structure - Build nested directories
- File Uploads - Upload files to different paths
- Directory Listing - Navigate and list directory contents
- File Downloads - Download and verify file integrity
Location: storage-interfaces/file-system/client/examples/basic_usage.rs
Before running the example, you need a running infrastructure:
# Terminal 1 - Start relay chain + parachain
just start-chain
# Wait for:
# ✓ Relay chain: ws://127.0.0.1:9900
# ✓ Parachain: ws://127.0.0.1:9944Verify blockchain is running:
bash scripts/check-chain.sh
# Should show: "Parachain is ready"# Terminal 2 - Start storage provider
export PROVIDER_ID=5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
export CHAIN_RPC=ws://127.0.0.1:9944
cargo run --release -p storage-provider-node
# Wait for:
# Storage provider listening on http://0.0.0.0:3000Verify provider is running:
curl http://localhost:3000/health
# Should return: {"status":"ok"}# Terminal 3 - Verify on-chain state
bash scripts/verify-setup.sh
# This checks:
# ✓ Provider is registered
# ✓ Provider settings are configured
# ✓ System is ready for storageIf setup is incomplete, follow the Quick Start Guide.
Once infrastructure is ready:
cd storage-interfaces/file-system/client
cargo run --example basic_usageExpected output:
🚀 File System Client - Basic Usage Example
============================================================
📡 Step 1: Connecting to blockchain and provider...
✅ Connected successfully!
📁 Step 2: Creating a new drive...
✅ Drive created with ID: 0
Name: My Documents
Capacity: 10 GB
Duration: 500 blocks
📂 Step 3: Creating directory structure...
Creating /documents...
✅ Created /documents
Creating /documents/work...
✅ Created /documents/work
Creating /photos...
✅ Created /photos
📝 Step 4: Uploading files...
Uploading /README.md (92 bytes)...
✅ Uploaded /README.md
Uploading /documents/work/report.txt (75 bytes)...
✅ Uploaded /documents/work/report.txt
Uploading /documents/notes.txt (93 bytes)...
✅ Uploaded /documents/notes.txt
📋 Step 5: Listing directory contents...
Contents of /:
📁 documents (0 bytes)
📁 photos (0 bytes)
📄 README.md (92 bytes)
Contents of /documents:
📁 work (0 bytes)
📄 notes.txt (93 bytes)
Contents of /documents/work:
📄 report.txt (75 bytes)
⬇️ Step 6: Downloading and verifying files...
Downloading /README.md...
✅ Downloaded 92 bytes
✅ Content verified!
Content preview: # My Documents
Welcome to my decentralized fil
Downloading /documents/work/report.txt...
✅ Downloaded 75 bytes
✅ Content verified!
Content:
Q4 2024 Report
Revenue: $1M
Growth: 50%
============================================================
🎉 Example completed successfully!
📊 Summary:
✅ Created drive: 0
✅ Created 3 directories
✅ Uploaded 3 files
✅ Listed directory contents
✅ Downloaded and verified files
💡 Next steps:
- Try clearing the drive: clear_drive()
- Try deleting the drive: delete_drive()
- Explore more file operations
- Check the on-chain state via polkadot.js
let mut fs_client = FileSystemClient::new(
"ws://127.0.0.1:9944", // Parachain WebSocket endpoint
"http://localhost:3000" // Provider HTTP endpoint
)
.await?
.with_dev_signer("alice") // Use Alice for testing
.await?;What happens:
- Connects to blockchain - Establishes WebSocket connection to parachain at port 9944
- Connects to provider - Sets HTTP endpoint for off-chain storage operations
- Sets signer - Configures Alice's dev account for transaction signing
Blockchain Integration:
- Uses
subxt::OnlineClientto connect to the parachain - Loads runtime metadata dynamically
- Prepares for transaction submission
let drive_id = fs_client
.create_drive(
Some("My Documents"), // Drive name
10_000_000_000, // 10 GB capacity
500, // 500 blocks duration
1_000_000_000_000, // 1 token payment (12 decimals)
None, // Auto-determine providers
Some(CommitStrategy::Batched { interval: 100 }), // Batch commits every 100 blocks
)
.await?;What happens:
- Constructs extrinsic - Builds
DriveRegistry::create_drivetransaction with dynamic encoding - Signs transaction - Signs with Alice's keypair
- Submits to chain - Sends transaction to parachain
- Watches events - Waits for
Finalizedstatus - Extracts drive_id - Parses
DriveCreatedevent to get new drive ID
On-Chain Effects:
- Creates entry in
Drivesstorage map - Adds drive to Alice's
UserDriveslist - Returns drive ID (e.g.,
0)
Automatic Setup (Future):
- Bucket creation in Layer 0 (planned)
- Provider selection and agreement setup (planned)
Parameters Explained:
10_000_000_000bytes = 10 GB500blocks ≈ 50 minutes (6s per block)1_000_000_000_000= 1 token with 12 decimalsNone= Auto-select provider count based on durationBatched { interval: 100 }= Commit every 100 blocks (≈10 minutes)
fs_client.create_directory(drive_id, "/documents", bucket_id).await?;
fs_client.create_directory(drive_id, "/documents/work", bucket_id).await?;
fs_client.create_directory(drive_id, "/photos", bucket_id).await?;What happens for each directory:
- Query current root - Gets drive's current root CID from on-chain storage
- Build directory node - Creates
DirectoryNodeprotobuf structure - Upload directory data - Uploads directory node to provider via HTTP
- Compute CID - Calculates blake2-256 hash of directory data
- Update parent - Recursively updates parent directories to include new entry
- Commit (if needed) - Updates root CID on-chain based on commit strategy
Directory Structure:
Root (/)
├── documents/ → CID: 0xabc...
│ ├── work/ → CID: 0xdef...
│ └── notes.txt → CID: 0x123...
├── photos/ → CID: 0x456...
└── README.md → CID: 0x789...
Content-Addressed: Each directory is stored as a blob with its own CID. The root CID represents the entire file system state.
let readme_content = b"# My Documents\n\nWelcome to my decentralized file system!";
fs_client.upload_file(drive_id, "/README.md", readme_content, bucket_id).await?;What happens:
- Parse path - Splits
/README.mdinto directory (/) and filename (README.md) - Chunk file - Splits content into chunks (if large)
- Upload chunks - Uploads each chunk to provider via HTTP POST
- Build manifest - Creates
FileManifestwith chunk CIDs - Upload manifest - Stores manifest as a blob
- Update directory - Adds file entry to parent directory
- Update ancestors - Recursively updates parent directories up to root
- Commit (if needed) - Updates root CID on-chain
For small files (<1MB): Single chunk is used
For large files: Multiple chunks with manifest tracking all CIDs
Nested paths (/documents/work/report.txt):
- Traverses down to
/documents/work/ - Adds
report.txtentry - Updates
/documents/work/directory - Updates
/documents/directory - Updates
/root directory
let entries = fs_client.list_directory(drive_id, "/documents").await?;
for entry in entries {
let entry_type = if entry.is_directory() { "📁" } else { "📄" };
println!("{} {} ({} bytes)", entry_type, entry.name, entry.size);
}What happens:
- Query root CID - Gets drive's current root from blockchain
- Traverse path - Follows path components to target directory:
- Start at root
/ - Look up
documentsentry - Get its CID
- Start at root
- Download directory - Fetches directory node from provider
- Parse entries - Decodes protobuf
DirectoryNode - Return list - Returns all entries with names, types, sizes, CIDs
Entry types:
- Directory - Has
directory_nodefield set (CID to nested directory) - File - Has
filefield set (CID to file manifest)
Sizes:
- Directories: 0 bytes (metadata only)
- Files: Sum of all chunk sizes
let downloaded = fs_client.download_file(drive_id, "/documents/work/report.txt").await?;What happens:
- Query root CID - Gets current root from blockchain
- Traverse path - Navigates to
/documents/work/report.txt - Get file manifest - Downloads manifest blob from provider
- Parse manifest - Extracts chunk CIDs
- Download chunks - Fetches each chunk from provider via HTTP GET
- Reconstruct file - Concatenates chunks in sequence order
- Verify integrity - Each chunk's CID is verified against expected hash
Verification:
- Each chunk is content-addressed (CID = blake2-256 hash)
- Tampering is immediately detected
- Provider cannot serve incorrect data without detection
FileSystemClient
└── create_drive()
└── SubstrateClient::create_drive_on_chain()
├── Build dynamic extrinsic
├── Sign with keypair
├── Submit via RPC
├── Watch for Finalized event
└── Extract drive_id from events
// Query drive root CID
let storage_client = self.substrate_client.api().storage().at_latest().await?;
// Manual storage key construction
let pallet_hash = twox_128(b"DriveRegistry");
let storage_hash = twox_128(b"Drives");
let key_hash = blake2_128(&drive_id.to_le_bytes());
// Fetch from chain
let bytes = storage_client.fetch_raw(storage_key).await?;
// Decode DriveInfo
let drive_info = decode_drive_info(bytes)?;
let root_cid = drive_info.root_cid;When commit strategy triggers:
FileSystemClient::upload_file()
└── update_drive_root_cid()
└── SubstrateClient::update_root_cid()
├── Build update_root_cid extrinsic
├── Sign and submit
└── Wait for finalization
Commit strategies:
- Immediate: Every file operation updates on-chain (expensive, real-time)
- Batched: Updates every N blocks (balanced, efficient)
- Manual: User controls when to commit (most efficient)
Problem: Cannot connect to blockchain
Solutions:
# Check blockchain is running
bash scripts/check-chain.sh
# Check port is accessible
curl http://localhost:9944
# Should NOT refuse connection
# Restart blockchain
pkill -f polkadot
pkill -f "polkadot-parachain\|polkadot-omni-node"
just start-chainProblem: Cannot reach storage provider
Solutions:
# Check provider is running
curl http://localhost:3000/health
# Check provider logs
tail -f provider-node.log
# Restart provider
cargo run --release -p storage-provider-nodeProblem: Drive was not created successfully
Solutions:
# Check on-chain state
bash scripts/verify-setup.sh
# View drive via polkadot.js
open "https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9944#/chainstate"
# Query: driveRegistry > drives(0)Problem: Transaction succeeded but event extraction failed
Debugging:
// Enable debug logging
RUST_LOG=debug cargo run --example basic_usage
// Look for:
// - "Submitting create_drive transaction"
// - "Transaction finalized"
// - "Event found: DriveCreated"Problem: Forgot to call with_dev_signer() or with_signer()
Solution:
// After creating client, must set signer
let fs_client = FileSystemClient::new(...).await?
.with_dev_signer("alice").await?; // Add this!After running the example successfully:
// Clear all drive contents
fs_client.clear_drive(drive_id).await?;
// Delete entire drive
fs_client.delete_drive(drive_id).await?;
// List all your drives
let drives = fs_client.list_drives().await?;Try changing:
- Drive parameters: Increase capacity, duration, or provider count
- Commit strategy: Try
ImmediateorManual - File sizes: Upload larger files to test chunking
- Directory depth: Create deeper nested structures
Via polkadot.js:
https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9944#/chainstate
Queries to try:
- driveRegistry.drives(0) - View drive metadata
- driveRegistry.userDrives(ALICE_ACCOUNT) - List Alice's drives
- driveRegistry.nextDriveId() - See next available ID
Via CLI:
# Watch blockchain events
polkadot-js-api --ws ws://127.0.0.1:9944 query.system.events
# Query specific drive
polkadot-js-api --ws ws://127.0.0.1:9944 query.driveRegistry.drives 0Use the example as a template for your own file storage application:
// Your app
use file_system_client::FileSystemClient;
use file_system_primitives::CommitStrategy;
async fn my_app() -> Result<(), Box<dyn std::error::Error>> {
let mut fs_client = FileSystemClient::new(
"ws://your-parachain:9944",
"http://your-provider:3000",
)
.await?
.with_signer(your_keypair)
.await?;
// Build your application logic here
// ...
Ok(())
}- User Guide - Complete user workflows
- API Reference - Full API documentation
- Client README - SDK documentation
- Quick Start - Initial setup
The basic_usage.rs example demonstrates the complete Layer 1 file system workflow:
✅ Blockchain Integration - Real on-chain transactions via subxt
✅ Drive Management - Create drives with automatic setup
✅ File Operations - Upload, download, verify files
✅ Directory Management - Hierarchical organization
✅ Content-Addressed Storage - All data is verifiable via CIDs
✅ Flexible Commits - Control when changes go on-chain
This provides a solid foundation for building decentralized file storage applications on Scalable Web3 Storage!