revaultd/commands/
mod.rs

1//! # Revault daemon command interface.
2//!
3//! This module regroups multiple commands to query or alter the state of the Revault daemon.
4//! All commands here assume an accessible and sane database. They will **panic** on a failure
5//! to query it.
6
7mod utils;
8pub use crate::{
9    bitcoind::{interface::WalletTransaction, BitcoindError},
10    communication::ServerStatus,
11    revaultd::{BlockchainTip, UserRole, VaultStatus},
12};
13use crate::{
14    communication::{
15        announce_spend_transaction, check_spend_transaction_size, coord_share_rev_signatures,
16        coordinator_status, cosigners_status, fetch_cosigs_signatures, share_unvault_signatures,
17        watchtowers_status, wts_share_rev_signatures, CommunicationError,
18    },
19    database::{
20        actions::{
21            db_delete_spend, db_insert_spend, db_mark_activating_vault,
22            db_mark_broadcastable_spend, db_mark_securing_vault, db_update_presigned_txs,
23            db_update_spend, db_update_vault_status,
24        },
25        interface::{
26            db_cancel_transaction_by_txid, db_emer_transaction, db_list_spends,
27            db_spend_transaction, db_tip, db_unvault_emer_transaction, db_unvault_transaction,
28            db_vault_by_deposit, db_vault_by_unvault_txid, db_vaults, db_vaults_from_spend,
29            db_vaults_min_status,
30        },
31        schema::DbTransaction,
32    },
33    threadmessages::BitcoindThread,
34    DaemonControl, VERSION,
35};
36use utils::{
37    deser_amount_from_sats, deser_from_str, finalized_emer_txs, gethistory, listvaults_from_db,
38    presigned_txs, ser_amount, ser_to_string, spend_entry, unvault_tx, vaults_from_deposits,
39};
40
41use revault_tx::{
42    bitcoin::{
43        consensus::encode, secp256k1, util::bip32, Address, Amount, Network, OutPoint,
44        PublicKey as BitcoinPubKey, Transaction as BitcoinTransaction, TxOut, Txid,
45    },
46    scripts::{CpfpDescriptor, DepositDescriptor, UnvaultDescriptor},
47    transactions::{
48        spend_tx_from_deposits, transaction_chain, transaction_chain_manager, CancelTransaction,
49        CpfpableTransaction, EmergencyTransaction, RevaultPresignedTransaction, RevaultTransaction,
50        SpendTransaction, UnvaultEmergencyTransaction, UnvaultTransaction,
51    },
52    txouts::{DepositTxOut, SpendTxOut},
53};
54
55use std::{collections::BTreeMap, fmt};
56
57use serde::{Deserialize, Serialize};
58
59/// An error raised when calling a command
60#[derive(Debug)]
61pub enum CommandError {
62    UnknownOutpoint(OutPoint),
63    /// (Got, Expected)
64    InvalidStatus(VaultStatus, VaultStatus),
65    InvalidStatusFor(VaultStatus, OutPoint),
66    // TODO: remove in favour of specific variants
67    InvalidParams(String),
68    UnknownCancel(Txid),
69    Communication(CommunicationError),
70    Bitcoind(BitcoindError),
71    Tx(revault_tx::Error),
72    /// (Required, Actual)
73    SpendFeerateTooLow(u64, u64),
74    SpendTooLarge,
75    SpendUnknownUnVault(Txid),
76    UnknownSpend(Txid),
77    SpendSpent(Txid),
78    /// (Got, Expected)
79    SpendNotEnoughSig(usize, usize),
80    SpendInvalidSig(Vec<u8>),
81    MissingCpfpKey,
82    ManagerOnly,
83    StakeholderOnly,
84    Race,
85}
86
87impl fmt::Display for CommandError {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        match self {
90            Self::UnknownOutpoint(op) => write!(f, "No vault at '{}'", op),
91            Self::InvalidStatus(got, expected) => {
92                write!(f, "Invalid vault status: '{}'. Need '{}'.", got, expected)
93            }
94            Self::InvalidStatusFor(status, outpoint) => write!(
95                f,
96                "Invalid vault status '{}' for deposit outpoint '{}'",
97                status, outpoint
98            ),
99            Self::InvalidParams(e) => write!(f, "{}", e),
100            Self::Communication(e) => write!(f, "Communication error: '{}'", e),
101            Self::Bitcoind(e) => write!(f, "Bitcoind error: '{}'", e),
102            Self::Tx(e) => write!(f, "Transaction related error: '{}'", e),
103            Self::SpendFeerateTooLow(req, actual) => write!(
104                f,
105                "Required feerate ('{}') is significantly higher than actual feerate ('{}')",
106                req, actual
107            ),
108            Self::SpendTooLarge => write!(
109                f,
110                "Spend transaction is too large, try spending less outpoints"
111            ),
112            Self::SpendUnknownUnVault(txid) => {
113                write!(f, "Spend transaction refers an unknown Unvault: '{}'", txid)
114            }
115            Self::UnknownSpend(txid) => {
116                write!(f, "Unknown Spend transaction '{}'", txid)
117            }
118            Self::SpendSpent(txid) => {
119                write!(f, "Spend '{}' refers to a spent vault", txid)
120            }
121            Self::MissingCpfpKey => {
122                write!(
123                    f,
124                    "Can't read the cpfp key. Make sure you have a file called \
125                     cpfp_secret containing the private key in your datadir and \
126                     restart the daemon."
127                )
128            }
129            Self::SpendNotEnoughSig(got, req) => {
130                write!(
131                    f,
132                    "Not enough signatures, needed: {}, current: {}",
133                    req, got
134                )
135            }
136            Self::SpendInvalidSig(sig) => {
137                write!(
138                    f,
139                    "Spend PSBT contains an invalid signature: '{}'",
140                    encode::serialize_hex(&sig)
141                )
142            }
143            Self::StakeholderOnly => {
144                write!(f, "This is a stakeholder command")
145            }
146            Self::ManagerOnly => {
147                write!(f, "This is a manager command")
148            }
149            Self::Race => write!(f, "Internal error due to a race. Please try again."),
150            Self::UnknownCancel(txid) => write!(f, "Unknown Cancel transaction: '{}'", txid),
151        }
152    }
153}
154
155impl std::error::Error for CommandError {}
156
157impl From<BitcoindError> for CommandError {
158    fn from(e: BitcoindError) -> Self {
159        Self::Bitcoind(e)
160    }
161}
162
163impl From<CommunicationError> for CommandError {
164    fn from(e: CommunicationError) -> Self {
165        Self::Communication(e)
166    }
167}
168
169impl From<revault_tx::Error> for CommandError {
170    fn from(e: revault_tx::Error) -> Self {
171        Self::Tx(e)
172    }
173}
174
175impl CommandError {
176    pub fn code(&self) -> ErrorCode {
177        match self {
178            CommandError::UnknownOutpoint(_) => ErrorCode::RESOURCE_NOT_FOUND_ERROR,
179            CommandError::InvalidStatus(..) => ErrorCode::INVALID_STATUS_ERROR,
180            CommandError::InvalidStatusFor(..) => ErrorCode::INVALID_STATUS_ERROR,
181            CommandError::InvalidParams(_) => ErrorCode::INVALID_PARAMS,
182            CommandError::Communication(e) => match e {
183                CommunicationError::Net(_) => ErrorCode::TRANSPORT_ERROR,
184                CommunicationError::WatchtowerNack(_, _) => ErrorCode::WT_SIG_NACK,
185                CommunicationError::SignatureStorage => ErrorCode::COORDINATOR_SIG_STORE_ERROR,
186                CommunicationError::SpendTxStorage => ErrorCode::COORDINATOR_SPEND_STORE_ERROR,
187                CommunicationError::CosigAlreadySigned => ErrorCode::COSIGNER_ALREADY_SIGN_ERROR,
188                CommunicationError::CosigInsanePsbt => ErrorCode::COSIGNER_INSANE_ERROR,
189            },
190            CommandError::Bitcoind(_) => ErrorCode::BITCOIND_ERROR,
191            CommandError::Tx(_) => ErrorCode::INTERNAL_ERROR,
192            CommandError::SpendFeerateTooLow(_, _) => ErrorCode::INVALID_PARAMS,
193            // TODO: some of these probably need specific error codes
194            CommandError::SpendTooLarge
195            | CommandError::SpendUnknownUnVault(_)
196            | CommandError::UnknownSpend(_)
197            | CommandError::SpendSpent(_)
198            | CommandError::SpendNotEnoughSig(_, _)
199            | CommandError::SpendInvalidSig(_)
200            | CommandError::MissingCpfpKey
201            | CommandError::UnknownCancel(_) => ErrorCode::INVALID_PARAMS,
202            CommandError::StakeholderOnly | CommandError::ManagerOnly => ErrorCode::INVALID_REQUEST,
203            CommandError::Race => ErrorCode::INTERNAL_ERROR,
204        }
205    }
206}
207
208#[allow(non_camel_case_types)]
209pub enum ErrorCode {
210    /// Invalid Params (identical to jsonrpc error code)
211    INVALID_PARAMS = -32602,
212    /// Invalid Request (identical to jsonrpc error code)
213    INVALID_REQUEST = -32600,
214    /// Internal error (identical to jsonrpc error code)
215    INTERNAL_ERROR = -32603,
216    /// An error internal to revault_net, generally a transport error
217    TRANSPORT_ERROR = 12000,
218    /// The watchtower refused our signatures
219    WT_SIG_NACK = 13_000,
220    /// The Coordinator told us they could not store our signature
221    COORDINATOR_SIG_STORE_ERROR = 13100,
222    /// The Coordinator told us they could not store our Spend transaction
223    COORDINATOR_SPEND_STORE_ERROR = 13101,
224    /// The Cosigning Server returned null to our request!
225    COSIGNER_ALREADY_SIGN_ERROR = 13201,
226    /// The Cosigning Server tried to fool us!
227    COSIGNER_INSANE_ERROR = 13202,
228    /// Bitcoind error
229    BITCOIND_ERROR = 14000,
230    /// Resource not found
231    RESOURCE_NOT_FOUND_ERROR = 15000,
232    /// Vault status was invalid
233    INVALID_STATUS_ERROR = 15001,
234}
235
236macro_rules! stakeholder_only {
237    ($revaultd:ident) => {
238        if !$revaultd.is_stakeholder() {
239            return Err(CommandError::StakeholderOnly);
240        }
241    };
242}
243
244macro_rules! manager_only {
245    ($revaultd:ident) => {
246        if !$revaultd.is_manager() {
247            return Err(CommandError::ManagerOnly);
248        }
249    };
250}
251
252impl DaemonControl {
253    /// Get information about the current state of the daemon
254    pub fn get_info(&self) -> GetInfoResult {
255        let revaultd = self.revaultd.read().unwrap();
256
257        // This means blockheight == 0 for IBD.
258        let BlockchainTip {
259            height: blockheight,
260            ..
261        } = db_tip(&revaultd.db_file()).expect("Database must not be dead");
262        let number_of_vaults = self
263            .list_vaults(None, None)
264            .iter()
265            .filter(|l| {
266                l.status != VaultStatus::Spent
267                    && l.status != VaultStatus::Canceled
268                    && l.status != VaultStatus::Unvaulted
269                    && l.status != VaultStatus::EmergencyVaulted
270            })
271            .count();
272
273        assert!(revaultd.is_stakeholder() || revaultd.is_manager());
274        let participant_type = if revaultd.is_manager() && revaultd.is_stakeholder() {
275            UserRole::StakeholderManager
276        } else if revaultd.is_manager() {
277            UserRole::Manager
278        } else {
279            UserRole::Stakeholder
280        };
281
282        GetInfoResult {
283            version: VERSION.to_string(),
284            network: revaultd.bitcoind_config.network,
285            blockheight: blockheight as i32,
286            sync: self.bitcoind_conn.sync_progress(),
287            vaults: number_of_vaults,
288            managers_threshold: revaultd.managers_threshold(),
289            descriptors: GetInfoDescriptors {
290                deposit: revaultd.deposit_descriptor.clone(),
291                unvault: revaultd.unvault_descriptor.clone(),
292                cpfp: revaultd.cpfp_descriptor.clone(),
293            },
294            participant_type,
295        }
296    }
297
298    /// List the current vaults, optionally filtered by status and/or deposit outpoints.
299    pub fn list_vaults(
300        &self,
301        statuses: Option<&[VaultStatus]>,
302        deposit_outpoints: Option<&[OutPoint]>,
303    ) -> Vec<ListVaultsEntry> {
304        let revaultd = self.revaultd.read().unwrap();
305        listvaults_from_db(&revaultd, statuses, deposit_outpoints)
306            .expect("Database must be available")
307    }
308
309    /// Get the deposit address at the lowest still unused derivation index
310    pub fn get_deposit_address(&self) -> Address {
311        self.revaultd.read().unwrap().deposit_address()
312    }
313
314    // Internal only, used for testing
315    pub(crate) fn get_deposit_address_at(&self, index: bip32::ChildNumber) -> Address {
316        self.revaultd.read().unwrap().vault_address(index)
317    }
318
319    /// Get the revocation transactions for the vault identified by this outpoint.
320    /// Returns None if there are no *confirmed* vault at this outpoint.
321    ///
322    /// ## Errors
323    /// - If called by a non-stakeholder
324    /// - If called for an unknown or unconfirmed vault
325    pub fn get_revocation_txs(
326        &self,
327        deposit_outpoint: OutPoint,
328    ) -> Result<RevocationTransactions, CommandError> {
329        let revaultd = self.revaultd.read().unwrap();
330        stakeholder_only!(revaultd);
331        let db_path = &revaultd.db_file();
332
333        // First, make sure the vault exists and is confirmed.
334        let vault = db_vault_by_deposit(db_path, &deposit_outpoint)
335            .expect("Database must be available")
336            .ok_or_else(|| CommandError::UnknownOutpoint(deposit_outpoint))?;
337        if matches!(vault.status, VaultStatus::Unconfirmed) {
338            return Err(CommandError::InvalidStatus(
339                vault.status,
340                VaultStatus::Funded,
341            ));
342        };
343
344        let emer_address = revaultd
345            .emergency_address
346            .clone()
347            .expect("Must be stakeholder");
348        let (_, cancel_batch, emergency_tx, emergency_unvault_tx) = transaction_chain(
349            deposit_outpoint,
350            vault.amount,
351            &revaultd.deposit_descriptor,
352            &revaultd.unvault_descriptor,
353            &revaultd.cpfp_descriptor,
354            vault.derivation_index,
355            emer_address,
356            &revaultd.secp_ctx,
357        )
358        .expect("We wouldn't have put a vault with an invalid chain in DB");
359
360        Ok(RevocationTransactions {
361            cancel_txs: cancel_batch.all_feerates(),
362            emergency_tx,
363            emergency_unvault_tx,
364        })
365    }
366
367    /// Set the signed revocation transactions for the vault at this outpoint.
368    ///
369    /// ## Errors
370    /// - If called for a non-stakeholder
371    /// - If called for an unknown or not 'funded' vault
372    /// - If given insane revocation txs PSBTs (without our signatures, with invalid sigs, ..)
373    pub fn set_revocation_txs(
374        &self,
375        deposit_outpoint: OutPoint,
376        revocation_txs: RevocationTransactions,
377    ) -> Result<(), CommandError> {
378        let revaultd = self.revaultd.read().unwrap();
379        stakeholder_only!(revaultd);
380        let db_path = revaultd.db_file();
381        let secp_ctx = &revaultd.secp_ctx;
382
383        // They may only send revocation transactions for confirmed and not-yet-presigned
384        // vaults.
385        let db_vault = db_vault_by_deposit(&db_path, &deposit_outpoint)
386            .expect("Database must be available")
387            .ok_or_else(|| CommandError::UnknownOutpoint(deposit_outpoint))?;
388        if !matches!(db_vault.status, VaultStatus::Funded) {
389            return Err(CommandError::InvalidStatus(
390                db_vault.status,
391                VaultStatus::Funded,
392            ));
393        };
394
395        // Sanity check they didn't send us garbaged PSBTs
396        let mut cancel_txs_sigs = Vec::with_capacity(revocation_txs.cancel_txs.len());
397        for cancel_tx in revocation_txs.cancel_txs.iter() {
398            let cancel_db_tx = db_cancel_transaction_by_txid(&db_path, &cancel_tx.txid())
399                .expect("The database must be available")
400                .ok_or_else(|| CommandError::UnknownCancel(cancel_tx.txid()))?;
401            let rpc_txid = cancel_tx.tx().wtxid();
402            let db_txid = cancel_db_tx.psbt.wtxid();
403            if rpc_txid != db_txid {
404                return Err(CommandError::InvalidParams(format!(
405                    "Invalid Cancel tx: db wtxid is '{}' but this PSBT's is '{}' ",
406                    db_txid, rpc_txid
407                )));
408            }
409            cancel_txs_sigs.push((cancel_db_tx, cancel_tx.signatures()));
410        }
411        let mut emer_db_tx = db_emer_transaction(&revaultd.db_file(), db_vault.id)
412            .expect("The database must be available")
413            .ok_or(CommandError::Race)?;
414        let rpc_txid = revocation_txs.emergency_tx.tx().wtxid();
415        let db_txid = emer_db_tx.psbt.wtxid();
416        if rpc_txid != db_txid {
417            return Err(CommandError::InvalidParams(format!(
418                "Invalid Emergency tx: db wtxid is '{}' but this PSBT's is '{}' ",
419                db_txid, rpc_txid
420            )));
421        }
422        let mut unvault_emer_db_tx = db_unvault_emer_transaction(&revaultd.db_file(), db_vault.id)
423            .expect("The database must be available")
424            .ok_or(CommandError::Race)?;
425        let rpc_txid = revocation_txs.emergency_unvault_tx.tx().wtxid();
426        let db_txid = unvault_emer_db_tx.psbt.wtxid();
427        if rpc_txid != db_txid {
428            return Err(CommandError::InvalidParams(format!(
429                "Invalid Unvault Emergency tx: db wtxid is '{}' but this PSBT's is '{}' ",
430                db_txid, rpc_txid
431            )));
432        }
433
434        // Alias some vars we'll reuse
435        let deriv_index = db_vault.derivation_index;
436        let emer_sigs = revocation_txs.emergency_tx.signatures();
437        let unvault_emer_sigs = revocation_txs.emergency_unvault_tx.signatures();
438
439        // They must have included *at least* a signature for our pubkey
440        let our_pubkey = revaultd
441            .our_stk_xpub_at(deriv_index)
442            .expect("We are a stakeholder, checked at the beginning of the call.");
443        for (_, sigs) in cancel_txs_sigs.iter() {
444            if !sigs.contains_key(&our_pubkey) {
445                return Err(CommandError::InvalidParams(format!(
446                    "No signature for ourselves ({}) in Cancel transaction",
447                    our_pubkey
448                )));
449            }
450        }
451        // We use the same public key across the transaction chain, that's pretty
452        // neat from an usability perspective.
453        if !emer_sigs.contains_key(&our_pubkey) {
454            return Err(CommandError::InvalidParams(
455                "No signature for ourselves in Emergency transaction".to_string(),
456            ));
457        }
458        if !unvault_emer_sigs.contains_key(&our_pubkey) {
459            return Err(CommandError::InvalidParams(
460                "No signature for ourselves in UnvaultEmergency transaction".to_string(),
461            ));
462        }
463
464        // There is no reason for them to include an unnecessary signature, so be strict.
465        let stk_keys = revaultd.stakeholders_xpubs_at(deriv_index);
466        for (_, sigs) in cancel_txs_sigs.iter() {
467            for (ref key, _) in sigs.iter() {
468                if !stk_keys.contains(key) {
469                    return Err(CommandError::InvalidParams(format!(
470                        "Unknown key in Cancel transaction signatures: {}",
471                        key
472                    )));
473                }
474            }
475        }
476        for (ref key, _) in emer_sigs.iter() {
477            if !stk_keys.contains(key) {
478                return Err(CommandError::InvalidParams(format!(
479                    "Unknown key in Emergency transaction signatures: {}",
480                    key
481                )));
482            }
483        }
484        for (ref key, _) in unvault_emer_sigs.iter() {
485            if !stk_keys.contains(key) {
486                return Err(CommandError::InvalidParams(format!(
487                    "Unknown key in UnvaultEmergency transaction signatures: {}",
488                    key
489                )));
490            }
491        }
492
493        // Add the signatures to the DB transactions.
494        for (ref mut cancel_db_tx, sigs) in cancel_txs_sigs.iter_mut() {
495            for (key, sig) in sigs.iter() {
496                if sig.is_empty() {
497                    return Err(CommandError::InvalidParams(format!(
498                        "Empty signature for key '{}' in Cancel PSBT",
499                        key
500                    )));
501                }
502                let sig = secp256k1::Signature::from_der(&sig[..sig.len() - 1]).map_err(|_| {
503                    CommandError::InvalidParams(format!("Non DER signature in Cancel PSBT"))
504                })?;
505                cancel_db_tx
506                    .psbt
507                    .add_signature(key.key, sig, secp_ctx)
508                    .map_err(|e| {
509                        CommandError::InvalidParams(format!(
510                            "Invalid signature '{}' in Cancel PSBT: '{}'",
511                            sig, e
512                        ))
513                    })?;
514            }
515        }
516        for (key, sig) in emer_sigs {
517            if sig.is_empty() {
518                return Err(CommandError::InvalidParams(format!(
519                    "Empty signature for key '{}' in Emergency PSBT",
520                    key
521                )));
522            }
523            let sig = secp256k1::Signature::from_der(&sig[..sig.len() - 1]).map_err(|_| {
524                CommandError::InvalidParams(format!("Non DER signature in Emergency PSBT"))
525            })?;
526            emer_db_tx
527                .psbt
528                .add_signature(key.key, sig, secp_ctx)
529                .map_err(|e| {
530                    CommandError::InvalidParams(format!(
531                        "Invalid signature '{}' in Emergency PSBT: '{}'",
532                        sig, e
533                    ))
534                })?;
535        }
536        for (key, sig) in unvault_emer_sigs {
537            if sig.is_empty() {
538                return Err(CommandError::InvalidParams(format!(
539                    "Empty signature for key '{}' in UnvaultEmergency PSBT",
540                    key
541                )));
542            }
543            let sig = secp256k1::Signature::from_der(&sig[..sig.len() - 1]).map_err(|_| {
544                CommandError::InvalidParams(format!("Non DER signature in UnvaultEmergency PSBT",))
545            })?;
546            unvault_emer_db_tx
547                .psbt
548                .add_signature(key.key, sig, secp_ctx)
549                .map_err(|e| {
550                    CommandError::InvalidParams(format!(
551                        "Invalid signature '{}' in UnvaultEmergency PSBT: '{}'",
552                        sig, e
553                    ))
554                })?;
555        }
556
557        // Then add them to the PSBTs in database. Take care to update the vault
558        // status if all signatures were given via the RPC.
559        let rev_txs: Vec<DbTransaction> = cancel_txs_sigs
560            .into_iter()
561            .map(|(tx, _)| tx)
562            .chain([emer_db_tx, unvault_emer_db_tx].iter().cloned())
563            .collect();
564        db_update_presigned_txs(&db_path, &db_vault, rev_txs.clone(), secp_ctx)
565            .expect("The database must be available");
566        db_mark_securing_vault(&db_path, db_vault.id).expect("The database must be available");
567
568        // Now, check whether this made all revocation transactions fully signed
569        let emer_tx = db_emer_transaction(&db_path, db_vault.id)
570            .expect("Database must be available")
571            .ok_or(CommandError::Race)?;
572        let (_, cancel_batch) = transaction_chain_manager(
573            db_vault.deposit_outpoint,
574            db_vault.amount,
575            &revaultd.deposit_descriptor,
576            &revaultd.unvault_descriptor,
577            &revaultd.cpfp_descriptor,
578            db_vault.derivation_index,
579            &revaultd.secp_ctx,
580        )
581        .expect("We wouldn't have put a vault with an invalid chain in DB");
582        let cancel_txs = cancel_batch
583            .feerates_map()
584            .into_iter()
585            .map(|(amount, cancel_tx)| {
586                db_cancel_transaction_by_txid(&db_path, &cancel_tx.txid())
587                    .expect("Database must be available")
588                    .ok_or(CommandError::Race)
589                    .map(|tx| (amount, tx))
590            })
591            .collect::<Result<BTreeMap<_, _>, CommandError>>()?;
592        let unemer_tx = db_unvault_emer_transaction(&db_path, db_vault.id)
593            .expect("Database must be available")
594            .ok_or(CommandError::Race)?;
595        let all_rev_fully_signed = emer_tx
596            .psbt
597            .unwrap_emer()
598            .is_finalizable(&revaultd.secp_ctx)
599            && cancel_txs.iter().all(|(_, cancel_tx)| {
600                cancel_tx
601                    .psbt
602                    .unwrap_cancel()
603                    .is_finalizable(&revaultd.secp_ctx)
604            })
605            && unemer_tx
606                .psbt
607                .unwrap_unvault_emer()
608                .is_finalizable(&revaultd.secp_ctx);
609
610        // If it did, share their signatures with our watchtowers
611        if all_rev_fully_signed {
612            if let Some(ref watchtowers) = revaultd.watchtowers {
613                wts_share_rev_signatures(
614                    &revaultd.noise_secret,
615                    &watchtowers,
616                    db_vault.deposit_outpoint,
617                    db_vault.derivation_index,
618                    &emer_tx,
619                    &cancel_txs,
620                    &unemer_tx,
621                )?;
622            }
623        }
624        db_update_vault_status(&db_path, &db_vault).expect("The database must be available");
625
626        // Share them with our felow stakeholders.
627        coord_share_rev_signatures(
628            revaultd.coordinator_host,
629            &revaultd.noise_secret,
630            &revaultd.coordinator_noisekey,
631            &rev_txs,
632        )?;
633
634        Ok(())
635    }
636
637    /// Get the unvault transaction for the vault identified by this outpoint.
638    /// Returns None if there are no *confirmed* vault at this outpoint.
639    ///
640    /// ## Errors
641    /// - If called for a non stakeholder
642    /// - If called for an unknown or not 'funded' vault
643    pub fn get_unvault_tx(
644        &self,
645        deposit_outpoint: OutPoint,
646    ) -> Result<UnvaultTransaction, CommandError> {
647        let revaultd = self.revaultd.read().unwrap();
648        stakeholder_only!(revaultd);
649        let db_path = &revaultd.db_file();
650        assert!(revaultd.is_stakeholder());
651
652        // We allow the call for Funded 'only' as unvaulttx would later fail if it's
653        // not 'secured'.
654        let vault = db_vault_by_deposit(db_path, &deposit_outpoint)
655            .expect("The database must be available")
656            .ok_or_else(|| CommandError::UnknownOutpoint(deposit_outpoint))?;
657        if matches!(vault.status, VaultStatus::Unconfirmed) {
658            return Err(CommandError::InvalidStatus(
659                vault.status,
660                VaultStatus::Funded,
661            ));
662        }
663
664        Ok(unvault_tx(&revaultd, &vault)
665            .expect("We wouldn't have a vault with an invalid Unvault in DB"))
666    }
667
668    /// Set the signed unvault transaction for the vault at this outpoint.
669    ///
670    /// ## Errors
671    /// - If called for a non-stakeholder
672    /// - If called for an unknown or not 'secured' vault
673    /// - If passed an insane Unvault transaction (no sig for ourselves, invalid sig, ..)
674    pub fn set_unvault_tx(
675        &self,
676        deposit_outpoint: OutPoint,
677        unvault_tx: UnvaultTransaction,
678    ) -> Result<(), CommandError> {
679        let revaultd = self.revaultd.read().unwrap();
680        stakeholder_only!(revaultd);
681        let db_path = revaultd.db_file();
682        let secp_ctx = &revaultd.secp_ctx;
683
684        // If they haven't got all the signatures for the revocation transactions, we'd
685        // better not send our unvault sig!
686        // If the vault is already active (or more) there is no point in spamming the
687        // coordinator.
688        let db_vault = db_vault_by_deposit(&db_path, &deposit_outpoint)
689            .expect("The database must be available")
690            .ok_or_else(|| CommandError::UnknownOutpoint(deposit_outpoint))?;
691        if !matches!(db_vault.status, VaultStatus::Secured) {
692            return Err(CommandError::InvalidStatus(
693                db_vault.status,
694                VaultStatus::Secured,
695            ));
696        }
697
698        // Sanity check they didn't send us a garbaged PSBT
699        let mut unvault_db_tx = db_unvault_transaction(&db_path, db_vault.id)
700            .expect("The database must be available")
701            .ok_or(CommandError::Race)?;
702        let rpc_txid = unvault_tx.tx().wtxid();
703        let db_txid = unvault_db_tx.psbt.wtxid();
704        if rpc_txid != db_txid {
705            return Err(CommandError::InvalidParams(format!(
706                "Invalid Unvault tx: db wtxid is '{}' but this PSBT's is '{}' ",
707                db_txid, rpc_txid
708            )));
709        }
710
711        let sigs = unvault_tx.signatures();
712        let stk_keys = revaultd.stakeholders_xpubs_at(db_vault.derivation_index);
713        let our_key = revaultd
714            .our_stk_xpub_at(db_vault.derivation_index)
715            .expect("We are a stakeholder, checked at the beginning.");
716        // They must have included *at least* a signature for our pubkey, and must not include an
717        // unnecessary signature.
718        if !sigs.contains_key(&our_key) {
719            return Err(CommandError::InvalidParams(format!(
720                "No signature for ourselves ({}) in Unvault transaction",
721                our_key
722            )));
723        }
724
725        for (key, sig) in sigs {
726            // There is no reason for them to include an unnecessary signature, so be strict.
727            if !stk_keys.contains(&key) {
728                return Err(CommandError::InvalidParams(format!(
729                    "Unknown key in Unvault transaction signatures: {}",
730                    key
731                )));
732            }
733
734            if sig.is_empty() {
735                return Err(CommandError::InvalidParams(format!(
736                    "Empty signature for key '{}' in Unvault PSBT",
737                    key
738                )));
739            }
740            let sig = secp256k1::Signature::from_der(&sig[..sig.len() - 1]).map_err(|_| {
741                CommandError::InvalidParams(format!("Non DER signature in Unvault PSBT"))
742            })?;
743
744            unvault_db_tx
745                .psbt
746                .add_signature(key.key, sig, secp_ctx)
747                .map_err(|e| {
748                    CommandError::InvalidParams(format!(
749                        "Invalid signature '{}' in Unvault PSBT: '{}'",
750                        sig, e
751                    ))
752                })?;
753        }
754
755        // Sanity checks passed. Store it then share it.
756        db_update_presigned_txs(&db_path, &db_vault, vec![unvault_db_tx.clone()], secp_ctx)
757            .expect("The database must be available");
758        db_mark_activating_vault(&db_path, db_vault.id).expect("The database must be available");
759        db_update_vault_status(&db_path, &db_vault).expect("The database must be available");
760        share_unvault_signatures(
761            revaultd.coordinator_host,
762            &revaultd.noise_secret,
763            &revaultd.coordinator_noisekey,
764            &unvault_db_tx,
765        )?;
766
767        Ok(())
768    }
769
770    /// List the presigned transactions for the vaults at these outpoints. If `outpoints` is empty,
771    /// list the presigned transactions for all vaults.
772    ///
773    /// # Errors
774    /// - If an outpoint does not refer to a known deposit, or if the status of the vault is
775    /// part of `invalid_statuses`.
776    pub fn list_presigned_txs(
777        &self,
778        outpoints: &[OutPoint],
779    ) -> Result<Vec<ListPresignedTxEntry>, CommandError> {
780        let revaultd = self.revaultd.read().unwrap();
781        let db_path = revaultd.db_file();
782        let db_vaults = if outpoints.is_empty() {
783            db_vaults_min_status(&db_path, VaultStatus::Funded).expect("Database must be available")
784        } else {
785            vaults_from_deposits(&db_path, &outpoints, &[VaultStatus::Unconfirmed])?
786        };
787
788        presigned_txs(&revaultd, db_vaults).ok_or(CommandError::Race)
789    }
790
791    /// List the onchain transactions for the vaults at these outpoints. If `outpoints` is empty, list
792    /// the onchain transactions for all vaults.
793    ///
794    /// # Errors
795    /// - If an outpoint does not refer to a known deposit, or if the status of the vault is
796    /// part of `invalid_statuses`.
797    pub fn list_onchain_txs(
798        &self,
799        outpoints: &[OutPoint],
800    ) -> Result<Vec<ListOnchainTxEntry>, CommandError> {
801        let revaultd = self.revaultd.read().unwrap();
802        let db_path = &revaultd.db_file();
803
804        let db_vaults = if outpoints.is_empty() {
805            db_vaults(&db_path).expect("Database must be available")
806        } else {
807            // We accept any status
808            vaults_from_deposits(&db_path, &outpoints, &[])?
809        };
810
811        let mut tx_list = Vec::with_capacity(db_vaults.len());
812        for db_vault in db_vaults {
813            let vault_outpoint = db_vault.deposit_outpoint;
814
815            // If the vault exist, there must always be a deposit transaction available.
816            let deposit = self
817                .bitcoind_conn
818                .wallet_tx(db_vault.deposit_outpoint.txid)?
819                .expect("Vault exists but not deposit tx?");
820
821            // For the other transactions, it depends on the status of the vault. For the sake of
822            // simplicity bitcoind will tell us (but we could have some optimisation eventually here,
823            // eg returning None early on Funded vaults).
824            let (unvault, cancel, emergency, unvault_emergency, spend) = match db_vault.status {
825                VaultStatus::Unvaulting | VaultStatus::Unvaulted => {
826                    let unvault_db_tx = db_unvault_transaction(db_path, db_vault.id)
827                        .expect("Database must be available")
828                        .ok_or(CommandError::Race)?;
829                    let unvault = self.bitcoind_conn.wallet_tx(unvault_db_tx.psbt.txid())?;
830                    (unvault, None, None, None, None)
831                }
832                VaultStatus::Spending | VaultStatus::Spent => {
833                    let unvault_db_tx = db_unvault_transaction(db_path, db_vault.id)
834                        .expect("Database must be available")
835                        .ok_or(CommandError::Race)?;
836                    let unvault = self.bitcoind_conn.wallet_tx(unvault_db_tx.psbt.txid())?;
837                    let spend = if let Some(spend_txid) = db_vault.final_txid {
838                        self.bitcoind_conn.wallet_tx(spend_txid)?
839                    } else {
840                        None
841                    };
842                    (unvault, None, None, None, spend)
843                }
844                VaultStatus::Canceling | VaultStatus::Canceled => {
845                    let unvault_db_tx = db_unvault_transaction(db_path, db_vault.id)
846                        .expect("Database must be available")
847                        .ok_or(CommandError::Race)?;
848                    let unvault = self.bitcoind_conn.wallet_tx(unvault_db_tx.psbt.txid())?;
849                    let cancel = if let Some(cancel_txid) = db_vault.final_txid {
850                        self.bitcoind_conn.wallet_tx(cancel_txid)?
851                    } else {
852                        None
853                    };
854                    (unvault, cancel, None, None, None)
855                }
856                VaultStatus::EmergencyVaulting | VaultStatus::EmergencyVaulted => {
857                    // Emergencies are only for stakeholders!
858                    if revaultd.is_stakeholder() {
859                        let emer_db_tx = db_emer_transaction(db_path, db_vault.id)
860                            .expect("Database must be available")
861                            .ok_or(CommandError::Race)?;
862                        let emergency = self.bitcoind_conn.wallet_tx(emer_db_tx.psbt.txid())?;
863                        (None, None, emergency, None, None)
864                    } else {
865                        (None, None, None, None, None)
866                    }
867                }
868                VaultStatus::UnvaultEmergencyVaulting | VaultStatus::UnvaultEmergencyVaulted => {
869                    let unvault_db_tx = db_unvault_transaction(db_path, db_vault.id)
870                        .expect("Database must be available")
871                        .ok_or(CommandError::Race)?;
872                    let unvault = self.bitcoind_conn.wallet_tx(unvault_db_tx.psbt.txid())?;
873
874                    // Emergencies are only for stakeholders!
875                    if revaultd.is_stakeholder() {
876                        let unemer_db_tx = db_emer_transaction(db_path, db_vault.id)
877                            .expect("Database must be available")
878                            .ok_or(CommandError::Race)?;
879                        let unvault_emergency =
880                            self.bitcoind_conn.wallet_tx(unemer_db_tx.psbt.txid())?;
881                        (unvault, None, None, unvault_emergency, None)
882                    } else {
883                        (unvault, None, None, None, None)
884                    }
885                }
886                // Other statuses do not have on chain transactions apart the deposit.
887                VaultStatus::Unconfirmed
888                | VaultStatus::Funded
889                | VaultStatus::Securing
890                | VaultStatus::Secured
891                | VaultStatus::Activating
892                | VaultStatus::Active => (None, None, None, None, None),
893            };
894
895            tx_list.push(ListOnchainTxEntry {
896                vault_outpoint,
897                deposit,
898                unvault,
899                cancel,
900                emergency,
901                unvault_emergency,
902                spend,
903            });
904        }
905
906        Ok(tx_list)
907    }
908
909    /// Create a Spend transaction for these deposit outpoints, paying to the specified addresses
910    /// at the given feerate (with a tolerance of 10% below it and any % above it if we can't create
911    /// a change output).
912    /// Mind we add a CPFP output, which must be taken into account by the feerate.
913    ///
914    /// # Errors
915    /// - If called for a non-manager
916    /// - If provided outpoints for unknown or not 'active' vaults
917    /// - If the Spend transaction creation fails (for instance due to too-high fees or dust outputs)
918    /// - If the created Spend transaction's feerate is more than 10% below the required feerate
919    /// - If the created Spend transaction is too large to be transmitted to the coordinator
920    pub fn get_spend_tx(
921        &self,
922        outpoints: &[OutPoint],
923        destinations: &BTreeMap<Address, u64>,
924        feerate_vb: u64,
925    ) -> Result<ListSpendEntry, CommandError> {
926        let revaultd = self.revaultd.read().unwrap();
927        manager_only!(revaultd);
928        let db_file = &revaultd.db_file();
929
930        // FIXME: have a feerate type to avoid that
931        assert!(feerate_vb > 0, "Spend feerate can't be null.");
932
933        // TODO: remove the txins vec, just use the spent_vaults one.
934        let mut spent_vaults = Vec::with_capacity(outpoints.len());
935        // Reconstruct the DepositTxin s from the outpoints and the vaults informations
936        let mut txins = Vec::with_capacity(outpoints.len());
937        // If we need a change output, use the highest derivation index of the vaults
938        // spent. This avoids leaking a new address needlessly while not introducing
939        // disrepancy between our indexes.
940        let mut change_index = bip32::ChildNumber::from(0);
941        for outpoint in outpoints {
942            let vault = db_vault_by_deposit(db_file, outpoint)
943                .expect("Database must be available")
944                .ok_or_else(|| CommandError::UnknownOutpoint(*outpoint))?;
945            if matches!(vault.status, VaultStatus::Active) {
946                if vault.derivation_index > change_index {
947                    change_index = vault.derivation_index;
948                }
949                txins.push((*outpoint, vault.amount, vault.derivation_index));
950                spent_vaults.push(vault);
951            } else {
952                return Err(CommandError::InvalidStatus(
953                    vault.status,
954                    VaultStatus::Active,
955                ));
956            }
957        }
958
959        let txos: Vec<SpendTxOut> = destinations
960            .iter()
961            .map(|(addr, value)| {
962                let script_pubkey = addr.script_pubkey();
963                SpendTxOut::new(TxOut {
964                    value: *value,
965                    script_pubkey,
966                })
967            })
968            .collect();
969
970        log::debug!(
971            "Creating a Spend transaction with deposit txins: '{:?}' and txos: '{:?}'",
972            &txins,
973            &txos
974        );
975
976        // This adds the CPFP output so create a dummy one to accurately compute the
977        // feerate.
978        let nochange_tx = spend_tx_from_deposits(
979            txins.clone(),
980            txos.clone(),
981            None, // No change :)
982            &revaultd.deposit_descriptor,
983            &revaultd.unvault_descriptor,
984            &revaultd.cpfp_descriptor,
985            revaultd.lock_time,
986            /* Deactivate insane feerate check */
987            false,
988            &revaultd.secp_ctx,
989        )
990        .map_err(|e| revault_tx::Error::from(e))?;
991
992        log::debug!(
993            "Spend tx without change: '{}'",
994            nochange_tx.as_psbt_string()
995        );
996
997        // If the feerate of the transaction would be much lower (< 90/100) than what they
998        // requested for, tell them.
999        let nochange_feerate_vb = nochange_tx
1000            .max_feerate()
1001            .checked_mul(4)
1002            .expect("bug in feerate computation");
1003        if nochange_feerate_vb * 10 < feerate_vb * 9 {
1004            return Err(CommandError::SpendFeerateTooLow(
1005                feerate_vb,
1006                nochange_feerate_vb,
1007            ));
1008        }
1009
1010        // Add a change output if it would not be dust according to our standard (500k sats
1011        // atm, see DEPOSIT_MIN_SATS).
1012        // 8 (amount) + 1 (len) + 1 (v0) + 1 (push) + 32 (witscript hash)
1013        const P2WSH_TXO_WEIGHT: u64 = 43 * 4;
1014        let with_change_weight = nochange_tx
1015            .max_weight()
1016            .checked_add(P2WSH_TXO_WEIGHT)
1017            .expect("weight computation bug");
1018        let cur_fees = nochange_tx.fees();
1019        let want_fees = with_change_weight
1020            // Mental gymnastic: sat/vbyte to sat/wu rounded up
1021            .checked_mul(feerate_vb + 3)
1022            .map(|vbyte| Amount::from_sat(vbyte.checked_div(4).unwrap()));
1023        let change_value = want_fees.map(|f| cur_fees.checked_sub(f)).flatten();
1024        log::debug!(
1025            "Weight with change: '{}'  --  Fees without change: '{}'  --  Wanted feerate: '{}'  \
1026                    --  Wanted fees: '{:?}'  --  Change value: '{:?}'",
1027            with_change_weight,
1028            cur_fees,
1029            feerate_vb,
1030            want_fees,
1031            change_value
1032        );
1033
1034        let change_txo = change_value.and_then(|change_value| {
1035            // The overhead incurred to the value of the CPFP output by the change output
1036            // See https://github.com/revault/practical-revault/blob/master/transactions.md#spend_tx
1037            let cpfp_overhead = Amount::from_sat(16 * P2WSH_TXO_WEIGHT);
1038            let min_deposit =
1039                Amount::from_sat(revault_tx::transactions::DEPOSIT_MIN_SATS) + cpfp_overhead;
1040            // TODO: allow such outputs post deposit split
1041            if change_value > min_deposit {
1042                let change_txo = DepositTxOut::new(
1043                    // arithmetic checked above
1044                    change_value - cpfp_overhead,
1045                    &revaultd
1046                        .deposit_descriptor
1047                        .derive(change_index, &revaultd.secp_ctx),
1048                );
1049                log::debug!("Adding a change txo: '{:?}'", change_txo);
1050                Some(change_txo)
1051            } else {
1052                None
1053            }
1054        });
1055
1056        // Now we can hand them the resulting transaction (sanity checked for insane fees).
1057        let tx_res = spend_tx_from_deposits(
1058            txins,
1059            txos,
1060            change_txo,
1061            &revaultd.deposit_descriptor,
1062            &revaultd.unvault_descriptor,
1063            &revaultd.cpfp_descriptor,
1064            revaultd.lock_time,
1065            true,
1066            &revaultd.secp_ctx,
1067        )
1068        .map_err(|e| revault_tx::Error::from(e))?;
1069
1070        if !check_spend_transaction_size(&revaultd, tx_res.clone()) {
1071            return Err(CommandError::SpendTooLarge);
1072        };
1073        log::debug!("Final Spend transaction: '{:?}'", tx_res);
1074
1075        Ok(spend_entry(
1076            &revaultd,
1077            tx_res,
1078            spent_vaults.iter(),
1079            ListSpendStatus::NonFinal,
1080        ))
1081    }
1082
1083    /// Store a new or update an existing Spend transaction in database.
1084    ///
1085    /// ## Errors
1086    /// - If called for a non-manager
1087    /// - If the given Spend transaction refers to an unknown Unvault txid
1088    /// - If the Spend refers to an Unvault of a vault that isn't 'active'
1089    pub fn update_spend_tx(&self, spend_tx: SpendTransaction) -> Result<(), CommandError> {
1090        let revaultd = self.revaultd.read().unwrap();
1091        manager_only!(revaultd);
1092        let db_path = revaultd.db_file();
1093        let spend_txid = spend_tx.tx().txid();
1094
1095        // Fetch the Unvault it spends from the DB
1096        let spend_inputs = &spend_tx.tx().input;
1097        let mut db_unvaults = Vec::with_capacity(spend_inputs.len());
1098        for txin in spend_inputs.iter() {
1099            let (db_vault, db_unvault) =
1100                db_vault_by_unvault_txid(&db_path, &txin.previous_output.txid)
1101                    .expect("Database must be available")
1102                    .ok_or_else(|| CommandError::SpendUnknownUnVault(txin.previous_output.txid))?;
1103
1104            if !matches!(db_vault.status, VaultStatus::Active) {
1105                return Err(CommandError::InvalidStatus(
1106                    db_vault.status,
1107                    VaultStatus::Active,
1108                ));
1109            }
1110
1111            db_unvaults.push(db_unvault);
1112        }
1113
1114        // The user has the ability to set priority to the transaction in
1115        // setspendtx, here we always set it to false.
1116        if db_spend_transaction(&db_path, &spend_txid)
1117            .expect("Database must be available")
1118            .is_some()
1119        {
1120            log::debug!("Updating Spend transaction '{}'", spend_txid);
1121            db_update_spend(&db_path, &spend_tx, false).expect("Database must be available");
1122        } else {
1123            log::debug!("Storing new Spend transaction '{}'", spend_txid);
1124            db_insert_spend(&db_path, &db_unvaults, &spend_tx).expect("Database must be available");
1125        }
1126
1127        Ok(())
1128    }
1129
1130    /// Delete a Spend transaction by txid.
1131    /// **Note**: this does nothing if no Spend with this txid exist.
1132    ///
1133    /// ## Errors
1134    /// - If called for a non-manager
1135    pub fn del_spend_tx(&self, spend_txid: &Txid) -> Result<(), CommandError> {
1136        let revaultd = self.revaultd.read().unwrap();
1137        manager_only!(revaultd);
1138        let db_path = revaultd.db_file();
1139        db_delete_spend(&db_path, spend_txid).expect("Database must be available");
1140        Ok(())
1141    }
1142
1143    /// List all Spend transaction, optionally curated by their status.
1144    ///
1145    /// ## Errors
1146    /// - If called for a non-manager
1147    ///  TODO: this command can greatly be improved by changing the status in database for an
1148    ///  integer, updating it when polling bitcoind and filtering it at the SQL level.
1149    pub fn list_spend_txs(
1150        &self,
1151        statuses: Option<&[ListSpendStatus]>,
1152    ) -> Result<Vec<ListSpendEntry>, CommandError> {
1153        let revaultd = self.revaultd.read().unwrap();
1154        manager_only!(revaultd);
1155        let db_path = revaultd.db_file();
1156
1157        let spend_tx_map = db_list_spends(&db_path).expect("Database must be available");
1158        let mut listspend_entries = Vec::with_capacity(spend_tx_map.len());
1159        for (_, (db_spend, _)) in spend_tx_map {
1160            let mut status = match db_spend.broadcasted {
1161                Some(true) => ListSpendStatus::Broadcasted,
1162                Some(false) => ListSpendStatus::Pending,
1163                None => ListSpendStatus::NonFinal,
1164            };
1165
1166            let spent_vaults = db_vaults_from_spend(&db_path, &db_spend.psbt.txid())
1167                .expect("Database must be available");
1168
1169            if let Some((_, vault)) = spent_vaults
1170                .iter()
1171                .find(|(_, v)| v.status == VaultStatus::Spent || v.status == VaultStatus::Canceled)
1172            {
1173                // If one the vault was canceled or one of spent by another transaction
1174                // then the spend tx cannot be used anymore.
1175                if vault.status == VaultStatus::Canceled
1176                    || vault.final_txid != Some(db_spend.psbt.txid())
1177                {
1178                    status = ListSpendStatus::Deprecated;
1179                } else {
1180                    status = ListSpendStatus::Confirmed;
1181                }
1182            };
1183
1184            // Filter by status
1185            if let Some(s) = &statuses {
1186                if !s.contains(&status) {
1187                    continue;
1188                }
1189            }
1190
1191            listspend_entries.push(spend_entry(
1192                &revaultd,
1193                db_spend.psbt,
1194                spent_vaults.values(),
1195                status,
1196            ));
1197        }
1198
1199        Ok(listspend_entries)
1200    }
1201
1202    /// Announce a Spend transaction to be used (after having optionally polled the cosigning servers),
1203    /// broadcast its corresponding Unvault transactions and register it for being broadcast it as soon
1204    /// as the timelock expires.
1205    /// If `priority` is set to `true`, we'll automatically try to feebump the Unvault and then the
1206    /// Spend transactions in the background if they don't confirm.
1207    ///
1208    /// ## Errors
1209    /// - If `priority` is set to `true` and we don't have access to a CPFP private key
1210    /// - If the txid doesn't refer to a known Spend (must be stored using `updatespendtx` first)
1211    /// - If the Spend PSBT doesn't contain enough signatures, or contain invalid ones
1212    /// - If the Spend is too large to be announced
1213    pub fn set_spend_tx(&self, spend_txid: &Txid, priority: bool) -> Result<(), CommandError> {
1214        let revaultd = self.revaultd.read().unwrap();
1215        manager_only!(revaultd);
1216        let db_path = revaultd.db_file();
1217
1218        if priority && revaultd.cpfp_key.is_none() {
1219            return Err(CommandError::MissingCpfpKey);
1220        }
1221
1222        // Get the referenced Spend and the vaults it spends from the DB
1223        let mut spend_tx = db_spend_transaction(&db_path, &spend_txid)
1224            .expect("Database must be available")
1225            .ok_or_else(|| CommandError::UnknownSpend(*spend_txid))?;
1226        let spent_vaults =
1227            db_vaults_from_spend(&db_path, &spend_txid).expect("Database must be available");
1228        let tx = &spend_tx.psbt.tx();
1229        if spent_vaults.len() < tx.input.len() {
1230            return Err(CommandError::SpendSpent(*spend_txid));
1231        }
1232
1233        // Sanity check the Spend transaction is actually valid before announcing
1234        // it. revault_tx already implements the signature checks so don't duplicate
1235        // the logic and re-add the signatures to the PSBT.
1236        let signatures: Vec<BTreeMap<BitcoinPubKey, Vec<u8>>> = spend_tx
1237            .psbt
1238            .psbt()
1239            .inputs
1240            .iter()
1241            .map(|i| i.partial_sigs.clone())
1242            .collect();
1243        let mans_thresh = revaultd.managers_threshold();
1244        for (i, sigmap) in signatures.iter().enumerate() {
1245            if sigmap.len() < mans_thresh {
1246                return Err(CommandError::SpendNotEnoughSig(sigmap.len(), mans_thresh));
1247            }
1248            for (pubkey, raw_sig) in sigmap {
1249                let sig = secp256k1::Signature::from_der(&raw_sig[..raw_sig.len() - 1])
1250                    .map_err(|_| CommandError::SpendInvalidSig(raw_sig.clone()))?;
1251                spend_tx
1252                    .psbt
1253                    .add_signature(i, pubkey.key, sig, &revaultd.secp_ctx)
1254                    .map_err(|_| CommandError::SpendInvalidSig(raw_sig.clone()))?
1255                    .expect("The signature was already there");
1256            }
1257        }
1258
1259        // FIXME: shouldn't `updatespendtx` make sure this doesn't happen??
1260        // Check that we can actually send the tx to the coordinator...
1261        if !check_spend_transaction_size(&revaultd, spend_tx.psbt.clone()) {
1262            return Err(CommandError::SpendTooLarge);
1263        };
1264
1265        // Now, if needed, we can ask all the cosigning servers for their
1266        // signatures
1267        let cosigs = revaultd.cosigs.as_ref().expect("We are manager");
1268        if !cosigs.is_empty() {
1269            log::debug!("Fetching signatures from Cosigning servers");
1270            fetch_cosigs_signatures(
1271                &revaultd.secp_ctx,
1272                &revaultd.noise_secret,
1273                &mut spend_tx.psbt,
1274                cosigs,
1275            )?;
1276        }
1277        let mut finalized_spend = spend_tx.psbt.clone();
1278        finalized_spend.finalize(&revaultd.secp_ctx)?;
1279
1280        // And then announce it to the Coordinator
1281        let deposit_outpoints: Vec<_> = spent_vaults
1282            .values()
1283            .map(|db_vault| db_vault.deposit_outpoint)
1284            .collect();
1285        announce_spend_transaction(
1286            revaultd.coordinator_host,
1287            &revaultd.noise_secret,
1288            &revaultd.coordinator_noisekey,
1289            finalized_spend,
1290            deposit_outpoints,
1291        )?;
1292        db_update_spend(&db_path, &spend_tx.psbt, priority).expect("Database must be available");
1293
1294        // Finally we can broadcast the Unvault(s) transaction(s) and store the Spend
1295        // transaction for later broadcast
1296        log::debug!(
1297            "Broadcasting Unvault transactions with ids '{:?}'",
1298            spent_vaults.keys()
1299        );
1300        let bitcoin_txs = spent_vaults
1301            .values()
1302            .into_iter()
1303            .map(|db_vault| {
1304                let mut unvault_tx = db_unvault_transaction(&db_path, db_vault.id)
1305                    .expect("Database must be available")
1306                    .ok_or(CommandError::Race)?
1307                    .psbt
1308                    .assert_unvault();
1309                unvault_tx.finalize(&revaultd.secp_ctx)?;
1310                Ok(unvault_tx.into_psbt().extract_tx())
1311            })
1312            .collect::<Result<Vec<BitcoinTransaction>, CommandError>>()?;
1313        self.bitcoind_conn.broadcast(bitcoin_txs)?;
1314        db_mark_broadcastable_spend(&db_path, spend_txid).expect("Database must be available");
1315
1316        Ok(())
1317    }
1318
1319    /// Broadcast a Cancel transaction for an unvaulted vault. Currently picks the lower feerate
1320    /// one.
1321    ///
1322    /// ## Errors
1323    /// - If the outpoint doesn't refer to an existing, unvaulted (or unvaulting) vault
1324    /// - If the transaction broadcast fails for some reason
1325    pub fn revault(&self, deposit_outpoint: OutPoint) -> Result<(), CommandError> {
1326        let revaultd = self.revaultd.read().unwrap();
1327        let db_path = revaultd.db_file();
1328
1329        // Checking that the vault is secured, otherwise we don't have the cancel
1330        // transactions
1331        let vault = db_vault_by_deposit(&db_path, &deposit_outpoint)
1332            .expect("Database must be accessible")
1333            .ok_or_else(|| CommandError::UnknownOutpoint(deposit_outpoint))?;
1334
1335        if !matches!(
1336            vault.status,
1337            VaultStatus::Unvaulting | VaultStatus::Unvaulted | VaultStatus::Spending
1338        ) {
1339            return Err(CommandError::InvalidStatus(
1340                vault.status,
1341                VaultStatus::Unvaulting,
1342            ));
1343        }
1344
1345        // Instead of querying all the Cancel txs and checking their feerate, we create the needed
1346        // one and quety the BD by txid. It's a roundabout way, but we should move to only have the
1347        // signatures and not the PSBTs in DB anyways.
1348        let (_, cancel_batch) = transaction_chain_manager(
1349            deposit_outpoint,
1350            vault.amount,
1351            &revaultd.deposit_descriptor,
1352            &revaultd.unvault_descriptor,
1353            &revaultd.cpfp_descriptor,
1354            vault.derivation_index,
1355            &revaultd.secp_ctx,
1356        )
1357        .expect("We wouldn't have put a vault with an invalid chain in DB");
1358        // TODO: feerate estimation from bitcoind instead of using the smallest one
1359        let mut cancel_tx =
1360            db_cancel_transaction_by_txid(&db_path, &cancel_batch.feerate_20().txid())
1361                .expect("Database must be available")
1362                .ok_or(CommandError::Race)?
1363                .psbt
1364                .assert_cancel();
1365
1366        cancel_tx.finalize(&revaultd.secp_ctx)?;
1367        let transaction = cancel_tx.into_psbt().extract_tx();
1368        log::debug!(
1369            "Broadcasting Cancel transactions with id '{:?}'",
1370            transaction.txid()
1371        );
1372        self.bitcoind_conn.broadcast(vec![transaction])?;
1373
1374        Ok(())
1375    }
1376
1377    /// Broadcast Emergency transactions for all existing vaults.
1378    ///
1379    /// ## Errors
1380    /// - If called for a non-stakeholder
1381    pub fn emergency(&self) -> Result<(), CommandError> {
1382        let revaultd = self.revaultd.read().unwrap();
1383        stakeholder_only!(revaultd);
1384
1385        // FIXME: there is a ton of edge cases not covered here. We should additionally opt for a
1386        // bulk method, like broadcasting all Emergency transactions in a thread forever without
1387        // trying to be smart by differentiating between Emer and UnvaultEmer until we die or all
1388        // vaults are confirmed in the EDV.
1389        let emers = finalized_emer_txs(&revaultd)?;
1390        self.bitcoind_conn.broadcast(emers)?;
1391
1392        Ok(())
1393    }
1394
1395    /// Get information about all the configured servers.
1396    pub fn get_servers_statuses(&self) -> ServersStatuses {
1397        let revaultd = self.revaultd.read().unwrap();
1398        let coordinator = coordinator_status(&revaultd);
1399        let cosigners = cosigners_status(&revaultd);
1400        let watchtowers = watchtowers_status(&revaultd);
1401
1402        ServersStatuses {
1403            coordinator,
1404            cosigners,
1405            watchtowers,
1406        }
1407    }
1408
1409    /// Get a paginated list of accounting events. This returns a maximum of `limit` events occuring
1410    /// between the dates `start` and `end`, filtered by kind of events.
1411    /// Aiming to give an accounting point of view, the amounts returned by this call are the total
1412    /// of inflows and outflows net of any change amount (that is technically a transaction output, but
1413    /// not a cash outflow).
1414    pub fn get_history(
1415        &self,
1416        start: u32,
1417        end: u32,
1418        limit: u64,
1419        kind: &[HistoryEventKind],
1420    ) -> Result<Vec<HistoryEvent>, CommandError> {
1421        let revaultd = self.revaultd.read().unwrap();
1422        gethistory(&revaultd, &self.bitcoind_conn, start, end, limit, kind)
1423    }
1424}
1425
1426/// Descriptors the daemon was configured with
1427#[derive(Debug, Clone, Serialize, Deserialize)]
1428pub struct GetInfoDescriptors {
1429    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_from_str")]
1430    pub deposit: DepositDescriptor,
1431    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_from_str")]
1432    pub unvault: UnvaultDescriptor,
1433    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_from_str")]
1434    pub cpfp: CpfpDescriptor,
1435}
1436
1437/// Information about the current state of the daemon
1438#[derive(Debug, Clone, Serialize, Deserialize)]
1439pub struct GetInfoResult {
1440    pub version: String,
1441    pub network: Network,
1442    pub blockheight: i32,
1443    pub sync: f64,
1444    pub vaults: usize,
1445    pub managers_threshold: usize,
1446    pub descriptors: GetInfoDescriptors,
1447    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_from_str")]
1448    pub participant_type: UserRole,
1449}
1450
1451/// Information about a vault.
1452#[derive(Debug, Clone, Serialize, Deserialize)]
1453pub struct ListVaultsEntry {
1454    #[serde(
1455        serialize_with = "ser_amount",
1456        deserialize_with = "deser_amount_from_sats"
1457    )]
1458    pub amount: Amount,
1459    pub blockheight: Option<u32>,
1460    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_from_str")]
1461    pub status: VaultStatus,
1462    pub txid: Txid,
1463    pub vout: u32,
1464    pub derivation_index: bip32::ChildNumber,
1465    pub address: Address,
1466    pub funded_at: Option<u32>,
1467    pub secured_at: Option<u32>,
1468    pub delegated_at: Option<u32>,
1469    pub moved_at: Option<u32>,
1470}
1471
1472/// Revocation transactions for a given vault
1473#[derive(Debug, Clone, Serialize, Deserialize)]
1474pub struct RevocationTransactions {
1475    pub cancel_txs: [CancelTransaction; 5],
1476    pub emergency_tx: EmergencyTransaction,
1477    // FIXME: consistent naming
1478    pub emergency_unvault_tx: UnvaultEmergencyTransaction,
1479}
1480
1481/// Information about a vault's presigned transactions.
1482#[derive(Debug, Clone, Serialize, Deserialize)]
1483pub struct ListPresignedTxEntry {
1484    pub vault_outpoint: OutPoint,
1485    pub unvault: UnvaultTransaction,
1486    pub cancel: [CancelTransaction; 5],
1487    /// Always None if not stakeholder
1488    pub emergency: Option<EmergencyTransaction>,
1489    /// Always None if not stakeholder
1490    pub unvault_emergency: Option<UnvaultEmergencyTransaction>,
1491}
1492
1493/// Information about a vault's onchain transactions.
1494#[derive(Debug, Clone, Serialize, Deserialize)]
1495pub struct ListOnchainTxEntry {
1496    pub vault_outpoint: OutPoint,
1497    pub deposit: WalletTransaction,
1498    pub unvault: Option<WalletTransaction>,
1499    pub cancel: Option<WalletTransaction>,
1500    /// Always None if not stakeholder
1501    pub emergency: Option<WalletTransaction>,
1502    /// Always None if not stakeholder
1503    pub unvault_emergency: Option<WalletTransaction>,
1504    pub spend: Option<WalletTransaction>,
1505}
1506
1507/// Status of a Spend transaction
1508#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1509#[serde(rename_all = "snake_case")]
1510pub enum ListSpendStatus {
1511    NonFinal,
1512    Pending,
1513    Broadcasted,
1514    /// The spend tx was confirmed in the blockchain.
1515    Confirmed,
1516    /// The spend tx cannot be used anymore, one of its input was spent by another transaction
1517    Deprecated,
1518}
1519
1520/// Information about a Spend transaction
1521#[derive(Debug, Clone, Serialize, Deserialize)]
1522pub struct ListSpendEntry {
1523    pub deposit_outpoints: Vec<OutPoint>,
1524    #[serde(
1525        serialize_with = "ser_amount",
1526        deserialize_with = "deser_amount_from_sats"
1527    )]
1528    pub deposit_amount: Amount,
1529    /// Total of amount of the cpfp outputs of the unvault transactions and the spend transaction.
1530    #[serde(
1531        serialize_with = "ser_amount",
1532        deserialize_with = "deser_amount_from_sats"
1533    )]
1534    pub cpfp_amount: Amount,
1535    pub psbt: SpendTransaction,
1536    pub cpfp_index: usize,
1537    pub change_index: Option<usize>,
1538    pub status: ListSpendStatus,
1539}
1540
1541/// Information about the configured servers.
1542#[derive(Debug, Clone, Serialize, Deserialize)]
1543pub struct ServersStatuses {
1544    pub coordinator: ServerStatus,
1545    pub cosigners: Vec<ServerStatus>,
1546    pub watchtowers: Vec<ServerStatus>,
1547}
1548
1549/// The type of an accounting event.
1550#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1551pub enum HistoryEventKind {
1552    #[serde(rename = "cancel")]
1553    Cancel,
1554    #[serde(rename = "deposit")]
1555    Deposit,
1556    #[serde(rename = "spend")]
1557    Spend,
1558}
1559
1560impl fmt::Display for HistoryEventKind {
1561    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1562        match self {
1563            Self::Cancel => write!(f, "Cancel"),
1564            Self::Deposit => write!(f, "Deposit"),
1565            Self::Spend => write!(f, "Spend"),
1566        }
1567    }
1568}
1569
1570/// An accounting event.
1571#[derive(Debug, Clone, Serialize, Deserialize)]
1572pub struct HistoryEvent {
1573    pub kind: HistoryEventKind,
1574    pub date: u32,
1575    pub blockheight: u32,
1576    pub amount: Option<u64>,
1577    pub cpfp_amount: Option<u64>,
1578    pub miner_fee: Option<u64>,
1579    pub txid: Txid,
1580    pub vaults: Vec<OutPoint>,
1581}