Skip to content

Commit 7708fcf

Browse files
committed
Merge #408: Change getspendtx response of ListSpendtxEntry
7f388fc Change getspendtx response of ListSpendtxEntry (edouard) Pull request description: for revault/revault-gui#358 Some information are needed. User cannot use listspendtx because at this step of the spend tx creation, the transaction is not yet saved in the database ACKs for top commit: darosior: utACK 7f388fc Tree-SHA512: a6e417a7c068cedfb3a897de63afe50d185f8a343d7bfdbcbba60464b4d0317b81ab1dc588c63fd05020b62af458e4ba453ad81a33d92e000a530eb0fe1c7ea5
2 parents 6cb5913 + 7f388fc commit 7708fcf

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)