conflux/
cli.rs

1use crate::command::dump::DumpCommand;
2use clap::{Args, Parser, Subcommand, ValueEnum};
3
4/// Conflux client
5#[derive(Parser, Debug)]
6#[clap(
7    name = "conflux",
8    about = "Conflux client",
9    author = "The Conflux Team",
10    version
11)]
12pub struct Cli {
13    /// Use the preset testing configurations. dev or test.
14    #[arg(long, value_name = "MODE", value_enum)]
15    pub mode: Option<String>,
16
17    /// Specify the port for P2P connections.
18    // clap `id` must equal the hyphenated config field (`tcp_port`) that
19    // `RawConfiguration::parse` looks up; the user-facing flag stays `--port`.
20    #[arg(id = "tcp-port", long = "port", short = 'p', value_name = "PORT")]
21    pub port: Option<String>,
22
23    /// Specify the UDP port for peer discovery.
24    #[arg(id = "udp-port", long = "udp-port", value_name = "PORT")]
25    pub udp_port: Option<String>,
26
27    /// Specify the port for the WebSocket JSON-RPC API server.
28    #[arg(
29        id = "jsonrpc-ws-port",
30        long = "jsonrpc-ws-port",
31        value_name = "PORT"
32    )]
33    pub jsonrpc_ws_port: Option<String>,
34
35    /// Specify the port for the HTTP JSON-RPC API server.
36    #[arg(
37        id = "jsonrpc-http-port",
38        long = "jsonrpc-http-port",
39        value_name = "PORT"
40    )]
41    pub jsonrpc_http_port: Option<String>,
42
43    /// Specify CORS header for HTTP JSON-RPC API responses.
44    #[arg(id = "jsonrpc-cors", long = "jsonrpc-cors", value_name = "URL")]
45    pub jsonrpc_cors: Option<String>,
46
47    /// Enable HTTP/1.1 keep alive header.
48    #[arg(
49        id = "jsonrpc-http-keep-alive",
50        long = "jsonrpc-http-keep-alive",
51        value_name = "BOOL"
52    )]
53    pub jsonrpc_http_keep_alive: Option<String>,
54
55    /// Specify the filename for the log. Stdout will be used by default if
56    /// omitted.
57    #[arg(id = "log-file", long = "log-file", value_name = "FILE")]
58    pub log_file: Option<String>,
59
60    /// Can be error/warn/info/debug/trace. Default is the info level.
61    #[arg(id = "log-level", long = "log-level", value_name = "LEVEL")]
62    pub log_level: Option<String>,
63
64    /// Sets a custom log config file.
65    #[arg(id = "log-conf", long = "log-conf", value_name = "FILE")]
66    pub log_conf: Option<String>,
67
68    /// Sets a custom config file.
69    #[arg(short = 'c', long, value_name = "FILE")]
70    pub config: Option<String>,
71
72    /// Sets a custom list of bootnodes.
73    #[arg(long, value_name = "NODES")]
74    // "bootnodes" does not contain a hyphen
75    pub bootnodes: Option<String>,
76
77    /// Sets a custom directory for network configurations.
78    #[arg(id = "netconf-dir", long = "netconf-dir", value_name = "DIR")]
79    pub netconf_dir: Option<String>,
80
81    /// Sets a custom public address to be connected by others.
82    #[arg(
83        id = "public-address",
84        long = "public-address",
85        value_name = "IP ADDRESS"
86    )]
87    pub public_address: Option<String>,
88
89    /// Sets a custom secret key to generate unique node ID.
90    #[arg(id = "net-key", long = "net-key", value_name = "KEY")]
91    pub net_key: Option<String>,
92
93    /// Set the address to receive mining rewards.
94    #[arg(
95        id = "mining-author",
96        long = "mining-author",
97        value_name = "ADDRESS"
98    )]
99    pub mining_author: Option<String>,
100
101    /// Sets the ledger cache size.
102    #[arg(
103        id = "ledger-cache-size",
104        long = "ledger-cache-size",
105        value_name = "SIZE"
106    )]
107    pub ledger_cache_size: Option<String>,
108
109    /// Sets the db cache size.
110    #[arg(
111        id = "rocksdb-cache-size",
112        long = "db-cache-size",
113        value_name = "SIZE"
114    )]
115    pub db_cache_size: Option<String>,
116
117    /// Enable discovery protocol.
118    #[arg(
119        id = "enable-discovery",
120        long = "enable-discovery",
121        value_name = "BOOL"
122    )]
123    pub enable_discovery: Option<String>,
124
125    /// How often Conflux updates its peer table (default 300).
126    #[arg(
127        id = "node-table-timeout-s",
128        long = "node-table-timeout-s",
129        value_name = "SEC"
130    )]
131    pub node_table_timeout_s: Option<String>,
132
133    /// How long Conflux waits for promoting a peer to trustworthy (default 3 *
134    /// 24 * 3600).
135    #[arg(
136        id = "node-table-promotion-timeout-s",
137        long = "node-table-promotion-timeout-s",
138        value_name = "SEC"
139    )]
140    pub node_table_promotion_timeout_s: Option<String>,
141
142    /// Sets the compaction profile of RocksDB.
143    #[arg(
144        id = "rocksdb-compaction-profile",
145        long = "db-compact-profile",
146        value_name = "ENUM"
147    )]
148    pub db_compact_profile: Option<String>,
149
150    /// Sets the root path of db.
151    #[arg(id = "block-db-dir", long = "block-db-dir", value_name = "DIR")]
152    pub block_db_dir: Option<String>,
153
154    /// Sets egress queue capacity of P2P network.
155    #[arg(
156        id = "egress-queue-capacity",
157        long = "egress-queue-capacity",
158        value_name = "MB"
159    )]
160    pub egress_queue_capacity: Option<String>,
161
162    /// Sets minimum throttling queue size of egress.
163    #[arg(
164        id = "egress-min-throttle",
165        long = "egress-min-throttle",
166        value_name = "MB"
167    )]
168    pub egress_min_throttle: Option<String>,
169
170    /// Sets maximum throttling queue size of egress.
171    #[arg(
172        id = "egress-max-throttle",
173        long = "egress-max-throttle",
174        value_name = "MB"
175    )]
176    pub egress_max_throttle: Option<String>,
177
178    /// Sets the size of the epoch batches used during log filtering.
179    #[arg(
180        id = "get-logs-epoch-batch-size",
181        long = "get-logs-epoch-batch-size",
182        value_name = "SIZE"
183    )]
184    pub get_logs_epoch_batch_size: Option<String>,
185
186    /// Sets the maximum number of allowed epochs during log filtering.
187    #[arg(
188        id = "get-logs-filter-max-epoch-range",
189        long = "get-logs-filter-max-epoch-range",
190        value_name = "SIZE"
191    )]
192    pub get_logs_filter_max_epoch_range: Option<String>,
193
194    /// Sets the maximum number of log entries returned during log filtering.
195    #[arg(
196        id = "get-logs-filter-max-limit",
197        long = "get-logs-filter-max-limit",
198        value_name = "SIZE"
199    )]
200    pub get_logs_filter_max_limit: Option<String>,
201
202    /// Sets the maximum number of allowed blocks during log filtering.
203    #[arg(
204        id = "get-logs-filter-max-block-number-range",
205        long = "get-logs-filter-max-block-number-range",
206        value_name = "SIZE"
207    )]
208    pub get_logs_filter_max_block_number_range: Option<String>,
209
210    /// Sets the time after which accounts are re-read from disk.
211    #[arg(
212        id = "account-provider-refresh-time-ms",
213        long = "account-provider-refresh-time-ms",
214        value_name = "MS"
215    )]
216    pub account_provider_refresh_time_ms: Option<String>,
217
218    ///  Sets the encryption password for the pos private key file. It's used
219    /// to encrypt a new generated key or to decrypt an existing key file.
220    #[arg(
221        id = "dev-pos-private-key-encryption-password",
222        long = "dev-pos-private-key-encryption-password",
223        value_name = "PASSWD"
224    )]
225    pub dev_pos_private_key_encryption_password: Option<String>,
226
227    /// If true, the node will start PoS election and voting if it's available.
228    #[arg(
229        id = "pos-started-as-voter",
230        long = "pos-started-as-voter",
231        value_name = "BOOL"
232    )]
233    pub pos_started_as_voter: Option<String>,
234
235    #[arg(long)]
236    pub light: bool,
237    #[arg(long)]
238    pub archive: bool,
239    #[arg(long)]
240    pub full: bool,
241
242    #[command(subcommand)]
243    pub command: Option<Commands>,
244}
245
246#[derive(Subcommand, Debug)]
247pub enum Commands {
248    /// Manage accounts
249    #[command(subcommand_required = true, arg_required_else_help = true)]
250    Account(AccountSubcommands),
251    /// Dump eSpace account state at a given block number
252    #[command(subcommand_required = false, arg_required_else_help = false)]
253    Dump(DumpCommand),
254    /// RPC based subcommands to query blockchain information and send
255    /// transactions
256    #[command(subcommand_required = true, arg_required_else_help = true)]
257    Rpc(Box<RpcCommand>),
258}
259
260/// Account Subcommands
261#[derive(Args, Debug)]
262pub struct AccountSubcommands {
263    #[command(subcommand)]
264    pub command: AccountCommand,
265}
266
267#[derive(Subcommand, Debug)]
268pub enum AccountCommand {
269    /// Create a new account (and its associated key) for the given --chain
270    /// (default conflux).
271    New(AccountNewArgs),
272    /// List existing accounts of the given --chain (default conflux).
273    List,
274    /// Import accounts from JSON UTC keystore files
275    Import(AccountImportArgs),
276}
277
278#[derive(Args, Debug)]
279pub struct AccountNewArgs {
280    /// Specify the number of iterations to use when deriving key from the
281    /// password (bigger is more secure).
282    #[arg(
283        id = "keys-iterations",
284        long = "keys-iterations",
285        value_name = "NUM",
286        default_value = "10240"
287    )]
288    pub keys_iterations: Option<u32>,
289    /// Provide a file containing a password for unlocking an account.
290    #[arg(long, value_name = "FILE")]
291    pub password: Option<String>,
292}
293
294#[derive(Args, Debug)]
295pub struct AccountImportArgs {
296    /// A list of file paths to import.
297    #[arg(id = "import-path", long = "import-path", value_name = "PATH", required = true, num_args = 1..)]
298    pub import_path: Vec<String>,
299}
300
301/**
302 * --------------- RPC Subcommands ---------------
303 */
304
305// RPC Subcommands
306#[derive(Args, Debug)]
307pub struct RpcCommand {
308    /// URL of RPC server
309    #[arg(
310        long,
311        value_name = "URL",
312        default_value = "http://localhost:12539",
313        global = true
314    )]
315    pub url: String,
316    #[command(subcommand)]
317    pub command: RpcSubcommands,
318}
319
320#[derive(Subcommand, Debug)]
321pub enum RpcSubcommands {
322    /// Get recent mean gas price
323    Price(RpcPriceArgs),
324    /// Get epoch number
325    Epoch(RpcEpochArgs),
326    /// Get balance of specified account
327    Balance(RpcBalanceArgs),
328    /// Get bytecode of specified contract
329    Code(RpcCodeArgs),
330    /// Get block by hash
331    #[command(name = "block-by-hash")]
332    BlockByHash(RpcBlockByHashArgs),
333    /// Get block by hash with pivot chain assumption
334    #[command(name = "block-with-assumption")]
335    BlockWithAssumption(RpcBlockWithAssumptionArgs),
336    /// Get block by epoch
337    #[command(name = "block-by-epoch")]
338    BlockByEpoch(RpcBlockByEpochArgs),
339    /// Get the best block hash
340    #[command(name = "best-block-hash")]
341    BestBlockHash(RpcBestBlockHashArgs),
342    /// Get nonce of specified account
343    Nonce(RpcNonceArgs),
344    /// Send a signed transaction and return its hash
345    Send(RpcSendArgs),
346    /// Get transaction by hash
347    Tx(RpcTxArgs),
348    /// Get blocks of specified epoch
349    Blocks(RpcBlocksArgs),
350    /// Get skipped blocks of specified epoch
351    #[command(name = "skipped-blocks")]
352    SkippedBlocks(RpcSkippedBlocksArgs),
353    /// Get receipt by transaction hash
354    Receipt(RpcReceiptArgs),
355    /// Executes a new message call immediately without creating a transaction
356    Call(RpcCallArgs),
357    /// Executes a call request and returns the gas used
358    #[command(name = "estimate-gas")]
359    EstimateGas(RpcEstimateGasArgs),
360    /// Local subcommands (requires jsonrpc_local_http_port configured)
361    #[command(subcommand_required = true, arg_required_else_help = true)]
362    Local(RpcLocalSubcommands),
363}
364
365#[derive(Args, Debug)]
366pub struct RpcPriceArgs {
367    #[arg(
368        id = "rpc-method",
369        long = "rpc-method",
370        default_value = "cfx_gasPrice",
371        hide = true
372    )]
373    pub rpc_method: String,
374}
375
376#[derive(Args, Debug)]
377pub struct RpcEpochArgs {
378    #[arg(
379        id = "rpc-method",
380        long = "rpc-method",
381        default_value = "cfx_epochNumber",
382        hide = true
383    )]
384    pub rpc_method: String,
385    #[arg(
386        id = "rpc-args",
387        long = "rpc-args",
388        hide = true,
389        default_value = "epoch",
390        value_delimiter = ','
391    )]
392    pub rpc_args: Vec<String>,
393    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
394    /// format)
395    #[arg(long, value_name = "EPOCH")]
396    pub epoch: Option<String>,
397}
398
399#[derive(Args, Debug)]
400pub struct RpcBalanceArgs {
401    #[arg(
402        id = "rpc-method",
403        long = "rpc-method",
404        default_value = "cfx_getBalance",
405        hide = true
406    )]
407    pub rpc_method: String,
408    #[arg(
409        id = "rpc-args",
410        long = "rpc-args",
411        hide = true,
412        default_value = "address,epoch",
413        value_delimiter = ','
414    )]
415    pub rpc_args: Vec<String>,
416    /// Account address / Contract address
417    #[arg(long, required = true, value_name = "ADDRESS")]
418    pub address: String,
419    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
420    /// format)
421    #[arg(long, value_name = "EPOCH")]
422    pub epoch: Option<String>,
423}
424
425#[derive(Args, Debug)]
426pub struct RpcCodeArgs {
427    #[arg(
428        id = "rpc-method",
429        long = "rpc-method",
430        default_value = "cfx_getCode",
431        hide = true
432    )]
433    pub rpc_method: String,
434    #[arg(
435        id = "rpc-args",
436        long = "rpc-args",
437        hide = true,
438        default_value = "address,epoch",
439        value_delimiter = ','
440    )]
441    pub rpc_args: Vec<String>,
442    #[arg(long = "address", required = true, value_name = "ADDRESS")]
443    pub address: String,
444    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
445    /// format)
446    #[arg(long, required = true, value_name = "EPOCH")]
447    pub epoch: String,
448}
449
450#[derive(Args, Debug)]
451pub struct RpcBlockByHashArgs {
452    #[arg(
453        id = "rpc-method",
454        long = "rpc-method",
455        default_value = "cfx_getBlockByHash",
456        hide = true
457    )]
458    pub rpc_method: String,
459    #[arg(
460        id = "rpc-args",
461        long = "rpc-args",
462        hide = true,
463        default_value = "hash,include-txs:bool",
464        value_delimiter = ','
465    )]
466    pub rpc_args: Vec<String>,
467    /// Block hash / Transaction hash
468    #[arg(long, required = true, value_name = "HASH")]
469    pub hash: String,
470    /// Whether to return detailed transactions in block
471    #[arg(id = "include-txs", long = "include-txs")]
472    pub include_txs: bool,
473}
474
475#[derive(Args, Debug)]
476pub struct RpcBlockWithAssumptionArgs {
477    #[arg(
478        id = "rpc-method",
479        long = "rpc-method",
480        default_value = "cfx_getBlockByHashWithPivotAssumption",
481        hide = true
482    )]
483    pub rpc_method: String,
484    #[arg(
485        id = "rpc-args",
486        long = "rpc-args",
487        hide = true,
488        default_value = "block-hash,pivot-hash,epoch-number:u64",
489        value_delimiter = ','
490    )]
491    pub rpc_args: Vec<String>,
492    /// Block hash
493    #[arg(
494        id = "block-hash",
495        long = "block-hash",
496        required = true,
497        value_name = "HASH"
498    )]
499    pub block_hash: String,
500    /// Pivot block hash
501    #[arg(
502        id = "pivot-hash",
503        long = "pivot-hash",
504        required = true,
505        value_name = "HASH"
506    )]
507    pub pivot_hash: String,
508    /// Epoch number
509    #[arg(
510        id = "epoch-number",
511        long = "epoch-number",
512        required = true,
513        value_name = "NUMBER"
514    )]
515    pub epoch_number: u64,
516}
517
518#[derive(Args, Debug)]
519pub struct RpcBlockByEpochArgs {
520    #[arg(
521        id = "rpc-method",
522        long = "rpc-method",
523        default_value = "cfx_getBlockByEpochNumber",
524        hide = true
525    )]
526    pub rpc_method: String,
527    #[arg(
528        id = "rpc-args",
529        long = "rpc-args",
530        hide = true,
531        default_value = "epoch,include-txs:bool",
532        value_delimiter = ','
533    )]
534    pub rpc_args: Vec<String>,
535    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
536    /// format)
537    #[arg(long, required = true, value_name = "EPOCH")]
538    pub epoch: String,
539    /// Whether to return detailed transactions in block
540    #[arg(id = "include-txs", long = "include-txs")]
541    pub include_txs: bool,
542}
543
544#[derive(Args, Debug)]
545pub struct RpcBestBlockHashArgs {
546    #[arg(
547        id = "rpc-method",
548        long = "rpc-method",
549        default_value = "cfx_getBestBlockHash",
550        hide = true
551    )]
552    pub rpc_method: String,
553}
554
555#[derive(Args, Debug)]
556pub struct RpcNonceArgs {
557    #[arg(
558        id = "rpc-method",
559        long = "rpc-method",
560        default_value = "cfx_getNextNonce",
561        hide = true
562    )]
563    pub rpc_method: String,
564    #[arg(
565        id = "rpc-args",
566        long = "rpc-args",
567        hide = true,
568        default_value = "address,epoch",
569        value_delimiter = ','
570    )]
571    pub rpc_args: Vec<String>,
572    /// Account address / Contract address
573    #[arg(long, required = true, value_name = "ADDRESS")]
574    pub address: String,
575    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
576    /// format)
577    #[arg(long, value_name = "EPOCH")]
578    pub epoch: Option<String>,
579}
580
581#[derive(Args, Debug)]
582pub struct RpcSendArgs {
583    #[arg(
584        id = "rpc-method",
585        long = "rpc-method",
586        default_value = "cfx_sendRawTransaction",
587        hide = true
588    )]
589    pub rpc_method: String,
590    #[arg(
591        id = "rpc-args",
592        long = "rpc-args",
593        hide = true,
594        default_value = "raw-bytes",
595        value_delimiter = ','
596    )]
597    pub rpc_args: Vec<String>,
598    /// Signed transaction data
599    #[arg(
600        id = "raw-bytes",
601        long = "raw-bytes",
602        required = true,
603        value_name = "HEX"
604    )]
605    pub raw_bytes: String,
606}
607
608#[derive(Args, Debug)]
609pub struct RpcTxArgs {
610    #[arg(
611        id = "rpc-method",
612        long = "rpc-method",
613        default_value = "cfx_getTransactionByHash",
614        hide = true
615    )]
616    pub rpc_method: String,
617    #[arg(
618        id = "rpc-args",
619        long = "rpc-args",
620        hide = true,
621        default_value = "hash",
622        value_delimiter = ','
623    )]
624    pub rpc_args: Vec<String>,
625    /// Block hash / Transaction hash
626    #[arg(long, required = true, value_name = "HASH")]
627    pub hash: String,
628}
629
630#[derive(Args, Debug)]
631pub struct RpcBlocksArgs {
632    #[arg(
633        id = "rpc-method",
634        long = "rpc-method",
635        default_value = "cfx_getBlocksByEpoch",
636        hide = true
637    )]
638    pub rpc_method: String,
639    #[arg(
640        id = "rpc-args",
641        long = "rpc-args",
642        hide = true,
643        default_value = "epoch",
644        value_delimiter = ','
645    )]
646    pub rpc_args: Vec<String>,
647    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
648    /// format)
649    #[arg(long, required = true, value_name = "EPOCH")]
650    pub epoch: String,
651}
652
653#[derive(Args, Debug)]
654pub struct RpcSkippedBlocksArgs {
655    #[arg(
656        id = "rpc-method",
657        long = "rpc-method",
658        default_value = "cfx_getSkippedBlocksByEpoch",
659        hide = true
660    )]
661    pub rpc_method: String,
662    #[arg(
663        id = "rpc-args",
664        long = "rpc-args",
665        hide = true,
666        default_value = "epoch",
667        value_delimiter = ','
668    )]
669    pub rpc_args: Vec<String>,
670    /// Epoch (latest_mined, latest_state, earliest or epoch number in HEX
671    /// format)
672    #[arg(long, required = true, value_name = "EPOCH")]
673    pub epoch: String,
674}
675
676#[derive(Args, Debug)]
677pub struct RpcReceiptArgs {
678    #[arg(
679        id = "rpc-method",
680        long = "rpc-method",
681        default_value = "cfx_getTransactionReceipt",
682        hide = true
683    )]
684    pub rpc_method: String,
685    #[arg(
686        id = "rpc-args",
687        long = "rpc-args",
688        hide = true,
689        default_value = "hash",
690        value_delimiter = ','
691    )]
692    pub rpc_args: Vec<String>,
693    /// Block hash / Transaction hash
694    #[arg(long, required = true, value_name = "HASH")]
695    pub hash: String,
696}
697
698#[derive(Args, Debug)]
699pub struct RpcCallArgs {
700    #[arg(
701        id = "rpc-method",
702        long = "rpc-method",
703        default_value = "cfx_call",
704        hide = true
705    )]
706    pub rpc_method: String,
707    #[arg(
708        id = "rpc-args",
709        long = "rpc-args",
710        hide = true,
711        default_value = "tx:map(from;to;gas-price;type;max-fee-per-gas;max-priority-fee-per-gas;gas;value;data;nonce),epoch",
712        value_delimiter = ','
713    )]
714    pub rpc_args: Vec<String>,
715    /// Transaction from address
716    #[arg(long, value_name = "ADDRESS")]
717    pub from: Option<String>,
718    /// Transaction to address
719    #[arg(long, value_name = "ADDRESS")]
720    pub to: Option<String>,
721    /// Transaction gas price
722    #[arg(id = "gas-price", long = "gas-price", value_name = "HEX")]
723    pub gas_price: Option<String>,
724    /// Transaction type
725    #[arg(id = "type", long = "type", value_name = "HEX")]
726    // "type" does not contain a hyphen
727    pub tx_type: Option<String>,
728    /// Transaction max fee per gas
729    #[arg(
730        id = "max-fee-per-gas",
731        long = "max-fee-per-gas",
732        value_name = "HEX"
733    )]
734    pub max_fee_per_gas: Option<String>,
735    /// Transaction max priority fee per gas
736    #[arg(
737        id = "max-priority-fee-per-gas",
738        long = "max-priority-fee-per-gas",
739        value_name = "HEX"
740    )]
741    pub max_priority_fee_per_gas: Option<String>,
742    /// Gas provided for transaction execution
743    #[arg(long, value_name = "HEX")]
744    pub gas: Option<String>,
745    /// value sent with this transaction
746    #[arg(long, value_name = "HEX")]
747    pub value: Option<String>,
748    /// Hash of the method signature and encoded parameters
749    #[arg(long, value_name = "HEX")]
750    pub data: Option<String>,
751    /// Transaction nonce
752    #[arg(long, value_name = "HEX")]
753    pub nonce: Option<String>,
754    /// Epoch
755    #[arg(long, value_name = "EPOCH")]
756    pub epoch: Option<String>,
757}
758
759#[derive(Args, Debug)]
760pub struct RpcEstimateGasArgs {
761    #[arg(
762        id = "rpc-method",
763        long = "rpc-method",
764        default_value = "cfx_estimateGas",
765        hide = true
766    )]
767    pub rpc_method: String,
768    #[arg(
769        id = "rpc-args",
770        long = "rpc-args",
771        hide = true,
772        default_value = "tx:map(from;to;gas-price;type;max-fee-per-gas;max-priority-fee-per-gas;gas;value;data;nonce),epoch",
773        value_delimiter = ','
774    )]
775    pub rpc_args: Vec<String>,
776    /// Transaction from address
777    #[arg(long, value_name = "ADDRESS")]
778    pub from: Option<String>,
779    /// Transaction to address
780    #[arg(long, value_name = "ADDRESS")]
781    pub to: Option<String>,
782    /// Transaction gas price
783    #[arg(id = "gas-price", long = "gas-price", value_name = "HEX")]
784    pub gas_price: Option<String>,
785    /// Transaction type
786    #[arg(id = "type", long = "type", value_name = "HEX")]
787    // "type" does not contain a hyphen
788    pub tx_type: Option<String>,
789    /// Transaction max fee per gas
790    #[arg(
791        id = "max-fee-per-gas",
792        long = "max-fee-per-gas",
793        value_name = "HEX"
794    )]
795    pub max_fee_per_gas: Option<String>,
796    /// Transaction max priority fee per gas
797    #[arg(
798        id = "max-priority-fee-per-gas",
799        long = "max-priority-fee-per-gas",
800        value_name = "HEX"
801    )]
802    pub max_priority_fee_per_gas: Option<String>,
803    /// Gas provided for transaction execution
804    #[arg(long, value_name = "HEX")]
805    pub gas: Option<String>,
806    /// value sent with this transaction
807    #[arg(long, value_name = "HEX")]
808    pub value: Option<String>,
809    /// Hash of the method signature and encoded parameters
810    #[arg(long, value_name = "HEX")]
811    pub data: Option<String>,
812    /// Transaction nonce
813    #[arg(long, value_name = "HEX")]
814    pub nonce: Option<String>,
815    /// Epoch
816    #[arg(long, value_name = "EPOCH")]
817    pub epoch: Option<String>,
818}
819
820/**
821 * --------------- RPC Local Subcommands ---------------
822 */
823
824// RPC Local Subcommands
825#[derive(Args, Debug)]
826pub struct RpcLocalSubcommands {
827    #[command(subcommand)]
828    pub command: RpcLocalCommand,
829}
830
831#[derive(Subcommand, Debug)]
832pub enum RpcLocalCommand {
833    /// Send a transaction and return its hash
834    Send(Box<RpcLocalSendArgs>),
835    /// Account related subcommands
836    #[command(subcommand_required = true, arg_required_else_help = true)]
837    Account(RpcLocalAccountSubcommands),
838    /// Transaction pool subcommands
839    #[command(subcommand_required = true, arg_required_else_help = true)]
840    Txpool(RpcLocalTxpoolSubcommands),
841    /// Network subcommands
842    #[command(subcommand_required = true, arg_required_else_help = true)]
843    Net(RpcLocalNetSubcommands),
844    /// Get the current synchronization phase
845    #[command(name = "sync-phase")]
846    SyncPhase(RpcLocalSyncPhaseArgs),
847    /// Get the consensus graph state
848    #[command(name = "consensus-graph-state")]
849    ConsensusGraphState(RpcLocalConsensusGraphStateArgs),
850    /// Test subcommands (used for test purpose only)
851    #[command(subcommand_required = true, arg_required_else_help = true)]
852    Test(RpcLocalTestSubcommands),
853    /// PoS subcommands
854    #[command(subcommand_required = true, arg_required_else_help = true)]
855    Pos(RpcLocalPosSubcommands),
856}
857
858#[derive(Args, Debug)]
859pub struct RpcLocalSendArgs {
860    #[arg(
861        id = "rpc-method",
862        long = "rpc-method",
863        default_value = "cfx_sendTransaction",
864        hide = true
865    )]
866    pub rpc_method: String,
867    #[arg(
868        id = "rpc-args",
869        long = "rpc-args",
870        hide = true,
871        default_value = "tx:map(from;to;gas-price;type;max-fee-per-gas;max-priority-fee-per-gas;gas;value;data;nonce;storageLimit),password:password",
872        value_delimiter = ','
873    )]
874    pub rpc_args: Vec<String>,
875    /// Transaction from address
876    #[arg(long, required = true, value_name = "ADDRESS")]
877    pub from: String,
878    /// Transaction to address (empty to create contract)
879    #[arg(long, value_name = "ADDRESS")]
880    pub to: Option<String>,
881    /// Transaction gas price
882    #[arg(
883        id = "gas-price",
884        long = "gas-price",
885        value_name = "HEX",
886        default_value = "0x2540BE400"
887    )]
888    pub gas_price: Option<String>,
889    /// Transaction type
890    #[arg(id = "type", long = "type", value_name = "HEX")]
891    // "type" does not contain a hyphen
892    pub tx_type: Option<String>,
893    /// Transaction max fee per gas
894    #[arg(
895        id = "max-fee-per-gas",
896        long = "max-fee-per-gas",
897        value_name = "HEX"
898    )]
899    pub max_fee_per_gas: Option<String>,
900    /// Transaction max priority fee per gas
901    #[arg(
902        id = "max-priority-fee-per-gas",
903        long = "max-priority-fee-per-gas",
904        value_name = "HEX"
905    )]
906    pub max_priority_fee_per_gas: Option<String>,
907    /// Gas provided for transaction execution
908    #[arg(long, value_name = "HEX", default_value = "0x5208")]
909    pub gas: Option<String>,
910    /// value sent with this transaction
911    #[arg(long, required = true, value_name = "HEX")]
912    pub value: String,
913    /// Hash of the method signature and encoded parameters
914    #[arg(long, value_name = "HEX")]
915    pub data: Option<String>,
916    /// Transaction nonce
917    #[arg(long, value_name = "HEX")]
918    pub nonce: Option<String>,
919    /// Storage limit for the transaction
920    #[arg(
921        id = "storage-limit",
922        long = "storage-limit",
923        value_name = "HEX",
924        default_value = "0x0"
925    )]
926    pub storage_limit: Option<String>,
927}
928
929#[derive(Args, Debug)]
930pub struct RpcLocalAccountSubcommands {
931    #[command(subcommand)]
932    pub command: RpcLocalAccountCommand,
933}
934
935#[derive(Subcommand, Debug)]
936pub enum RpcLocalAccountCommand {
937    /// List all accounts
938    List(RpcLocalAccountListArgs),
939    /// Create a new account
940    New(RpcLocalAccountNewArgs),
941    /// Unlock an account
942    Unlock(RpcLocalAccountUnlockArgs),
943    /// Lock an unlocked account
944    Lock(RpcLocalAccountLockArgs),
945}
946
947#[derive(Args, Debug)]
948pub struct RpcLocalAccountListArgs {
949    #[arg(
950        id = "rpc-method",
951        long = "rpc-method",
952        default_value = "cfx_accounts",
953        hide = true
954    )]
955    pub rpc_method: String,
956}
957
958#[derive(Args, Debug)]
959pub struct RpcLocalAccountNewArgs {
960    #[arg(
961        id = "rpc-method",
962        long = "rpc-method",
963        default_value = "cfx_newAccount",
964        hide = true
965    )]
966    pub rpc_method: String,
967    #[arg(
968        id = "rpc-args",
969        long = "rpc-args",
970        hide = true,
971        default_value = "password:password2",
972        value_delimiter = ','
973    )]
974    pub rpc_args: Vec<String>,
975}
976
977#[derive(Args, Debug)]
978pub struct RpcLocalAccountUnlockArgs {
979    #[arg(
980        id = "rpc-method",
981        long = "rpc-method",
982        default_value = "cfx_unlockAccount",
983        hide = true
984    )]
985    pub rpc_method: String,
986    #[arg(
987        id = "rpc-args",
988        long = "rpc-args",
989        hide = true,
990        default_value = "address,password:password,duration",
991        value_delimiter = ','
992    )]
993    pub rpc_args: Vec<String>,
994    /// Address of the account
995    #[arg(long, required = true, value_name = "ADDRESS")]
996    pub address: String,
997    /// Duration to unlock the account, use 0x0 to unlock permanently (strongly
998    /// not recommended!).
999    #[arg(long, value_name = "DURATION", default_value = "0x3c")]
1000    pub duration: Option<String>,
1001}
1002
1003#[derive(Args, Debug)]
1004pub struct RpcLocalAccountLockArgs {
1005    #[arg(
1006        id = "rpc-method",
1007        long = "rpc-method",
1008        default_value = "cfx_lockAccount",
1009        hide = true
1010    )]
1011    pub rpc_method: String,
1012    #[arg(
1013        id = "rpc-args",
1014        long = "rpc-args",
1015        hide = true,
1016        default_value = "address",
1017        value_delimiter = ','
1018    )]
1019    pub rpc_args: Vec<String>,
1020    /// Address of the account
1021    #[arg(long, required = true, value_name = "ADDRESS")]
1022    pub address: String,
1023}
1024
1025#[derive(Args, Debug)]
1026pub struct RpcLocalTxpoolSubcommands {
1027    #[command(subcommand)]
1028    pub command: RpcLocalTxpoolCommand,
1029}
1030
1031#[derive(Subcommand, Debug)]
1032pub enum RpcLocalTxpoolCommand {
1033    /// Get the number of transactions for different status
1034    Status(RpcLocalTxpoolStatusArgs),
1035    /// Get the detailed status of specified transaction
1036    #[command(name = "inspect-one")]
1037    InspectOne(RpcLocalTxpoolInspectOneArgs),
1038    /// List textual summary of all transactions
1039    Inspect(RpcLocalTxpoolInspectArgs),
1040    /// List exact details of all transactions
1041    Content(RpcLocalTxpoolContentArgs),
1042    /// Remove all transactions
1043    Clear(RpcLocalTxpoolClearArgs),
1044}
1045
1046#[derive(Args, Debug)]
1047pub struct RpcLocalTxpoolStatusArgs {
1048    #[arg(
1049        id = "rpc-method",
1050        long = "rpc-method",
1051        default_value = "txpool_status",
1052        hide = true
1053    )]
1054    pub rpc_method: String,
1055}
1056
1057#[derive(Args, Debug)]
1058pub struct RpcLocalTxpoolInspectOneArgs {
1059    #[arg(
1060        id = "rpc-method",
1061        long = "rpc-method",
1062        default_value = "txpool_txWithPoolInfo",
1063        hide = true
1064    )]
1065    pub rpc_method: String,
1066    #[arg(
1067        id = "rpc-args",
1068        long = "rpc-args",
1069        hide = true,
1070        default_value = "hash",
1071        value_delimiter = ','
1072    )]
1073    pub rpc_args: Vec<String>,
1074    /// Block hash / Transaction hash
1075    #[arg(long, required = true, value_name = "HASH")]
1076    pub hash: String,
1077}
1078
1079#[derive(Args, Debug)]
1080pub struct RpcLocalTxpoolInspectArgs {
1081    #[arg(
1082        id = "rpc-method",
1083        long = "rpc-method",
1084        default_value = "debug_inspectTxPool",
1085        hide = true
1086    )]
1087    pub rpc_method: String,
1088    #[arg(
1089        id = "rpc-args",
1090        long = "rpc-args",
1091        hide = true,
1092        default_value = "address",
1093        value_delimiter = ','
1094    )]
1095    pub rpc_args: Vec<String>,
1096    /// Account address
1097    #[arg(long, value_name = "ADDRESS")]
1098    pub address: Option<String>,
1099}
1100
1101#[derive(Args, Debug)]
1102pub struct RpcLocalTxpoolContentArgs {
1103    #[arg(
1104        id = "rpc-method",
1105        long = "rpc-method",
1106        default_value = "debug_txPoolContent",
1107        hide = true
1108    )]
1109    pub rpc_method: String,
1110    #[arg(
1111        id = "rpc-args",
1112        long = "rpc-args",
1113        hide = true,
1114        default_value = "address",
1115        value_delimiter = ','
1116    )]
1117    pub rpc_args: Vec<String>,
1118    /// Account address
1119    #[arg(long, value_name = "ADDRESS")]
1120    pub address: Option<String>,
1121}
1122
1123#[derive(Args, Debug)]
1124pub struct RpcLocalTxpoolClearArgs {
1125    #[arg(
1126        id = "rpc-method",
1127        long = "rpc-method",
1128        default_value = "debug_clearTxPool",
1129        hide = true
1130    )]
1131    pub rpc_method: String,
1132}
1133
1134#[derive(Args, Debug)]
1135pub struct RpcLocalNetSubcommands {
1136    #[command(subcommand)]
1137    pub command: RpcLocalNetCommand,
1138}
1139
1140#[derive(Subcommand, Debug)]
1141pub enum RpcLocalNetCommand {
1142    /// Get the current throttling information
1143    Throttling(RpcLocalNetThrottlingArgs),
1144    /// Get node information by ID
1145    Node(RpcLocalNetNodeArgs),
1146    /// Disconnect a node
1147    Disconnect(RpcLocalNetDisconnectArgs),
1148    /// Get active session(s)
1149    Session(RpcLocalNetSessionArgs),
1150}
1151
1152#[derive(Args, Debug)]
1153pub struct RpcLocalNetThrottlingArgs {
1154    #[arg(
1155        id = "rpc-method",
1156        long = "rpc-method",
1157        default_value = "debug_getNetThrottling",
1158        hide = true
1159    )]
1160    pub rpc_method: String,
1161}
1162
1163#[derive(Args, Debug)]
1164pub struct RpcLocalNetNodeArgs {
1165    #[arg(
1166        id = "rpc-method",
1167        long = "rpc-method",
1168        default_value = "debug_getNetNode",
1169        hide = true
1170    )]
1171    pub rpc_method: String,
1172    #[arg(
1173        id = "rpc-args",
1174        long = "rpc-args",
1175        hide = true,
1176        default_value = "id",
1177        value_delimiter = ','
1178    )]
1179    pub rpc_args: Vec<String>,
1180    /// Node ID
1181    #[arg(long, required = true, value_name = "ID")]
1182    pub id: String,
1183}
1184
1185#[derive(ValueEnum, Clone, Debug)]
1186pub enum NodeDisconnectOperation {
1187    Failure,
1188    Demotion,
1189    Remove,
1190}
1191
1192#[derive(Args, Debug)]
1193pub struct RpcLocalNetDisconnectArgs {
1194    #[arg(
1195        id = "rpc-method",
1196        long = "rpc-method",
1197        default_value = "debug_disconnectNetNode",
1198        hide = true
1199    )]
1200    pub rpc_method: String,
1201    #[arg(
1202        id = "rpc-args",
1203        long = "rpc-args",
1204        hide = true,
1205        default_value = "id,operation",
1206        value_delimiter = ','
1207    )]
1208    pub rpc_args: Vec<String>,
1209    /// Node ID
1210    #[arg(long, required = true, value_name = "ID")]
1211    pub id: String,
1212    /// Operation to update node database
1213    #[arg(long, value_name = "OPERATION", value_enum)]
1214    pub operation: Option<NodeDisconnectOperation>,
1215}
1216
1217#[derive(Args, Debug)]
1218pub struct RpcLocalNetSessionArgs {
1219    #[arg(
1220        id = "rpc-method",
1221        long = "rpc-method",
1222        default_value = "debug_getNetSessions",
1223        hide = true
1224    )]
1225    pub rpc_method: String,
1226    #[arg(
1227        id = "rpc-args",
1228        long = "rpc-args",
1229        hide = true,
1230        default_value = "id",
1231        value_delimiter = ','
1232    )]
1233    pub rpc_args: Vec<String>,
1234    /// Node ID
1235    #[arg(long, value_name = "ID")]
1236    pub id: Option<String>,
1237}
1238
1239#[derive(Args, Debug)]
1240pub struct RpcLocalSyncPhaseArgs {
1241    #[arg(
1242        id = "rpc-method",
1243        long = "rpc-method",
1244        default_value = "debug_currentSyncPhase",
1245        hide = true
1246    )]
1247    pub rpc_method: String,
1248}
1249
1250#[derive(Args, Debug)]
1251pub struct RpcLocalConsensusGraphStateArgs {
1252    #[arg(
1253        id = "rpc-method",
1254        long = "rpc-method",
1255        default_value = "debug_consensusGraphState",
1256        hide = true
1257    )]
1258    pub rpc_method: String,
1259}
1260
1261#[derive(Args, Debug)]
1262pub struct RpcLocalTestSubcommands {
1263    #[command(subcommand)]
1264    pub command: RpcLocalTestCommand,
1265}
1266
1267#[derive(Subcommand, Debug)]
1268pub enum RpcLocalTestCommand {
1269    /// Get the total block count
1270    #[command(name = "block-count")]
1271    BlockCount(RpcLocalTestBlockCountArgs),
1272    /// Get the recent transaction good TPS
1273    Goodput(RpcLocalTestGoodputArgs),
1274    /// List "ALL" blocks in topological order
1275    Chain(RpcLocalTestChainArgs),
1276    /// Stop the conflux program
1277    Stop(RpcLocalTestStopArgs),
1278    /// Get the current status of Conflux
1279    Status(RpcLocalTestStatusArgs),
1280}
1281
1282#[derive(Args, Debug)]
1283pub struct RpcLocalTestBlockCountArgs {
1284    #[arg(
1285        id = "rpc-method",
1286        long = "rpc-method",
1287        default_value = "test_getBlockCount",
1288        hide = true
1289    )]
1290    pub rpc_method: String,
1291}
1292#[derive(Args, Debug)]
1293pub struct RpcLocalTestGoodputArgs {
1294    #[arg(
1295        id = "rpc-method",
1296        long = "rpc-method",
1297        default_value = "test_getGoodPut",
1298        hide = true
1299    )]
1300    pub rpc_method: String,
1301}
1302#[derive(Args, Debug)]
1303pub struct RpcLocalTestChainArgs {
1304    #[arg(
1305        id = "rpc-method",
1306        long = "rpc-method",
1307        default_value = "test_getChain",
1308        hide = true
1309    )]
1310    pub rpc_method: String,
1311}
1312#[derive(Args, Debug)]
1313pub struct RpcLocalTestStopArgs {
1314    #[arg(
1315        id = "rpc-method",
1316        long = "rpc-method",
1317        default_value = "test_stop",
1318        hide = true
1319    )]
1320    pub rpc_method: String,
1321}
1322#[derive(Args, Debug)]
1323pub struct RpcLocalTestStatusArgs {
1324    #[arg(
1325        id = "rpc-method",
1326        long = "rpc-method",
1327        default_value = "cfx_getStatus",
1328        hide = true
1329    )]
1330    pub rpc_method: String,
1331}
1332#[derive(Args, Debug)]
1333pub struct RpcLocalPosSubcommands {
1334    #[command(subcommand)]
1335    pub command: RpcLocalPosCommand,
1336}
1337
1338#[derive(Subcommand, Debug)]
1339pub enum RpcLocalPosCommand {
1340    /// Return the transaction data needed to register the PoS keys
1341    Register(RpcLocalPosRegisterArgs),
1342    /// Stop sending PoS election transactions.
1343    #[command(name = "stop_election")]
1344    StopElection(RpcLocalPosStopElectionArgs),
1345    /// Start PoS voting.
1346    #[command(name = "start_voting")]
1347    StartVoting(RpcLocalPosStartVotingArgs),
1348    /// Stop PoS voting.
1349    #[command(name = "stop_voting")]
1350    StopVoting(RpcLocalPosStopVotingArgs),
1351    /// Show if the node is voting.
1352    #[command(name = "voting_status")]
1353    VotingStatus(RpcLocalPosVotingStatusArgs),
1354}
1355
1356#[derive(Args, Debug)]
1357pub struct RpcLocalPosRegisterArgs {
1358    #[arg(
1359        id = "rpc-method",
1360        long = "rpc-method",
1361        default_value = "test_posRegister",
1362        hide = true
1363    )]
1364    pub rpc_method: String,
1365    #[arg(
1366        id = "rpc-args",
1367        long = "rpc-args",
1368        hide = true,
1369        default_value = "power:u64",
1370        value_delimiter = ','
1371    )]
1372    pub rpc_args: Vec<String>,
1373    /// The voting power to register (one voting power is 100 staked CFX)
1374    #[arg(long, required = true, value_name = "POWER")]
1375    pub power: u64,
1376}
1377
1378#[derive(Args, Debug)]
1379pub struct RpcLocalPosStopElectionArgs {
1380    #[arg(
1381        id = "rpc-method",
1382        long = "rpc-method",
1383        default_value = "test_posStopElection",
1384        hide = true
1385    )]
1386    pub rpc_method: String,
1387}
1388
1389#[derive(Args, Debug)]
1390pub struct RpcLocalPosStartVotingArgs {
1391    #[arg(
1392        id = "rpc-method",
1393        long = "rpc-method",
1394        default_value = "test_posStartVoting",
1395        hide = true
1396    )]
1397    pub rpc_method: String,
1398    #[arg(
1399        id = "rpc-args",
1400        long = "rpc-args",
1401        hide = true,
1402        default_value = "initialize:bool",
1403        value_delimiter = ','
1404    )]
1405    pub rpc_args: Vec<String>,
1406    /// set this means the node uses its local safety data instead of a saved
1407    /// data file from another primary node
1408    #[arg(long)]
1409    pub initialize: bool,
1410}
1411
1412#[derive(Args, Debug)]
1413pub struct RpcLocalPosStopVotingArgs {
1414    #[arg(
1415        id = "rpc-method",
1416        long = "rpc-method",
1417        default_value = "test_posStopVoting",
1418        hide = true
1419    )]
1420    pub rpc_method: String,
1421}
1422#[derive(Args, Debug)]
1423pub struct RpcLocalPosVotingStatusArgs {
1424    #[arg(
1425        id = "rpc-method",
1426        long = "rpc-method",
1427        default_value = "test_posVotingStatus",
1428        hide = true
1429    )]
1430    pub rpc_method: String,
1431}
1432
1433#[cfg(test)]
1434mod tests {
1435    use super::Cli;
1436    use clap::CommandFactory;
1437    use client::configuration::RawConfiguration;
1438    use std::collections::HashSet;
1439
1440    // clap ids consumed directly in `Configuration::parse` /
1441    // `RawConfiguration::parse` rather than through a config-field lookup.
1442    const EXPLICIT_HANDLERS: &[&str] = &["config", "archive", "full", "light"];
1443
1444    #[test]
1445    fn port_flag_overrides_tcp_port() {
1446        let matches = Cli::command()
1447            .try_get_matches_from(["conflux", "--port", "42424"])
1448            .expect("--port should parse");
1449        let raw = RawConfiguration::parse(&matches).expect("config parses");
1450        assert_eq!(raw.tcp_port, 42424);
1451    }
1452
1453    #[test]
1454    fn db_flags_override_rocksdb_config() {
1455        let matches = Cli::command()
1456            .try_get_matches_from([
1457                "conflux",
1458                "--db-cache-size",
1459                "256",
1460                "--db-compact-profile",
1461                "hdd",
1462            ])
1463            .expect("db flags should parse");
1464        let raw = RawConfiguration::parse(&matches).expect("config parses");
1465        assert_eq!(raw.rocksdb_cache_size, Some(256));
1466        assert_eq!(raw.rocksdb_compaction_profile, Some("hdd".to_string()));
1467    }
1468
1469    // Guards against the id/field mismatch class: a declared option whose clap
1470    // `id` matches no config field (and no explicit handler) is silently
1471    // dropped at parse time, so the flag looks documented but does nothing.
1472    #[test]
1473    fn every_top_level_option_is_wired() {
1474        let keys: HashSet<String> =
1475            RawConfiguration::cli_lookup_keys().into_iter().collect();
1476        for arg in Cli::command().get_arguments() {
1477            let id = arg.get_id().as_str();
1478            if id == "help" || id == "version" {
1479                continue;
1480            }
1481            assert!(
1482                keys.contains(id) || EXPLICIT_HANDLERS.contains(&id),
1483                "CLI option `{}` resolves to no config field or handler",
1484                id
1485            );
1486        }
1487    }
1488}