Skip to content

[POC BDK] Introduce modular trait-based watch-only wallet architecture#950

Draft
moisesPompilio wants to merge 5 commits intogetfloresta:masterfrom
moisesPompilio:add-bdk
Draft

[POC BDK] Introduce modular trait-based watch-only wallet architecture#950
moisesPompilio wants to merge 5 commits intogetfloresta:masterfrom
moisesPompilio:add-bdk

Conversation

@moisesPompilio
Copy link
Copy Markdown
Collaborator

Description and Notes

This PR introduces a proof of concept for integrating BDK into Floresta by restructuring the watch-only wallet architecture into a more modular and specialized design.

The new architecture splits responsibilities across three main layers:

  • Service (Wallet): provides the high-level API and coordinates wallet operations
  • Repository: handles persistence and wallet data storage
  • Provider: handles descriptor-level wallet logic and blockchain-related information

It also adds shared models used for communication between layers, such as richer balance structures that expose more detailed information like trusted, used, and untrusted values.

All layers communicate through traits, which makes the architecture more flexible and extensible. This allows multiple implementations for the same layer, for example:

  • different repositories, such as SQLite or in-memory
  • different providers, such as a BDK-backed provider or a native Floresta provider
  • third-party implementations of custom providers or repositories

For testing, the idea is to build an integration-test architecture around the traits themselves. I already started this approach in the provider layer, and in the service layer I left some usage examples to illustrate the intended behavior. The goal is to validate that the expected behavior of the repository, provider, and service traits remains consistent regardless of the concrete implementation being used.

With this approach, implementation-specific code should not need to duplicate unit tests for the core behavior already defined by the traits. Instead, concrete implementations would only need tests for auxiliary or internal helper functions that are specific to that implementation, similar to what is already done in the provider layer. This helps ensure that, no matter which Floresta implementation is plugged in, the same rules and expectations apply across the board.

In addition, this PR includes a short documentation update in crates/floresta-watch-only/README.md to explain how the new design works.

Notes for reviewers:

  • This is a proof of concept aimed at making the watch-only architecture more modular and easier to extend.
  • The goal is to define stable trait-based contracts so that different implementations can follow the same expected behavior.
  • The service layer is responsible for orchestrating the lower layers, while the provider and repository focus on their own specialties.
  • this PR bumps Rust to 1.85.0, which is the minimum version supported by bdk

How to verify the changes you have done?

  • Verify the new wallet architecture and confirm that the service, repository, and provider layers are separated as intended.
  • Run the unit tests and make sure they pass successfully.
  • Review the unit test scenarios to confirm they cover the expected behavior of the implemented traits.
  • Run the integration tests, when available, to validate that the trait implementations.
  • Check that the provider tests cover descriptor management, transaction handling, balance calculations, and event processing as expected.

…anagement

The provider layer establishes a new abstraction for managing all descriptor-related
operations in the watch-only wallet. It centralizes responsibility for tracking wallet
state across descriptors, including address derivation, transaction indexing, and balance
calculations. The provider serves as the single source of truth for descriptor-scoped
information, isolating this logic from higher-level wallet coordination.

Key responsibilities:
- Manage descriptor persistence and retrieval
- Track generated and observed addresses per descriptor
- Index transactions associated with each descriptor
- Maintain UTXO sets and output tracking
- Calculate balances on a per-descriptor basis
- Process blockchain events (blocks, mempool) and emit descriptor-specific events

- Added `WalletProvider` trait defining the provider interface
- Introduced `WalletProviderEvent` enum for event-driven transaction notifications
- Defined `WalletProviderError` for comprehensive error handling
- Implemented feature-gated BDK provider backend via `bdk-provider` feature

test(provider): add comprehensive provider unit tests

Established test coverage for the provider interface, validating core descriptor and
transaction management operations.

Test scenarios:
- Descriptor lifecycle (persist, retrieve, list, deduplicate)
- Transaction indexing and querying by descriptor
- Balance calculations with confirmation requirements
- Address generation and management
- UTXO tracking and spend status filtering
- Mempool and block event processing
- Edge cases (empty wallets, nonexistent descriptors, duplicate operations)
- Script buffer management and local output tracking
…sistence

The repository layer establishes a higher-level persistence abstraction for the watch-only
wallet, centralizing all data storage operations. It manages wallet metadata, descriptor
configurations, transaction indexing, and script tracking—providing a clean interface
between the wallet service and the underlying database backend.

Core responsibilities:
- Persist wallet names and lifecycle management
- Store descriptors associated with each wallet with their metadata (active status, change flag, labels)
- Maintain descriptor configurations and derivation information
- Index and retrieve transactions for Electrum protocol support
- Track script buffers and derive addresses for transaction monitoring
- Provide auxiliary transaction data structures needed for Electrum responses

Implementation:
- Added SQLiteRepository backed by rusqlite with migration-based schema initialization
- Designed comprehensive WalletPersist trait defining the repository interface
- Implemented wallet CRUD operations supporting multi-wallet environments
- Created database schema with normalized tables for wallets, descriptors, transactions, and script buffers
- Established foreign key constraints for data integrity

Test coverage:
- Wallet creation, listing, and deletion operations
- Descriptor lifecycle (persist, retrieve, update, deduplication)
- Multi-wallet descriptor management and isolation
- Transaction indexing and querying
- Script buffer operations and state tracking
- Edge cases and error conditions

Dependencies:
- rusqlite: SQLite driver for Rust
- refinery: Database migration management
…n and lifecycle management

The wallet service establishes the coordination layer between the provider, repository, and
metadata layers, orchestrating wallet operations and managing the complete wallet lifecycle.
It serves as the primary interface for wallet clients (such as Electrum servers), translating
high-level operations into coordinated calls across the underlying layers while maintaining
consistent wallet state.

Architecture & Responsibilities:

*Provider Integration:*
- Queries descriptor-specific transaction data and balance information
- Retrieves address generation and UTXOs for each descriptor
- Processes blockchain events and emits descriptor-scoped notifications

*Repository Integration:*
- Persists wallet metadata (name, creation, deletion)
- Stores descriptor configurations with metadata (active flag, change flag, labels)
- Maintains transaction index for Electrum protocol responses
- Tracks script buffers and historical transaction data

*Metadata Management:*
- Maintains in-memory wallet state and descriptor registry
- Administers descriptor lifecycle (add, activate, deactivate, replace)
- Handles descriptor state transitions when adding new descriptors
- Enforces single active descriptor per category (external/change)

*Core Operations:*
- Wallet creation and loading from persistent storage
- Descriptor management with automatic deactivation of replaced descriptors
- Block and mempool transaction processing with event propagation
- Balance calculations aggregating across descriptors
- Transaction history and proof retrieval for Electrum clients
- Address generation delegated to active descriptors

Implementation:
- Implemented `Wallet` trait defining the complete service interface
- Multi-layered error handling with service-specific error types
- RwLock-based concurrency for thread-safe metadata access
- Deterministic descriptor ID generation via SHA256 hashing
- Add architecture overview with three-layer design explanation
- Include Mermaid class diagram showing trait relationships
- Add sequence diagram for block processing data flow
- Provide practical usage examples (wallet creation, descriptors, blocks, queries)
- Document feature flags (bdk-provider, sqlite) and combinations
- Detail error types, concurrency model, and development guidelines
- All content in English with clear code examples
@moisesPompilio
Copy link
Copy Markdown
Collaborator Author

In this case, what is in service.rs will basically replace everything from lib.rs, however I didn't put it there so the code can still compile in case someone wants to test something

@moisesPompilio moisesPompilio self-assigned this Apr 9, 2026
@moisesPompilio moisesPompilio added reliability Related to runtime reliability, stability and production readiness ecosystem support Enable interoperability, compatibility and practical integration with the broader Bitcoin ecosystem labels Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecosystem support Enable interoperability, compatibility and practical integration with the broader Bitcoin ecosystem reliability Related to runtime reliability, stability and production readiness

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant