Open
Conversation
# Conflicts: # proto/vendor/celestia/signal/v1/query.proto
rach-id
reviewed
Mar 19, 2026
Member
rach-id
left a comment
There was a problem hiding this comment.
As per sync discussion: we will have an e2e test that submits a fibre blob and downloads it to make sure the flow works as expected.
Added a few comments from asking claude to review the PR.
Member
There was a problem hiding this comment.
probably this file shouldn't be pushed
| } | ||
| } | ||
|
|
||
| Ok(()) |
Member
There was a problem hiding this comment.
Claude:
2. Upload upload_shards silently succeeds without enough signatures
File: fibre/src/client/upload.rs:1530-1533
// After the loop exits:
Ok(())
When the upload_shards loop exhausts all validators without the signature threshold being met, it returns Ok(()). The caller then calls sig_set.signatures() which will return Err(NotEnoughSignatures), so it
does ultimately fail — but this is a confusing control flow. The upload_shards function's contract should be clearer about whether it's responsible for checking the threshold. More importantly, if
sig_set.signatures() is the only guard, then there's a subtle race: if upload_shards returns Ok(()) after all tasks complete with errors (so sig_set has zero signatures), the error message will say "not
enough voting power: collected 0, required X" — which is correct but not as helpful as "all validator uploads failed."
| } | ||
| if unique_rows >= original_rows { | ||
| break; | ||
| } |
Member
There was a problem hiding this comment.
Claude:
8. Download loop doesn't cancel inflight tasks when original_rows is reached
File: fibre/src/client/download.rs:626-628
if unique_rows >= original_rows {
break;
}
When enough rows are collected, the loop breaks, but already-spawned download tasks continue running in the background. Unlike uploads (where background delivery is intentional), downloads have no benefit
from continuing — the blob is already reconstructable. These tasks consume bandwidth and connection slots unnecessarily.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fibre Protocol Flow
Fibre is a blob storage layer on top of Celestia. Instead of posting blobs directly on-chain (expensive), clients distribute blob shards across validators off-chain and only post a small on-chain transaction (
MsgPayForFibre) as proof of storage.Default Protocol Parameters
Upload Flow
1. Encode the blob
The raw data gets a 5-byte header (version + size), then is split into 4096 rows. Rows are extended using Reed-Solomon erasure coding — the 4096 original rows become 16384 total rows (12288 parity rows). A Merkle tree is built over all rows, producing a blob commitment (the Merkle root).
2. Create a Payment Promise
The client creates a
PaymentPromise— a signed message saying "I promise to pay for storing this blob." It contains the chain ID, client's secp256k1 public key, namespace, blob size, commitment, blob version, height, and timestamp. The client signs this with its private key.3. Assign shards to validators
Using a deterministic shuffle (seeded by the blob commitment), the 16384 rows are distributed across the active validator set (up to 100 validators). Each validator gets a subset of rows (their "shard") — at least 148 rows each, up to 4096 for the highest-staked validator. The shuffle uses Go-compatible ChaCha8 PRNG so both Rust client and Go server agree on the assignment.
4. Fan-out upload
For each validator (up to 100 concurrently), the client:
UploadShardgRPC request with the payment promise, row proofs, and RLC coefficientsupload_concurrencysemaphore (default: 100)5. Collect signatures
Each validator verifies the shard (checks Merkle proofs, verifies the payment promise signature, validates RLC) and responds with an ed25519 validator signature over the payment promise. The client collects signatures until it has enough voting power (2/3+ of the validator set).
6. Build on-chain transaction
The collected signatures form a
SignatureSet. The client buildsMsgPayForFibrecontaining the payment promise and signature set, which gets submitted on-chain. The on-chain module verifies the signatures against the active validator set and processes payment.Download Flow
1. Fetch validator set and assignments
The client fetches the validator set for the blob's height and recomputes the deterministic shard assignment (same ChaCha8 shuffle) to know which validators hold which rows.
2. Adaptive fan-out download
The client doesn't contact all validators at once. It starts with a subset and expands (up to 100 concurrent tasks):
DownloadShardfrom selected validatorsset_row3. Reconstruct
Once 4096 original rows (or any 4096 of the 16384 total rows) are collected, the blob can be reconstructed using Reed-Solomon decoding. The header is decoded from the first row to extract the original data. Only 34 validators (1/3 of 100) are needed for reconstruction.
Proofs Used
MsgPayForFibreServer Side (Go)
UploadShard handler: Verifies the payment promise signature, checks Merkle proofs for each row, performs RLC verification, stores the shard in a local DB, and returns an ed25519 signature.
DownloadShard handler: Looks up the stored shard by blob ID, returns the row inclusion proofs to the requesting client.
On-chain (
x/fibremodule):MsgPayForFibrehandler verifies the signature set has sufficient voting power (2/3+), checks the payment promise, and processes the storage payment.