bpwallet/
wallet.rs

1// Modern, minimalistic & standard-compliant cold wallet library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2020-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2020-2024 LNP/BP Standards Association. All rights reserved.
9// Copyright (C) 2020-2024 Dr Maxim Orlovsky. All rights reserved.
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15//     http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22
23use std::cmp;
24use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
25use std::marker::PhantomData;
26use std::ops::{AddAssign, Deref};
27
28use bpstd::{
29    Address, AddressNetwork, DerivedAddr, Descriptor, Idx, IdxBase, Keychain, Network, NormalIndex,
30    Outpoint, Sats, ScriptPubkey, Txid, Vout,
31};
32use nonasync::persistence::{
33    CloneNoPersistence, Persistence, PersistenceError, PersistenceProvider, Persisting,
34};
35use psbt::{Psbt, PsbtConstructor, PsbtMeta, Utxo};
36
37use crate::{
38    BlockInfo, CoinRow, Indexer, Layer2, Layer2Cache, Layer2Data, Layer2Descriptor, Layer2Empty,
39    MayError, MiningInfo, NoLayer2, Party, TxCredit, TxDebit, TxRow, TxStatus, WalletAddr,
40    WalletTx, WalletUtxo,
41};
42
43#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
44#[display(doc_comments)]
45pub enum NonWalletItem {
46    /// transaction {0} is not known to the wallet.
47    NonWalletTx(Txid),
48    /// transaction {0} doesn't contains output number {1}.
49    NoOutput(Txid, Vout),
50    /// transaction output {0} doesn't belong to the wallet.
51    NonWalletUtxo(Outpoint),
52}
53
54pub struct AddrIter<'descr, K, D: Descriptor<K>> {
55    generator: &'descr D,
56    network: AddressNetwork,
57    keychain: Keychain,
58    index: NormalIndex,
59    remainder: VecDeque<DerivedAddr>,
60    _phantom: PhantomData<K>,
61}
62
63impl<K, D: Descriptor<K>> Iterator for AddrIter<'_, K, D> {
64    type Item = DerivedAddr;
65
66    fn next(&mut self) -> Option<Self::Item> {
67        loop {
68            if let Some(derived) = self.remainder.pop_front() {
69                return Some(derived);
70            }
71            self.remainder = self
72                .generator
73                .derive_address(self.network, self.keychain, self.index)
74                .map(|addr| DerivedAddr::new(addr, self.keychain, self.index))
75                .collect();
76            self.index.checked_inc_assign()?;
77        }
78    }
79}
80
81#[cfg_attr(
82    feature = "serde",
83    derive(serde::Serialize, serde::Deserialize),
84    serde(
85        crate = "serde_crate",
86        rename_all = "camelCase",
87        bound(
88            serialize = "D: serde::Serialize, L2: serde::Serialize",
89            deserialize = "D: serde::Deserialize<'de>, L2: serde::Deserialize<'de>"
90        )
91    )
92)]
93#[derive(Getters, Debug)]
94pub struct WalletDescr<K, D, L2 = Layer2Empty>
95where
96    D: Descriptor<K>,
97    L2: Layer2Descriptor,
98{
99    #[getter(skip)]
100    #[cfg_attr(feature = "serde", serde(skip))]
101    persistence: Option<Persistence<Self>>,
102
103    generator: D,
104    #[getter(as_copy)]
105    network: Network,
106    layer2: L2,
107    #[cfg_attr(feature = "serde", serde(skip))]
108    _phantom: PhantomData<K>,
109}
110
111impl<K, D: Descriptor<K>> WalletDescr<K, D, Layer2Empty> {
112    pub fn new_standard(descr: D, network: Network) -> Self {
113        WalletDescr {
114            persistence: None,
115            generator: descr,
116            network,
117            layer2: none!(),
118            _phantom: PhantomData,
119        }
120    }
121}
122
123impl<K, D: Descriptor<K>, L2: Layer2Descriptor> WalletDescr<K, D, L2> {
124    pub fn new_layer2(descr: D, layer2: L2, network: Network) -> Self {
125        WalletDescr {
126            persistence: None,
127            generator: descr,
128            network,
129            layer2,
130            _phantom: PhantomData,
131        }
132    }
133
134    pub fn addresses(&self, keychain: impl Into<Keychain>) -> AddrIter<K, D> {
135        AddrIter {
136            generator: &self.generator,
137            network: self.network.into(),
138            keychain: keychain.into(),
139            index: NormalIndex::ZERO,
140            remainder: VecDeque::new(),
141            _phantom: PhantomData,
142        }
143    }
144
145    pub fn with_descriptor<T, E>(
146        &mut self,
147        f: impl FnOnce(&mut D) -> Result<T, E>,
148    ) -> Result<T, E> {
149        let res = f(&mut self.generator)?;
150        self.mark_dirty();
151        Ok(res)
152    }
153}
154
155impl<K, D: Descriptor<K>, L2: Layer2Descriptor> Deref for WalletDescr<K, D, L2> {
156    type Target = D;
157
158    fn deref(&self) -> &Self::Target { &self.generator }
159}
160
161impl<K, D: Descriptor<K>, L2: Layer2Descriptor> CloneNoPersistence for WalletDescr<K, D, L2> {
162    fn clone_no_persistence(&self) -> Self {
163        Self {
164            persistence: None,
165            generator: self.generator.clone(),
166            network: self.network,
167            layer2: self.layer2.clone(),
168            _phantom: PhantomData,
169        }
170    }
171}
172
173impl<K, D: Descriptor<K>, L2: Layer2Descriptor> Persisting for WalletDescr<K, D, L2> {
174    #[inline]
175    fn persistence(&self) -> Option<&Persistence<Self>> { self.persistence.as_ref() }
176    #[inline]
177    fn persistence_mut(&mut self) -> Option<&mut Persistence<Self>> { self.persistence.as_mut() }
178    #[inline]
179    fn as_mut_persistence(&mut self) -> &mut Option<Persistence<Self>> { &mut self.persistence }
180}
181
182impl<K, D: Descriptor<K>, L2: Layer2Descriptor> Drop for WalletDescr<K, D, L2> {
183    fn drop(&mut self) {
184        if self.is_autosave() && self.is_dirty() {
185            if let Err(e) = self.store() {
186                #[cfg(feature = "log")]
187                log::error!("impossible to automatically-save wallet descriptor on Drop: {e}");
188                #[cfg(not(feature = "log"))]
189                eprintln!("impossible to automatically-save wallet descriptor on Drop: {e}")
190            }
191        }
192    }
193}
194
195#[derive(Debug)]
196#[cfg_attr(
197    feature = "serde",
198    derive(serde::Serialize, serde::Deserialize),
199    serde(
200        crate = "serde_crate",
201        rename_all = "camelCase",
202        bound(serialize = "L2: serde::Serialize", deserialize = "L2: serde::Deserialize<'de>")
203    )
204)]
205pub struct WalletData<L2: Layer2Data> {
206    #[cfg_attr(feature = "serde", serde(skip))]
207    persistence: Option<Persistence<Self>>,
208
209    /// This field is used by applications to link data with other wallet components
210    #[cfg_attr(feature = "serde", serde(skip))]
211    pub id: Option<String>,
212    pub name: String,
213    pub tx_annotations: BTreeMap<Txid, String>,
214    pub txout_annotations: BTreeMap<Outpoint, String>,
215    pub txin_annotations: BTreeMap<Outpoint, String>,
216    pub addr_annotations: BTreeMap<Address, String>,
217    pub last_used: BTreeMap<Keychain, NormalIndex>,
218    pub layer2: L2,
219}
220
221impl<L2: Layer2Data> CloneNoPersistence for WalletData<L2> {
222    fn clone_no_persistence(&self) -> Self {
223        Self {
224            persistence: None,
225            id: self.id.clone(),
226            name: self.name.clone(),
227            tx_annotations: self.tx_annotations.clone(),
228            txout_annotations: self.txout_annotations.clone(),
229            txin_annotations: self.txin_annotations.clone(),
230            addr_annotations: self.addr_annotations.clone(),
231            layer2: self.layer2.clone(),
232            last_used: self.last_used.clone(),
233        }
234    }
235}
236
237impl<L2: Layer2Data> Persisting for WalletData<L2> {
238    #[inline]
239    fn persistence(&self) -> Option<&Persistence<Self>> { self.persistence.as_ref() }
240    #[inline]
241    fn persistence_mut(&mut self) -> Option<&mut Persistence<Self>> { self.persistence.as_mut() }
242    #[inline]
243    fn as_mut_persistence(&mut self) -> &mut Option<Persistence<Self>> { &mut self.persistence }
244}
245
246impl WalletData<Layer2Empty> {
247    pub fn new_layer1() -> Self {
248        WalletData {
249            persistence: None,
250            id: None,
251            name: none!(),
252            tx_annotations: empty!(),
253            txout_annotations: empty!(),
254            txin_annotations: empty!(),
255            addr_annotations: empty!(),
256            layer2: none!(),
257            last_used: empty!(),
258        }
259    }
260}
261
262impl<L2: Layer2Data> WalletData<L2> {
263    pub fn new_layer2() -> Self
264    where L2: Default {
265        WalletData {
266            persistence: None,
267            id: None,
268            name: none!(),
269            tx_annotations: empty!(),
270            txout_annotations: empty!(),
271            txin_annotations: empty!(),
272            addr_annotations: empty!(),
273            layer2: none!(),
274            last_used: empty!(),
275        }
276    }
277}
278
279impl<L2: Layer2Data> Drop for WalletData<L2> {
280    fn drop(&mut self) {
281        if self.is_autosave() && self.is_dirty() {
282            if let Err(e) = self.store() {
283                #[cfg(feature = "log")]
284                log::error!("impossible to automatically-save wallet data on Drop: {e}");
285                #[cfg(not(feature = "log"))]
286                eprintln!("impossible to automatically-save wallet data on Drop: {e}")
287            }
288        }
289    }
290}
291
292#[cfg_attr(
293    feature = "serde",
294    derive(serde::Serialize, serde::Deserialize),
295    serde(
296        crate = "serde_crate",
297        rename_all = "camelCase",
298        bound(serialize = "L2: serde::Serialize", deserialize = "L2: serde::Deserialize<'de>")
299    )
300)]
301#[derive(Debug)]
302pub struct WalletCache<L2: Layer2Cache> {
303    #[cfg_attr(feature = "serde", serde(skip))]
304    persistence: Option<Persistence<Self>>,
305
306    /// This field is used by applications to link data with other wallet components
307    #[cfg_attr(feature = "serde", serde(skip))]
308    pub id: Option<String>,
309    pub last_block: MiningInfo,
310    pub last_change: NormalIndex,
311    pub headers: BTreeSet<BlockInfo>,
312    pub tx: BTreeMap<Txid, WalletTx>,
313    pub utxo: BTreeSet<Outpoint>,
314    pub addr: BTreeMap<Keychain, BTreeSet<WalletAddr>>,
315    pub layer2: L2,
316}
317
318impl<L2C: Layer2Cache> WalletCache<L2C> {
319    pub(crate) fn new_nonsync() -> Self {
320        WalletCache {
321            persistence: None,
322            id: None,
323            last_block: MiningInfo::genesis(),
324            last_change: NormalIndex::ZERO,
325            headers: none!(),
326            tx: none!(),
327            utxo: none!(),
328            addr: none!(),
329            layer2: none!(),
330        }
331    }
332
333    pub fn with<I: Indexer, K, D: Descriptor<K>, L2: Layer2<Cache = L2C>>(
334        descriptor: &WalletDescr<K, D, L2::Descr>,
335        indexer: &I,
336    ) -> MayError<Self, Vec<I::Error>> {
337        indexer.create::<K, D, L2>(descriptor)
338    }
339
340    pub fn update<I: Indexer, K, D: Descriptor<K>, L2: Layer2<Cache = L2C>>(
341        &mut self,
342        descriptor: &WalletDescr<K, D, L2::Descr>,
343        indexer: &I,
344    ) -> MayError<usize, Vec<I::Error>> {
345        let res = indexer.update::<K, D, L2>(descriptor, self);
346        self.mark_dirty();
347        res
348    }
349
350    pub fn register_psbt(&mut self, psbt: &Psbt, meta: &PsbtMeta) {
351        let unsigned_tx = psbt.to_unsigned_tx();
352        let txid = unsigned_tx.txid();
353        let wallet_tx = WalletTx {
354            txid,
355            status: TxStatus::Mempool,
356            inputs: psbt
357                .inputs()
358                .map(|input| {
359                    let addr = Address::with(&input.prev_txout().script_pubkey, meta.network).ok();
360                    TxCredit {
361                        outpoint: input.previous_outpoint,
362                        payer: match (self.utxo.get(&input.previous_outpoint), addr) {
363                            (Some(_), Some(addr)) => {
364                                let (keychain, index) = self
365                                    .addr
366                                    .iter()
367                                    .flat_map(|(keychain, addrs)| {
368                                        addrs.iter().map(|a| (*keychain, a))
369                                    })
370                                    .find(|(_, a)| a.addr == addr)
371                                    .map(|(keychain, a)| (keychain, a.terminal.index))
372                                    .expect("address cache inconsistency");
373                                Party::Wallet(DerivedAddr::new(addr, keychain, index))
374                            }
375                            (_, Some(addr)) => Party::Counterparty(addr),
376                            _ => Party::Unknown(input.prev_txout().script_pubkey.clone()),
377                        },
378                        sequence: unsigned_tx.inputs[input.index()].sequence,
379                        coinbase: false,
380                        script_sig: none!(),
381                        witness: none!(),
382                        value: input.value(),
383                    }
384                })
385                .collect(),
386            outputs: psbt
387                .outputs()
388                .map(|output| {
389                    let vout = Vout::from_u32(output.index() as u32);
390                    let addr = Address::with(&output.script, meta.network).ok();
391                    TxDebit {
392                        outpoint: Outpoint::new(txid, vout),
393                        beneficiary: match (meta.change, addr) {
394                            (Some(change), Some(addr)) if change.vout == vout => {
395                                Party::Wallet(DerivedAddr::new(
396                                    addr,
397                                    change.terminal.keychain,
398                                    change.terminal.index,
399                                ))
400                            }
401                            (_, Some(addr)) => Party::Counterparty(addr),
402                            (_, _) => Party::Unknown(output.script.clone()),
403                        },
404                        value: output.value(),
405                        spent: None,
406                    }
407                })
408                .collect(),
409            fee: meta.fee,
410            size: meta.size,
411            weight: meta.weight,
412            version: unsigned_tx.version,
413            locktime: unsigned_tx.lock_time,
414        };
415        self.tx.insert(txid, wallet_tx);
416        if let Some(change) = meta.change {
417            self.utxo.insert(Outpoint::new(txid, change.vout));
418        }
419    }
420
421    pub fn addresses_on(&self, keychain: Keychain) -> &BTreeSet<WalletAddr> {
422        self.addr.get(&keychain).unwrap_or_else(|| {
423            panic!("keychain #{keychain} is not supported by the wallet descriptor")
424        })
425    }
426
427    pub fn has_outpoint(&self, outpoint: Outpoint) -> bool {
428        let Some(tx) = self.tx.get(&outpoint.txid) else {
429            return false;
430        };
431        let Some(out) = tx.outputs.get(outpoint.vout.to_usize()) else {
432            return false;
433        };
434        matches!(out.beneficiary, Party::Wallet(_))
435    }
436
437    #[inline]
438    pub fn is_unspent(&self, outpoint: Outpoint) -> bool { self.utxo.contains(&outpoint) }
439
440    pub fn outpoint_by(
441        &self,
442        outpoint: Outpoint,
443    ) -> Result<(WalletUtxo, ScriptPubkey), NonWalletItem> {
444        let tx = self.tx.get(&outpoint.txid).ok_or(NonWalletItem::NonWalletTx(outpoint.txid))?;
445        let debit = tx
446            .outputs
447            .get(outpoint.vout.into_usize())
448            .ok_or(NonWalletItem::NoOutput(outpoint.txid, outpoint.vout))?;
449        let terminal = debit.derived_addr().ok_or(NonWalletItem::NonWalletUtxo(outpoint))?.terminal;
450        // TODO: Check whether TXO is spend
451        let utxo = WalletUtxo {
452            outpoint,
453            value: debit.value,
454            terminal,
455            status: tx.status,
456        };
457        let spk =
458            debit.beneficiary.script_pubkey().ok_or(NonWalletItem::NonWalletUtxo(outpoint))?;
459        Ok((utxo, spk))
460    }
461
462    // TODO: Rename WalletUtxo into WalletTxo and add `spent_by` optional field.
463    pub fn txos(&self) -> impl Iterator<Item = WalletUtxo> + '_ {
464        self.tx.iter().flat_map(|(txid, tx)| {
465            tx.outputs.iter().enumerate().filter_map(|(vout, out)| {
466                if let Party::Wallet(w) = out.beneficiary {
467                    Some(WalletUtxo {
468                        outpoint: Outpoint::new(*txid, vout as u32),
469                        value: out.value,
470                        terminal: w.terminal,
471                        status: tx.status,
472                    })
473                } else {
474                    None
475                }
476            })
477        })
478    }
479
480    pub fn utxos(&self) -> impl Iterator<Item = WalletUtxo> + '_ {
481        self.utxo.iter().map(|outpoint| {
482            let tx = self.tx.get(&outpoint.txid).expect("cache data inconsistency");
483            let debit = tx.outputs.get(outpoint.vout_usize()).expect("cache data inconsistency");
484            let terminal =
485                debit.derived_addr().expect("UTXO doesn't belong to the wallet").terminal;
486            // TODO: Check whether TXO is spend
487            WalletUtxo {
488                outpoint: *outpoint,
489                value: debit.value,
490                terminal,
491                status: tx.status,
492            }
493        })
494    }
495}
496
497impl<L2: Layer2Cache> CloneNoPersistence for WalletCache<L2> {
498    fn clone_no_persistence(&self) -> Self {
499        Self {
500            persistence: None,
501            id: self.id.clone(),
502            last_block: self.last_block,
503            last_change: self.last_change,
504            headers: self.headers.clone(),
505            tx: self.tx.clone(),
506            utxo: self.utxo.clone(),
507            addr: self.addr.clone(),
508            layer2: self.layer2.clone(),
509        }
510    }
511}
512
513impl<L2: Layer2Cache> Persisting for WalletCache<L2> {
514    #[inline]
515    fn persistence(&self) -> Option<&Persistence<Self>> { self.persistence.as_ref() }
516    #[inline]
517    fn persistence_mut(&mut self) -> Option<&mut Persistence<Self>> { self.persistence.as_mut() }
518    #[inline]
519    fn as_mut_persistence(&mut self) -> &mut Option<Persistence<Self>> { &mut self.persistence }
520}
521
522impl<L2: Layer2Cache> Drop for WalletCache<L2> {
523    fn drop(&mut self) {
524        if self.is_autosave() && self.is_dirty() {
525            if let Err(e) = self.store() {
526                #[cfg(feature = "log")]
527                log::error!("impossible to automatically-save wallet cache on Drop: {e}");
528                #[cfg(not(feature = "log"))]
529                eprintln!("impossible to automatically-save wallet cache on Drop: {e}")
530            }
531        }
532    }
533}
534
535#[derive(Debug)]
536pub struct Wallet<K, D: Descriptor<K>, L2: Layer2 = NoLayer2> {
537    descr: WalletDescr<K, D, L2::Descr>,
538    data: WalletData<L2::Data>,
539    cache: WalletCache<L2::Cache>,
540    layer2: L2,
541}
542
543impl<K, D: Descriptor<K>, L2: Layer2> Deref for Wallet<K, D, L2> {
544    type Target = WalletDescr<K, D, L2::Descr>;
545
546    fn deref(&self) -> &Self::Target { &self.descr }
547}
548
549impl<K, D: Descriptor<K>, L2: Layer2> CloneNoPersistence for Wallet<K, D, L2> {
550    fn clone_no_persistence(&self) -> Self {
551        Self {
552            descr: self.descr.clone_no_persistence(),
553            data: self.data.clone_no_persistence(),
554            cache: self.cache.clone_no_persistence(),
555            layer2: self.layer2.clone_no_persistence(),
556        }
557    }
558}
559
560impl<K, D: Descriptor<K>, L2: Layer2> PsbtConstructor for Wallet<K, D, L2> {
561    type Key = K;
562    type Descr = D;
563
564    fn descriptor(&self) -> &D { &self.descr.generator }
565
566    fn utxo(&self, outpoint: Outpoint) -> Option<(Utxo, ScriptPubkey)> {
567        self.cache.outpoint_by(outpoint).ok().map(|(utxo, spk)| (utxo.into_utxo(), spk))
568    }
569
570    fn network(&self) -> Network { self.descr.network }
571
572    fn next_derivation_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex {
573        let keychain = keychain.into();
574        let mut idx = self.last_published_derivation_index(keychain);
575        let last_index = self.data.last_used.entry(keychain).or_default();
576        idx = cmp::max(*last_index, idx);
577        if shift {
578            *last_index = idx.saturating_add(1u32);
579            self.data.mark_dirty();
580        }
581        idx
582    }
583
584    fn after_construct_psbt(&mut self, psbt: &Psbt, meta: &PsbtMeta) {
585        debug_assert_eq!(AddressNetwork::from(self.network), meta.network);
586        self.cache.register_psbt(psbt, meta);
587    }
588}
589
590impl<K, D: Descriptor<K>> Wallet<K, D> {
591    pub fn new_layer1(descr: D, network: Network) -> Self {
592        Wallet {
593            cache: WalletCache::new_nonsync(),
594            data: WalletData::new_layer1(),
595            descr: WalletDescr::new_standard(descr, network),
596            layer2: none!(),
597        }
598    }
599}
600
601impl<K, D: Descriptor<K>, L2: Layer2> Wallet<K, D, L2> {
602    pub fn new_layer2(descr: D, l2_descr: L2::Descr, layer2: L2, network: Network) -> Self {
603        Wallet {
604            cache: WalletCache::new_nonsync(),
605            data: WalletData::new_layer2(),
606            descr: WalletDescr::new_layer2(descr, l2_descr, network),
607            layer2,
608        }
609    }
610
611    pub fn set_name(&mut self, name: String) {
612        self.data.name = name;
613        self.data.mark_dirty();
614    }
615
616    pub fn with_descriptor<T, E>(
617        &mut self,
618        f: impl FnOnce(&mut D) -> Result<T, E>,
619    ) -> Result<T, E> {
620        self.descr.with_descriptor(f)
621    }
622
623    pub fn data_l2(&self) -> &L2::Data { &self.data.layer2 }
624    pub fn cache_l2(&self) -> &L2::Cache { &self.cache.layer2 }
625
626    pub fn with_data<T, E>(
627        &mut self,
628        f: impl FnOnce(&mut WalletData<L2::Data>) -> Result<T, E>,
629    ) -> Result<T, E> {
630        let res = f(&mut self.data)?;
631        self.data.mark_dirty();
632        Ok(res)
633    }
634
635    pub fn with_data_l2<T, E>(
636        &mut self,
637        f: impl FnOnce(&mut L2::Data) -> Result<T, E>,
638    ) -> Result<T, E> {
639        let res = f(&mut self.data.layer2)?;
640        self.data.mark_dirty();
641        Ok(res)
642    }
643
644    pub fn with_cache_l2<T, E>(
645        &mut self,
646        f: impl FnOnce(&mut L2::Cache) -> Result<T, E>,
647    ) -> Result<T, E> {
648        let res = f(&mut self.cache.layer2)?;
649        self.cache.mark_dirty();
650        Ok(res)
651    }
652
653    #[must_use]
654    pub fn update<I: Indexer>(&mut self, indexer: &I) -> MayError<(), Vec<I::Error>> {
655        self.cache.update::<I, K, D, L2>(&self.descr, indexer).map(|_| ())
656    }
657
658    pub fn to_deriver(&self) -> D
659    where
660        D: Clone,
661        K: Clone,
662    {
663        self.descr.clone()
664    }
665
666    fn last_published_derivation_index(&self, keychain: impl Into<Keychain>) -> NormalIndex {
667        let keychain = keychain.into();
668        self.address_coins()
669            .keys()
670            .filter(|ad| ad.terminal.keychain == keychain)
671            .map(|ad| ad.terminal.index)
672            .max()
673            .as_ref()
674            .map(NormalIndex::saturating_inc)
675            .unwrap_or_default()
676    }
677
678    pub fn last_derivation_index(&self, keychain: impl Into<Keychain>) -> NormalIndex {
679        let keychain = keychain.into();
680        let last_index = self.data.last_used.get(&keychain).copied().unwrap_or_default();
681        cmp::max(last_index, self.last_published_derivation_index(keychain))
682    }
683
684    pub fn next_address(&mut self, keychain: impl Into<Keychain>, shift: bool) -> Address {
685        let keychain = keychain.into();
686        let index = self.next_derivation_index(keychain, shift);
687        self.addresses(keychain)
688            .nth(index.index() as usize)
689            .expect("address iterator always can produce address")
690            .addr
691    }
692
693    pub fn balance(&self) -> Sats { self.cache.coins().map(|utxo| utxo.amount).sum::<Sats>() }
694
695    #[inline]
696    pub fn transactions(&self) -> &BTreeMap<Txid, WalletTx> { &self.cache.tx }
697
698    #[inline]
699    pub fn coins(&self) -> impl Iterator<Item = CoinRow<<L2::Cache as Layer2Cache>::Coin>> + '_ {
700        self.cache.coins()
701    }
702
703    pub fn address_coins(
704        &self,
705    ) -> HashMap<DerivedAddr, Vec<CoinRow<<L2::Cache as Layer2Cache>::Coin>>> {
706        let map = HashMap::new();
707        self.coins().fold(map, |mut acc, txo| {
708            acc.entry(txo.address).or_default().push(txo);
709            acc
710        })
711    }
712
713    pub fn address_balance(&self) -> impl Iterator<Item = WalletAddr> + '_ {
714        self.cache.addr.values().flat_map(|set| set.iter()).copied()
715    }
716
717    #[inline]
718    pub fn history(&self) -> impl Iterator<Item = TxRow<<L2::Cache as Layer2Cache>::Tx>> + '_ {
719        self.cache.history()
720    }
721
722    pub fn has_outpoint(&self, outpoint: Outpoint) -> bool { self.cache.has_outpoint(outpoint) }
723    pub fn is_unspent(&self, outpoint: Outpoint) -> bool { self.cache.is_unspent(outpoint) }
724
725    pub fn outpoint_by(
726        &self,
727        outpoint: Outpoint,
728    ) -> Result<(WalletUtxo, ScriptPubkey), NonWalletItem> {
729        self.cache.outpoint_by(outpoint)
730    }
731
732    pub fn txos(&self) -> impl Iterator<Item = WalletUtxo> + '_ { self.cache.txos() }
733    pub fn utxos(&self) -> impl Iterator<Item = WalletUtxo> + '_ { self.cache.utxos() }
734
735    pub fn coinselect<'a>(
736        &'a self,
737        up_to: Sats,
738        selector: impl Fn(&WalletUtxo) -> bool + 'a,
739    ) -> impl Iterator<Item = Outpoint> + 'a {
740        let mut selected = Sats::ZERO;
741        self.utxos()
742            .filter(selector)
743            .take_while(move |utxo| {
744                if selected <= up_to {
745                    selected.add_assign(utxo.value);
746                    true
747                } else {
748                    false
749                }
750            })
751            .map(|utxo| utxo.outpoint)
752    }
753}
754
755impl<K, D: Descriptor<K>, L2: Layer2> Wallet<K, D, L2> {
756    pub fn load<P>(provider: P, autosave: bool) -> Result<Wallet<K, D, L2>, PersistenceError>
757    where P: Clone
758            + PersistenceProvider<WalletDescr<K, D, L2::Descr>>
759            + PersistenceProvider<WalletData<L2::Data>>
760            + PersistenceProvider<WalletCache<L2::Cache>>
761            + PersistenceProvider<L2>
762            + 'static {
763        let descr = WalletDescr::<K, D, L2::Descr>::load(provider.clone(), autosave)?;
764        let data = WalletData::<L2::Data>::load(provider.clone(), autosave)?;
765        let cache = WalletCache::<L2::Cache>::load(provider.clone(), autosave)?;
766        let layer2 = L2::load(provider, autosave)?;
767
768        Ok(Wallet {
769            descr,
770            data,
771            cache,
772            layer2,
773        })
774    }
775
776    pub fn set_id(&mut self, id: &impl ToString) {
777        self.data.id = Some(id.to_string());
778        self.cache.id = Some(id.to_string());
779    }
780
781    pub fn make_persistent<P>(
782        &mut self,
783        provider: P,
784        autosave: bool,
785    ) -> Result<bool, PersistenceError>
786    where
787        P: Clone
788            + PersistenceProvider<WalletDescr<K, D, L2::Descr>>
789            + PersistenceProvider<WalletData<L2::Data>>
790            + PersistenceProvider<WalletCache<L2::Cache>>
791            + PersistenceProvider<L2>
792            + 'static,
793    {
794        let a = self.descr.make_persistent(provider.clone(), autosave)?;
795        let b = self.data.make_persistent(provider.clone(), autosave)?;
796        let c = self.cache.make_persistent(provider.clone(), autosave)?;
797        let d = self.layer2.make_persistent(provider, autosave)?;
798        Ok(a && b && c && d)
799    }
800
801    pub fn store(&mut self) -> Result<(), PersistenceError> {
802        // TODO: Revert on failure
803
804        self.descr.store()?;
805        self.data.store()?;
806        self.cache.store()?;
807        self.layer2.store()?;
808
809        Ok(())
810    }
811}