Skip to content

Commit 7f388fc

Browse files
committed
Change getspendtx response of ListSpendtxEntry
1 parent 6cb5913 commit 7f388fc

File tree

7 files changed

+170
-97
lines changed

7 files changed

+170
-97
lines changed

doc/API.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,9 @@ feerate.
341341

342342
#### Response
343343

344-
| Field | Type | Description |
345-
| ---------- | ------ | ----------------------------------------------- |
346-
| `spend_tx` | string | Base64-encoded Spend transaction PSBT |
344+
| Field | Type | Description |
345+
| ---------- | ----------------------------------------------------------- | ------------------------------ |
346+
| `spend_tx` | [Spend transaction resources](#spend_transaction_resources) | Spend transaction informations |
347347

348348

349349
### `updatespendtx`
@@ -401,7 +401,7 @@ Please note that this status refers only to the Spend transaction, with regardin
401401

402402
| Field | Type | Description |
403403
| -------------- | ------ | -------------------------------------------------------------------- |
404-
| `spend_txs` | array | Array of [Spend transaction resources](#spend_transaction_reources) |
404+
| `spend_txs` | array | Array of [Spend transaction resources](#spend_transaction_resources) |
405405

406406
##### Spend transaction resources
407407

src/commands/mod.rs

Lines changed: 18 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,21 @@ use crate::{
3535
};
3636
use utils::{
3737
deser_amount_from_sats, deser_from_str, finalized_emer_txs, gethistory, listvaults_from_db,
38-
presigned_txs, ser_amount, ser_to_string, unvault_tx, vaults_from_deposits,
38+
presigned_txs, ser_amount, ser_to_string, spend_entry, unvault_tx, vaults_from_deposits,
3939
};
4040

4141
use revault_tx::{
4242
bitcoin::{
4343
consensus::encode, secp256k1, util::bip32, Address, Amount, Network, OutPoint,
4444
PublicKey as BitcoinPubKey, Transaction as BitcoinTransaction, TxOut, Txid,
4545
},
46-
miniscript::DescriptorTrait,
4746
scripts::{CpfpDescriptor, DepositDescriptor, UnvaultDescriptor},
4847
transactions::{
4948
spend_tx_from_deposits, transaction_chain, transaction_chain_manager, CancelTransaction,
5049
CpfpableTransaction, EmergencyTransaction, RevaultPresignedTransaction, RevaultTransaction,
5150
SpendTransaction, UnvaultEmergencyTransaction, UnvaultTransaction,
5251
},
53-
txins::RevaultTxIn,
54-
txouts::{DepositTxOut, RevaultTxOut, SpendTxOut},
52+
txouts::{DepositTxOut, SpendTxOut},
5553
};
5654

5755
use std::{collections::BTreeMap, fmt};
@@ -924,14 +922,16 @@ impl DaemonControl {
924922
outpoints: &[OutPoint],
925923
destinations: &BTreeMap<Address, u64>,
926924
feerate_vb: u64,
927-
) -> Result<SpendTransaction, CommandError> {
925+
) -> Result<ListSpendEntry, CommandError> {
928926
let revaultd = self.revaultd.read().unwrap();
929927
manager_only!(revaultd);
930928
let db_file = &revaultd.db_file();
931929

932930
// FIXME: have a feerate type to avoid that
933931
assert!(feerate_vb > 0, "Spend feerate can't be null.");
934932

933+
// TODO: remove the txins vec, just use the spent_vaults one.
934+
let mut spent_vaults = Vec::with_capacity(outpoints.len());
935935
// Reconstruct the DepositTxin s from the outpoints and the vaults informations
936936
let mut txins = Vec::with_capacity(outpoints.len());
937937
// If we need a change output, use the highest derivation index of the vaults
@@ -947,6 +947,7 @@ impl DaemonControl {
947947
change_index = vault.derivation_index;
948948
}
949949
txins.push((*outpoint, vault.amount, vault.derivation_index));
950+
spent_vaults.push(vault);
950951
} else {
951952
return Err(CommandError::InvalidStatus(
952953
vault.status,
@@ -1071,7 +1072,12 @@ impl DaemonControl {
10711072
};
10721073
log::debug!("Final Spend transaction: '{:?}'", tx_res);
10731074

1074-
Ok(tx_res)
1075+
Ok(spend_entry(
1076+
&revaultd,
1077+
tx_res,
1078+
spent_vaults.iter(),
1079+
ListSpendStatus::NonFinal,
1080+
))
10751081
}
10761082

10771083
/// Store a new or update an existing Spend transaction in database.
@@ -1150,7 +1156,7 @@ impl DaemonControl {
11501156

11511157
let spend_tx_map = db_list_spends(&db_path).expect("Database must be available");
11521158
let mut listspend_entries = Vec::with_capacity(spend_tx_map.len());
1153-
for (_, (db_spend, deposit_outpoints)) in spend_tx_map {
1159+
for (_, (db_spend, _)) in spend_tx_map {
11541160
let mut status = match db_spend.broadcasted {
11551161
Some(true) => ListSpendStatus::Broadcasted,
11561162
Some(false) => ListSpendStatus::Pending,
@@ -1182,62 +1188,12 @@ impl DaemonControl {
11821188
}
11831189
}
11841190

1185-
let (deposit_amount, mut cpfp_amount) = spent_vaults.iter().fold(
1186-
(Amount::from_sat(0), Amount::from_sat(0)),
1187-
|(deposit_total, cpfp_total), (_, vault)| {
1188-
let unvault = unvault_tx(&revaultd, vault)
1189-
.expect("Spent vault must have a correct unvault transaction");
1190-
1191-
let cpfp_amount = Amount::from_sat(
1192-
unvault
1193-
.cpfp_txin(&revaultd.cpfp_descriptor, &revaultd.secp_ctx)
1194-
.expect("Unvault tx has always a cpfp output")
1195-
.txout()
1196-
.txout()
1197-
.value,
1198-
);
1199-
1200-
(deposit_total + vault.amount, cpfp_total + cpfp_amount)
1201-
},
1202-
);
1203-
1204-
let derivation_index = spent_vaults
1205-
.values()
1206-
.map(|v| v.derivation_index)
1207-
.max()
1208-
.expect("Spent vaults should not be empty");
1209-
let cpfp_script_pubkey = revaultd
1210-
.cpfp_descriptor
1211-
.derive(derivation_index, &revaultd.secp_ctx)
1212-
.into_inner()
1213-
.script_pubkey();
1214-
let deposit_address = revaultd
1215-
.deposit_descriptor
1216-
.derive(derivation_index, &revaultd.secp_ctx)
1217-
.into_inner()
1218-
.script_pubkey();
1219-
let mut cpfp_index = None;
1220-
let mut change_index = None;
1221-
for (i, txout) in db_spend.psbt.tx().output.iter().enumerate() {
1222-
if cpfp_index.is_none() && cpfp_script_pubkey == txout.script_pubkey {
1223-
cpfp_index = Some(i);
1224-
cpfp_amount += Amount::from_sat(txout.value);
1225-
}
1226-
1227-
if deposit_address == txout.script_pubkey {
1228-
change_index = Some(i);
1229-
}
1230-
}
1231-
1232-
listspend_entries.push(ListSpendEntry {
1233-
psbt: db_spend.psbt,
1234-
deposit_outpoints,
1235-
deposit_amount,
1236-
cpfp_amount,
1237-
cpfp_index: cpfp_index.expect("We always create a CPFP output"),
1238-
change_index,
1191+
listspend_entries.push(spend_entry(
1192+
&revaultd,
1193+
db_spend.psbt,
1194+
spent_vaults.values(),
12391195
status,
1240-
});
1196+
));
12411197
}
12421198

12431199
Ok(listspend_entries)

src/commands/utils.rs

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
55
use crate::{
66
commands::{
7-
CommandError, HistoryEvent, HistoryEventKind, ListPresignedTxEntry, ListVaultsEntry,
7+
CommandError, HistoryEvent, HistoryEventKind, ListPresignedTxEntry, ListSpendEntry,
8+
ListSpendStatus, ListVaultsEntry,
89
},
910
database::{
1011
interface::{
@@ -21,18 +22,20 @@ use crate::{
2122

2223
use revault_tx::{
2324
bitcoin::{
24-
consensus::encode, hashes::hex::FromHex, Amount, OutPoint,
25+
consensus::encode, hashes::hex::FromHex, util::bip32, Amount, OutPoint,
2526
Transaction as BitcoinTransaction, Txid,
2627
},
2728
miniscript::DescriptorTrait,
2829
transactions::{
29-
transaction_chain_manager, CpfpableTransaction, RevaultTransaction, UnvaultTransaction,
30+
transaction_chain_manager, CpfpableTransaction, RevaultTransaction, SpendTransaction,
31+
UnvaultTransaction,
3032
},
3133
txins::{DepositTxIn, RevaultTxIn},
3234
txouts::{DepositTxOut, RevaultTxOut},
3335
};
3436

3537
use std::{
38+
cmp,
3639
collections::{HashMap, HashSet},
3740
fmt,
3841
str::FromStr,
@@ -490,6 +493,82 @@ pub fn gethistory<T: BitcoindThread>(
490493
Ok(events)
491494
}
492495

496+
/// Get the ListSpendEntry for a given Spend transaction.
497+
/// This relies on brittle assumptions about how we construct the Spend. Those might not hold if
498+
/// used on a Spend PSBT we did not create.
499+
pub fn spend_entry<'a>(
500+
revaultd: &RevaultD,
501+
psbt: SpendTransaction,
502+
spent_vaults: impl IntoIterator<Item = &'a DbVault>,
503+
status: ListSpendStatus,
504+
) -> ListSpendEntry {
505+
// The derivation index for the change is assumed to be reusing the largest one of the inputs.
506+
let (deposit_amount, mut cpfp_amount, deposit_outpoints, derivation_index) =
507+
spent_vaults.into_iter().fold(
508+
(
509+
Amount::from_sat(0),
510+
Amount::from_sat(0),
511+
Vec::new(),
512+
bip32::ChildNumber::from(0),
513+
),
514+
|(deposit_total, cpfp_total, mut deposit_outpoints, derivation_index), vault| {
515+
deposit_outpoints.push(vault.deposit_outpoint);
516+
517+
let unvault = unvault_tx(&revaultd, &vault)
518+
.expect("Spent vault must have a correct unvault transaction");
519+
let cpfp_amount = Amount::from_sat(
520+
unvault
521+
.cpfp_txin(&revaultd.cpfp_descriptor, &revaultd.secp_ctx)
522+
.expect("Unvault tx has always a cpfp output")
523+
.txout()
524+
.txout()
525+
.value,
526+
);
527+
528+
(
529+
deposit_total + vault.amount,
530+
cpfp_total + cpfp_amount,
531+
deposit_outpoints,
532+
cmp::max(derivation_index, vault.derivation_index),
533+
)
534+
},
535+
);
536+
537+
let cpfp_script_pubkey = revaultd
538+
.cpfp_descriptor
539+
.derive(derivation_index, &revaultd.secp_ctx)
540+
.into_inner()
541+
.script_pubkey();
542+
let deposit_address = revaultd
543+
.deposit_descriptor
544+
.derive(derivation_index, &revaultd.secp_ctx)
545+
.into_inner()
546+
.script_pubkey();
547+
let mut cpfp_index = None;
548+
let mut change_index = None;
549+
for (i, txout) in psbt.tx().output.iter().enumerate() {
550+
if cpfp_index.is_none() && cpfp_script_pubkey == txout.script_pubkey {
551+
cpfp_index = Some(i);
552+
cpfp_amount += Amount::from_sat(txout.value);
553+
}
554+
555+
if deposit_address == txout.script_pubkey {
556+
change_index = Some(i);
557+
}
558+
}
559+
560+
ListSpendEntry {
561+
psbt,
562+
deposit_outpoints,
563+
deposit_amount,
564+
cpfp_amount,
565+
// FIXME: this won't hold post optional-CPFP
566+
cpfp_index: cpfp_index.expect("We always have a CPFP index"),
567+
change_index,
568+
status,
569+
}
570+
}
571+
493572
#[cfg(test)]
494573
mod tests {
495574
use super::*;

tests/test_chain.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
676676
revault_network.activate_vault(vault)
677677
deposits = [f"{vault['txid']}:{vault['vout']}"]
678678
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
679-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
679+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
680680
for m in [man] + mans:
681681
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
682682
mans[0].rpc.updatespendtx(spend_tx)
@@ -719,7 +719,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
719719
revault_network.activate_vault(vault)
720720
deposits = [f"{vault['txid']}:{vault['vout']}"]
721721
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
722-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
722+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
723723
for m in [man] + mans:
724724
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
725725
mans[0].rpc.updatespendtx(spend_tx)
@@ -759,7 +759,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
759759
revault_network.activate_vault(vault)
760760
deposits = [f"{vault['txid']}:{vault['vout']}"]
761761
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
762-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
762+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
763763
for m in [man] + mans:
764764
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
765765
mans[0].rpc.updatespendtx(spend_tx)
@@ -800,7 +800,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
800800
revault_network.activate_vault(vault)
801801
deposits = [f"{vault['txid']}:{vault['vout']}"]
802802
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
803-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
803+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
804804
for m in [man] + mans:
805805
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
806806
mans[0].rpc.updatespendtx(spend_tx)
@@ -842,7 +842,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
842842
revault_network.activate_vault(vault)
843843
deposits = [f"{vault['txid']}:{vault['vout']}"]
844844
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
845-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
845+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
846846
for m in [man] + mans:
847847
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
848848
mans[0].rpc.updatespendtx(spend_tx)
@@ -876,7 +876,7 @@ def test_retrieve_vault_status(revault_network, bitcoind):
876876
revault_network.activate_vault(vault)
877877
deposits = [f"{vault['txid']}:{vault['vout']}"]
878878
destinations = {bitcoind.rpc.getnewaddress(): vault["amount"] // 2}
879-
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]
879+
spend_tx = mans[0].rpc.getspendtx(deposits, destinations, 1)["spend_tx"]["psbt"]
880880
for m in [man] + mans:
881881
spend_tx = m.man_keychain.sign_spend_psbt(spend_tx, [vault["derivation_index"]])
882882
mans[0].rpc.updatespendtx(spend_tx)

tests/test_framework/revault_network.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,9 @@ def broadcast_unvaults(self, vaults, destinations, feerate, priority=False):
608608
deriv_indexes.append(v["derivation_index"])
609609
man.wait_for_active_vaults(deposits)
610610

611-
spend_tx = man.rpc.getspendtx(deposits, destinations, feerate)["spend_tx"]
611+
spend_tx = man.rpc.getspendtx(deposits, destinations, feerate)["spend_tx"][
612+
"psbt"
613+
]
612614
for man in self.mans():
613615
spend_tx = man.man_keychain.sign_spend_psbt(spend_tx, deriv_indexes)
614616
man.rpc.updatespendtx(spend_tx)
@@ -654,7 +656,9 @@ def spend_vaults_unconfirmed(self, vaults, destinations, feerate, priority=False
654656
for man in self.mans():
655657
man.wait_for_active_vaults(deposits)
656658

657-
spend_tx = man.rpc.getspendtx(deposits, destinations, feerate)["spend_tx"]
659+
spend_tx = man.rpc.getspendtx(deposits, destinations, feerate)["spend_tx"][
660+
"psbt"
661+
]
658662
for man in self.mans():
659663
spend_tx = man.man_keychain.sign_spend_psbt(spend_tx, deriv_indexes)
660664
man.rpc.updatespendtx(spend_tx)

0 commit comments

Comments
 (0)