diff --git a/key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs b/key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs index 2a456ca91..a59e0c282 100644 --- a/key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs +++ b/key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs @@ -157,6 +157,11 @@ impl TransactionBuilder { /// must therefore not be held across an `await` between `add_funding` and /// `build_signed` or `assemble_unsigned`, since suspending there reopens the /// read-then-reserve window for a concurrent build. + /// + /// A change address already set by [`set_change_address`](Self::set_change_address) + /// wins: the funding account is only asked for one when the builder has none. + /// Deriving unconditionally would both discard the caller's choice and burn a + /// pool address, since `next_change_address` advances the pool state. pub fn add_funding(self, funds_acc: &mut ManagedCoreFundsAccount, acc: &Account) -> Self { self.fund_from(funds_acc, acc, true) } @@ -2023,12 +2028,29 @@ mod tests { funds.reservations().reserve(&[reserved.outpoint], 200, ReservationToken::next()); - let builder = - TransactionBuilder::new().set_current_height(200).add_funding(&mut funds, &account); + // Set change address to test the branch that avoids it being replaced + // by add_funding() + let explicit = Address::dummy(Network::Testnet, 1); + let builder = TransactionBuilder::new() + .set_current_height(200) + .set_change_address(explicit.clone()) + .add_funding(&mut funds, &account); let candidates: Vec = builder.inputs.iter().map(|utxo| utxo.outpoint).collect(); assert!(!candidates.contains(&reserved.outpoint)); assert!(candidates.contains(&free.outpoint)); + + assert_eq!( + builder.change_addr.as_ref(), + Some(&explicit), + "funding must not override a change address the caller chose" + ); + // Nor may it burn a pool address to derive one it then discards. + let mut control = ManagedCoreFundsAccount::dummy_bip44(); + assert_eq!( + funds.next_change_address(Some(&account.account_xpub), true).expect("change address"), + control.next_change_address(Some(&account.account_xpub), true).expect("change address"), + ); } #[test]