Skip to content

Commit f979c2c

Browse files
feat: Quilt client: CLI (#2197)
* CLI support for Quilt
1 parent ae22ca1 commit f979c2c

File tree

11 files changed

+898
-76
lines changed

11 files changed

+898
-76
lines changed

crates/walrus-core/src/encoding/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ pub enum QuiltError {
194194
/// Failed to decode extension.
195195
#[error("failed to decode extension {0}: {1}")]
196196
FailedToDecodeExtension(String, bcs::Error),
197+
/// QuiltPatchId is invalid.
198+
#[error("QuiltPatchId: {0} is invalid")]
199+
QuiltPatchIdParseError(String),
197200
/// Other error.
198201
#[error("other error: {0}")]
199202
Other(String),

crates/walrus-core/src/encoding/quilt_encoding.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ pub trait QuiltApi<V: QuiltVersion> {
129129
target_value: &str,
130130
) -> Result<Vec<QuiltStoreBlob<'static>>, QuiltError>;
131131

132+
/// Returns all the blobs in the quilt.
133+
fn get_all_blobs(&self) -> Result<Vec<QuiltStoreBlob<'static>>, QuiltError>;
134+
132135
/// Returns the quilt index.
133136
fn quilt_index(&self) -> Result<&V::QuiltIndex, QuiltError>;
134137

@@ -445,6 +448,13 @@ impl QuiltEnum {
445448
}
446449
}
447450

451+
/// Returns all the blobs in the quilt.
452+
pub fn get_all_blobs(&self) -> Result<Vec<QuiltStoreBlob<'static>>, QuiltError> {
453+
match self {
454+
QuiltEnum::V1(quilt_v1) => quilt_v1.get_all_blobs(),
455+
}
456+
}
457+
448458
/// Returns the quilt index.
449459
pub fn get_quilt_index(&self) -> Result<QuiltIndex, QuiltError> {
450460
match self {
@@ -457,7 +467,7 @@ impl QuiltEnum {
457467
///
458468
/// A valid identifier is a string that contains only alphanumeric characters,
459469
/// underscores, hyphens, and periods.
460-
#[derive(Debug, Clone, PartialEq, Eq)]
470+
#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
461471
pub struct QuiltStoreBlob<'a> {
462472
/// The blob data, either borrowed or owned.
463473
blob: Cow<'a, [u8]>,
@@ -892,6 +902,14 @@ impl QuiltApi<QuiltVersionV1> for QuiltV1 {
892902
.collect()
893903
}
894904

905+
fn get_all_blobs(&self) -> Result<Vec<QuiltStoreBlob<'static>>, QuiltError> {
906+
self.quilt_index()?
907+
.patches()
908+
.iter()
909+
.map(|patch| QuiltVersionV1::decode_blob(self, usize::from(patch.start_index)))
910+
.collect()
911+
}
912+
895913
fn quilt_index(&self) -> Result<&QuiltIndexV1, QuiltError> {
896914
self.quilt_index
897915
.as_ref()
@@ -1453,7 +1471,7 @@ impl QuiltEncoderApi<QuiltVersionV1> for QuiltEncoderV1<'_> {
14531471

14541472
let (sliver_pairs, metadata) = encoder.encode_with_metadata();
14551473
let quilt_metadata = QuiltMetadata::V1(QuiltMetadataV1 {
1456-
quilt_blob_id: *metadata.blob_id(),
1474+
quilt_id: *metadata.blob_id(),
14571475
metadata: metadata.metadata().clone(),
14581476
index: QuiltIndexV1 {
14591477
quilt_patches: quilt.quilt_index()?.quilt_patches.clone(),
@@ -2305,7 +2323,7 @@ mod tests {
23052323

23062324
let (quilt_blob, metadata_with_id) = decoder
23072325
.decode_and_verify(
2308-
&quilt_metadata_v1.quilt_blob_id,
2326+
&quilt_metadata_v1.quilt_id,
23092327
sliver_pairs
23102328
.iter()
23112329
.map(|s| s.secondary.clone())

crates/walrus-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ impl Debug for BlobId {
197197
///
198198
/// A quilt patch is a individual blob within a quilt.
199199
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
200+
#[serde(rename_all = "camelCase")]
200201
pub struct QuiltPatchId {
201202
/// The BlobId of the quilt as a Walrus blob.
202203
pub quilt_id: BlobId,

crates/walrus-core/src/metadata.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub enum VerificationError {
6161

6262
/// Represents a blob within a unencoded quilt.
6363
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64+
#[serde(rename_all = "camelCase")]
6465
pub struct QuiltPatchV1 {
6566
/// The start sliver index of the blob.
6667
#[serde(skip)]
@@ -147,6 +148,7 @@ impl QuiltPatchV1 {
147148

148149
/// A enum wrapper around the quilt index.
149150
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151+
#[serde(rename_all = "camelCase", rename_all_fields = "camelCase")]
150152
pub enum QuiltIndex {
151153
/// QuiltIndexV1.
152154
V1(QuiltIndexV1),
@@ -164,6 +166,13 @@ impl QuiltIndex {
164166
}
165167
}
166168
}
169+
170+
/// Returns the patches of the quilt index.
171+
pub fn patches(&self) -> &[QuiltPatchV1] {
172+
match self {
173+
QuiltIndex::V1(quilt_index) => &quilt_index.quilt_patches,
174+
}
175+
}
167176
}
168177

169178
/// QuiltPatchInternalIdV1.
@@ -249,6 +258,7 @@ impl QuiltPatchInternalIdV1 {
249258
/// mapped to a contiguous index range.
250259
// INV: The patches are sorted by their end indices.
251260
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
261+
#[serde(rename_all = "camelCase")]
252262
pub struct QuiltIndexV1 {
253263
/// Location/identity index of the blob in the quilt.
254264
pub quilt_patches: Vec<QuiltPatchV1>,
@@ -311,7 +321,7 @@ impl QuiltMetadata {
311321
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
312322
pub struct QuiltMetadataV1 {
313323
/// The BlobId of the quilt blob.
314-
pub quilt_blob_id: BlobId,
324+
pub quilt_id: BlobId,
315325
/// The blob metadata of the quilt blob.
316326
pub metadata: BlobMetadata,
317327
/// The index of the quilt.
@@ -321,10 +331,7 @@ pub struct QuiltMetadataV1 {
321331
impl QuiltMetadataV1 {
322332
/// Returns the verified metadata for the quilt blob.
323333
pub fn get_verified_metadata(&self) -> VerifiedBlobMetadataWithId {
324-
VerifiedBlobMetadataWithId::new_verified_unchecked(
325-
self.quilt_blob_id,
326-
self.metadata.clone(),
327-
)
334+
VerifiedBlobMetadataWithId::new_verified_unchecked(self.quilt_id, self.metadata.clone())
328335
}
329336
}
330337

crates/walrus-sdk/src/client/client_types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(crate) const BLOB_SPAN_LEVEL: Level = Level::DEBUG;
3131

3232
/// Identifies a stored quilt patch.
3333
#[derive(Debug, Clone, Serialize, Deserialize)]
34+
#[serde(rename_all = "camelCase")]
3435
pub struct StoredQuiltPatch {
3536
/// The identifier of the quilt patch.
3637
pub identifier: String,

crates/walrus-sdk/src/client/quilt_client.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,30 @@ where
301301
.collect::<Result<Vec<_>, _>>(),
302302
}
303303
}
304+
305+
/// Retrieves all the blobs from the quilt.
306+
pub async fn get_all_blobs(
307+
&mut self,
308+
metadata: &VerifiedBlobMetadataWithId,
309+
certified_epoch: Epoch,
310+
) -> ClientResult<Vec<QuiltStoreBlob<'static>>> {
311+
match &self.reader {
312+
QuiltCacheReader::Uninitialized | QuiltCacheReader::Decoder(_) => {
313+
let quilt = self
314+
.client
315+
.get_full_quilt(metadata, certified_epoch)
316+
.await?;
317+
self.reader = QuiltCacheReader::FullQuilt(quilt);
318+
}
319+
QuiltCacheReader::FullQuilt(_) => {}
320+
}
321+
322+
let quilt = match &self.reader {
323+
QuiltCacheReader::FullQuilt(quilt) => quilt,
324+
_ => unreachable!(),
325+
};
326+
quilt.get_all_blobs().map_err(ClientError::other)
327+
}
304328
}
305329

306330
/// Configuration for the QuiltClient.
@@ -379,7 +403,7 @@ impl<T: ReadClient> QuiltClient<'_, T> {
379403

380404
let quilt_metadata = match quilt_index {
381405
QuiltIndex::V1(quilt_index) => QuiltMetadata::V1(QuiltMetadataV1 {
382-
quilt_blob_id: *quilt_id,
406+
quilt_id: *quilt_id,
383407
metadata: metadata.metadata().clone(),
384408
index: quilt_index.clone(),
385409
}),
@@ -632,6 +656,28 @@ impl<T: ReadClient> QuiltClient<'_, T> {
632656
.await
633657
}
634658

659+
/// Retrieves all the blobs from the quilt.
660+
pub async fn get_all_blobs(
661+
&self,
662+
quilt_id: &BlobId,
663+
) -> ClientResult<Vec<QuiltStoreBlob<'static>>> {
664+
let (certified_epoch, _) = self
665+
.client
666+
.get_blob_status_and_certified_epoch(quilt_id, None)
667+
.await?;
668+
let metadata = self
669+
.client
670+
.retrieve_metadata(certified_epoch, quilt_id)
671+
.await?;
672+
673+
let mut quilt_reader =
674+
QuiltReader::<'_, QuiltVersionV1, T>::new(self, self.config.clone(), None).await;
675+
quilt_reader
676+
.get_all_blobs(&metadata, certified_epoch)
677+
.await
678+
.map_err(ClientError::other)
679+
}
680+
635681
/// Retrieves the quilt from Walrus.
636682
async fn get_full_quilt(
637683
&self,

crates/walrus-service/src/client/cli.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use anyhow::{Context, Result};
1313
use colored::{Color, ColoredString, Colorize};
1414
use num_bigint::BigUint;
1515
use serde::{Deserialize, Serialize};
16-
use walrus_core::BlobId;
16+
use walrus_core::{BlobId, QuiltPatchId, encoding::QuiltError};
1717
use walrus_sdk::{
1818
blocklist::Blocklist,
1919
client::Client,
@@ -36,6 +36,11 @@ pub use args::{
3636
NodeSelection,
3737
NodeSortBy,
3838
PublisherArgs,
39+
QuiltBlobInput,
40+
QuiltPatchByIdentifier,
41+
QuiltPatchByPatchId,
42+
QuiltPatchByTag,
43+
QuiltPatchSelector,
3944
SortBy,
4045
};
4146
pub use cli_output::CliOutput;
@@ -418,6 +423,11 @@ pub fn parse_blob_id(input: &str) -> Result<BlobId, BlobIdParseError> {
418423
})
419424
}
420425

426+
/// Parses a [`QuiltPatchId`] from a string.
427+
pub fn parse_quilt_patch_id(input: &str) -> Result<QuiltPatchId, QuiltError> {
428+
QuiltPatchId::from_str(input).map_err(|_| QuiltError::QuiltPatchIdParseError(input.to_string()))
429+
}
430+
421431
/// Helper struct to parse and format blob IDs as decimal numbers.
422432
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
423433
#[repr(transparent)]

0 commit comments

Comments
 (0)