dg_xch_cli_lib/cli/
mod.rs

1use bip39::Mnemonic;
2use clap::{Parser, Subcommand, ValueEnum};
3use dg_xch_core::blockchain::sized_bytes::Bytes32;
4use dialoguer::theme::ColorfulTheme;
5use dialoguer::Input;
6use std::io::{Error, ErrorKind};
7use std::str::FromStr;
8
9#[derive(Parser, Debug)]
10#[command(author, version, about, long_about = None)]
11pub struct Cli {
12    #[arg(short, long, value_name = "Path to the chia ssl folder")]
13    pub ssl_path: Option<String>,
14    #[arg(short, long, value_name = "Timeout When Connecting to Fullnode")]
15    pub timeout: Option<u64>,
16
17    #[arg(long, value_name = "Fullnode Hostname")]
18    pub fullnode_host: Option<String>,
19    #[arg(long, value_name = "Fullnode Port")]
20    pub fullnode_port: Option<u16>,
21
22    #[arg(long, value_name = "Wallet Hostname")]
23    pub wallet_host: Option<String>,
24    #[arg(long, value_name = "Wallet Port")]
25    pub wallet_port: Option<u16>,
26
27    #[arg(long, value_name = "Network to use, default to Mainnet")]
28    pub network: Option<String>,
29
30    #[command(subcommand)]
31    pub action: RootCommands,
32}
33
34#[derive(Debug, Subcommand)]
35pub enum RootCommands {
36    //START OF FULLNODE API
37    #[command(about = "Get the current BlockchainState", long_about = None)]
38    PrintPlottingInfo {
39        #[arg(long)]
40        launcher_id: Option<Bytes32>,
41    },
42    //START OF FULLNODE API
43    #[command(about = "Get the current BlockchainState", long_about = None)]
44    GetBlockchainState,
45    #[command(about = "Loads a FullBlock by header_hash", long_about = None)]
46    GetBlock {
47        #[arg(long)]
48        header_hash: Bytes32,
49    },
50    #[command(about = "Loads current BlockStateMetrics", long_about = None)]
51    GetBlockCountMetrics,
52    #[command(about = "Loads FullBlocks between start and end", long_about = None)]
53    GetBlocks {
54        #[arg(long)]
55        start: u32,
56        #[arg(long)]
57        end: u32,
58        #[arg(long)]
59        exclude_header_hash: bool,
60        #[arg(long)]
61        exclude_reorged: bool,
62    },
63    #[command(about = "Loads all FullBlocks between start and end", long_about = None)]
64    GetAllBlocks {
65        #[arg(long)]
66        start: u32,
67        #[arg(long)]
68        end: u32,
69    },
70    #[command(about = "Loads a BlockRecord by header_hash", long_about = None)]
71    GetBlockRecord {
72        #[arg(long)]
73        header_hash: Bytes32,
74    },
75    #[command(about = "Loads a BlockRecord by height", long_about = None)]
76    GetBlockRecordByHeight {
77        #[arg(long)]
78        height: u32,
79    },
80    #[command(about = "Loads all BlockRecords between start and end", long_about = None)]
81    GetBlockRecords {
82        #[arg(long)]
83        start: u32,
84        #[arg(long)]
85        end: u32,
86    },
87    #[command(about = "Loads UnfinishedBlocks", long_about = None)]
88    GetUnfinishedBlocks,
89    #[command(about = "Get Est network Space between two header_hashes", long_about = None)]
90    GetNetworkSpace {
91        #[arg(long)]
92        older_block_header_hash: Bytes32,
93        #[arg(long)]
94        newer_block_header_hash: Bytes32,
95    },
96    #[command(about = "Get Est network Space between two heights", long_about = None)]
97    GetNetworkSpaceaByHeight {
98        #[arg(long)]
99        start: u32,
100        #[arg(long)]
101        end: u32,
102    },
103    #[command(about = "Get additions and removals by header_hash", long_about = None)]
104    GetAdditionsAndRemovals {
105        #[arg(long)]
106        header_hash: Bytes32,
107    },
108    #[command(about = "Loads InitialFreezePeriod", long_about = None)]
109    GetInitialFreezePeriod,
110    #[command(about = "Loads InitialFreezePeriod", long_about = None)]
111    GetNetworkInfo,
112    #[command(about = "Get SignagePoint or End Of Subslot, Only Provide one of sp_hash or challenge_hash", long_about = None)]
113    GetSignagePointOrEOS {
114        #[arg(long)]
115        sp_hash: Option<Bytes32>,
116        #[arg(long)]
117        challenge_hash: Option<Bytes32>,
118    },
119    #[command(about = "Get CoinRecords by puzzle_hashs", long_about = None)]
120    GetCoinRecords {
121        #[arg(long)]
122        puzzle_hashes: Vec<Bytes32>,
123        #[arg(long)]
124        include_spent_coins: Option<bool>,
125        #[arg(long)]
126        start_height: Option<u32>,
127        #[arg(long)]
128        end_height: Option<u32>,
129    },
130    #[command(about = "Get CoinRecord by name", long_about = None)]
131    GetCoinRecordByName {
132        #[arg(long)]
133        name: Bytes32,
134    },
135    #[command(about = "Get CoinRecords by names", long_about = None)]
136    GetCoinRecordsByNames {
137        #[arg(long)]
138        names: Vec<Bytes32>,
139        #[arg(long)]
140        include_spent_coins: bool,
141        #[arg(long)]
142        start_height: u32,
143        #[arg(long)]
144        end_height: u32,
145    },
146    #[command(about = "Get CoinRecords by parent ids", long_about = None)]
147    GetCoinRecordsByParentIds {
148        #[arg(long)]
149        parent_ids: Vec<Bytes32>,
150        #[arg(long)]
151        include_spent_coins: bool,
152        #[arg(long)]
153        start_height: u32,
154        #[arg(long)]
155        end_height: u32,
156    },
157    #[command(about = "Get CoinRecords by hint", long_about = None)]
158    GetCoinRecordsByhint {
159        #[arg(long)]
160        hint: Bytes32,
161        #[arg(long)]
162        include_spent_coins: bool,
163        #[arg(long)]
164        start_height: u32,
165        #[arg(long)]
166        end_height: u32,
167    },
168    #[command(about = "Get CoinSpend for coin_id at height", long_about = None)]
169    GetPuzzleAndSolution {
170        #[arg(long)]
171        coin_id: Bytes32,
172        #[arg(long)]
173        height: u32,
174    },
175    #[command(about = "Get CoinSpend for coin_id at height", long_about = None)]
176    GetCoinSpend {
177        #[arg(long)]
178        coin_id: Bytes32,
179        #[arg(long)]
180        height: u32,
181    },
182    #[command(about = "Loads All Mempool Transaction Ids", long_about = None)]
183    GetAllMempoolTxIds,
184    #[command(about = "Loads All Mempool Items", long_about = None)]
185    GetAllMempoolItems,
186    #[command(about = "Get MempoolItem with TxID", long_about = None)]
187    GetMempoolItemByTxID {
188        #[arg(long)]
189        tx_id: String,
190    },
191    #[command(about = "Get MempoolItem by name", long_about = None)]
192    GetMempoolItemByName {
193        #[arg(long)]
194        coin_name: Bytes32,
195    },
196    #[command(about = "Get MempoolItem by name", long_about = None)]
197    GetFeeEstimate {
198        #[arg(long)]
199        cost: Option<u64>,
200        #[arg(long)]
201        spend_bundle: Option<String>,
202        #[arg(long)]
203        spend_type: Option<String>,
204        #[arg(long)]
205        target_times: Vec<u64>,
206    },
207    //END FULLNODE API
208    //START EXTENDED FULLNODEAPI
209    #[command(about = "Get Singleton by LauncherID", long_about = None)]
210    GetSingletonByLauncherId {
211        #[arg(long)]
212        launcher_id: Bytes32,
213    },
214    #[command(about = "Get additions and removals with hints by header_hash", long_about = None)]
215    GetAdditionsAndRemovalsWithHints {
216        #[arg(long)]
217        header_hash: Bytes32,
218    },
219    #[command(about = "Get CoinRecords by hint", long_about = None)]
220    GetCoinRecordsByHints {
221        #[arg(long)]
222        hints: Vec<Bytes32>,
223        #[arg(long)]
224        include_spent_coins: bool,
225        #[arg(long)]
226        start_height: u32,
227        #[arg(long)]
228        end_height: u32,
229    },
230    #[command(about = "Get CoinRecords by hint", long_about = None)]
231    GetCoinRecordsByHintsPaginated {
232        #[arg(long)]
233        hints: Vec<Bytes32>,
234        #[arg(long)]
235        include_spent_coins: Option<bool>,
236        #[arg(long)]
237        start_height: Option<u32>,
238        #[arg(long)]
239        end_height: Option<u32>,
240        #[arg(long)]
241        page_size: u32,
242        #[arg(long)]
243        last_id: Option<Bytes32>,
244    },
245    #[command(about = "Get CoinRecords by hint", long_about = None)]
246    GetCoinRecordsByPuzzleHashesPaginated {
247        #[arg(long)]
248        puzzle_hashes: Vec<Bytes32>,
249        #[arg(long)]
250        include_spent_coins: Option<bool>,
251        #[arg(long)]
252        start_height: Option<u32>,
253        #[arg(long)]
254        end_height: Option<u32>,
255        #[arg(long)]
256        page_size: u32,
257        #[arg(long)]
258        last_id: Option<Bytes32>,
259    },
260    #[command(about = "Get Hints by CoinIds", long_about = None)]
261    GetHintsByCoinIds {
262        #[arg(long)]
263        coin_ids: Vec<Bytes32>,
264    },
265    #[command(about = "Get Hints by CoinIds", long_about = None)]
266    GetPuzzleAndSoultionsByNames {
267        #[arg(long)]
268        names: Vec<Bytes32>,
269        #[arg(long)]
270        include_spent_coins: Option<bool>,
271        #[arg(long)]
272        start_height: Option<u32>,
273        #[arg(long)]
274        end_height: Option<u32>,
275    },
276    //END EXTENDED FULLNODE API
277    #[command(about = "Migrates a PlotNFT using a mnemonic", long_about = None)]
278    MovePlotNFT {
279        #[arg(long)]
280        target_pool: String,
281        #[arg(long)]
282        launcher_id: Bytes32,
283        #[arg(long)]
284        target_address: Bytes32,
285        #[arg(long)]
286        mnemonic: String,
287        #[arg(long)]
288        fee: Option<u64>,
289    },
290    #[command(about = "Migrates a PlotNFT using an owner_secrey_key", long_about = None)]
291    MovePlotNFTWithOwnerKey {
292        #[arg(long)]
293        target_pool: String,
294        #[arg(long)]
295        launcher_id: Bytes32,
296        #[arg(long)]
297        target_address: Bytes32,
298        #[arg(long)]
299        owner_key: String,
300    },
301    #[command(about = "Gets plotnft state for launcher_id", long_about = None)]
302    GetPlotnftState {
303        #[arg(long)]
304        launcher_id: Bytes32,
305    },
306    #[command(about = "Create Login link for Pool", long_about = None)]
307    CreatePoolLoginLink {
308        #[arg(short, long)]
309        target_pool: String,
310        #[arg(short, long)]
311        launcher_id: Bytes32,
312        #[arg(short, long)]
313        auth_key: Bytes32,
314    },
315    #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
316    CreateWallet {
317        #[command(subcommand)]
318        action: WalletAction,
319    },
320    #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
321    Curry {
322        #[arg(short = 'p', long = "program")]
323        program: String,
324        #[arg(short = 'a', long = "args")]
325        args: String,
326        #[arg(short = 'o', long = "output")]
327        output: Option<ProgramOutput>,
328    },
329    #[command(about = "Create a cold wallet or a PlotNFT wallet", long_about = None)]
330    Run {
331        #[arg(short = 'p', long = "program")]
332        program: String,
333        #[arg(short = 'a', long = "args")]
334        args: String,
335        #[arg(short = 'o', long = "output")]
336        output: Option<ProgramOutput>,
337    },
338}
339
340#[derive(Default, ValueEnum, Copy, Clone, Debug)]
341pub enum ProgramOutput {
342    #[default]
343    Hex,
344    String,
345}
346
347#[derive(Debug, Subcommand)]
348pub enum WalletAction {
349    #[command(about = "Creates a wallet with a plotnft", long_about = None)]
350    WithNFT {
351        #[arg(long)]
352        pool_url: Option<String>,
353        faucet_request_url: Option<String>,
354        faucet_request_payload: Option<String>,
355    },
356    #[command(about = "Creates a Cold wallet", long_about = None)]
357    Cold,
358}
359
360pub fn prompt_for_mnemonic() -> Result<Mnemonic, Error> {
361    Mnemonic::from_str(
362        &Input::<String>::with_theme(&ColorfulTheme::default())
363            .with_prompt("Please Input Your Mnemonic: ")
364            .validate_with(|input: &String| -> Result<(), &str> {
365                if Mnemonic::from_str(input).is_ok() {
366                    Ok(())
367                } else {
368                    Err("You did not input a valid Mnemonic, Please try again.")
369                }
370            })
371            .interact_text()
372            .map_err(|e| {
373                Error::new(
374                    ErrorKind::InvalidInput,
375                    format!("Failed to read user Input for Mnemonic: {e:?}"),
376                )
377            })?,
378    )
379    .map_err(|e| {
380        Error::new(
381            ErrorKind::InvalidInput,
382            format!("Failed to parse Mnemonic: {e:?}"),
383        )
384    })
385}