/ adl / cli / commands / query / block.rs
block.rs
 1  // Copyright (C) 2019-2025 Alpha-Delta Network Inc.
 2  // This file is part of the ADL library.
 3  
 4  // The ADL library is free software: you can redistribute it and/or modify
 5  // it under the terms of the GNU General Public License as published by
 6  // the Free Software Foundation, either version 3 of the License, or
 7  // (at your option) any later version.
 8  
 9  // The ADL library is distributed in the hope that it will be useful,
10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  // GNU General Public License for more details.
13  
14  // You should have received a copy of the GNU General Public License
15  // along with the ADL library. If not, see <https://www.gnu.org/licenses/>.
16  
17  use super::*;
18  
19  use crate::cli::context::Context;
20  use clap::Parser;
21  
22  // Query on-chain information related to blocks.
23  #[derive(Parser, Debug)]
24  pub struct AdlBlock {
25      #[clap(help = "Fetch a block by specifying its height or hash", required_unless_present_any = &["latest", "latest_hash", "latest_height", "range"])]
26      pub(crate) id: Option<String>,
27      #[arg(short, long, help = "Get the latest block", default_value = "false", conflicts_with_all(["latest_hash", "latest_height", "range", "transactions", "to_height"]))]
28      pub(crate) latest: bool,
29      #[arg(long, help = "Get the latest block hash", default_value = "false", conflicts_with_all(["latest", "latest_height", "range", "transactions", "to_height"]))]
30      pub(crate) latest_hash: bool,
31      #[arg(long, help = "Get the latest block height", default_value = "false", conflicts_with_all(["latest", "latest_hash", "range", "transactions", "to_height"]))]
32      pub(crate) latest_height: bool,
33      #[arg(short, long, help = "Get up to 50 consecutive blocks", number_of_values = 2, value_names = &["START_HEIGHT", "END_HEIGHT"], conflicts_with_all(["latest", "latest_hash", "latest_height", "transactions", "to_height"]))]
34      pub(crate) range: Option<Vec<String>>,
35      #[arg(
36          short,
37          long,
38          help = "Get all transactions at the specified block height",
39          conflicts_with("to_height"),
40          default_value = "false"
41      )]
42      pub(crate) transactions: bool,
43      #[arg(long, help = "Lookup the block height corresponding to a hash value", default_value = "false")]
44      pub(crate) to_height: bool,
45  }
46  
47  impl Command for AdlBlock {
48      type Input = ();
49      type Output = String;
50  
51      fn log_span(&self) -> Span {
52          tracing::span!(tracing::Level::INFO, "Adl")
53      }
54  
55      fn prelude(&self, _context: Context) -> Result<Self::Input> {
56          Ok(())
57      }
58  
59      fn apply(self, _context: Context, _input: Self::Input) -> Result<Self::Output> {
60          // Build custom url to fetch from based on the flags and user's input.
61          let url = if self.latest_height {
62              "block/height/latest".to_string()
63          } else if self.latest_hash {
64              "block/hash/latest".to_string()
65          } else if self.latest {
66              "block/latest".to_string()
67          } else if let Some(range) = self.range {
68              // Make sure the range is composed of valid numbers.
69              is_valid_numerical_input(&range[0])?;
70              is_valid_numerical_input(&range[1])?;
71  
72              // Parse the range values.
73              let end = &range[1].parse::<u32>().map_err(|_| UtilError::invalid_bound(&range[1]))?;
74              let start = &range[0].parse::<u32>().map_err(|_| UtilError::invalid_bound(&range[0]))?;
75              // Make sure the range is not too large.
76              if end - start > 50 {
77                  return Err(UtilError::invalid_range().into());
78              }
79              format!("blocks?start={}&end={}", range[0], range[1])
80          } else if self.transactions {
81              is_valid_numerical_input(&self.id.clone().unwrap())?;
82              format!("block/{}/transactions", self.id.unwrap()).to_string()
83          } else if self.to_height {
84              let id = self.id.unwrap();
85              is_valid_hash(&id)?;
86              format!("height/{id}").to_string()
87          } else if let Some(id) = self.id {
88              is_valid_height_or_hash(&id)?;
89              format!("block/{id}")
90          } else {
91              unreachable!("All cases are covered")
92          };
93  
94          Ok(url)
95      }
96  }