Add Wallet Integration Tests and Fix Descriptor Persistence Issues#821
Add Wallet Integration Tests and Fix Descriptor Persistence Issues#821moisesPompilio wants to merge 11 commits intogetfloresta:masterfrom
Conversation
|
The first commit seems unrelated |
The first commit introduces |
jaoleal
left a comment
There was a problem hiding this comment.
Concept ACK.
Heres some initial review
| self.run_node(self.florestad) | ||
|
|
||
| self.log("Checking initial descriptors...") | ||
| descriptors = self.florestad.rpc.list_descriptors() |
There was a problem hiding this comment.
One thing to add, both listdescriptors.py and loaddescriptor.oy is fine but they are dependent on each other, if one fails they will both fail...
that will be a problem if breaking one raises a false alarm about the other.
There was a problem hiding this comment.
In integration tests it's common to have some interdependencies, such as between wallet_conf and wallet_flag. They also depend on listdescriptors, because that's the single method we use to check the descriptors Floresta has stored.
There was a problem hiding this comment.
Yeah, fine to depend on listdescriptors, but listdescriptors test dont need to depend on loaddescriptor you can instantiate and insert a descriptor from the Config.toml. It can help for this case.
There was a problem hiding this comment.
But in this case, if there is a problem with listdescriptors, the wallet_conf and wallet_flag tests will also fail, because they also depend on that RPC.
| /// Parses each descriptor, validates it, and derives the specified number of addresses | ||
| /// starting from the given index. | ||
| pub fn derive_addresses_from_list_descriptors( | ||
| descriptors: &[String], |
There was a problem hiding this comment.
There was a problem hiding this comment.
The descriptor is already a String, so using a &[&str] will make us waste resources converting it to &str. It's better to use it as a String directly; this method is used inside the wallet internals which already obtain the descriptor as a String.
There was a problem hiding this comment.
Theres no cost casting to a &str. but i understand thats how our codebase is written right now, fine.
IIUC theres no cost at all to cast Strings to &strs, but theres some when casting &str to String, basically the same relation between [B] and Vec<B>... Being picky with this may lead us to less runtime allocs.
The ideal is to declare functions that only read and compare strings with a generic S where S: AsRef<str>
There was a problem hiding this comment.
The problem here is that we can have multiple database implementations; so if we pass only pointers (&) to objects retrieved from the database it will be wrong, because the objects live shorter than the pointers. It's better to pass the object in this case, for example we pass a Vec<String> for descriptors, because if we passed Vec<&str> the &str would need to live longer than the Vec<String> that only exists inside the descriptor-retrieval function.
That's why when we receive a Vec<String> and it needs to be used by another function, that function takes a &[String] to avoid allocating memory by creating a new array of strings.
|
Needs rebase |
27e55ad to
c2bff7d
Compare
|
c2bff7d Performed a rebase and added some review requests. |
|
In ae433c1 there's still a |
c2bff7d to
5ce80a0
Compare
7b9921f to
7f9b79b
Compare
|
7f9b79b I did the rebase. |
| fn parse_xpubs(xpubs: &[String]) -> Result<Vec<Descriptor<DescriptorPublicKey>>, FlorestadError> { | ||
| fn parse_xpubs( | ||
| xpubs: &[String], | ||
| network: Network, |
There was a problem hiding this comment.
I think you dont need to take ownership to read enums
There was a problem hiding this comment.
I couldn't understand the comment here
There was a problem hiding this comment.
You can read the network enum by reference
There was a problem hiding this comment.
Copy types should never be passed by reference
e18de15 to
1c97cb5
Compare
1c97cb5 to
21eca0d
Compare
|
3c66053 Implemented the changes requested by @JoseSK999 |
Davidson-Souza
left a comment
There was a problem hiding this comment.
This PR centralizes so many things, we should call it REDACTED 🤣
3c66053 to
285993f
Compare
|
285993f Implemented the changes requested by @Davidson-Souza |
285993f to
16ecc70
Compare
| @@ -365,7 +368,7 @@ impl Florestad { | |||
| .map_err(FlorestadError::CouldNotInitializeWallet)?; | |||
There was a problem hiding this comment.
Cant these go inside Florestad::setup_wallet() ?
There was a problem hiding this comment.
I think it's better to keep it as it is
There was a problem hiding this comment.
I think its confusing the way it is.
It calls two times a method that supposed to make the wallet setup, since theyre separate is clear that theyre different. Also, the setup_wallet() doesnt need more to take the &mut wallet since it could be instantiated inside there.
I suggest for the code between lines 365 and 368 to be moved inside the setup_wallet method, turning it into a single entry for the node to load and instantiate the wallet instead of two
There was a problem hiding this comment.
Done! I took the opportunity and combined the load_wallet method with wallet_setup.
| let is_cached = wallet.is_cached(&descriptor)?; | ||
| for descriptor in self.get_descriptors() { | ||
| match wallet.push_descriptor(&descriptor) { | ||
| Ok(_) => info!("Added descriptor to wallet: {descriptor}"), |
There was a problem hiding this comment.
| Ok(_) => info!("Added descriptor to wallet: {descriptor}"), | |
| Ok(()) => info!("Added descriptor to wallet: {descriptor}"), |
There was a problem hiding this comment.
In this case you can't use it like that, because a Vec of addresses is returned.
| wallet.push_descriptor(&descriptor)?; | ||
| for xpub in self.get_xpubs() { | ||
| match wallet.push_xpub(&xpub, self.config.network) { | ||
| Ok(_) => info!("Added xpubs to wallet: {xpub}"), |
There was a problem hiding this comment.
| Ok(_) => info!("Added xpubs to wallet: {xpub}"), | |
| Ok(()) => info!("Added xpubs to wallet: {xpub}"), |
| use bitcoin::bip32::Xpub; | ||
|
|
||
| /// Magical version bytes for xpub: bitcoin mainnet public key for P2PKH or P2SH | ||
| const VERSION_MAGIC_XPUB: [u8; 4] = [0x04, 0x88, 0xB2, 0x1E]; |
There was a problem hiding this comment.
Space between these constants ?
| /// Extended private keys are unsupported | ||
| XprivUnsupported, | ||
|
|
||
| /// Extended public keys for multisig are unsupported |
There was a problem hiding this comment.
Forgot to delete this line ?
There was a problem hiding this comment.
No, this line is fine. The line below is just an explanation showing how a multisig should be imported.
There was a problem hiding this comment.
Can you add a dot by the end ? I think itll help
| let mut data = base58::decode_check(s)?; | ||
|
|
||
| let prefix: [u8; 4] = extract_slip132_prefix(s)?; | ||
| let slice = match prefix { |
There was a problem hiding this comment.
| let slice = match prefix { | |
| let prefix = match prefix { |
There was a problem hiding this comment.
"I changed it to bip44_prefix, because it's from this BIP that we use the version.
16ecc70 to
138c79c
Compare
|
I added just two suggestions more on ongoing comments, besides them this LGTM |
138c79c to
ba83c7d
Compare
| } | ||
|
|
||
| fn load_wallet(data_dir: &String) -> Result<AddressCache<KvDatabase>, FlorestadError> { | ||
| fn setup_wallet(&self, data_dir: &String) -> Result<AddressCache<KvDatabase>, FlorestadError> { |
There was a problem hiding this comment.
Nit: datadir can be accessed from self, no need for it to be a input
ba83c7d to
48bc23f
Compare
Created a `constants.py` file to centralize all constants used in the test framework. This change improves maintainability by providing a single location for managing constants and makes it easier to understand their purpose and usage across the tests.
Updated the documentation for the `wallet_descriptor` option to explicitly describe its behavior with descriptors. The previous text incorrectly referred to xpubs, which could cause confusion. Add a short help-note to the `wallet_descriptor` and `wallet_xpub` CLI flags: if `descriptors`/`xpubs` are added after IBD finishes or when IBD was performed with `assume-valid`, related transactions may not be discovered. In that case use the JSON-RPC `rescanblockchain` to find those transactions.
Previously, it was possible to save the same descriptor multiple times, leading to redundancy and potential inconsistencies. Adds a verification step in the `push_descriptor` method to ensure that a descriptor is only added if it does not already exist.
- Updated xpub parsing to reject xpriv and xpubs related to multisig, ensuring only standard public keys are accepted for descriptor generation. - Fixed an issue in descriptor generation by xpub where only `wpkh` descriptors were being created. Descriptors are now generated dynamically as `wpkh`, `pkh`, or `sh-wpkh`, depending on the xpub provided. - Added a network validation step to ensure the xpub matches the network the node is running on. - Added \_typos\.toml configuration to prevent https://github.com/crate-ci/typos from analyzing words with lengths between 32 and 150 characters, as these correspond to hashes, public keys, private keys, XPUBs, descriptors, and Bitcoin addresses.
- Added `wallet_conf.py` to test wallet loading via `config.toml`. This test verifies: - Descriptors and XPUBs are correctly loaded from the configuration file. - XPRIVs and descriptors with private keys are rejected. - Added `wallet_flag.py` to test wallet loading via command-line flags. This test performs the same checks as `wallet_conf.py`, but uses flags to pass wallet information during node initialization.
- Added tests to verify that the `loaddescriptor` RPC method can successfully load a descriptor. - Ensured that duplicate descriptors cannot be loaded. - Verified that descriptors containing private keys are rejected.
…thod - Added tests to verify that the `listdescriptors` RPC method correctly displays the descriptors loaded in the node. - Ensured that the method returns all loaded descriptors in the expected format.
…-only` module Improved code organization by consolidating descriptor handling in a single module - Moved `parse_descriptors`, `parse_xpubs`, and `slip132` to the `floresta-watch-only` module. - Centralized descriptor and XPUB parsing logic, previously scattered across `floresta-node` and `floresta-common`. - Removed descriptor-related features and the `miniscript` dependency from `floresta-common`.
… and refactor wallet handling
- Centralized address derivation in `floresta-watch-only`:
- Address derivation is now automatically performed when pushing a descriptor.
- Added `push_xpub` method to convert `XPUBs` into `descriptors`, push them,
and derive addresses for the wallet.
- Removed `walletInput` file from floresta-node:
- This file was no longer necessary as its functionality (`XPUB`, `descriptor`,
and address derivation) is now handled automatically by `floresta-watch-only`.
Prefer an explicit `config_file` when present, otherwise build `{data_dir}/config.toml`
inside `get_config_file`.
Combine setup_wallet and load_wallet operations into one unified method that handles both initialization and loading, eliminating redundant calls
48bc23f to
c9b6795
Compare
|
ACK c9b6795 |
Description and Notes
Adds comprehensive integration tests for wallet functionality and fixes several issues related to descriptor persistence and handling.
Key fixes:
Duplicate descriptor prevention: Added centralized validation to prevent the same descriptor from being stored multiple times in the database. The
push_descriptormethod now checks if a descriptor already exists before adding it.Improved xpub parsing and descriptor generation:
key improvements:
Centralized address derivation: Address derivation logic has been moved to
floresta-watch-only. When a descriptor is added, addresses are automatically derived and cached, simplifying wallet initialization and management.Consolidated descriptor parsing: Moved
parse_descriptors,parse_xpubs, and slip132 logic to thefloresta-watch-onlymodule, removing scattered code fromfloresta-nodeandfloresta-common. Removed the miniscript dependency fromfloresta-common.Documentation clarity: Updated wallet_descriptor option documentation to accurately describe descriptor behavior (previously incorrectly referred to xpubs).
Typos configuration: Added
_typos.tomlto prevent the typos checker from analyzing strings between 32-150 characters (hashes, keys, addresses, descriptors).New integration tests:
wallet_conf.py: Tests wallet loading viaconfig.toml, verifying correct descriptor/xpub loading and rejection of xprivs and private key descriptorswallet_flag.py: Tests wallet loading via command-line flags with the same validationsRPC method tests for
loaddescriptor(including duplicate detection and private key rejection) andlistdescriptorsConstants centralization: Created
constants.pyto centralize all constants used across integration tests, improving maintainability.How to verify the changes you have done?
wallet_conf.py,wallet_flag.py,loaddescriptor,listdescriptors) pass successfully.loaddescriptorRPC method.