Skip to content

Commit a9bd7fb

Browse files
rphmeierpepyakin
authored andcommitted
shim: blob query
1 parent 8cb7267 commit a9bd7fb

File tree

4 files changed

+137
-49
lines changed

4 files changed

+137
-49
lines changed

sugondat/shim/src/cli.rs

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,6 @@ pub mod serve {
133133
pub mod query {
134134
//! CLI definition for the `query` subcommand.
135135
136-
// TODO: I envision several subcommands here. For example:
137-
// - query block <block_hash/number> — returns the information about a block and header.
138-
// - query blob <id> - returns the blob for a given key. The key here is the same sense as
139-
// described here https://github.com/thrumdev/blobs/issues/9#issuecomment-1814005570.
140-
141136
use super::{KeyManagementParams, SugondatRpcParams, ENV_SUGONDAT_NAMESPACE};
142137
use clap::{Args, Subcommand};
143138

@@ -153,64 +148,105 @@ pub mod query {
153148
Submit(submit::Params),
154149
/// Queries information about a block and header.
155150
Block(block::Params),
151+
/// Queries information about a specific blob.
152+
Blob(blob::Params),
156153
}
157154

158-
pub mod block {
159-
//! CLI definition for the `query block` subcommand.
155+
/// A reference to a block to query.
156+
#[derive(Debug, Clone)]
157+
pub enum BlockRef {
158+
/// The current best finalized block known by the node.
159+
Best,
160+
/// The number of the block to query.
161+
Number(u64),
162+
/// The hex-encoded hash of the block to query, prefixed with "0x".
163+
Hash([u8; 32]),
164+
}
160165

161-
use clap::Args;
166+
impl std::fmt::Display for BlockRef {
167+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
168+
match *self {
169+
BlockRef::Best => write!(f, "best"),
170+
BlockRef::Number(n) => write!(f, "{}", n),
171+
BlockRef::Hash(h) => write!(f, "0x{}", hex::encode(&h[..])),
172+
}
173+
}
174+
}
162175

163-
use super::SugondatRpcParams;
164-
165-
/// A reference to a block to query.
166-
#[derive(Debug, Clone)]
167-
pub enum BlockRef {
168-
/// The current best finalized block known by the node.
169-
Best,
170-
/// The number of the block to query.
171-
Number(u64),
172-
/// The hex-encoded hash of the block to query, prefixed with "0x".
173-
Hash([u8; 32]),
176+
impl std::str::FromStr for BlockRef {
177+
type Err = String;
178+
179+
fn from_str(input: &str) -> Result<Self, Self::Err> {
180+
if input == "best" {
181+
return Ok(BlockRef::Best);
182+
}
183+
184+
if let Some(hash) = decode_hash(input)? {
185+
return Ok(BlockRef::Hash(hash));
186+
}
187+
188+
if let Ok(n) = input.parse::<u64>() {
189+
Ok(BlockRef::Number(n))
190+
} else {
191+
Err(format!("parse error. see `--help`"))
192+
}
174193
}
194+
}
175195

176-
impl std::fmt::Display for BlockRef {
177-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
178-
match *self {
179-
BlockRef::Best => write!(f, "best"),
180-
BlockRef::Number(n) => write!(f, "{}", n),
181-
BlockRef::Hash(h) => write!(f, "0x{}", hex::encode(&h[..])),
182-
}
196+
fn decode_hash(input: &str) -> Result<Option<[u8; 32]>, String> {
197+
if let Some(s) = input.strip_prefix("0x") {
198+
let bytes =
199+
hex::decode(s).map_err(|_| "Invalid parameter: not hex encoded".to_owned())?;
200+
201+
let mut hash = [0u8; 32];
202+
if bytes.len() != 32 {
203+
return Err("Invalid parameter: hash not 32 bytes".to_owned());
183204
}
205+
206+
hash.copy_from_slice(&bytes[..]);
207+
Ok(Some(hash))
208+
} else {
209+
Ok(None)
184210
}
211+
}
185212

186-
impl std::str::FromStr for BlockRef {
187-
type Err = String;
213+
pub mod blob {
214+
use clap::Args;
188215

189-
fn from_str(input: &str) -> Result<Self, Self::Err> {
190-
if input == "best" {
191-
return Ok(BlockRef::Best);
192-
}
216+
use super::{BlockRef, SugondatRpcParams};
193217

194-
if let Some(s) = input.strip_prefix("0x") {
195-
let bytes = hex::decode(s)
196-
.map_err(|_| "Invalid parameter: not hex encoded".to_owned())?;
218+
#[derive(Debug, Args)]
219+
pub struct Params {
220+
#[clap(flatten)]
221+
pub rpc: SugondatRpcParams,
197222

198-
let mut hash = [0u8; 32];
199-
if bytes.len() != 32 {
200-
return Err("Invalid parameter: hash not 32 bytes".to_owned());
201-
}
223+
/// The block containing the blob to query.
224+
///
225+
/// Possible values: ["best", number, hash]
226+
///
227+
/// "best" is the highest finalized block.
228+
///
229+
/// Hashes must be 32 bytes, hex-encoded, and prefixed with "0x".
230+
#[arg(value_name = "BLOCK_REF")]
231+
pub block: BlockRef,
202232

203-
hash.copy_from_slice(&bytes[..]);
204-
return Ok(BlockRef::Hash(hash));
205-
}
233+
/// The index of the extrinsic (transaction) containing the blob.
234+
#[arg(value_name = "INDEX")]
235+
pub index: u32,
206236

207-
if let Ok(n) = input.parse::<u64>() {
208-
Ok(BlockRef::Number(n))
209-
} else {
210-
Err(format!("parse error. see `--help`"))
211-
}
212-
}
237+
/// Output the blob data as binary to stdout rather than hex, and omits
238+
/// any other details intended for human consumption.
239+
#[arg(long)]
240+
pub raw: bool,
213241
}
242+
}
243+
244+
pub mod block {
245+
//! CLI definition for the `query block` subcommand.
246+
247+
use clap::Args;
248+
249+
use super::{BlockRef, SugondatRpcParams};
214250

215251
#[derive(Debug, Args)]
216252
pub struct Params {

sugondat/shim/src/cmd/query/blob.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use super::connect_rpc;
2+
use crate::cli::query::{blob::Params, BlockRef};
3+
4+
use std::io::Write;
5+
6+
pub async fn run(params: Params) -> anyhow::Result<()> {
7+
let Params {
8+
rpc,
9+
block,
10+
index,
11+
raw,
12+
} = params;
13+
14+
let client = connect_rpc(rpc).await?;
15+
16+
let maybe_hash = match block {
17+
BlockRef::Best => None,
18+
BlockRef::Hash(h) => Some(h),
19+
BlockRef::Number(n) => Some(
20+
client
21+
.block_hash(n)
22+
.await?
23+
.ok_or_else(|| anyhow::anyhow!("No block with number {}", n))?
24+
.0,
25+
),
26+
};
27+
28+
let block = client.get_block_at(maybe_hash).await?;
29+
30+
let i = block
31+
.blobs
32+
.binary_search_by_key(&index, |b| b.extrinsic_index)
33+
.map_err(|_| anyhow::anyhow!("No blob with extrinsic index {}", index))?;
34+
35+
let blob = block.blobs.get(i).expect("verified to exist above; qed");
36+
37+
if raw {
38+
std::io::stdout().write_all(&blob.data)?;
39+
} else {
40+
println!(
41+
" Blob #{}, Namespace {}, {} bytes",
42+
i + 1,
43+
&blob.namespace,
44+
blob.data.len()
45+
);
46+
println!("{}", hex::encode(&blob.data));
47+
}
48+
49+
Ok(())
50+
}

sugondat/shim/src/cmd/query/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::connect_rpc;
2-
use crate::cli::query::block::{BlockRef, Params};
2+
use crate::cli::query::{block::Params, BlockRef};
33

44
pub async fn run(params: Params) -> anyhow::Result<()> {
55
let Params { rpc, block } = params;

sugondat/shim/src/cmd/query/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ use crate::{
33
sugondat_rpc,
44
};
55

6+
mod blob;
67
mod block;
78
mod submit;
89

910
pub async fn run(params: Params) -> anyhow::Result<()> {
1011
match params.command {
1112
Commands::Submit(params) => submit::run(params).await?,
1213
Commands::Block(params) => block::run(params).await?,
14+
Commands::Blob(params) => blob::run(params).await?,
1315
}
1416
Ok(())
1517
}

0 commit comments

Comments
 (0)