Skip to content

refactor(key-wallet)!: remove MnemonicWithPassphrase wallet type#747

Merged
xdustinface merged 3 commits into
v0.42-devfrom
claude/objective-mcnulty-c48584
May 8, 2026
Merged

refactor(key-wallet)!: remove MnemonicWithPassphrase wallet type#747
xdustinface merged 3 commits into
v0.42-devfrom
claude/objective-mcnulty-c48584

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented May 8, 2026

Summary

  • Drops the WalletType::MnemonicWithPassphrase variant and the entire passphrase-as-callback feature it powered.
  • Removes all *_with_passphrase constructors/methods/trait fns on Wallet, ManagedAccountOperations, and the wallet helpers (derive_extended_private_key_with_passphrase, create_accounts_with_passphrase_from_options, needs_passphrase, root_extended_priv_key_with_callback, etc.).
  • Drops the passphrase parameter from WalletManager::create_wallet_from_mnemonic[_return_serialized_bytes] and from the matching key-wallet-ffi / dash-spv-ffi extern functions; updates every call site (44 files).

Rationale

There's no realistic use case for a passphrase-protected wallet that still keeps the mnemonic in memory. If a caller cares enough about the passphrase to want it, they almost certainly want the keys to live behind an external signer (hardware wallet / remote signer) — i.e. WalletType::ExternalSignable. The other in-memory wallet variants (Mnemonic, Seed, ExtendedPrivKey) are primarily there for tests and embedded systems where the threat model that motivates a BIP39 passphrase doesn't apply. Carrying a callback-driven passphrase variant added a lot of API surface (every add_account / derive_* / add_managed_* had a _with_passphrase twin) for a path no production caller should be on.

Breaking changes

  • Serialized wallets containing the removed variant can no longer be deserialized.
  • The passphrase parameter is gone from the public Rust APIs and from extern "C" FFI functions — callers must update their code.

Test plan

  • cargo build --workspace --all-features --tests
  • cargo test --workspace --all-features --lib (key-wallet: 477, key-wallet-manager: 40, key-wallet-ffi: 215, dash-spv: 412, dash-spv-ffi: 44 — all pass)
  • cargo clippy --workspace --all-features --all-targets -- -D warnings
  • cargo fmt

🤖 Generated with Claude Code

Drops the entire passphrase-as-callback feature: callers can no longer
construct a wallet that retains a mnemonic and applies a BIP39 passphrase
on-demand. Removed across the workspace:

- `WalletType::MnemonicWithPassphrase` variant + Zeroize arm
- `Wallet::from_mnemonic_with_passphrase` constructor
- `add_account_with_passphrase`, `add_bls_account_with_passphrase`,
  `add_eddsa_account_with_passphrase` on `Wallet`
- `derive_extended_private_key_with_passphrase`,
  `create_accounts_with_passphrase_from_options`,
  `create_special_purpose_accounts_with_passphrase`,
  `needs_passphrase`, and `root_extended_priv_key_with_callback` helpers
- `add_managed_*_with_passphrase` on `ManagedAccountOperations`
- `passphrase` parameter from `WalletManager::create_wallet_from_mnemonic`
  and `create_wallet_from_mnemonic_return_serialized_bytes`
- `passphrase` parameter from FFI `wallet_create_from_mnemonic*`,
  `wallet_manager_add_wallet_from_mnemonic*`,
  `wallet_manager_add_wallet_from_mnemonic_return_serialized_bytes`
- All passphrase-only tests (`test_wallet_with_passphrase`,
  `test_passphrase_edge_cases`, the `passphrase_test` module, FFI
  `test_passphrase_wallets.rs`, etc.)

Breaking change: serialized wallets containing the
`MnemonicWithPassphrase` variant can no longer be deserialized, and the
FFI `passphrase` parameter is gone — callers must update their code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes BIP39 passphrase parameter support from wallet creation across the FFI and Rust library layers. The passphrase parameter is eliminated from exported function signatures, conditional wallet-creation logic is consolidated to always use mnemonic-only initialization, and passphrase-specific test modules and methods are removed. The WalletType::MnemonicWithPassphrase enum variant is deleted along with related helper methods and trait implementations.

Changes

Remove BIP39 Passphrase Support from Wallet Creation

Layer / File(s) Summary
Core FFI Function Signatures
key-wallet-ffi/src/wallet.rs, key-wallet-ffi/src/wallet_manager.rs
wallet_create_from_mnemonic, wallet_create_from_mnemonic_with_options, and wallet_manager_add_wallet_from_mnemonic* exported C/FFI functions remove the passphrase parameter. Implementation uses unconditional Wallet::from_mnemonic instead of conditional passphrase branching.
Rust Library Method Signatures
key-wallet-manager/src/lib.rs
WalletManager::create_wallet_from_mnemonic and create_wallet_from_mnemonic_return_serialized_bytes remove the passphrase parameter; passphrase-based conditional logic is removed and wallet creation always uses Wallet::from_mnemonic.
WalletType Enum & Wallet Initialization
key-wallet/src/wallet/mod.rs, key-wallet/src/wallet/initialization.rs, key-wallet/src/wallet/root_extended_keys.rs
WalletType::MnemonicWithPassphrase enum variant is removed; Wallet::from_mnemonic_with_passphrase() constructor is deleted; root extended key derivation for passphrase wallets is refactored to derive from private keys instead of stored public keys.
Wallet Helper & Root Key Methods
key-wallet/src/wallet/helper.rs, key-wallet/src/wallet/root_extended_keys.rs
Public methods needs_passphrase(), derive_extended_private_key_with_passphrase(), and root_extended_priv_key_with_callback() are removed; has_mnemonic() now only returns true for WalletType::Mnemonic.
Wallet Account Addition Methods
key-wallet/src/wallet/accounts.rs
Public account addition methods add_account_with_passphrase(), add_bls_account_with_passphrase(), and add_eddsa_account_with_passphrase() are removed; remaining methods do not depend on passphrase.
Managed Account Operations
key-wallet/src/wallet/managed_wallet_info/managed_account_operations.rs, key-wallet/src/wallet/managed_wallet_info/managed_accounts.rs
Trait method declarations and implementations for passphrase-based managed account additions are removed; imports updated to gate ManagedAccountTrait by feature.
FFI & Rust Library Call Site Updates
dash-spv-ffi/src/bin/ffi_cli.rs, dash-spv/src/main.rs, dash-spv/tests/*, key-wallet-manager/src/test_helpers.rs, key-wallet-manager/examples/wallet_creation.rs
Wallet creation invocations are updated to remove the empty-string passphrase argument from function calls.
FFI Unit Test Updates
key-wallet-ffi/src/, key-wallet-ffi/tests/*, key-wallet-ffi/examples/check_transaction.c
Test call sites are updated to remove passphrase CString construction and remove the passphrase argument from FFI function invocations; empty passphrase logic in mnemonic seed derivation tests is retained.
Serialization Tests & Integration Updates
key-wallet-ffi/src/wallet_manager_serialization_tests.rs, key-wallet-manager/tests/integration_test.rs, key-wallet-manager/tests/test_serialized_wallets.rs
Wallet serialization FFI tests and Rust library integration tests remove passphrase arguments; test cases comparing wallet IDs with and without passphrases are removed.
Test Module & Case Removals
key-wallet-ffi/tests/test_passphrase_wallets.rs (deleted), key-wallet/src/wallet/passphrase_test.rs (deleted), key-wallet/src/tests/edge_case_tests.rs, key-wallet/src/tests/wallet_tests.rs, key-wallet/src/wallet_comprehensive_tests.rs
Entire test files and individual test cases verifying passphrase-dependent wallet behavior are removed; tests comparing wallet IDs derived with vs. without passphrases are deleted.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • xdustinface

Poem

🐰 No passphrase, just the mnemonic way,
Wallets simpler now, come what may!
FFI calls grow lean and light,
Test suites trimmed to what is right.
Dash-spv hops along with glee! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor(key-wallet)!: remove MnemonicWithPassphrase wallet type' accurately and specifically describes the primary objective of the changeset—removing the MnemonicWithPassphrase variant and all related passphrase-handling functionality.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/objective-mcnulty-c48584

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
key-wallet/src/wallet/helper.rs (1)

348-361: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Distinguish ExternalSignable from WatchOnly in the error path.

This branch now returns a "watch-only wallet" error for WalletType::ExternalSignable too. That makes the hardened derive_* helpers report the wrong reason for failure and no longer matches the docs below, which describe external-signable wallets as a separate unsupported case.

Suggested fix
-            WalletType::ExternalSignable | WalletType::WatchOnly => {
+            WalletType::WatchOnly => {
                 return Err(Error::InvalidParameter(
-                    "Cannot derive private keys from watch-only wallet".to_string(),
+                    "Cannot derive private keys from watch-only wallet".to_string(),
+                ));
+            }
+            WalletType::ExternalSignable => {
+                return Err(Error::InvalidParameter(
+                    "Cannot derive private keys from external-signable wallet without root key material"
+                        .to_string(),
                 ));
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet/src/wallet/helper.rs` around lines 348 - 361, The match arm that
currently groups WalletType::ExternalSignable with WatchOnly returns a
"watch-only wallet" error; change it to return a distinct error for
ExternalSignable so the derive_* helpers and docs are accurate. Specifically, in
the match on self.wallet_type (the branch creating `master`), replace the
WalletType::ExternalSignable | WalletType::WatchOnly arm with two separate arms:
one for WalletType::ExternalSignable returning
Err(Error::InvalidParameter("Cannot derive private keys from external-signable
wallet".to_string())), and one for WalletType::WatchOnly returning
Err(Error::InvalidParameter("Cannot derive private keys from watch-only
wallet".to_string())). Ensure you update the arms that reference
root_extended_private_key and keep other match arms unchanged.
key-wallet-ffi/tests/debug_wallet_add.rs (1)

18-32: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale debug print messages still reference the removed passphrase.

Lines 18 and 32 say "Adding wallet with passphrase 'pass1'" and "Successfully added wallet with passphrase" but there is no longer any passphrase involved. This misleads anyone reading the test output or the test source.

✏️ Proposed fix
-    println!("Adding wallet with passphrase 'pass1'");
+    println!("Adding wallet from mnemonic (no passphrase)");
     let success = unsafe {
         wallet_manager::wallet_manager_add_wallet_from_mnemonic(manager, mnemonic.as_ptr(), error)
     };
 
     if !success {
         ...
     } else {
-        println!("Successfully added wallet with passphrase");
+        println!("Successfully added wallet from mnemonic");
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet-ffi/tests/debug_wallet_add.rs` around lines 18 - 32, The debug
prints in the test still refer to a passphrase even though no passphrase is
used; update the println! calls around the wallet creation that mention "Adding
wallet with passphrase 'pass1'" and "Successfully added wallet with passphrase"
to use accurate messages (e.g., "Adding wallet from mnemonic" and "Successfully
added wallet from mnemonic") so the output matches the call to
wallet_manager::wallet_manager_add_wallet_from_mnemonic and related error
handling using the error pointer.
key-wallet-ffi/src/wallet_manager_tests.rs (1)

82-92: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove stale passphrase-bug comment and the misleading // No passphrase annotation.

Lines 83-84 explain that different mnemonics are used "instead of different passphrases" due to a library bug with passphrase wallets, referencing wallet_manager/mod.rs:140-146. Both the referenced bug and the feature itself no longer exist after this PR, so the comment now actively misleads readers.

The new // No passphrase annotation on line 91 compounds the confusion – it implies the caller is choosing not to supply a passphrase, when in fact the parameter has been removed entirely.

🛠️ Suggested cleanup
-        // Note: We use different mnemonics instead of different passphrases
-        // because the library has a bug with passphrase wallets (see line 140-146 in wallet_manager/mod.rs)
+        // Use distinct mnemonics so each wallet has a unique ID.
         let mnemonics = [TEST_MNEMONIC, TEST_MNEMONIC_2, TEST_MNEMONIC_3];
         unsafe {
             for (i, mnemonic_str) in mnemonics.iter().enumerate() {
                 let mnemonic = CString::new(*mnemonic_str).unwrap();

                 let success = wallet_manager::wallet_manager_add_wallet_from_mnemonic(
                     manager,
-                    mnemonic.as_ptr(), // No passphrase
+                    mnemonic.as_ptr(),
                     error,
                 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet-ffi/src/wallet_manager_tests.rs` around lines 82 - 92, Remove the
stale bug-note and the misleading inline annotation: delete the comment block
that says different mnemonics are used "instead of different passphrases" and
remove the trailing `// No passphrase` comment next to mnemonic.as_ptr(); simply
keep the mnemonics array and the call to
wallet_manager::wallet_manager_add_wallet_from_mnemonic(manager,
mnemonic.as_ptr(), error, ...) as-is since the passphrase parameter/bug no
longer exists. Ensure no other references in this test point to
wallet_manager/mod.rs lines 140-146.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@key-wallet-ffi/src/wallet_manager_tests.rs`:
- Around line 82-92: Remove the stale bug-note and the misleading inline
annotation: delete the comment block that says different mnemonics are used
"instead of different passphrases" and remove the trailing `// No passphrase`
comment next to mnemonic.as_ptr(); simply keep the mnemonics array and the call
to wallet_manager::wallet_manager_add_wallet_from_mnemonic(manager,
mnemonic.as_ptr(), error, ...) as-is since the passphrase parameter/bug no
longer exists. Ensure no other references in this test point to
wallet_manager/mod.rs lines 140-146.

In `@key-wallet-ffi/tests/debug_wallet_add.rs`:
- Around line 18-32: The debug prints in the test still refer to a passphrase
even though no passphrase is used; update the println! calls around the wallet
creation that mention "Adding wallet with passphrase 'pass1'" and "Successfully
added wallet with passphrase" to use accurate messages (e.g., "Adding wallet
from mnemonic" and "Successfully added wallet from mnemonic") so the output
matches the call to wallet_manager::wallet_manager_add_wallet_from_mnemonic and
related error handling using the error pointer.

In `@key-wallet/src/wallet/helper.rs`:
- Around line 348-361: The match arm that currently groups
WalletType::ExternalSignable with WatchOnly returns a "watch-only wallet" error;
change it to return a distinct error for ExternalSignable so the derive_*
helpers and docs are accurate. Specifically, in the match on self.wallet_type
(the branch creating `master`), replace the WalletType::ExternalSignable |
WalletType::WatchOnly arm with two separate arms: one for
WalletType::ExternalSignable returning Err(Error::InvalidParameter("Cannot
derive private keys from external-signable wallet".to_string())), and one for
WalletType::WatchOnly returning Err(Error::InvalidParameter("Cannot derive
private keys from watch-only wallet".to_string())). Ensure you update the arms
that reference root_extended_private_key and keep other match arms unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fc3550d9-6d47-480c-b04d-48f00a3730c4

📥 Commits

Reviewing files that changed from the base of the PR and between 6e48993 and 434460f.

📒 Files selected for processing (44)
  • dash-spv-ffi/src/bin/ffi_cli.rs
  • dash-spv-ffi/tests/dashd_sync/context.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • dash-spv/src/main.rs
  • dash-spv/tests/dashd_sync/setup.rs
  • dash-spv/tests/dashd_sync/tests_multi_wallet.rs
  • key-wallet-ffi/examples/check_transaction.c
  • key-wallet-ffi/src/account_collection.rs
  • key-wallet-ffi/src/account_derivation_tests.rs
  • key-wallet-ffi/src/account_tests.rs
  • key-wallet-ffi/src/address_pool.rs
  • key-wallet-ffi/src/keys_tests.rs
  • key-wallet-ffi/src/managed_account.rs
  • key-wallet-ffi/src/managed_wallet.rs
  • key-wallet-ffi/src/managed_wallet_tests.rs
  • key-wallet-ffi/src/mnemonic_tests.rs
  • key-wallet-ffi/src/wallet.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet-ffi/src/wallet_manager_serialization_tests.rs
  • key-wallet-ffi/src/wallet_manager_tests.rs
  • key-wallet-ffi/src/wallet_tests.rs
  • key-wallet-ffi/tests/debug_wallet_add.rs
  • key-wallet-ffi/tests/integration_test.rs
  • key-wallet-ffi/tests/test_account_collection.rs
  • key-wallet-ffi/tests/test_import_wallet.rs
  • key-wallet-ffi/tests/test_managed_account_collection.rs
  • key-wallet-ffi/tests/test_passphrase_wallets.rs
  • key-wallet-manager/examples/wallet_creation.rs
  • key-wallet-manager/src/lib.rs
  • key-wallet-manager/src/process_block.rs
  • key-wallet-manager/src/test_helpers.rs
  • key-wallet-manager/tests/integration_test.rs
  • key-wallet-manager/tests/test_serialized_wallets.rs
  • key-wallet/src/tests/edge_case_tests.rs
  • key-wallet/src/tests/wallet_tests.rs
  • key-wallet/src/wallet/accounts.rs
  • key-wallet/src/wallet/helper.rs
  • key-wallet/src/wallet/initialization.rs
  • key-wallet/src/wallet/managed_wallet_info/managed_account_operations.rs
  • key-wallet/src/wallet/managed_wallet_info/managed_accounts.rs
  • key-wallet/src/wallet/mod.rs
  • key-wallet/src/wallet/passphrase_test.rs
  • key-wallet/src/wallet/root_extended_keys.rs
  • key-wallet/src/wallet_comprehensive_tests.rs
💤 Files with no reviewable changes (23)
  • key-wallet-ffi/src/address_pool.rs
  • key-wallet/src/wallet_comprehensive_tests.rs
  • key-wallet-ffi/tests/test_import_wallet.rs
  • key-wallet-manager/tests/integration_test.rs
  • key-wallet/src/wallet/managed_wallet_info/managed_account_operations.rs
  • key-wallet-ffi/src/managed_account.rs
  • key-wallet-manager/examples/wallet_creation.rs
  • key-wallet/src/wallet/initialization.rs
  • key-wallet-ffi/tests/test_managed_account_collection.rs
  • key-wallet/src/tests/wallet_tests.rs
  • dash-spv-ffi/src/bin/ffi_cli.rs
  • key-wallet/src/wallet/root_extended_keys.rs
  • key-wallet-ffi/tests/test_account_collection.rs
  • key-wallet/src/tests/edge_case_tests.rs
  • key-wallet-ffi/tests/test_passphrase_wallets.rs
  • dash-spv/src/main.rs
  • key-wallet/src/wallet/accounts.rs
  • dash-spv-ffi/tests/test_wallet_manager.rs
  • key-wallet-ffi/src/account_collection.rs
  • key-wallet/src/wallet/mod.rs
  • key-wallet-manager/tests/test_serialized_wallets.rs
  • key-wallet-ffi/src/wallet_manager_serialization_tests.rs
  • key-wallet/src/wallet/passphrase_test.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 8, 2026
Auto-generated docs were stale after removing the `passphrase` parameter
from `wallet_create_from_mnemonic*` and `wallet_manager_add_wallet_from_mnemonic*`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Distinguish ExternalSignable from WatchOnly in
  derive_extended_private_key error path so callers see why each variant
  rejects the request.
- Drop the stale "passphrase 'pass1'" debug prints in
  debug_wallet_add.rs that no longer reflect what the test does.
- Remove the obsolete bug-comment and "// No passphrase" annotation in
  wallet_manager_tests.rs — the bug and the parameter both no longer
  exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.78%. Comparing base (49c8c6d) to head (aab0e0b).
⚠️ Report is 1 commits behind head on v0.42-dev.

Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #747      +/-   ##
=============================================
+ Coverage      71.31%   71.78%   +0.47%     
=============================================
  Files            321      320       -1     
  Lines          70339    69411     -928     
=============================================
- Hits           50160    49828     -332     
+ Misses         20179    19583     -596     
Flag Coverage Δ
core 76.30% <ø> (-0.39%) ⬇️
ffi 46.49% <100.00%> (+1.51%) ⬆️
rpc 20.00% <ø> (ø)
spv 87.44% <ø> (-0.07%) ⬇️
wallet 71.18% <100.00%> (+1.32%) ⬆️
Files with missing lines Coverage Δ
dash-spv-ffi/src/bin/ffi_cli.rs 0.00% <ø> (ø)
dash-spv/src/main.rs 0.00% <ø> (ø)
key-wallet-ffi/src/account_collection.rs 74.64% <ø> (+26.97%) ⬆️
key-wallet-ffi/src/address_pool.rs 35.66% <ø> (+1.39%) ⬆️
key-wallet-ffi/src/managed_account.rs 54.70% <ø> (-2.71%) ⬇️
key-wallet-ffi/src/managed_wallet.rs 68.38% <100.00%> (-6.02%) ⬇️
key-wallet-ffi/src/wallet.rs 21.26% <100.00%> (-3.52%) ⬇️
key-wallet-ffi/src/wallet_manager.rs 71.10% <100.00%> (-4.45%) ⬇️
key-wallet-manager/src/lib.rs 75.80% <100.00%> (+0.54%) ⬆️
key-wallet-manager/src/process_block.rs 92.48% <100.00%> (-0.08%) ⬇️
... and 7 more

... and 27 files with indirect coverage changes

@xdustinface xdustinface merged commit fcbab8c into v0.42-dev May 8, 2026
38 checks passed
@xdustinface xdustinface deleted the claude/objective-mcnulty-c48584 branch May 8, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants