satellite_shard/
lib.rs

1//! # Satellite Account Shards
2//!
3//! A high-performance library for managing sharded account state in Bitcoin-based blockchain programs.
4//! This crate provides ergonomic tools for working with distributed UTXO sets
5//! across multiple shards while maintaining atomicity and consistency.
6//!
7//! ## Overview
8//!
9//! This library is designed for applications that need to manage large amounts of Bitcoin UTXOs
10//! and Rune tokens across multiple shards for scalability. It provides:
11//!
12//! - **Type-safe shard management** with compile-time state tracking
13//! - **Efficient UTXO distribution** across shards
14//! - **Atomic transaction building** with proper fee calculation
15//! - **Rune token support** for managing fungible tokens on Bitcoin
16//! - **Zero-copy serialization** for high-performance applications
17//!
18//! ## Quick start
19//!
20//! 1. **Add the dependency** to your `Cargo.toml` (defaults enable `runes` and
21//!    `utxo-consolidation`):
22//!
23//! ```toml
24//! saturn-account-shards = "0.1"
25//! ```
26//!
27//! 2. **Create a `ShardSet`, select shards, call helpers**:
28//!
29//! ```rust,no_run
30//! use satellite_shard::{ShardSet, StateShard};
31//! # use satellite_bitcoin::utxo_info::{UtxoInfo, SingleRuneSet};
32//! # #[derive(Default, Clone)]
33//! # struct DummyShard;
34//! # impl StateShard<UtxoInfo<SingleRuneSet>, SingleRuneSet> for DummyShard {
35//! #   fn btc_utxos(&self) -> &[UtxoInfo<SingleRuneSet>] { &[] }
36//! #   fn btc_utxos_mut(&mut self) -> &mut [UtxoInfo<SingleRuneSet>] { &mut [] }
37//! #   fn btc_utxos_retain(&mut self, _: &mut dyn FnMut(&UtxoInfo<SingleRuneSet>) -> bool) {}
38//! #   fn add_btc_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) -> Option<usize> { None }
39//! #   fn btc_utxos_len(&self) -> usize { 0 }
40//! #   fn btc_utxos_max_len(&self) -> usize { 0 }
41//! #   fn rune_utxo(&self) -> Option<&UtxoInfo<SingleRuneSet>> { None }
42//! #   fn rune_utxo_mut(&mut self) -> Option<&mut UtxoInfo<SingleRuneSet>> { None }
43//! #   fn clear_rune_utxo(&mut self) {}
44//! #   fn set_rune_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) {}
45//! # }
46//! # let mut shards_storage = [DummyShard::default(), DummyShard::default()];
47//! # let mut loaders: Vec<&mut DummyShard> = shards_storage.iter_mut().collect();
48//!
49//! // Build an *unselected* set and then pick the shards we need.
50//! let shard_set = ShardSet::<SingleRuneSet, UtxoInfo<SingleRuneSet>, DummyShard, 2>::from_loaders(&mut loaders);
51//! let mut selected = shard_set.select_with([0, 1]).unwrap();
52//!
53//! // Call any of the high-level helpers, e.g. redistribute BTC back to the shards.
54//! // selected.redistribute_remaining_btc_to_shards(...);
55//! ```
56//!
57//! ## Features
58//!
59//! | Feature flag | Purpose | Default |
60//! |--------------|---------|---------|
61//! | `runes` | Support for the Bitcoin **Runes** protocol (adds Rune-specific helpers) | ✅ |
62//! | `utxo-consolidation` | Convenience helpers to combine many small UTXOs into fewer large ones | ✅ |
63//! | `serde` | Enable `serde::{Serialize, Deserialize}` implementations where possible | ❌ |
64//!
65//! All items that require a feature are tagged on docs.rs via
66//! **`cfg(feature = "…")`** in their rust-doc header.
67//!
68//! ## Glossary
69//!
70//! | Term | Meaning |
71//! |------|---------|
72//! | **Shard** | A single on-chain account that stores a portion of the global program state. |
73//! | **Pool** | A collection of shards that together form one logical program state. |
74//! | **UTXO** | *Unspent Transaction Output*: holds satoshis (and optionally Rune amounts) on the Bitcoin chain. |
75//! | **Rune** | A fungible token defined by the Runes protocol and transported inside a Bitcoin UTXO. |
76//!
77//! ## Key Components
78//!
79//! ### ShardSet
80//!
81//! The central abstraction is [`ShardSet`], which provides a type-safe wrapper around
82//! a collection of shards. It uses the typestate pattern to ensure proper usage:
83//!
84//! ```rust,ignore
85//! # use saturn_account_shards::{ShardSet, StateShard};
86//! # use satellite_bitcoin::utxo_info::{UtxoInfo, SingleRuneSet};
87//! # #[derive(Default, Clone)]
88//! # struct DummyShard;
89//! # impl StateShard<UtxoInfo<SingleRuneSet>, SingleRuneSet> for DummyShard {
90//! #     fn btc_utxos(&self) -> &[UtxoInfo<SingleRuneSet>] { &[] }
91//! #     fn btc_utxos_mut(&mut self) -> &mut [UtxoInfo<SingleRuneSet>] { &mut [] }
92//! #     fn btc_utxos_retain(&mut self, _: &mut dyn FnMut(&UtxoInfo<SingleRuneSet>) -> bool) {}
93//! #     fn add_btc_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) -> Option<usize> { None }
94//! #     fn btc_utxos_len(&self) -> usize { 0 }
95//! #     fn btc_utxos_max_len(&self) -> usize { 0 }
96//! #     fn rune_utxo(&self) -> Option<&UtxoInfo<SingleRuneSet>> { None }
97//! #     fn rune_utxo_mut(&mut self) -> Option<&mut UtxoInfo<SingleRuneSet>> { None }
98//! #     fn clear_rune_utxo(&mut self) {}
99//! #     fn set_rune_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) {}
100//! # }
101//! # let mut shard1 = DummyShard::default();
102//! # let mut shard2 = DummyShard::default();
103//! # let mut shards = [&mut shard1, &mut shard2];
104//! // Create an unselected shard set
105//! let shard_set = ShardSet::<SingleRuneSet, UtxoInfo<SingleRuneSet>, DummyShard, 2>::new(&mut shards);
106//!
107//! // Select shards to work with
108//! let selected = shard_set.select_with([0, 1]).unwrap();
109//!
110//! // Now high-level operations are available
111//! // selected.redistribute_remaining_btc_to_shards(...);
112//! ```
113//!
114//! ### StateShard Trait
115//!
116//! All shards must implement the [`StateShard`] trait, which provides a unified interface
117//! for managing both Bitcoin UTXOs and Rune tokens:
118//!
119//! ```rust
120//! # use saturn_account_shards::{StateShard};
121//! # use satellite_bitcoin::utxo_info::{UtxoInfo, SingleRuneSet};
122//! # use satellite_bitcoin::generic::fixed_set::FixedCapacitySet;
123//! #
124//! struct MyShard {
125//!     btc_utxos: Vec<UtxoInfo<SingleRuneSet>>,
126//!     rune_utxo: Option<UtxoInfo<SingleRuneSet>>,
127//! }
128//!
129//! impl StateShard<UtxoInfo<SingleRuneSet>, SingleRuneSet> for MyShard {
130//!     fn btc_utxos(&self) -> &[UtxoInfo<SingleRuneSet>] {
131//!         &self.btc_utxos
132//!     }
133//!
134//!     fn btc_utxos_mut(&mut self) -> &mut [UtxoInfo<SingleRuneSet>] {
135//!         &mut self.btc_utxos
136//!     }
137//!
138//!     // ... implement other required methods
139//! #     fn btc_utxos_retain(&mut self, _: &mut dyn FnMut(&UtxoInfo<SingleRuneSet>) -> bool) {}
140//! #     fn add_btc_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) -> Option<usize> { None }
141//! #     fn btc_utxos_len(&self) -> usize { 0 }
142//! #     fn btc_utxos_max_len(&self) -> usize { 0 }
143//! #     fn rune_utxo(&self) -> Option<&UtxoInfo<SingleRuneSet>> { None }
144//! #     fn rune_utxo_mut(&mut self) -> Option<&mut UtxoInfo<SingleRuneSet>> { None }
145//! #     fn clear_rune_utxo(&mut self) {}
146//! #     fn set_rune_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) {}
147//! # }
148//! ```
149//!
150//! ## Common Operations
151//!
152//! ### Selecting Shards
153//!
154//! ```rust
155//! # use satellite_shard::{ShardSet, StateShard};
156//! # use satellite_bitcoin::utxo_info::{UtxoInfo, SingleRuneSet};
157//! # #[derive(Default, Clone)]
158//! # struct DummyShard;
159//! # impl StateShard<UtxoInfo<SingleRuneSet>, SingleRuneSet> for DummyShard {
160//! #     fn btc_utxos(&self) -> &[UtxoInfo<SingleRuneSet>] { &[] }
161//! #     fn btc_utxos_mut(&mut self) -> &mut [UtxoInfo<SingleRuneSet>] { &mut [] }
162//! #     fn btc_utxos_retain(&mut self, _: &mut dyn FnMut(&UtxoInfo<SingleRuneSet>) -> bool) {}
163//! #     fn add_btc_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) -> Option<usize> { None }
164//! #     fn btc_utxos_len(&self) -> usize { 0 }
165//! #     fn btc_utxos_max_len(&self) -> usize { 0 }
166//! #     fn rune_utxo(&self) -> Option<&UtxoInfo<SingleRuneSet>> { None }
167//! #     fn rune_utxo_mut(&mut self) -> Option<&mut UtxoInfo<SingleRuneSet>> { None }
168//! #     fn clear_rune_utxo(&mut self) {}
169//! #     fn set_rune_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) {}
170//! # }
171//! # let mut shards: Vec<DummyShard> = vec![DummyShard::default(); 5];
172//! # let mut shard_refs: Vec<&mut DummyShard> = shards.iter_mut().collect();
173//! let shard_set = ShardSet::<SingleRuneSet, UtxoInfo<SingleRuneSet>, DummyShard, 5>::new(&mut shard_refs);
174//!
175//! // Select specific shards by index
176//! let selected = shard_set.select_with([0, 2, 4]).unwrap();
177//!
178//! // Select the shard with minimum BTC
179//! // let selected = shard_set.select_min_by(|s| s.total_btc()).unwrap();
180//!
181//! // Select shards that meet a condition
182//! // let selected = shard_set.select_multiple_by(|s| s.btc_utxos_len() > 0).unwrap();
183//! ```
184//!
185//! ### Redistributing Liquidity
186//!
187//! ```rust,no_run
188//! # use satellite_shard::{ShardSet, StateShard};
189//! # use satellite_bitcoin::{TransactionBuilder, fee_rate::FeeRate};
190//! # use satellite_bitcoin::utxo_info::{UtxoInfo, SingleRuneSet};
191//! # use std::str::FromStr;
192//! # use bitcoin::ScriptBuf;
193//! # #[derive(Default, Clone)]
194//! # struct DummyShard;
195//! # impl StateShard<UtxoInfo<SingleRuneSet>, SingleRuneSet> for DummyShard {
196//! #     fn btc_utxos(&self) -> &[UtxoInfo<SingleRuneSet>] { &[] }
197//! #     fn btc_utxos_mut(&mut self) -> &mut [UtxoInfo<SingleRuneSet>] { &mut [] }
198//! #     fn btc_utxos_retain(&mut self, _: &mut dyn FnMut(&UtxoInfo<SingleRuneSet>) -> bool) {}
199//! #     fn add_btc_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) -> Option<usize> { None }
200//! #     fn btc_utxos_len(&self) -> usize { 0 }
201//! #     fn btc_utxos_max_len(&self) -> usize { 0 }
202//! #     fn rune_utxo(&self) -> Option<&UtxoInfo<SingleRuneSet>> { None }
203//! #     fn rune_utxo_mut(&mut self) -> Option<&mut UtxoInfo<SingleRuneSet>> { None }
204//! #     fn clear_rune_utxo(&mut self) {}
205//! #     fn set_rune_utxo(&mut self, _: UtxoInfo<SingleRuneSet>) {}
206//! # }
207//! # let mut shards: Vec<DummyShard> = vec![DummyShard::default(); 3];
208//! # let mut shard_refs: Vec<&mut DummyShard> = shards.iter_mut().collect();
209//! # let shard_set = ShardSet::<SingleRuneSet, UtxoInfo<SingleRuneSet>, DummyShard, 3>::new(&mut shard_refs);
210//! # let mut selected = shard_set.select_with([0, 1, 2]).unwrap();
211//! # let mut tx_builder = TransactionBuilder::<10, 3, SingleRuneSet>::new();
212//! # let program_script = ScriptBuf::new();
213//! # let fee_rate = FeeRate::from_str("1.0").unwrap();
214//! # let removed_from_shards = 1000u64;
215//! // Redistribute remaining BTC evenly across selected shards
216//! let redistributed = selected.redistribute_remaining_btc_to_shards(
217//!     &mut tx_builder,
218//!     removed_from_shards,
219//!     program_script,
220//!     &fee_rate,
221//! ).unwrap();
222//! ```
223
224#[cfg(test)]
225mod tests;
226
227mod error;
228mod select;
229mod shard;
230mod shard_indices;
231mod split;
232mod update;
233
234pub use shard::{AccountUtxos, StateShard};
235pub use shard_indices::IntoShardIndices;
236
237pub use select::*;
238pub use split::*;
239pub use update::*;
240
241pub use split::DistributionError;