feat(wallet): implement unified offline private key export API#2542
feat(wallet): implement unified offline private key export API#2542
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Offline Private Key Export API DocumentationOverviewThe Supported Export Modes1. Standard ModeExports the standard private key for each specified coin. 2. HD (Hierarchical Deterministic) ModeExports multiple HD-derived addresses for each coin within a specified index range. 3. Iguana ModeExports keys using the Iguana derivation method (legacy compatibility). API EndpointMethod: Request Parameters
cURL ExamplesStandard Modecurl --url http://127.0.0.1:7783 --data \
'{
"userpass": "USERPASS",
"mmrpc": "2.0",
"method": "get_private_keys",
"params": {
"coins": ["BTC", "LTC", "DOGE", "KMD"],
"mode": "standard"
}
}'HD Modecurl --url http://127.0.0.1:7783 --data \
'{
"userpass": "USERPASS",
"mmrpc": "2.0",
"method": "get_private_keys",
"params": {
"coins": ["BTC", "LTC", "DOGE", "KMD"],
"mode": "hd",
"start_index": 0,
"end_index": 5
}
}'Iguana Modecurl --url http://127.0.0.1:7783 --data \
'{
"userpass": "USERPASS",
"mmrpc": "2.0",
"method": "get_private_keys",
"params": {
"coins": ["BTC", "LTC", "DOGE", "KMD"],
"mode": "iguana"
}
}'Response FormatStandard Mode Response{
"mmrpc": "2.0",
"result": [
{
"coin": "BTC",
"pubkey": "03...",
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"priv_key": "L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1"
}
],
"id": null
}HD Mode Response{
"mmrpc": "2.0",
"result": [
{
"coin": "BTC",
"addresses": [
{
"index": 0,
"pubkey": "03...",
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"priv_key": "L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1"
},
{
"index": 1,
"pubkey": "02...",
"address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
"priv_key": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
}
]
}
],
"id": null
}Error HandlingThe API may return errors in the following format: {
"mmrpc": "2.0",
"error": "Error message description",
"error_path": "offline_keys",
"error_trace": "offline_keys:227]",
"error_type": "Internal",
"error_data": "Additional error details",
"id": null
}Common error scenarios:
Backwards CompatibilityThe existing Legacy show_priv_key Usagecurl --url "http://127.0.0.1:7783" --data '{"userpass":"USERPASS","method":"show_priv_key","coin":"KMD"}'This will work for both activated and non-activated coins with or without internet connection, maintaining full backwards compatibility. |
|
can we please also add input for account_index? Update: resolved ✔️ |
|
Table in prior comment indicates
|
|
No errors returned when using Update: resolved ✔️ |
|
I noticed that in {
"coin": "BBK",
"addresses": [
{
"index": 0,
"pubkey": "034ba1a9d6d275eb0749f17005e944d17bbe0d1eb3eed03960cc4d454e8797af23",
"address": "BLr7Axhbmz5B8oVZ6HXo81nhiF2CTH6CbM",
"priv_key": "GqbDKxbKEgcsP4tCcz15czd67ABzD1dYPh6fVgEvzcySNmeYKhYP"
},
{
"index": 1,
"pubkey": "03dcb8565e389b6aa04e00470e10b4dc6ddc2e2ce7189983d4da342f685402074e",
"address": "BBmrEwNgMfzXA2hpCSuiE42Tjr4BUsd9yX",
"priv_key": "Gy6mtEFi1evnTizY3QqEHDkqY6JyWkRwsU9Vd9EVRv6RyNdESULM"
},
{
"index": 2,
"pubkey": "03a7bfe05927de5e7f0959effc10d165d8009ae78c9b7553c3a9e14be8cf7519a9",
"address": "B5CrqcW7TLSesceTT3jH35RdL8vSPDn6pu",
"priv_key": "Gwn1ZcBfCvLG954Js2d6womB8wsv6UDvMYboZJNdxAXZLngJoK9L"
}
]
}Without knowledge of the For example: BTC and BCH share the same base58 params, but have different derivation paths ( {
"coin": "BTC",
"addresses": [
{
"index": 0,
"pubkey": "035a843f39a7fc686148c5e52ffebb41e84fbfefb3fdd2d64e5e82fddfbb0450ab",
"address": "1CRQnAaShSLH324WmZg4qhvfUW7tnkTqmZ",
"priv_key": "Kzzs67GWowSZjz9wJcLAR6h7Z2sDa1HUyvFCqKtH4FKG5fQ1PLoB"
}
]
},
{
"coin": "BCH",
"addresses": [
{
"index": 0,
"pubkey": "03c5db6d5b4a15d5a14d90a8d13bf8e7d54855daaecff7595b0bb9ef7beb91c5d4",
"address": "1L3dsQN1RfXsbk58dtz1UQqC4UyeeB1pMa",
"priv_key": "L3Pn8DWSuSwAmwSHYi2Nuo2VsJCKTpYurcuuvmBZjriEqownmpYc"
}
]
}Based on this, I believe we should avoid using default values for the purpose / coin_type parts of the derivation path, and return a Update: resolved ✔️ |
|
In the response, the {
"coin": "BTC",
"addresses": [
{
"derivation_path": "m/44'/0'/0'/0/0",
"pubkey": "035a843f39a7fc686148c5e52ffebb41e84fbfefb3fdd2d64e5e82fddfbb0450ab",
"address": "1CRQnAaShSLH324WmZg4qhvfUW7tnkTqmZ",
"priv_key": "Kzzs67GWowSZjz9wJcLAR6h7Z2sDa1HUyvFCqKtH4FKG5fQ1PLoB"
},
{
"derivation_path": "m/44'/0'/0'/0/1",
"pubkey": "0217f00e1d569b5b648c679f615e67be50eb6c1b9d1da9e0d5b5187093dd9701e4",
"address": "1Aby1HDmYQQVHA7KsG2PuURGR4BTgS9ynT",
"priv_key": "Kwn5NnGB5dpNTM2VzFNTBZ5PmpCn93T9RdS1a76bquAA8UcuGzXf"
}
]
},
{
"coin": "BCH",
"addresses": [
{
"derivation_path": "m/44'/145'/0'/0/0",
"pubkey": "03c5db6d5b4a15d5a14d90a8d13bf8e7d54855daaecff7595b0bb9ef7beb91c5d4",
"address": "1L3dsQN1RfXsbk58dtz1UQqC4UyeeB1pMa",
"priv_key": "L3Pn8DWSuSwAmwSHYi2Nuo2VsJCKTpYurcuuvmBZjriEqownmpYc"
},
{
"derivation_path": "m/44'/145'/0'/0/1",
"pubkey": "0235068668d119b8dbf50a6888f35f783aaeefcbf2ef4f23052a6814e0c6165123",
"address": "14oZPGZmWTAaZvUJb6yxk2gMU7kinPacCB",
"priv_key": "L4UApWP5WqGbNKTjZwdDHVD6KsgSpWijUYdPPV34BDmaH7TqRd5h"
}
]
}Update: resolved ✔️ |
|
When requesting a response for a coin which does not exist in the coins file, the returned error is {
"mmrpc": "2.0",
"error": "Internal error: Failed to parse protocol for COVID",
"error_path": "offline_keys",
"error_trace": "offline_keys:331]",
"error_type": "Internal",
"error_data": "Failed to parse protocol for COVID",
"id": null
}This error does not adequately explain the root cause of the problem, which is that COVID does not exist (in the coins configuration file), so a Update: resolved ✔️ |
|
While testing high values via postman, a response was received as 2.0 p2p.received_messages.count=0.0 p2p.received_messages.period_in_secs=60.0 p2p.relay_mesh.len=2.0
23 06:38:37, common:525] panicked at /home/smk/GITHUB/KP/komodo-defi-framework/mm2src/coins/rpc_command/offline_keys.rs:544:53:
attempt to add with overflow
23 06:38:37, common:526] backtrace
panicking.rs:676] rust_begin_unwind 0x60137133795c
panicking.rs:75] core::panicking::panic_fmt::h24e6103767c0d03d 0x60137135e59f
panicking.rs:178] core::panicking::panic_const::panic_const_add_overflow::hbb06823529aae6cf 0x601371368476
offline_keys.rs:544] coins::rpc_command::offline_keys::get_private_keys::{{closure}}::hf7fd9a968b93f3ad 0x60136cbe493b
dispatcher.rs:127] mm2_main::rpc::dispatcher::handle_mmrpc::{{closure}}::hc5c1be81d1497ace 0x60136d329e80
dispatcher.rs:227] mm2_main::rpc::dispatcher::dispatcher_v2::{{closure}}::he34427b73fd5a5e0 0x60136d3458b5
dispatcher.rs:100] mm2_main::rpc::dispatcher::process_single_request::{{closure}}::h55476e6c28f4ca45 0x60136d2d3745
rpc.rs:210] mm2_main::rpc::process_single_request::{{closure}}::hbbff86fa365ad223 0x60136cbd4612
rpc.rs:166] mm2_main::rpc::process_json_request::{{closure}}::hc83e52cd3654a2d3 0x60136cbd3432
rpc.rs:247] mm2_main::rpc::rpc_service::{{closure}}::process_rpc_request::{{closure}}::he3817059b296ca8f 0x60136cbd7b7e
rpc.rs:282] mm2_main::rpc::rpc_service::{{closure}}::hb81abf8a9b7af25b 0x60136cbd6a1a
rpc.rs:351] mm2_main::rpc::spawn_rpc::handle_request::{{closure}}::{{closure}}::h74b89b84eb0b0dbc 0x60136cbd8c00
unwind_safe.rs:272] <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h3e05c0d1f9c3c8a2 0x60136d7add60
??:0] __rust_try 0x60136d7974ea
task.rs:42] <tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll::h7881e505c4fbb207 0x601370fa5743
unwind_safe.rs:272] <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h943c3e231c0568cf 0x601370fa0f80
??:0] __rust_try 0x601370f652ca
unwind_safe.rs:272] <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::hca11c87d3b4ba599 0x601370fa0fe0
??:0] __rust_try 0x601370f7482a
pthread_create.c:447] start_thread 0x7f54b329caa3
clone3.S:78] clone3 0x7f54b3329c3b
Virtual memory addresses of /home/smk/GITHUB/KP/komodo-defi-framework/target/debug/kdf PHDR:0x601369e17040 INTERP:0x601369e172e0 LOAD:0x601369e17000 LOAD:0x60136be16a00 LOAD:0x60137136d600 LOAD:0x601371831970 TLS:0x60137136d600 DYNAMIC:0x6013715dd170 GNU_RELRO:0x60137136d600 GNU_EH_FRAME:0x60136b225344 GNU_STACK:0x601369e17000 NOTE:0x601369e172fcThis happens where higher values for {
"mmrpc": "2.0",
"error": "Error parsing request: invalid type: floating point `4.294967299566574e27`, expected u32",
"error_path": "dispatcher",
"error_trace": "dispatcher:126]",
"error_type": "InvalidRequest",
"error_data": "invalid type: floating point `4.294967299566574e27`, expected u32",
"id": null
}Update: Resolved ✅ |
|
Can we please follow PR titles and labels standards according to the following |
- Change GetPrivateKeysRequest.mode from KeyExportMode to Option<KeyExportMode> - Add serde default attribute to mode field - Update get_private_keys function to default to HD mode when ctx.enable_hd() is true, otherwise Iguana mode - Addresses GitHub comment by shamardy requesting optional mode parameter Resolves: #2542 (comment) Co-Authored-By: ca333 <ca333@users.noreply.github.com>
|
Since we require the wallet password for mnemonic export, we should consider requiring it here (and all other future/wip priv-key RPCs) since you could dump the private keys for all coins in a single RPC (same as having seed phrase). The Though, it would need further thought on how to handle wallets launched using the seed only (no |
|
You should add some unit tests and make sure they work, here are some test vectors for some coins: mnemonic: BTC:
BTC-Segwit:
ETH:
Atom:
|
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
|
For all coin protocols, in iguana mode (MM2.json): Note that output for Tendermint and segwit keys may be non-standard: For all coin protocols, in HD mode (MM2.json):
|
|
Output validation tests Address index: HD output at account/address index
Account index: HD output at account/address index
Additional comments ETH privkey output is in lowercase, rather than mixed case. Sometimes this triggers validation check failures in third party input fields. Ideally we should conform to https://eips.ethereum.org/EIPS/eip-55 for this output. The |
- Revert show_priv_key function to original behavior without offline fallback - Remove KeyExportMode::Standard from offline key export API - Remove offline_keys_export_internal function - Make mode parameter required in GetPrivateKeysRequest - Update API to only support HD and Iguana modes This addresses PR feedback to avoid confusion in HD mode where the fallback would incorrectly derive legacy Iguana private keys from HD wallet seeds. Co-Authored-By: ca333 <ca333@users.noreply.github.com>
- Change GetPrivateKeysRequest.mode from KeyExportMode to Option<KeyExportMode> - Add serde default attribute to mode field - Update get_private_keys function to default to HD mode when ctx.enable_hd() is true, otherwise Iguana mode - Addresses GitHub comment by shamardy requesting optional mode parameter Resolves: #2542 (comment) Co-Authored-By: ca333 <ca333@users.noreply.github.com>
- Remove unnecessary #[serde(default)] for Option<KeyExportMode> - Remove wallet_type checks since this field doesn't exist in coin config - Move get_private_keys import to top of dispatcher.rs file Co-Authored-By: ca333 <ca333@users.noreply.github.com>
- Add PrefixValues::Tendermint variant with account_prefix field - Update extract_prefix_values to handle TENDERMINT and TENDERMINTTOKEN protocols - Add Tendermint support to both HD and Iguana key export functions - Use tendermint::account_id_from_pubkey_hex for address generation - Format private keys with 0x prefix for consistency with other non-UTXO protocols - Support platform lookups for TENDERMINTTOKEN protocols Co-Authored-By: ca333 <ca333@users.noreply.github.com>
…ermint protocols - Remove 0x prefix from Tendermint private key formatting in both HD and Iguana export modes - Keep 0x prefix for Ethereum protocols as expected - Addresses shamardy's code review comment about incorrect Cosmos private key formatting Co-Authored-By: ca333 <ca333@users.noreply.github.com>
…tests (#2547) * feat: implement proper BIP39/44 HD key derivation with comprehensive tests * fix: apply cargo fmt to resolve CI formatting issues * fix: correct BTC-Segwit test vector private key and ensure all tests pass - Fixed typo in expected private key for BTC-Segwit test vector - All 6 offline key tests now pass successfully - BIP39/44 HD key derivation implementation verified as compliant - Segwit address generation working correctly with proper AddressFormat detection
) * feat(offline-keys): add ZHTLC offline key export support for ARRR * fix(offline-keys): apply cargo fmt formatting fixes * feat(offline-keys): implement actual ZHTLC key derivation for ARRR * fix(offline-keys): apply cargo fmt formatting fixes * fix(offline-keys): resolve compilation warnings by renaming unused variables
* fix: remove nested result field in offline key export response - Remove result field from IguanaKeysResponse and HdKeysResponse structures - Update internal functions to return data directly - Update GetPrivateKeysResponse enum to handle unwrapped data - Update all test functions to work with new response structure - Fixes nested 'result result' response issue * fmt code * fix: replace vec! with array literals to resolve clippy lint errors * fix: remove needless borrow in tendermint account_id_from_pubkey_hex call
e43c589 to
9d48928
Compare
…2552) This commit refines key and address generation for all Ethereum-based protocols (ETH, ERC20, NFT) to align with standard ecosystem conventions. - Public keys are now generated in the uncompressed format, which is necessary for correct address derivation. - All generated Ethereum addresses are now formatted with the EIP-55 mixed-case checksum, providing an integrity check against typos. - The public key display format in the RPC response is updated to use the standard `0x` prefix. - Tests have been added and updated to validate the new address and key formats.
The `get_private_keys` RPC previously handled Z-coins (ZHTLC) incorrectly by treating them like UTXO-based coins. This resulted in several issues: - It looped over an address index, attempting to derive multiple addresses for a single account, which is not how shielded accounts work. - It displayed a misleading derivation path that included an address index (e.g., .../0/0). - It generated and displayed a `secp256k1` public key that was irrelevant to the shielded address. This commit refactors the logic to handle Z-coins as a special case: 1. Key derivation for Z-coins now correctly stops at the account level, ignoring the address index. 2. The RPC returns a single, accurate entry for the Z-coin account instead of a redundant list. 3. The `derivation_path` in the response now correctly points to the account (e.g., m/44'/141'/0'). 4. The inapplicable `pubkey` field is now empty to avoid confusion, and the `viewing_key` is correctly populated as the relevant "public" component. The existing behavior for UTXO, ETH, and other indexed coins remains unchanged.
The pubkey field is now empty for Z-coins in both modes to avoid confusion, as a standard secp256k1 public key is not used for shielded addresses.
The key derivation process for multiple coins has been refactored to run concurrently. The sequential `for` loops in `offline_hd_keys_export_internal` and `offline_iguana_keys_export_internal` are replaced with `futures::future::try_join_all`. This significantly improves performance when exporting keys for several coins at once by executing the CPU-intensive cryptographic operations in parallel.
The `get_private_keys` RPC method was returning misleading derivation path information for ZHTLC-enabled (shielded) coins when used in HD mode. Previously, it would display the transparent derivation path (e.g., `m/44'/141'/0'`) while the shielded private key (`z_key`) was correctly derived using the `z_derivation_path` from the coin's configuration (e.g., `m/32'/141'/0'`). This discrepancy could lead to users being unable to recover their shielded funds, as they would be using the wrong derivation path. This commit corrects the issue by: 1. Adding a new optional `z_derivation_path` field to the `HdAddressInfo` struct in the RPC response. 2. Populating this new field with the correct shielded derivation path for ZHTLC coins. 3. Setting `z_derivation_path` to `None` for all other coin types to ensure the field only appears when relevant. This makes the exported key data accurate and reliable for wallet recovery.
* dev: (21 commits) feat(wallet-connect): impl BTC (UTxO) activation via WalletConnect (#2499) feat(utxo): add new fixed txfee option for DINGO-like coins (#2454) ci(pull-requests): review notification bot (#2468) improvement(walletconnect): return the `pairing_topic` in `new_connection` response (#2538) bless clippy (#2560) refactor(toolchain): use latest available stable compiler (#2557) feat(wallet): implement unified offline private key export API (#2542) improve note for docker test start failure (#2550) fix(DOCS): add note for macos to fix docker containers startup failure (#2544) refactor(toolchain): general stabilization for stable rust (#2528) fix(ci): adds nodejs 20 to ci-container (#2536) fix(WASM and Debian): fix build failures (#2534) improvement(event-streaming): impl DeriveStreamerId trait for all streamers (#2489) fix(eth): Propagate structured EIP-1559 fee errors (#2532) fix(eth): Correctly implement ETH max withdrawal logic (#2531) feat(use-clap-for-cli): use clap to parse CLI-Args #2215 (#2510) feat(orderbook): expirable maker orders (#2516) improvement(eth): drop parity support (#2527) chore(release): finalize changelog for v2.5.0-beta (#2524) chore(toolchain): upgrade toolchain to nightly 1.86.0 (#2444) ... # Conflicts: # mm2src/coins/lp_coins.rs # mm2src/coins/rpc_command/get_new_address.rs # mm2src/trezor/src/eth/eth_command.rs
* dev: feat(wallet-connect): impl BTC (UTxO) activation via WalletConnect (#2499) feat(utxo): add new fixed txfee option for DINGO-like coins (#2454) ci(pull-requests): review notification bot (#2468) improvement(walletconnect): return the `pairing_topic` in `new_connection` response (#2538) bless clippy (#2560) refactor(toolchain): use latest available stable compiler (#2557) feat(wallet): implement unified offline private key export API (#2542) improve note for docker test start failure (#2550) fix(DOCS): add note for macos to fix docker containers startup failure (#2544) refactor(toolchain): general stabilization for stable rust (#2528) fix(ci): adds nodejs 20 to ci-container (#2536) fix(WASM and Debian): fix build failures (#2534) improvement(event-streaming): impl DeriveStreamerId trait for all streamers (#2489) chore(release): v2.3.0-beta (#2284) # Conflicts: # mm2src/coins/eth.rs # mm2src/coins/eth/eth_swap_v2/eth_maker_swap_v2.rs # mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs # mm2src/coins/eth/eth_tests.rs # mm2src/coins/eth/eth_withdraw.rs # mm2src/coins/eth/v2_activation.rs # mm2src/coins/nft.rs # mm2src/coins/qrc20.rs # mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs # mm2src/mm2_main/src/rpc/lp_commands/one_inch/rpcs.rs # mm2src/mm2_main/src/rpc/lp_commands/tokens.rs # mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs
* dev: (24 commits) fix(ordermatch): ignore loop-back; clear on null root; reject stale keep-alives (#2580) fix(clippy): fix clippy warnings for #2565 (#2589) fix(Trezor): fix utxo and eth calls due to firmware changes (#2565) fix(utxo): calculate min_trading_vol based on fixed tx fees (#2564) feat(protocol): [0] solana support (#2586) fix(utxo): fix header deserialization; guard AuxPoW (#2583) chore(rust 1.89): make CI clippy/fmt pass (wasm32, all-targets) (#2581) fix(utxo): deserialize sapling root for PIVX block headers (#2572) improvement(dep-stack): security bumps (#2562) fix(utxo): correct block header deserialization for AuxPow and KAWPOW coins (#2563) feat(wallet-connect): impl BTC (UTxO) activation via WalletConnect (#2499) feat(utxo): add new fixed txfee option for DINGO-like coins (#2454) ci(pull-requests): review notification bot (#2468) improvement(walletconnect): return the `pairing_topic` in `new_connection` response (#2538) bless clippy (#2560) refactor(toolchain): use latest available stable compiler (#2557) feat(wallet): implement unified offline private key export API (#2542) improve note for docker test start failure (#2550) fix(DOCS): add note for macos to fix docker containers startup failure (#2544) refactor(toolchain): general stabilization for stable rust (#2528) ... # Conflicts: # mm2src/coins/eth.rs # mm2src/coins/eth/eth_swap_v2/eth_maker_swap_v2.rs # mm2src/coins/eth/eth_swap_v2/eth_taker_swap_v2.rs # mm2src/coins/eth/eth_tests.rs # mm2src/coins/eth/eth_withdraw.rs # mm2src/coins/eth/v2_activation.rs # mm2src/coins/nft.rs # mm2src/coins/qrc20.rs # mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs # mm2src/mm2_main/src/rpc/lp_commands/one_inch/rpcs.rs # mm2src/mm2_main/src/rpc/lp_commands/tokens.rs # mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs
* dev: improvement(`static mut`s): `static mut` removal (#2590) fix(orders): set subscription on kickstart and skip GC of own pubkeys (#2597) fix(ordermatch): ignore loop-back; clear on null root; reject stale keep-alives (#2580) fix(clippy): fix clippy warnings for #2565 (#2589) fix(Trezor): fix utxo and eth calls due to firmware changes (#2565) fix(utxo): calculate min_trading_vol based on fixed tx fees (#2564) feat(protocol): [0] solana support (#2586) fix(utxo): fix header deserialization; guard AuxPoW (#2583) chore(rust 1.89): make CI clippy/fmt pass (wasm32, all-targets) (#2581) fix(utxo): deserialize sapling root for PIVX block headers (#2572) improvement(dep-stack): security bumps (#2562) fix(utxo): correct block header deserialization for AuxPow and KAWPOW coins (#2563) feat(wallet-connect): impl BTC (UTxO) activation via WalletConnect (#2499) feat(utxo): add new fixed txfee option for DINGO-like coins (#2454) ci(pull-requests): review notification bot (#2468) improvement(walletconnect): return the `pairing_topic` in `new_connection` response (#2538) bless clippy (#2560) refactor(toolchain): use latest available stable compiler (#2557) feat(wallet): implement unified offline private key export API (#2542) chore(release): v2.3.0-beta (#2284) # Conflicts: # mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs

feat: implement unified offline private key export API
Summary
This PR implements a unified offline private key export API that allows exporting private keys for coins that are not currently activated/enabled in MM2. The implementation consolidates three export modes (Standard, HD, Iguana) into a single
get_private_keysRPC method and maintains backwards compatibility by updating the existingshow_priv_keymethod with offline fallback functionality.Key Changes:
get_private_keysRPC method supporting offline key export for inactive coinsshow_priv_keymethod with offline fallback when coins aren't activatedReview & Testing Checklist for Human
get_private_keysRPC method with all three modes (Standard, HD, Iguana) using different coin types (BTC, ETH, tokens) and verify exported private keys are cryptographically correctshow_priv_keymethod - ensure it still works normally for activated coins without any behavioral changesshow_priv_keynow works for non-activated coins by calling it with coins that aren't currently enabledRecommended Test Plan:
show_priv_keyworks as beforeshow_priv_keynow uses offline fallbackget_private_keyswith various coin configurations in all three modesDiagram
%%{ init : { "theme" : "default" }}%% graph TD Client[/"HTTP Client<br/>(curl/app)"/] --> Dispatcher["mm2_main/src/rpc/<br/>dispatcher/dispatcher.rs"]:::minor-edit Dispatcher -->|"get_private_keys"| OfflineKeys["coins/rpc_command/<br/>offline_keys.rs<br/>(NEW FILE)"]:::major-edit Dispatcher -->|"show_priv_key"| LpCoins["coins/lp_coins.rs<br/>show_priv_key()"]:::major-edit LpCoins -->|"fallback when<br/>coin not activated"| OfflineKeys LpCoins -->|"normal path when<br/>coin activated"| CoinInstance[/"Activated Coin<br/>Instance"/]:::context OfflineKeys --> CoinsConfig[/"coins.json<br/>Configuration"/]:::context OfflineKeys --> CryptoUtils[/"Key Derivation<br/>& Crypto Utils"/]:::context ModRs["coins/rpc_command/<br/>mod.rs"]:::minor-edit -->|"module<br/>registration"| OfflineKeys subgraph Legend L1[Major Edit]:::major-edit L2[Minor Edit]:::minor-edit L3[Context/No Edit]:::context end classDef major-edit fill:#90EE90 classDef minor-edit fill:#87CEEB classDef context fill:#FFFFFFNotes
show_priv_keymethod maintains existing behavior for activated coins while adding new offline capabilitySession Details: