cfx_config/
configuration.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use std::{collections::BTreeMap, convert::TryInto, path::PathBuf, sync::Arc};
6
7use cfx_rpc_builder::RpcModuleSelection;
8use lazy_static::*;
9use log::{error, warn};
10use parking_lot::RwLock;
11use rand::Rng;
12
13use cfx_addr::{cfx_addr_decode, Network};
14use cfx_executor::{machine::Machine, spec::CommonParams};
15use cfx_internal_common::{
16    ChainIdParams, ChainIdParamsInner, ChainIdParamsOneChainInner,
17};
18use cfx_parameters::{
19    block::DEFAULT_TARGET_BLOCK_GAS_LIMIT, tx_pool::TXPOOL_DEFAULT_NONCE_BITS,
20};
21use cfx_rpc_cfx_types::{
22    address::USE_SIMPLE_RPC_ADDRESS, apis::ApiSet, RpcImplConfiguration,
23};
24use cfx_storage::{
25    defaults::DEFAULT_DEBUG_SNAPSHOT_CHECKER_THREADS, storage_dir,
26    ConsensusParam, ProvideExtraSnapshotSyncConfig, StorageConfiguration,
27};
28use cfx_types::{
29    parse_hex_string, Address, AllChainID, Space, SpaceMap, H256, U256,
30};
31use cfxcore::{
32    block_data_manager::{DataManagerConfiguration, DbType},
33    block_parameters::*,
34    cache_config::{
35        DEFAULT_INVALID_BLOCK_HASH_CACHE_SIZE_IN_COUNT,
36        DEFAULT_LEDGER_CACHE_SIZE,
37        DEFAULT_TARGET_DIFFICULTIES_CACHE_SIZE_IN_COUNT,
38    },
39    consensus::{
40        consensus_inner::consensus_executor::ConsensusExecutionConfiguration,
41        pivot_hint::PivotHintConfig, ConsensusConfig, ConsensusInnerConfig,
42    },
43    consensus_internal_parameters::*,
44    consensus_parameters::*,
45    light_protocol::LightNodeConfiguration,
46    sync::{ProtocolConfiguration, StateSyncConfiguration, SyncGraphConfig},
47    sync_parameters::*,
48    transaction_pool::TxPoolConfig,
49    NodeType,
50};
51use diem_types::term_state::{
52    pos_state_config::PosStateConfig, IN_QUEUE_LOCKED_VIEWS,
53    OUT_QUEUE_LOCKED_VIEWS, ROUND_PER_TERM, TERM_ELECTED_SIZE, TERM_MAX_SIZE,
54};
55use jsonrpsee::server::ServerConfigBuilder;
56use metrics::MetricsConfiguration;
57use network::DiscoveryConfiguration;
58use primitives::block_header::CIP112_TRANSITION_HEIGHT;
59use txgen::TransactionGeneratorConfig;
60
61use crate::{HttpConfiguration, WsConfiguration};
62
63lazy_static! {
64    pub static ref CHAIN_ID: RwLock<Option<ChainIdParams>> = Default::default();
65}
66const BLOCK_DB_DIR_NAME: &str = "blockchain_db";
67const NET_CONFIG_DB_DIR_NAME: &str = "net_config";
68
69// usage:
70// ```
71// build_config! {
72//     {
73//         (name, (type), default_value)
74//         ...
75//     }
76//     {
77//         (name, (type), default_value, converter)
78//     }
79// }
80// ```
81// `converter` is a function used to convert a provided String to `Result<type,
82// String>`. For each entry, field `name` of type `type` will be created in
83// `RawConfiguration`, and it will be assigned to the value passed through
84// commandline argument or configuration file. Commandline argument will
85// override the configuration file if the parameter is given in both.
86build_config! {
87    {
88        // Configs are grouped by section. Within one section configs should
89        // be kept in alphabetical order for the sake of indexing and maintenance.
90        //
91        // Some preset configurations.
92        //
93        // For both `test` and `dev` modes, we will
94        //     * Set initial difficulty to 4
95        //     * Allow calling test and debug rpc from public port
96        //
97        // `test` mode is for Conflux testing and debugging, we will
98        //     * Add latency to peer connections
99        //     * Skip handshake encryption check
100        //     * Skip header timestamp verification
101        //     * Handle NewBlockHash even in catch-up mode
102        //     * Allow data propagation test
103        //     * Allow setting genesis accounts and generate tx from secrets
104        //
105        // `dev` mode is for users to run a single node that automatically
106        //     generates blocks with fixed intervals
107        //     * You are expected to also set `jsonrpc_ws_port`,
108        //       and `jsonrpc_http_port` if you want RPC functionalities.
109        //     * generate blocks automatically without PoW.
110        //     * Skip catch-up mode even there is no peer
111        //
112        (mode, (Option<String>), None)
113        // Development related section.
114        (debug_invalid_state_root, (bool), false)
115        (debug_invalid_state_root_epoch, (Option<String>), None)
116        (debug_dump_dir_invalid_state_root, (String), "./storage_db/debug_dump_invalid_state_root/".to_string())
117        // Controls block generation speed.
118        // Only effective in `dev` mode
119        (dev_block_interval_ms, (Option<u64>), None)
120        (dev_pack_tx_immediately, (Option<bool>), None)
121        (enable_state_expose, (bool), false)
122        (generate_tx, (bool), false)
123        (generate_tx_period_us, (Option<u64>), Some(100_000))
124        (log_conf, (Option<String>), None)
125        (log_file, (Option<String>), None)
126        (max_block_size_in_bytes, (usize), MAX_BLOCK_SIZE_IN_BYTES)
127        (evm_transaction_block_ratio,(u64),EVM_TRANSACTION_BLOCK_RATIO)
128        (evm_transaction_gas_ratio,(u64),EVM_TRANSACTION_GAS_RATIO)
129        (metrics_enabled, (bool), false)
130        (metrics_influxdb_host, (Option<String>), None)
131        (metrics_influxdb_db, (String), "conflux".into())
132        (metrics_influxdb_username, (Option<String>), None)
133        (metrics_influxdb_password, (Option<String>), None)
134        (metrics_influxdb_node, (Option<String>), None)
135        (metrics_output_file, (Option<String>), None)
136        (metrics_report_interval_ms, (u64), 3_000)
137        (metrics_prometheus_listen_addr, (Option<String>), None)
138        (profiling_listen_addr, (Option<String>), None)
139        (rocksdb_disable_wal, (bool), false)
140        (txgen_account_count, (usize), 10)
141
142        // Genesis section.
143        (adaptive_weight_beta, (u64), ADAPTIVE_WEIGHT_DEFAULT_BETA)
144        (anticone_penalty_ratio, (u64), ANTICONE_PENALTY_RATIO)
145        (chain_id, (Option<u32>), None)
146        (evm_chain_id, (Option<u32>), None)
147        (execute_genesis, (bool), true)
148        (default_transition_time, (Option<u64>), None)
149        // Snapshot Epoch Count is a consensus parameter. This flag overrides
150        // the parameter, which only take effect in `dev` mode.
151        (dev_snapshot_epoch_count, (u32), SNAPSHOT_EPOCHS_CAPACITY)
152        (era_epoch_count, (u64), ERA_DEFAULT_EPOCH_COUNT)
153        (heavy_block_difficulty_ratio, (u64), HEAVY_BLOCK_DEFAULT_DIFFICULTY_RATIO)
154        (genesis_accounts, (Option<String>), None)
155        (genesis_evm_secrets, (Option<String>), None)
156        (genesis_secrets, (Option<String>), None)
157        (pivot_hint_path, (Option<String>), None)
158        (pivot_hint_checksum, (Option<String>), None)
159        (initial_difficulty, (Option<u64>), None)
160        (referee_bound, (usize), REFEREE_DEFAULT_BOUND)
161        (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA)
162        (timer_chain_block_difficulty_ratio, (u64), TIMER_CHAIN_BLOCK_DEFAULT_DIFFICULTY_RATIO)
163        // FIXME: this is part of spec.
164        (transaction_epoch_bound, (u64), TRANSACTION_DEFAULT_EPOCH_BOUND)
165
166
167        // Hardfork section
168        // V1.1
169        (tanzanite_transition_height, (u64), TANZANITE_HEIGHT)
170        // V2.0
171        (hydra_transition_number, (Option<u64>), None)
172        (hydra_transition_height, (Option<u64>), None)
173        (cip43_init_end_number, (Option<u64>), None)
174        (cip78_patch_transition_number,(Option<u64>),None)
175        (cip90_transition_height,(Option<u64>),None)
176        (cip90_transition_number,(Option<u64>),None)
177        // V2.1
178        (dao_vote_transition_number, (Option<u64>), None)
179        (dao_vote_transition_height, (Option<u64>), None)
180        (cip105_transition_number, (Option<u64>), None)
181        (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD)
182        // V2.2
183        (sigma_fix_transition_number, (Option<u64>), None)
184        // V2.3
185        (cip107_transition_number, (Option<u64>), None)
186        (cip112_transition_height, (Option<u64>), None)
187        (cip118_transition_number, (Option<u64>), None)
188        (cip119_transition_number, (Option<u64>), None)
189        // V2.4
190        (base_fee_burn_transition_number, (Option<u64>), None)
191        (base_fee_burn_transition_height, (Option<u64>), None)
192        (cip1559_transition_height, (Option<u64>), None)
193        (cip130_transition_height, (Option<u64>), None)
194        (cancun_opcodes_transition_number, (Option<u64>), None)
195        (min_native_base_price, (Option<u64>), None)
196        (min_eth_base_price, (Option<u64>), None)
197        // V2.5
198        (c2_fix_transition_height, (Option<u64>), None)
199        // V3.0
200        (eoa_code_transition_height, (Option<u64>), None)
201        (cip151_transition_height, (Option<u64>), None)
202        (cip645_transition_height, (Option<u64>), None)
203        (cip145_fix_transition_height, (Option<u64>), None)
204        // For test only
205        (align_evm_transition_height, (u64), u64::MAX)
206
207        // V3.1
208        (osaka_opcode_transition_height, (Option<u64>), None)
209        (cip166_transition_height, (Option<u64>), None)
210        (cip167_transition_height, (Option<u64>), None)
211        (cip172_transition_height, (Option<u64>), None)
212        (cip174_transition_height, (Option<u64>), None)
213        (cip175_transition_height, (Option<u64>), None)
214        (cip176_transition_height, (Option<u64>), None)
215
216        // Mining section.
217        (mining_author, (Option<String>), None)
218        (mining_type, (Option<String>), None)
219        (stratum_listen_address, (String), "127.0.0.1".into())
220        (stratum_port, (u16), 32525)
221        (stratum_secret, (Option<String>), None)
222        (use_octopus_in_test_mode, (bool), false)
223        (pow_problem_window_size, (usize), 1)
224
225        // Network section.
226        (jsonrpc_local_http_port, (Option<u16>), None)
227        (jsonrpc_local_ws_port, (Option<u16>), None)
228        (jsonrpc_ws_port, (Option<u16>), None)
229        (jsonrpc_http_port, (Option<u16>), None)
230        (jsonrpc_http_threads, (Option<usize>), None)
231        (jsonrpc_cors, (Option<String>), None)
232        (jsonrpc_http_keep_alive, (bool), false)
233        (jsonrpc_ws_max_payload_bytes, (usize), 30 * 1024 * 1024)
234        (jsonrpc_http_eth_port, (Option<u16>), None)
235        (jsonrpc_ws_eth_port, (Option<u16>), None)
236        (jsonrpc_max_request_body_size, (u32), 10 * 1024 * 1024)
237        (jsonrpc_max_response_body_size, (u32), 10 * 1024 * 1024)
238        (jsonrpc_max_connections, (u32), 100)
239        (jsonrpc_max_subscriptions_per_connection, (u32), 1024)
240        (jsonrpc_message_buffer_capacity, (u32), 1024)
241        // The network_id, if unset, defaults to the chain_id.
242        // Only override the network_id for local experiments,
243        // when user would like to keep the existing blockchain data
244        // but disconnect from the public network.
245        (network_id, (Option<u64>), None)
246        (rpc_enable_metrics, (bool), false)
247        (tcp_port, (u16), 32323)
248        (public_tcp_port, (Option<u16>), None)
249        (public_address, (Option<String>), None)
250        (udp_port, (Option<u16>), Some(32323))
251        (max_estimation_gas_limit, (Option<u64>), None)
252        (rpc_address_simple_mode, (bool), false)
253
254        // Network parameters section.
255        (blocks_request_timeout_ms, (u64), 20_000)
256        (check_request_period_ms, (u64), 1_000)
257        (chunk_size_byte, (u64), DEFAULT_CHUNK_SIZE)
258        (demote_peer_for_timeout, (bool), false)
259        (dev_allow_phase_change_without_peer, (bool), false)
260        (egress_queue_capacity, (usize), 256)
261        (egress_min_throttle, (usize), 10)
262        (egress_max_throttle, (usize), 64)
263        (expire_block_gc_period_s, (u64), 900)
264        (headers_request_timeout_ms, (u64), 10_000)
265        (heartbeat_period_interval_ms, (u64), 30_000)
266        (heartbeat_timeout_ms, (u64), 180_000)
267        (inflight_pending_tx_index_maintain_timeout_ms, (u64), 30_000)
268        (max_allowed_timeout_in_observing_period, (u64), 10)
269        (max_chunk_number_in_manifest, (usize), 500)
270        (max_downloading_chunks, (usize), 8)
271        (max_downloading_chunk_attempts, (usize), 5)
272        (max_downloading_manifest_attempts, (usize), 5)
273        (max_handshakes, (usize), 64)
274        (max_incoming_peers, (usize), 64)
275        (max_inflight_request_count, (u64), 64)
276        (max_outgoing_peers, (usize), 8)
277        (max_outgoing_peers_archive, (Option<usize>), None)
278        (max_peers_tx_propagation, (usize), 128)
279        (max_unprocessed_block_size_mb, (usize), (128))
280        (min_peers_tx_propagation, (usize), 8)
281        (min_phase_change_normal_peer_count, (usize), 3)
282        (received_tx_index_maintain_timeout_ms, (u64), 300_000)
283        (request_block_with_public, (bool), false)
284        (send_tx_period_ms, (u64), 1300)
285        (snapshot_candidate_request_timeout_ms, (u64), 10_000)
286        (snapshot_chunk_request_timeout_ms, (u64), 30_000)
287        (snapshot_manifest_request_timeout_ms, (u64), 30_000)
288        (sync_expire_block_timeout_s, (u64), 7200)
289        (throttling_conf, (Option<String>), None)
290        (timeout_observing_period_s, (u64), 600)
291        (transaction_request_timeout_ms, (u64), 30_000)
292        (tx_maintained_for_peer_timeout_ms, (u64), 600_000)
293
294        // Peer management section.
295        (bootnodes, (Option<String>), None)
296        (discovery_discover_node_count, (u32), 16)
297        (discovery_expire_time_s, (u64), 20)
298        (discovery_fast_refresh_timeout_ms, (u64), 10_000)
299        (discovery_find_node_timeout_ms, (u64), 2_000)
300        (discovery_housekeeping_timeout_ms, (u64), 1_000)
301        (discovery_max_nodes_ping, (usize), 32)
302        (discovery_ping_timeout_ms, (u64), 2_000)
303        (discovery_round_timeout_ms, (u64), 500)
304        (discovery_throttling_interval_ms, (u64), 1_000)
305        (discovery_throttling_limit_ping, (usize), 20)
306        (discovery_throttling_limit_find_nodes, (usize), 10)
307        (enable_discovery, (bool), true)
308        (netconf_dir, (Option<String>), None)
309        (net_key, (Option<String>), None)
310        (node_table_timeout_s, (u64), 300)
311        (node_table_promotion_timeout_s, (u64), 3 * 24 * 3600)
312        (session_ip_limits, (String), "1,8,4,2".into())
313        (subnet_quota, (usize), 128)
314
315        // Transaction cache/transaction pool section.
316        (tx_cache_index_maintain_timeout_ms, (u64), 300_000)
317        (tx_pool_size, (usize), 50_000)
318        (tx_pool_min_native_tx_gas_price, (Option<u64>), None)
319        (tx_pool_min_eth_tx_gas_price, (Option<u64>), None)
320        (tx_pool_nonce_bits, (usize), TXPOOL_DEFAULT_NONCE_BITS)
321        (tx_pool_allow_gas_over_half_block, (bool), false)
322        (max_packing_batch_gas_limit, (u64), 3_000_000)
323        (max_packing_batch_size, (usize), 50)
324        (packing_pool_degree, (u8), 4)
325
326
327        // Storage Section.
328        (additional_maintained_snapshot_count, (u32), 1)
329        // `None` for `additional_maintained*` means the data is never garbage collected.
330        (additional_maintained_block_body_epoch_count, (Option<usize>), None)
331        (additional_maintained_execution_result_epoch_count, (Option<usize>), None)
332        (additional_maintained_reward_epoch_count, (Option<usize>), None)
333        (additional_maintained_trace_epoch_count, (Option<usize>), None)
334        (additional_maintained_transaction_index_epoch_count, (Option<usize>), None)
335        (block_cache_gc_period_ms, (u64), 5_000)
336        (block_db_dir, (Option<String>), None)
337        (block_db_type, (String), "rocksdb".to_string())
338        (checkpoint_gc_time_in_era_count, (f64), 0.5)
339        // The conflux data dir, if unspecified, is the workdir where conflux is started.
340        (conflux_data_dir, (String), "./blockchain_data".to_string())
341        (enable_single_mpt_storage, (bool), false)
342        (ledger_cache_size, (usize), DEFAULT_LEDGER_CACHE_SIZE)
343        (invalid_block_hash_cache_size_in_count, (usize), DEFAULT_INVALID_BLOCK_HASH_CACHE_SIZE_IN_COUNT)
344        (rocksdb_cache_size, (Option<usize>), Some(128))
345        (rocksdb_compaction_profile, (Option<String>), None)
346        (storage_delta_mpts_cache_recent_lfu_factor, (f64), cfx_storage::defaults::DEFAULT_DELTA_MPTS_CACHE_RECENT_LFU_FACTOR)
347        (storage_delta_mpts_cache_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_CACHE_SIZE)
348        (storage_delta_mpts_cache_start_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_CACHE_START_SIZE)
349        (storage_delta_mpts_node_map_vec_size, (u32), cfx_storage::defaults::MAX_CACHED_TRIE_NODES_R_LFU_COUNTER)
350        (storage_delta_mpts_slab_idle_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_SLAB_IDLE_SIZE)
351        (storage_single_mpt_cache_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_CACHE_SIZE * 2)
352        (storage_single_mpt_cache_start_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_CACHE_START_SIZE * 2)
353        (storage_single_mpt_slab_idle_size, (u32), cfx_storage::defaults::DEFAULT_DELTA_MPTS_SLAB_IDLE_SIZE * 2)
354        (storage_max_open_snapshots, (u16), cfx_storage::defaults::DEFAULT_MAX_OPEN_SNAPSHOTS)
355        (storage_max_open_mpt_count, (u32), cfx_storage::defaults::DEFAULT_MAX_OPEN_MPT)
356        (strict_tx_index_gc, (bool), true)
357        (sync_state_starting_epoch, (Option<u64>), None)
358        (sync_state_epoch_gap, (Option<u64>), None)
359        (target_difficulties_cache_size_in_count, (usize), DEFAULT_TARGET_DIFFICULTIES_CACHE_SIZE_IN_COUNT)
360
361        // General/Unclassified section.
362        (account_provider_refresh_time_ms, (u64), 1000)
363        (check_phase_change_period_ms, (u64), 1000)
364        (enable_optimistic_execution, (bool), true)
365        (future_block_buffer_capacity, (usize), 32768)
366        (get_logs_filter_max_limit, (Option<usize>), None)
367        (get_logs_filter_max_epoch_range, (Option<u64>), None)
368        (get_logs_filter_max_block_number_range, (Option<u64>), None)
369        (get_logs_epoch_batch_size, (usize), 32)
370        (max_trans_count_received_in_catch_up, (u64), 60_000)
371        (persist_tx_index, (bool), false)
372        (persist_block_number_index, (bool), true)
373        (print_memory_usage_period_s, (Option<u64>), None)
374        (target_block_gas_limit, (u64), DEFAULT_TARGET_BLOCK_GAS_LIMIT)
375        (executive_trace, (bool), false)
376        (check_status_genesis, (bool), true)
377        (packing_gas_limit_block_count, (u64), 10)
378        (poll_lifetime_in_seconds, (Option<u32>), None)
379
380        // TreeGraph Section.
381        (is_consortium, (bool), false)
382        (pos_config_path, (Option<String>), Some("./pos_config/pos_config.yaml".to_string()))
383        (pos_genesis_pivot_decision, (Option<H256>), None)
384        (vrf_proposal_threshold, (U256), U256::from_str("1111111111111100000000000000000000000000000000000000000000000000").unwrap())
385        // Deferred epoch count before a confirmed epoch.
386        (pos_pivot_decision_defer_epoch_count, (u64), 50)
387        (cip113_pivot_decision_defer_epoch_count, (u64), 20)
388        (cip113_transition_height, (u64), u64::MAX)
389        (pos_reference_enable_height, (u64), u64::MAX)
390        (pos_initial_nodes_path, (String), "./pos_config/initial_nodes.json".to_string())
391        (pos_private_key_path, (String), "./pos_config/pos_key".to_string())
392        (pos_round_per_term, (u64), ROUND_PER_TERM)
393        (pos_term_max_size, (usize), TERM_MAX_SIZE)
394        (pos_term_elected_size, (usize), TERM_ELECTED_SIZE)
395        (pos_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS)
396        (pos_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS)
397        (pos_cip99_transition_view, (u64), u64::MAX)
398        (pos_cip99_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS)
399        (pos_cip99_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS)
400        (nonce_limit_transition_view, (u64), u64::MAX)
401        (pos_cip136_transition_view, (u64), u64::MAX)
402        (pos_cip136_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS)
403        (pos_cip136_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS)
404        (pos_cip136_round_per_term, (u64), ROUND_PER_TERM)
405        (pos_cip156_transition_view, (u64), u64::MAX)
406        // 6 months with 30s rounds
407        (pos_cip156_dispute_locked_views, (u64), 6 * 30 * 24 * 60 * 2)
408        (pos_cip173_transition_view, (u64), u64::MAX)
409        (dev_pos_private_key_encryption_password, (Option<String>), None)
410        (pos_started_as_voter, (bool), true)
411
412        // Light node section
413        (ln_epoch_request_batch_size, (Option<usize>), None)
414        (ln_epoch_request_timeout_sec, (Option<u64>), None)
415        (ln_header_request_batch_size, (Option<usize>), None)
416        (ln_header_request_timeout_sec, (Option<u64>), None)
417        (ln_max_headers_in_flight, (Option<usize>), None)
418        (ln_max_parallel_epochs_to_request, (Option<usize>), None)
419        (ln_num_epochs_to_request, (Option<usize>), None)
420        (ln_num_waiting_headers_threshold, (Option<usize>), None)
421        (keep_snapshot_before_stable_checkpoint, (bool), true)
422        (force_recompute_height_during_construct_pivot, (Option<u64>), None)
423
424        // The snapshot database consists of two tables: snapshot_key_value and snapshot_mpt. However, the size of snapshot_mpt is significantly larger than that of snapshot_key_value.
425        // When the configuration parameter use_isolated_db_for_mpt_table is set to true, the snapshot_mpt table will be located in a separate database.
426        (use_isolated_db_for_mpt_table, (bool), false)
427        // The use_isolated_db_for_mpt_table_height parameter is utilized to determine when to enable the use_isolated_db_for_mpt_table option.
428        //  None: enabled since the next snapshot
429        //  u64: enabled since the specified height
430        (use_isolated_db_for_mpt_table_height, (Option<u64>), None)
431        // Recover the latest MPT snapshot from the era checkpoint
432        (recovery_latest_mpt_snapshot, (bool), false)
433        (keep_era_genesis_snapshot, (bool), true)
434
435        // This is designed for fast node catch-up but has not been thoroughly tested. Do not use it in production environments.
436        (backup_mpt_snapshot, (bool), true)
437    }
438    {
439        // Development related section.
440        (
441            log_level, (LevelFilter), LevelFilter::Info, |l| {
442                LevelFilter::from_str(l)
443                    .map_err(|_| format!("Invalid log level: {}", l))
444            }
445        )
446
447        // Genesis Section
448        // chain_id_params describes a complex setup where chain id can change over epochs.
449        // Usually this is needed to describe forks. This config overrides chain_id.
450        (chain_id_params, (Option<ChainIdParamsOneChainInner>), None,
451            ChainIdParamsOneChainInner::parse_config_str)
452
453        // Storage section.
454        (provide_more_snapshot_for_sync,
455            (Vec<ProvideExtraSnapshotSyncConfig>),
456            vec![ProvideExtraSnapshotSyncConfig::StableCheckpoint],
457            ProvideExtraSnapshotSyncConfig::parse_config_list)
458        (node_type, (Option<NodeType>), None, NodeType::from_str)
459        (public_rpc_apis, (ApiSet), ApiSet::Safe, ApiSet::from_str)
460        (public_evm_rpc_apis, (RpcModuleSelection), RpcModuleSelection::Evm, RpcModuleSelection::from_str)
461        (single_mpt_space, (Option<Space>), None, Space::from_str)
462    }
463}
464
465#[derive(Debug, Clone, Default)]
466pub struct Configuration {
467    pub raw_conf: RawConfiguration,
468}
469
470impl Configuration {
471    pub fn parse(matches: &clap::ArgMatches) -> Result<Configuration, String> {
472        let mut raw_conf = RawConfiguration::parse(matches)?;
473
474        if matches.get_flag("archive") {
475            raw_conf.node_type = Some(NodeType::Archive);
476        } else if matches.get_flag("full") {
477            raw_conf.node_type = Some(NodeType::Full);
478        } else if matches.get_flag("light") {
479            raw_conf.node_type = Some(NodeType::Light);
480        }
481
482        CIP112_TRANSITION_HEIGHT
483            .set(raw_conf.cip112_transition_height.unwrap_or(u64::MAX))
484            .expect("called once");
485
486        USE_SIMPLE_RPC_ADDRESS
487            .set(raw_conf.rpc_address_simple_mode)
488            .expect("called once");
489
490        Ok(Configuration { raw_conf })
491    }
492
493    pub fn from_file(config_path: &str) -> Result<Configuration, String> {
494        Ok(Configuration {
495            raw_conf: RawConfiguration::from_file(config_path)?,
496        })
497    }
498
499    fn network_id(&self) -> u64 {
500        match self.raw_conf.network_id {
501            Some(x) => x,
502            // If undefined, the network id is set to the native space chain_id
503            // at genesis.
504            None => {
505                self.chain_id_params()
506                    .read()
507                    .get_chain_id(/* epoch_number = */ 0)
508                    .in_native_space() as u64
509            }
510        }
511    }
512
513    pub fn net_config(&self) -> Result<NetworkConfiguration, String> {
514        let mut network_config = NetworkConfiguration::new_with_port(
515            self.network_id(),
516            self.raw_conf.tcp_port,
517            self.discovery_protocol(),
518        );
519
520        network_config.is_consortium = self.raw_conf.is_consortium;
521        network_config.discovery_enabled = self.raw_conf.enable_discovery;
522        network_config.boot_nodes = to_bootnodes(&self.raw_conf.bootnodes)
523            .map_err(|e| format!("failed to parse bootnodes: {}", e))?;
524        network_config.config_path = Some(match &self.raw_conf.netconf_dir {
525            Some(dir) => dir.clone(),
526            None => Path::new(&self.raw_conf.conflux_data_dir)
527                .join(NET_CONFIG_DB_DIR_NAME)
528                .into_os_string()
529                .into_string()
530                .unwrap(),
531        });
532        network_config.use_secret =
533            self.raw_conf.net_key.as_ref().map(|sec_str| {
534                parse_hex_string(sec_str)
535                    .expect("net_key is not a valid secret string")
536            });
537        if let Some(addr) = self.raw_conf.public_address.clone() {
538            let addr_ip = if let Some(idx) = addr.find(":") {
539                warn!("Public address configuration should not contain port! (val = {}). Content after ':' is ignored.", &addr);
540                addr[0..idx].to_string()
541            } else {
542                addr
543            };
544            let addr_with_port = match self.raw_conf.public_tcp_port {
545                Some(port) => addr_ip + ":" + &port.to_string(),
546                None => addr_ip + ":" + &self.raw_conf.tcp_port.to_string(),
547            };
548            network_config.public_address =
549                match addr_with_port.to_socket_addrs().map(|mut i| i.next()) {
550                    Ok(sock_addr) => sock_addr,
551                    Err(_e) => {
552                        warn!("public_address in config is invalid");
553                        None
554                    }
555                };
556        }
557        network_config.node_table_timeout =
558            Duration::from_secs(self.raw_conf.node_table_timeout_s);
559        network_config.connection_lifetime_for_promotion =
560            Duration::from_secs(self.raw_conf.node_table_promotion_timeout_s);
561        network_config.test_mode = self.is_test_mode();
562        network_config.subnet_quota = self.raw_conf.subnet_quota;
563        network_config.session_ip_limit_config =
564            self.raw_conf.session_ip_limits.clone().try_into().map_err(
565                |e| format!("failed to parse session ip limit config: {}", e),
566            )?;
567        network_config.fast_discovery_refresh_timeout = Duration::from_millis(
568            self.raw_conf.discovery_fast_refresh_timeout_ms,
569        );
570        network_config.discovery_round_timeout =
571            Duration::from_millis(self.raw_conf.discovery_round_timeout_ms);
572        network_config.housekeeping_timeout = Duration::from_millis(
573            self.raw_conf.discovery_housekeeping_timeout_ms,
574        );
575        network_config.max_handshakes = self.raw_conf.max_handshakes;
576        network_config.max_incoming_peers = self.raw_conf.max_incoming_peers;
577        network_config.max_outgoing_peers = self.raw_conf.max_outgoing_peers;
578        network_config.max_outgoing_peers_archive =
579            self.raw_conf.max_outgoing_peers_archive.unwrap_or(0);
580        Ok(network_config)
581    }
582
583    pub fn cache_config(&self) -> CacheConfig {
584        CacheConfig {
585            ledger: self.raw_conf.ledger_cache_size,
586            invalid_block_hashes_cache_size_in_count: self
587                .raw_conf
588                .invalid_block_hash_cache_size_in_count,
589            target_difficulties_cache_size_in_count: self
590                .raw_conf
591                .target_difficulties_cache_size_in_count,
592        }
593    }
594
595    pub fn db_config(&self) -> (PathBuf, DatabaseConfig) {
596        let db_dir: PathBuf = match &self.raw_conf.block_db_dir {
597            Some(dir) => dir.into(),
598            None => Path::new(&self.raw_conf.conflux_data_dir)
599                .join(BLOCK_DB_DIR_NAME),
600        };
601        if let Err(e) = fs::create_dir_all(&db_dir) {
602            panic!("Error creating database directory: {:?}", e);
603        }
604
605        let compact_profile =
606            match self.raw_conf.rocksdb_compaction_profile.as_ref() {
607                Some(p) => db::DatabaseCompactionProfile::from_str(p).unwrap(),
608                None => db::DatabaseCompactionProfile::default(),
609            };
610        let db_config = db::db_config(
611            &db_dir,
612            self.raw_conf.rocksdb_cache_size,
613            compact_profile,
614            NUM_COLUMNS,
615            self.raw_conf.rocksdb_disable_wal,
616        );
617        (db_dir, db_config)
618    }
619
620    pub fn chain_id_params(&self) -> ChainIdParams {
621        if CHAIN_ID.read().is_none() {
622            let mut to_init = CHAIN_ID.write();
623            if to_init.is_none() {
624                if let Some(_chain_id_params) = &self.raw_conf.chain_id_params {
625                    unreachable!("Upgradable ChainId is not ready.")
626                // *to_init = Some(ChainIdParamsInner::new_from_inner(
627                //     chain_id_params,
628                // ))
629                } else {
630                    let chain_id = self
631                        .raw_conf
632                        .chain_id
633                        .unwrap_or_else(|| rand::rng().random());
634                    let evm_chain_id =
635                        self.raw_conf.evm_chain_id.unwrap_or(chain_id);
636                    *to_init = Some(ChainIdParamsInner::new_simple(
637                        AllChainID::new(chain_id, evm_chain_id),
638                    ));
639                }
640            }
641        }
642        CHAIN_ID.read().as_ref().unwrap().clone()
643    }
644
645    pub fn consensus_config(&self) -> ConsensusConfig {
646        let enable_optimistic_execution = if DEFERRED_STATE_EPOCH_COUNT <= 1 {
647            false
648        } else {
649            self.raw_conf.enable_optimistic_execution
650        };
651        let pivot_hint_conf = match (
652            &self.raw_conf.pivot_hint_path,
653            &self.raw_conf.pivot_hint_checksum,
654        ) {
655            (Some(path), Some(checksum)) => {
656                let checksum = H256::from_str(checksum)
657                    .expect("Cannot parse `pivot_hint_checksum` as hex string");
658                Some(PivotHintConfig::new(path, checksum))
659            }
660            (None, None) => None,
661            _ => {
662                panic!("`pivot_hint_path` and `pivot_hint_checksum` must be both set or both unset");
663            }
664        };
665        let mut conf = ConsensusConfig {
666            chain_id: self.chain_id_params(),
667            inner_conf: ConsensusInnerConfig {
668                adaptive_weight_beta: self.raw_conf.adaptive_weight_beta,
669                heavy_block_difficulty_ratio: self
670                    .raw_conf
671                    .heavy_block_difficulty_ratio,
672                timer_chain_block_difficulty_ratio: self
673                    .raw_conf
674                    .timer_chain_block_difficulty_ratio,
675                timer_chain_beta: self.raw_conf.timer_chain_beta,
676                era_epoch_count: self.raw_conf.era_epoch_count,
677                enable_optimistic_execution,
678                enable_state_expose: self.raw_conf.enable_state_expose,
679                pos_pivot_decision_defer_epoch_count: self.raw_conf.pos_pivot_decision_defer_epoch_count,
680                cip113_pivot_decision_defer_epoch_count: self.raw_conf.cip113_pivot_decision_defer_epoch_count,
681                cip113_transition_height: self.raw_conf.cip113_transition_height,
682                debug_dump_dir_invalid_state_root: if self
683                    .raw_conf
684                    .debug_invalid_state_root
685                {
686                    Some(
687                        self.raw_conf.debug_dump_dir_invalid_state_root.clone(),
688                    )
689                } else {
690                    None
691                },
692
693                debug_invalid_state_root_epoch: self
694                    .raw_conf
695                    .debug_invalid_state_root_epoch.as_ref().map(|epoch_hex| H256::from_str(epoch_hex).expect("debug_invalid_state_root_epoch byte length is incorrect.")),
696                force_recompute_height_during_construct_pivot: self.raw_conf.force_recompute_height_during_construct_pivot,
697                recovery_latest_mpt_snapshot: self.raw_conf.recovery_latest_mpt_snapshot,
698                use_isolated_db_for_mpt_table: self.raw_conf.use_isolated_db_for_mpt_table,
699            },
700            bench_mode: false,
701            transaction_epoch_bound: self.raw_conf.transaction_epoch_bound,
702            referee_bound: self.raw_conf.referee_bound,
703            get_logs_epoch_batch_size: self.raw_conf.get_logs_epoch_batch_size,
704            get_logs_filter_max_epoch_range: self.raw_conf.get_logs_filter_max_epoch_range,
705            get_logs_filter_max_block_number_range: self.raw_conf.get_logs_filter_max_block_number_range,
706            get_logs_filter_max_limit: self.raw_conf.get_logs_filter_max_limit,
707            sync_state_starting_epoch: self.raw_conf.sync_state_starting_epoch,
708            sync_state_epoch_gap: self.raw_conf.sync_state_epoch_gap,
709            pivot_hint_conf,
710        };
711        match self.raw_conf.node_type {
712            Some(NodeType::Archive) => {
713                if conf.sync_state_starting_epoch.is_none() {
714                    conf.sync_state_starting_epoch = Some(0);
715                }
716            }
717            _ => {
718                if conf.sync_state_epoch_gap.is_none() {
719                    conf.sync_state_epoch_gap =
720                        Some(CATCH_UP_EPOCH_LAG_THRESHOLD);
721                }
722            }
723        }
724        conf
725    }
726
727    pub fn pow_config(&self) -> ProofOfWorkConfig {
728        let stratum_secret =
729            self.raw_conf.stratum_secret.as_ref().map(|hex_str| {
730                parse_hex_string(hex_str)
731                    .expect("Stratum secret should be 64-digit hex string")
732            });
733
734        ProofOfWorkConfig::new(
735            self.is_test_or_dev_mode(),
736            self.raw_conf.use_octopus_in_test_mode,
737            self.raw_conf.mining_type.as_ref().map_or_else(
738                || {
739                    // Enable stratum implicitly if `mining_author` is set.
740                    if self.raw_conf.mining_author.is_some() {
741                        "stratum"
742                    } else {
743                        "disable"
744                    }
745                },
746                |s| s.as_str(),
747            ),
748            self.raw_conf.initial_difficulty,
749            self.raw_conf.stratum_listen_address.clone(),
750            self.raw_conf.stratum_port,
751            stratum_secret,
752            self.raw_conf.pow_problem_window_size,
753            self.common_params().transition_heights.cip86,
754        )
755    }
756
757    pub fn verification_config(
758        &self, machine: Arc<Machine>,
759    ) -> VerificationConfig {
760        VerificationConfig::new(
761            self.is_test_mode(),
762            self.raw_conf.referee_bound,
763            self.raw_conf.max_block_size_in_bytes,
764            self.raw_conf.transaction_epoch_bound,
765            self.raw_conf.tx_pool_nonce_bits,
766            self.raw_conf.pos_reference_enable_height,
767            machine,
768        )
769    }
770
771    pub fn tx_gen_config(&self) -> Option<TransactionGeneratorConfig> {
772        if self.is_test_or_dev_mode() &&
773            // FIXME: this is not a good condition to check.
774            self.raw_conf.genesis_secrets.is_some()
775        {
776            Some(TransactionGeneratorConfig::new(
777                self.raw_conf.generate_tx,
778                self.raw_conf.generate_tx_period_us.expect("has default"),
779                self.raw_conf.txgen_account_count,
780            ))
781        } else {
782            None
783        }
784    }
785
786    pub fn storage_config(&self, node_type: &NodeType) -> StorageConfiguration {
787        let conflux_data_path = Path::new(&self.raw_conf.conflux_data_dir);
788        StorageConfiguration {
789            additional_maintained_snapshot_count: self
790                .raw_conf
791                .additional_maintained_snapshot_count,
792            consensus_param: ConsensusParam {
793                snapshot_epoch_count: if self.is_test_mode() {
794                    self.raw_conf.dev_snapshot_epoch_count
795                } else {
796                    SNAPSHOT_EPOCHS_CAPACITY
797                },
798                era_epoch_count: self.raw_conf.era_epoch_count,
799            },
800            debug_snapshot_checker_threads:
801                DEFAULT_DEBUG_SNAPSHOT_CHECKER_THREADS,
802            delta_mpts_cache_recent_lfu_factor: self
803                .raw_conf
804                .storage_delta_mpts_cache_recent_lfu_factor,
805            delta_mpts_cache_size: self.raw_conf.storage_delta_mpts_cache_size,
806            delta_mpts_cache_start_size: self
807                .raw_conf
808                .storage_delta_mpts_cache_start_size,
809            delta_mpts_node_map_vec_size: self
810                .raw_conf
811                .storage_delta_mpts_node_map_vec_size,
812            delta_mpts_slab_idle_size: self
813                .raw_conf
814                .storage_delta_mpts_slab_idle_size,
815            single_mpt_cache_start_size: self
816                .raw_conf
817                .storage_single_mpt_cache_start_size,
818            single_mpt_cache_size: self.raw_conf.storage_single_mpt_cache_size,
819            single_mpt_slab_idle_size: self
820                .raw_conf
821                .storage_single_mpt_slab_idle_size,
822            max_open_snapshots: self.raw_conf.storage_max_open_snapshots,
823            path_delta_mpts_dir: conflux_data_path
824                .join(&*storage_dir::DELTA_MPTS_DIR),
825            path_snapshot_dir: conflux_data_path
826                .join(&*storage_dir::SNAPSHOT_DIR),
827            path_snapshot_info_db: conflux_data_path
828                .join(&*storage_dir::SNAPSHOT_INFO_DB_PATH),
829            path_storage_dir: conflux_data_path
830                .join(&*storage_dir::STORAGE_DIR),
831            provide_more_snapshot_for_sync: self
832                .raw_conf
833                .provide_more_snapshot_for_sync
834                .clone(),
835            max_open_mpt_count: self.raw_conf.storage_max_open_mpt_count,
836            enable_single_mpt_storage: match node_type {
837                NodeType::Archive => self.raw_conf.enable_single_mpt_storage,
838                _ => {
839                    if self.raw_conf.enable_single_mpt_storage {
840                        error!("enable_single_mpt_storage is only supported for Archive nodes!")
841                    }
842                    false
843                }
844            },
845            single_mpt_space: self.raw_conf.single_mpt_space,
846            cip90a: self
847                .raw_conf
848                .cip90_transition_height
849                .unwrap_or(self.raw_conf.hydra_transition_height.unwrap_or(0)),
850            keep_snapshot_before_stable_checkpoint: self
851                .raw_conf
852                .keep_snapshot_before_stable_checkpoint,
853            use_isolated_db_for_mpt_table: self
854                .raw_conf
855                .use_isolated_db_for_mpt_table,
856            use_isolated_db_for_mpt_table_height: self
857                .raw_conf
858                .use_isolated_db_for_mpt_table_height,
859            keep_era_genesis_snapshot: self.raw_conf.keep_era_genesis_snapshot,
860            backup_mpt_snapshot: self.raw_conf.backup_mpt_snapshot,
861        }
862    }
863
864    pub fn protocol_config(&self) -> ProtocolConfiguration {
865        ProtocolConfiguration {
866            is_consortium: self.raw_conf.is_consortium,
867            send_tx_period: Duration::from_millis(
868                self.raw_conf.send_tx_period_ms,
869            ),
870            check_request_period: Duration::from_millis(
871                self.raw_conf.check_request_period_ms,
872            ),
873            check_phase_change_period: Duration::from_millis(
874                self.raw_conf.check_phase_change_period_ms,
875            ),
876            heartbeat_period_interval: Duration::from_millis(
877                self.raw_conf.heartbeat_period_interval_ms,
878            ),
879            block_cache_gc_period: Duration::from_millis(
880                self.raw_conf.block_cache_gc_period_ms,
881            ),
882            expire_block_gc_period: Duration::from_secs(
883                self.raw_conf.expire_block_gc_period_s,
884            ),
885            headers_request_timeout: Duration::from_millis(
886                self.raw_conf.headers_request_timeout_ms,
887            ),
888            blocks_request_timeout: Duration::from_millis(
889                self.raw_conf.blocks_request_timeout_ms,
890            ),
891            transaction_request_timeout: Duration::from_millis(
892                self.raw_conf.transaction_request_timeout_ms,
893            ),
894            tx_maintained_for_peer_timeout: Duration::from_millis(
895                self.raw_conf.tx_maintained_for_peer_timeout_ms,
896            ),
897            max_inflight_request_count: self
898                .raw_conf
899                .max_inflight_request_count,
900            request_block_with_public: self.raw_conf.request_block_with_public,
901            received_tx_index_maintain_timeout: Duration::from_millis(
902                self.raw_conf.received_tx_index_maintain_timeout_ms,
903            ),
904            inflight_pending_tx_index_maintain_timeout: Duration::from_millis(
905                self.raw_conf.inflight_pending_tx_index_maintain_timeout_ms,
906            ),
907            max_trans_count_received_in_catch_up: self
908                .raw_conf
909                .max_trans_count_received_in_catch_up,
910            min_peers_tx_propagation: self.raw_conf.min_peers_tx_propagation,
911            max_peers_tx_propagation: self.raw_conf.max_peers_tx_propagation,
912            max_downloading_chunks: self.raw_conf.max_downloading_chunks,
913            max_downloading_chunk_attempts: self
914                .raw_conf
915                .max_downloading_chunk_attempts,
916            test_mode: self.is_test_mode(),
917            dev_mode: self.is_dev_mode(),
918            throttling_config_file: self.raw_conf.throttling_conf.clone(),
919            snapshot_candidate_request_timeout: Duration::from_millis(
920                self.raw_conf.snapshot_candidate_request_timeout_ms,
921            ),
922            snapshot_manifest_request_timeout: Duration::from_millis(
923                self.raw_conf.snapshot_manifest_request_timeout_ms,
924            ),
925            snapshot_chunk_request_timeout: Duration::from_millis(
926                self.raw_conf.snapshot_chunk_request_timeout_ms,
927            ),
928            chunk_size_byte: self.raw_conf.chunk_size_byte,
929            max_chunk_number_in_manifest: self
930                .raw_conf
931                .max_chunk_number_in_manifest,
932            timeout_observing_period_s: self
933                .raw_conf
934                .timeout_observing_period_s,
935            max_allowed_timeout_in_observing_period: self
936                .raw_conf
937                .max_allowed_timeout_in_observing_period,
938            demote_peer_for_timeout: self.raw_conf.demote_peer_for_timeout,
939            heartbeat_timeout: Duration::from_millis(
940                self.raw_conf.heartbeat_timeout_ms,
941            ),
942            max_unprocessed_block_size: self
943                .raw_conf
944                .max_unprocessed_block_size_mb
945                * 1_000_000,
946            sync_expire_block_timeout: Duration::from_secs(
947                self.raw_conf.sync_expire_block_timeout_s,
948            ),
949            allow_phase_change_without_peer: if self.is_dev_mode() {
950                true
951            } else {
952                self.raw_conf.dev_allow_phase_change_without_peer
953            },
954            min_phase_change_normal_peer_count: self
955                .raw_conf
956                .min_phase_change_normal_peer_count,
957            pos_genesis_pivot_decision: self
958                .raw_conf
959                .pos_genesis_pivot_decision
960                .expect("set to genesis if none"),
961            check_status_genesis: self.raw_conf.check_status_genesis,
962            pos_started_as_voter: self.raw_conf.pos_started_as_voter,
963        }
964    }
965
966    pub fn state_sync_config(&self) -> StateSyncConfiguration {
967        StateSyncConfiguration {
968            max_downloading_chunks: self.raw_conf.max_downloading_chunks,
969            candidate_request_timeout: Duration::from_millis(
970                self.raw_conf.snapshot_candidate_request_timeout_ms,
971            ),
972            chunk_request_timeout: Duration::from_millis(
973                self.raw_conf.snapshot_chunk_request_timeout_ms,
974            ),
975            manifest_request_timeout: Duration::from_millis(
976                self.raw_conf.snapshot_manifest_request_timeout_ms,
977            ),
978            max_downloading_manifest_attempts: self
979                .raw_conf
980                .max_downloading_manifest_attempts,
981        }
982    }
983
984    pub fn data_mananger_config(&self) -> DataManagerConfiguration {
985        let mut conf = DataManagerConfiguration {
986            persist_tx_index: self.raw_conf.persist_tx_index,
987            persist_block_number_index: self
988                .raw_conf
989                .persist_block_number_index,
990            tx_cache_index_maintain_timeout: Duration::from_millis(
991                self.raw_conf.tx_cache_index_maintain_timeout_ms,
992            ),
993            db_type: match self.raw_conf.block_db_type.as_str() {
994                "rocksdb" => DbType::Rocksdb,
995                "sqlite" => DbType::Sqlite,
996                _ => panic!("Invalid block_db_type parameter!"),
997            },
998            additional_maintained_block_body_epoch_count: self
999                .raw_conf
1000                .additional_maintained_block_body_epoch_count,
1001            additional_maintained_execution_result_epoch_count: self
1002                .raw_conf
1003                .additional_maintained_execution_result_epoch_count,
1004            additional_maintained_reward_epoch_count: self
1005                .raw_conf
1006                .additional_maintained_reward_epoch_count,
1007            additional_maintained_trace_epoch_count: self
1008                .raw_conf
1009                .additional_maintained_trace_epoch_count,
1010            additional_maintained_transaction_index_epoch_count: self
1011                .raw_conf
1012                .additional_maintained_transaction_index_epoch_count,
1013            checkpoint_gc_time_in_epoch_count: (self
1014                .raw_conf
1015                .checkpoint_gc_time_in_era_count
1016                * self.raw_conf.era_epoch_count as f64)
1017                as usize,
1018            strict_tx_index_gc: self.raw_conf.strict_tx_index_gc,
1019        };
1020
1021        // By default, we do not keep the block data for additional period,
1022        // but `node_type = "archive"` is a shortcut for keeping all them.
1023        if !matches!(self.raw_conf.node_type, Some(NodeType::Archive)) {
1024            if conf.additional_maintained_block_body_epoch_count.is_none() {
1025                conf.additional_maintained_block_body_epoch_count = Some(0);
1026            }
1027            if conf
1028                .additional_maintained_execution_result_epoch_count
1029                .is_none()
1030            {
1031                conf.additional_maintained_execution_result_epoch_count =
1032                    Some(0);
1033            }
1034            if conf
1035                .additional_maintained_transaction_index_epoch_count
1036                .is_none()
1037            {
1038                conf.additional_maintained_transaction_index_epoch_count =
1039                    Some(0);
1040            }
1041            if conf.additional_maintained_reward_epoch_count.is_none() {
1042                conf.additional_maintained_reward_epoch_count = Some(0);
1043            }
1044            if conf.additional_maintained_trace_epoch_count.is_none() {
1045                conf.additional_maintained_trace_epoch_count = Some(0);
1046            }
1047        }
1048        if conf.additional_maintained_transaction_index_epoch_count != Some(0) {
1049            conf.persist_tx_index = true;
1050        }
1051        conf
1052    }
1053
1054    pub fn sync_graph_config(&self) -> SyncGraphConfig {
1055        SyncGraphConfig {
1056            future_block_buffer_capacity: self
1057                .raw_conf
1058                .future_block_buffer_capacity,
1059            enable_state_expose: self.raw_conf.enable_state_expose,
1060            is_consortium: self.raw_conf.is_consortium,
1061        }
1062    }
1063
1064    pub fn metrics_config(&self) -> MetricsConfiguration {
1065        MetricsConfiguration {
1066            enabled: self.raw_conf.metrics_enabled,
1067            report_interval: Duration::from_millis(
1068                self.raw_conf.metrics_report_interval_ms,
1069            ),
1070            file_report_output: self.raw_conf.metrics_output_file.clone(),
1071            influxdb_report_host: self.raw_conf.metrics_influxdb_host.clone(),
1072            influxdb_report_db: self.raw_conf.metrics_influxdb_db.clone(),
1073            influxdb_report_username: self
1074                .raw_conf
1075                .metrics_influxdb_username
1076                .clone(),
1077            influxdb_report_password: self
1078                .raw_conf
1079                .metrics_influxdb_password
1080                .clone(),
1081            influxdb_report_node: self.raw_conf.metrics_influxdb_node.clone(),
1082            prometheus_listen_addr: self
1083                .raw_conf
1084                .metrics_prometheus_listen_addr
1085                .clone(),
1086        }
1087    }
1088
1089    pub fn txpool_config(&self) -> TxPoolConfig {
1090        let (min_native_tx_price_default, min_eth_tx_price_default) =
1091            if self.is_test_or_dev_mode() {
1092                (1, 1)
1093            } else {
1094                (ONE_GDRIP_IN_DRIP, 20 * ONE_GDRIP_IN_DRIP)
1095            };
1096        TxPoolConfig {
1097            capacity: self.raw_conf.tx_pool_size,
1098            half_block_gas_limit: RwLock::new(U256::from(
1099                DEFAULT_TARGET_BLOCK_GAS_LIMIT / 2,
1100            )),
1101            min_native_tx_price: self
1102                .raw_conf
1103                .tx_pool_min_native_tx_gas_price
1104                .unwrap_or(min_native_tx_price_default),
1105            allow_gas_over_half_block: self
1106                .raw_conf
1107                .tx_pool_allow_gas_over_half_block,
1108            target_block_gas_limit: self.raw_conf.target_block_gas_limit,
1109            min_eth_tx_price: self
1110                .raw_conf
1111                .tx_pool_min_eth_tx_gas_price
1112                .unwrap_or(min_eth_tx_price_default),
1113            max_packing_batch_gas_limit: self
1114                .raw_conf
1115                .max_packing_batch_gas_limit,
1116            max_packing_batch_size: self.raw_conf.max_packing_batch_size,
1117            packing_pool_degree: self.raw_conf.packing_pool_degree,
1118        }
1119    }
1120
1121    pub fn rpc_impl_config(&self) -> RpcImplConfiguration {
1122        RpcImplConfiguration {
1123            get_logs_filter_max_limit: self.raw_conf.get_logs_filter_max_limit,
1124            dev_pack_tx_immediately: self
1125                .raw_conf
1126                .dev_pack_tx_immediately
1127                .unwrap_or_else(|| {
1128                    self.is_dev_mode()
1129                        && self.raw_conf.dev_block_interval_ms.is_none()
1130                }),
1131            max_payload_bytes: self.raw_conf.jsonrpc_ws_max_payload_bytes,
1132            enable_metrics: self.raw_conf.rpc_enable_metrics,
1133            poll_lifetime_in_seconds: self.raw_conf.poll_lifetime_in_seconds,
1134            max_estimation_gas_limit: self
1135                .raw_conf
1136                .max_estimation_gas_limit
1137                .map(U256::from),
1138        }
1139    }
1140
1141    pub fn local_http_config(&self) -> HttpConfiguration {
1142        HttpConfiguration::new(
1143            Some((127, 0, 0, 1)),
1144            self.raw_conf.jsonrpc_local_http_port,
1145            self.raw_conf.jsonrpc_http_keep_alive,
1146            self.raw_conf.jsonrpc_http_threads,
1147            self.raw_conf.jsonrpc_cors.clone(),
1148        )
1149    }
1150
1151    pub fn local_ws_config(&self) -> WsConfiguration {
1152        WsConfiguration::new(
1153            Some((127, 0, 0, 1)),
1154            self.raw_conf.jsonrpc_local_ws_port,
1155            self.raw_conf.jsonrpc_ws_max_payload_bytes,
1156            self.raw_conf.jsonrpc_cors.clone(),
1157        )
1158    }
1159
1160    pub fn http_config(&self) -> HttpConfiguration {
1161        HttpConfiguration::new(
1162            None,
1163            self.raw_conf.jsonrpc_http_port,
1164            self.raw_conf.jsonrpc_http_keep_alive,
1165            self.raw_conf.jsonrpc_http_threads,
1166            self.raw_conf.jsonrpc_cors.clone(),
1167        )
1168    }
1169
1170    pub fn ws_config(&self) -> WsConfiguration {
1171        WsConfiguration::new(
1172            None,
1173            self.raw_conf.jsonrpc_ws_port,
1174            self.raw_conf.jsonrpc_ws_max_payload_bytes,
1175            self.raw_conf.jsonrpc_cors.clone(), // use same cors option as http
1176        )
1177    }
1178
1179    pub fn eth_http_config(&self) -> HttpConfiguration {
1180        HttpConfiguration::new(
1181            None,
1182            self.raw_conf.jsonrpc_http_eth_port,
1183            self.raw_conf.jsonrpc_http_keep_alive,
1184            self.raw_conf.jsonrpc_http_threads,
1185            self.raw_conf.jsonrpc_cors.clone(),
1186        )
1187    }
1188
1189    pub fn eth_ws_config(&self) -> WsConfiguration {
1190        WsConfiguration::new(
1191            None,
1192            self.raw_conf.jsonrpc_ws_eth_port,
1193            self.raw_conf.jsonrpc_ws_max_payload_bytes,
1194            self.raw_conf.jsonrpc_cors.clone(), // use same cors option as http
1195        )
1196    }
1197
1198    pub fn jsonrpsee_server_builder(&self) -> ServerConfigBuilder {
1199        ServerConfigBuilder::default()
1200            .max_request_body_size(self.raw_conf.jsonrpc_max_request_body_size)
1201            .max_response_body_size(
1202                self.raw_conf.jsonrpc_max_response_body_size,
1203            )
1204            .max_connections(self.raw_conf.jsonrpc_max_connections)
1205            .max_subscriptions_per_connection(
1206                self.raw_conf.jsonrpc_max_subscriptions_per_connection,
1207            )
1208            .set_message_buffer_capacity(
1209                self.raw_conf.jsonrpc_message_buffer_capacity,
1210            )
1211    }
1212
1213    pub fn execution_config(&self) -> ConsensusExecutionConfiguration {
1214        ConsensusExecutionConfiguration {
1215            executive_trace: self.raw_conf.executive_trace,
1216        }
1217    }
1218
1219    pub fn discovery_protocol(&self) -> DiscoveryConfiguration {
1220        DiscoveryConfiguration {
1221            discover_node_count: self.raw_conf.discovery_discover_node_count,
1222            expire_time: Duration::from_secs(
1223                self.raw_conf.discovery_expire_time_s,
1224            ),
1225            find_node_timeout: Duration::from_millis(
1226                self.raw_conf.discovery_find_node_timeout_ms,
1227            ),
1228            max_nodes_ping: self.raw_conf.discovery_max_nodes_ping,
1229            ping_timeout: Duration::from_millis(
1230                self.raw_conf.discovery_ping_timeout_ms,
1231            ),
1232            throttling_interval: Duration::from_millis(
1233                self.raw_conf.discovery_throttling_interval_ms,
1234            ),
1235            throttling_limit_ping: self
1236                .raw_conf
1237                .discovery_throttling_limit_ping,
1238            throttling_limit_find_nodes: self
1239                .raw_conf
1240                .discovery_throttling_limit_find_nodes,
1241        }
1242    }
1243
1244    pub fn is_test_mode(&self) -> bool {
1245        matches!(self.raw_conf.mode.as_deref(), Some("test"))
1246    }
1247
1248    pub fn is_dev_mode(&self) -> bool {
1249        matches!(self.raw_conf.mode.as_deref(), Some("dev"))
1250    }
1251
1252    pub fn is_test_or_dev_mode(&self) -> bool {
1253        matches!(self.raw_conf.mode.as_deref(), Some("dev") | Some("test"))
1254    }
1255
1256    pub fn is_consortium(&self) -> bool { self.raw_conf.is_consortium }
1257
1258    pub fn light_node_config(&self) -> LightNodeConfiguration {
1259        LightNodeConfiguration {
1260            epoch_request_batch_size: self.raw_conf.ln_epoch_request_batch_size,
1261            epoch_request_timeout: self
1262                .raw_conf
1263                .ln_epoch_request_timeout_sec
1264                .map(Duration::from_secs),
1265            header_request_batch_size: self
1266                .raw_conf
1267                .ln_header_request_batch_size,
1268            header_request_timeout: self
1269                .raw_conf
1270                .ln_header_request_timeout_sec
1271                .map(Duration::from_secs),
1272            max_headers_in_flight: self.raw_conf.ln_max_headers_in_flight,
1273            max_parallel_epochs_to_request: self
1274                .raw_conf
1275                .ln_max_parallel_epochs_to_request,
1276            num_epochs_to_request: self.raw_conf.ln_num_epochs_to_request,
1277            num_waiting_headers_threshold: self
1278                .raw_conf
1279                .ln_num_waiting_headers_threshold,
1280        }
1281    }
1282
1283    pub fn common_params(&self) -> CommonParams {
1284        let mut params = CommonParams::default();
1285
1286        if self.is_test_or_dev_mode() {
1287            params.early_set_internal_contracts_states = true;
1288        }
1289
1290        let non_test_default = SpaceMap::new(
1291            INITIAL_1559_CORE_BASE_PRICE,
1292            INITIAL_1559_ETH_BASE_PRICE,
1293        );
1294        let test_default = SpaceMap::new(1u64, 1);
1295        let config = SpaceMap::new(
1296            self.raw_conf.min_native_base_price,
1297            self.raw_conf.min_eth_base_price,
1298        );
1299        let base_price = SpaceMap::zip3(non_test_default, test_default, config)
1300            .map_all(|(non_test, test, config)| {
1301                if let Some(x) = config {
1302                    x
1303                } else if self.is_test_or_dev_mode() {
1304                    test
1305                } else {
1306                    non_test
1307                }
1308            });
1309        params.min_base_price = base_price.map_all(U256::from);
1310
1311        params.chain_id = self.chain_id_params();
1312        params.anticone_penalty_ratio = self.raw_conf.anticone_penalty_ratio;
1313        params.evm_transaction_block_ratio =
1314            self.raw_conf.evm_transaction_block_ratio;
1315        params.evm_transaction_gas_ratio =
1316            self.raw_conf.evm_transaction_gas_ratio;
1317
1318        params.params_dao_vote_period = self.raw_conf.params_dao_vote_period;
1319
1320        self.set_cips(&mut params);
1321
1322        params
1323    }
1324
1325    pub fn node_type(&self) -> NodeType {
1326        self.raw_conf.node_type.unwrap_or(NodeType::Full)
1327    }
1328
1329    pub fn pos_state_config(&self) -> PosStateConfig {
1330        // The current implementation requires the round number to be an even
1331        // number.
1332        assert_eq!(self.raw_conf.pos_round_per_term % 2, 0);
1333        PosStateConfig::new(
1334            self.raw_conf.pos_round_per_term,
1335            self.raw_conf.pos_term_max_size,
1336            self.raw_conf.pos_term_elected_size,
1337            self.raw_conf.pos_in_queue_locked_views,
1338            self.raw_conf.pos_out_queue_locked_views,
1339            self.raw_conf.pos_cip99_transition_view,
1340            self.raw_conf.pos_cip99_in_queue_locked_views,
1341            self.raw_conf.pos_cip99_out_queue_locked_views,
1342            self.raw_conf.nonce_limit_transition_view,
1343            20_000, // 2 * 10^7 CFX
1344            self.raw_conf.pos_cip136_transition_view,
1345            self.raw_conf.pos_cip136_in_queue_locked_views,
1346            self.raw_conf.pos_cip136_out_queue_locked_views,
1347            self.raw_conf.pos_cip136_round_per_term,
1348            self.raw_conf.pos_cip156_transition_view,
1349            self.raw_conf.pos_cip156_dispute_locked_views,
1350            self.raw_conf.pos_cip173_transition_view,
1351        )
1352    }
1353
1354    fn set_cips(&self, params: &mut CommonParams) {
1355        let default_transition_time =
1356            if let Some(num) = self.raw_conf.default_transition_time {
1357                num
1358            } else if self.is_test_or_dev_mode() {
1359                0u64
1360            } else {
1361                u64::MAX
1362            };
1363
1364        // This is to set the default transition time for the CIPs that cannot
1365        // be enabled in the genesis.
1366        let non_genesis_default_transition_time =
1367            match self.raw_conf.default_transition_time {
1368                Some(num) if num > 0 => num,
1369                _ => {
1370                    if self.is_test_or_dev_mode() {
1371                        1u64
1372                    } else {
1373                        u64::MAX
1374                    }
1375                }
1376            };
1377
1378        //
1379        // Tanzanite hardfork
1380        //
1381        params.transition_heights.cip40 =
1382            self.raw_conf.tanzanite_transition_height;
1383        let mut base_block_rewards = BTreeMap::new();
1384        base_block_rewards.insert(0, INITIAL_BASE_MINING_REWARD_IN_UCFX.into());
1385        base_block_rewards.insert(
1386            params.transition_heights.cip40,
1387            MINING_REWARD_TANZANITE_IN_UCFX.into(),
1388        );
1389        params.base_block_rewards = base_block_rewards;
1390
1391        //
1392        // Hydra hardfork (V2.0)
1393        //
1394        set_conf!(
1395            self.raw_conf.hydra_transition_number.unwrap_or(default_transition_time);
1396            params.transition_numbers => { cip43a, cip64, cip71, cip78a, cip92 }
1397        );
1398        set_conf!(
1399            self.raw_conf.hydra_transition_height.unwrap_or(default_transition_time);
1400            params.transition_heights => { cip76, cip86 }
1401        );
1402        params.transition_numbers.cip43b =
1403            self.raw_conf.cip43_init_end_number.unwrap_or(
1404                if self.is_test_or_dev_mode() {
1405                    u64::MAX
1406                } else {
1407                    params.transition_numbers.cip43a
1408                },
1409            );
1410        params.transition_numbers.cip62 = if self.is_test_or_dev_mode() {
1411            0u64
1412        } else {
1413            BN128_ENABLE_NUMBER
1414        };
1415        params.transition_numbers.cip78b = self
1416            .raw_conf
1417            .cip78_patch_transition_number
1418            .unwrap_or(params.transition_numbers.cip78a);
1419        params.transition_heights.cip90a = self
1420            .raw_conf
1421            .cip90_transition_height
1422            .or(self.raw_conf.hydra_transition_height)
1423            .unwrap_or(default_transition_time);
1424        params.transition_numbers.cip90b = self
1425            .raw_conf
1426            .cip90_transition_number
1427            .or(self.raw_conf.hydra_transition_number)
1428            .unwrap_or(default_transition_time);
1429
1430        //
1431        // DAO vote hardfork (V2.1)
1432        //
1433        set_conf!(
1434            self.raw_conf.dao_vote_transition_number.unwrap_or(default_transition_time);
1435            params.transition_numbers => { cip97, cip98 }
1436        );
1437        params.transition_numbers.cip94n = self
1438            .raw_conf
1439            .dao_vote_transition_number
1440            .unwrap_or(non_genesis_default_transition_time);
1441        params.transition_heights.cip94h = self
1442            .raw_conf
1443            .dao_vote_transition_height
1444            .unwrap_or(non_genesis_default_transition_time);
1445        params.transition_numbers.cip105 = self
1446            .raw_conf
1447            .cip105_transition_number
1448            .or(self.raw_conf.dao_vote_transition_number)
1449            .unwrap_or(default_transition_time);
1450
1451        //
1452        // Sigma protocol fix hardfork (V2.2)
1453        //
1454        params.transition_numbers.cip_sigma_fix = self
1455            .raw_conf
1456            .sigma_fix_transition_number
1457            .unwrap_or(default_transition_time);
1458
1459        //
1460        // Burn collateral hardfork (V2.3)
1461        //
1462        params.transition_numbers.cip107 = self
1463            .raw_conf
1464            .cip107_transition_number
1465            .unwrap_or(default_transition_time);
1466        params.transition_heights.cip112 =
1467            *CIP112_TRANSITION_HEIGHT.get().expect("initialized");
1468        params.transition_numbers.cip118 = self
1469            .raw_conf
1470            .cip118_transition_number
1471            .unwrap_or(default_transition_time);
1472        params.transition_numbers.cip119 = self
1473            .raw_conf
1474            .cip119_transition_number
1475            .unwrap_or(default_transition_time);
1476
1477        //
1478        // 1559 hardfork (V2.4)
1479        //
1480        set_conf!(
1481            self.raw_conf.base_fee_burn_transition_number.unwrap_or(default_transition_time);
1482            params.transition_numbers => { cip131, cip132, cip133b, cip137, cip144, cip145 }
1483        );
1484        set_conf!(
1485            self.raw_conf.base_fee_burn_transition_height.unwrap_or(default_transition_time);
1486            params.transition_heights => { cip130, cip133e }
1487        );
1488        // TODO: disable 1559 test during dev
1489        params.transition_heights.cip1559 = self
1490            .raw_conf
1491            .cip1559_transition_height
1492            .or(self.raw_conf.base_fee_burn_transition_height)
1493            .unwrap_or(non_genesis_default_transition_time);
1494        params.transition_heights.cip130 = self
1495            .raw_conf
1496            .cip130_transition_height
1497            .or(self.raw_conf.base_fee_burn_transition_height)
1498            .unwrap_or(default_transition_time);
1499        params.transition_numbers.cancun_opcodes = self
1500            .raw_conf
1501            .cancun_opcodes_transition_number
1502            .or(self.raw_conf.base_fee_burn_transition_number)
1503            .unwrap_or(default_transition_time);
1504
1505        if params.transition_heights.cip1559
1506            < self.raw_conf.pos_reference_enable_height
1507        {
1508            panic!("1559 can not be activated earlier than pos reference: 1559 (epoch {}), pos (epoch {})", params.transition_heights.cip1559, self.raw_conf.pos_reference_enable_height);
1509        }
1510
1511        //
1512        // hardfork (V2.5)
1513        //
1514        params.transition_heights.cip_c2_fix = self
1515            .raw_conf
1516            .c2_fix_transition_height
1517            .unwrap_or(default_transition_time);
1518
1519        //
1520        // 7702 hardfork (V3.0)
1521        //
1522        set_conf!(
1523            self.raw_conf.eoa_code_transition_height.unwrap_or(default_transition_time);
1524            params.transition_heights => { cip150, cip151, cip152, cip154, cip7702, cip645, eip2537, eip2935, eip7623, cip145_fix }
1525        );
1526        if let Some(x) = self.raw_conf.cip151_transition_height {
1527            params.transition_heights.cip151 = x;
1528        }
1529        if let Some(x) = self.raw_conf.cip645_transition_height {
1530            params.transition_heights.cip645 = x;
1531        }
1532        if let Some(x) = self.raw_conf.cip145_fix_transition_height {
1533            params.transition_heights.cip145_fix = x;
1534        }
1535        params.transition_heights.align_evm =
1536            self.raw_conf.align_evm_transition_height;
1537
1538        // hardfork (V3.1)
1539        set_conf!(
1540            self.raw_conf.osaka_opcode_transition_height.unwrap_or(default_transition_time);
1541            params.transition_heights => { cip166, cip167, cip172, cip174, cip175, cip176 }
1542        );
1543        if let Some(x) = self.raw_conf.cip166_transition_height {
1544            params.transition_heights.cip166 = x;
1545        }
1546        if let Some(x) = self.raw_conf.cip167_transition_height {
1547            params.transition_heights.cip167 = x;
1548        }
1549        if let Some(x) = self.raw_conf.cip172_transition_height {
1550            params.transition_heights.cip172 = x;
1551        }
1552        if let Some(x) = self.raw_conf.cip174_transition_height {
1553            params.transition_heights.cip174 = x;
1554        }
1555        if let Some(x) = self.raw_conf.cip175_transition_height {
1556            params.transition_heights.cip175 = x;
1557        }
1558        if let Some(x) = self.raw_conf.cip176_transition_height {
1559            params.transition_heights.cip176 = x;
1560        }
1561    }
1562}
1563
1564/// Validates and formats bootnodes option.
1565pub fn to_bootnodes(bootnodes: &Option<String>) -> Result<Vec<String>, String> {
1566    match *bootnodes {
1567        Some(ref x) if !x.is_empty() => x
1568            .split(',')
1569            // ignore empty strings
1570            .filter(|s| !s.is_empty())
1571            .map(|s| match validate_node_url(s){
1572                None => Ok(s.to_owned()),
1573                Some(network::Error::AddressResolve(_)) => Err(format!(
1574                    "Failed to resolve hostname of a boot node: {}",
1575                    s
1576                )),
1577                Some(e) => Err(format!(
1578                    "Invalid node address format given for a boot node: {} err={:?}",
1579                    s, e
1580                )),
1581            })
1582            .collect(),
1583        Some(_) => Ok(vec![]),
1584        None => Ok(vec![]),
1585    }
1586}
1587
1588pub fn parse_config_address_string(
1589    addr: &str, network: &Network,
1590) -> Result<Address, String> {
1591    let base32_err = match cfx_addr_decode(addr) {
1592        Ok(address) => {
1593            return if address.network != *network {
1594                Err(format!(
1595                    "address in configuration has unmatching network id: expected network={},\
1596                     address.network={}",
1597                    network,
1598                    address.network
1599                ))
1600            } else {
1601                address
1602                    .hex_address
1603                    .ok_or("decoded address has wrong byte length".into())
1604            };
1605        }
1606        Err(e) => e,
1607    };
1608    let hex_err = match parse_hex_string(addr) {
1609        Ok(address) => return Ok(address),
1610        Err(e) => e,
1611    };
1612    // An address from config must be valid.
1613    Err(format!("Address from configuration should be a valid base32 address or a 40-digit hex string!
1614            base32_err={:?}
1615            hex_err={:?}",
1616                base32_err, hex_err))
1617}
1618
1619#[cfg(test)]
1620mod tests {
1621    use cfx_addr::Network;
1622
1623    use crate::configuration::parse_config_address_string;
1624
1625    #[test]
1626    fn test_config_address_string() {
1627        let addr = parse_config_address_string(
1628            "0x1a2f80341409639ea6a35bbcab8299066109aa55",
1629            &Network::Main,
1630        )
1631        .unwrap();
1632        // Allow omitting the leading "0x" prefix.
1633        assert_eq!(
1634            addr,
1635            parse_config_address_string(
1636                "1a2f80341409639ea6a35bbcab8299066109aa55",
1637                &Network::Main,
1638            )
1639            .unwrap()
1640        );
1641        // Allow CIP-37 base32 address.
1642        assert_eq!(
1643            addr,
1644            parse_config_address_string(
1645                "cfx:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg",
1646                &Network::Main,
1647            )
1648            .unwrap()
1649        );
1650        // Allow optional fields in CIP-37 base32 address.
1651        assert_eq!(
1652            addr,
1653            parse_config_address_string(
1654                "cfx:type.user:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg",
1655                &Network::Main,
1656            )
1657            .unwrap()
1658        );
1659    }
1660}