Skip to content

Commit bbefa73

Browse files
authored
Merge pull request #726 from benthecarman/add-chan-to-wallet
Insert channel funding outputs into Wallet
2 parents 5d0fb14 + 9b325ff commit bbefa73

File tree

7 files changed

+104
-24
lines changed

7 files changed

+104
-24
lines changed

bindings/ldk_node.udl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ enum NodeError {
339339
"InvalidNodeAlias",
340340
"InvalidDateTime",
341341
"InvalidFeeRate",
342+
"InvalidScriptPubKey",
342343
"DuplicatePayment",
343344
"UnsupportedCurrency",
344345
"InsufficientFunds",
@@ -575,6 +576,7 @@ dictionary ChannelDetails {
575576
ChannelId channel_id;
576577
PublicKey counterparty_node_id;
577578
OutPoint? funding_txo;
579+
ScriptBuf? funding_redeem_script;
578580
u64? short_channel_id;
579581
u64? outbound_scid_alias;
580582
u64? inbound_scid_alias;
@@ -901,3 +903,6 @@ typedef string LSPS1OrderId;
901903

902904
[Custom]
903905
typedef string LSPSDateTime;
906+
907+
[Custom]
908+
typedef string ScriptBuf;

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ pub enum Error {
113113
InvalidDateTime,
114114
/// The given fee rate is invalid.
115115
InvalidFeeRate,
116+
/// The given script public key is invalid.
117+
InvalidScriptPubKey,
116118
/// A payment with the given hash has already been initiated.
117119
DuplicatePayment,
118120
/// The provided offer was denonminated in an unsupported currency.
@@ -186,6 +188,7 @@ impl fmt::Display for Error {
186188
Self::InvalidNodeAlias => write!(f, "The given node alias is invalid."),
187189
Self::InvalidDateTime => write!(f, "The given date time is invalid."),
188190
Self::InvalidFeeRate => write!(f, "The given fee rate is invalid."),
191+
Self::InvalidScriptPubKey => write!(f, "The given script pubkey is invalid."),
189192
Self::DuplicatePayment => {
190193
write!(f, "A payment with the given hash has already been initiated.")
191194
},

src/ffi/types.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use bip39::Mnemonic;
2020
use bitcoin::hashes::sha256::Hash as Sha256;
2121
use bitcoin::hashes::Hash;
2222
use bitcoin::secp256k1::PublicKey;
23-
pub use bitcoin::{Address, BlockHash, FeeRate, Network, OutPoint, Txid};
23+
pub use bitcoin::{Address, BlockHash, FeeRate, Network, OutPoint, ScriptBuf, Txid};
2424
pub use lightning::chain::channelmonitor::BalanceSource;
2525
pub use lightning::events::{ClosureReason, PaymentFailureReason};
2626
use lightning::ln::channelmanager::PaymentId;
@@ -106,6 +106,22 @@ impl UniffiCustomTypeConverter for Address {
106106
}
107107
}
108108

109+
impl UniffiCustomTypeConverter for ScriptBuf {
110+
type Builtin = String;
111+
112+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
113+
if let Ok(key) = ScriptBuf::from_hex(&val) {
114+
return Ok(key);
115+
}
116+
117+
Err(Error::InvalidScriptPubKey.into())
118+
}
119+
120+
fn from_custom(obj: Self) -> Self::Builtin {
121+
obj.to_string()
122+
}
123+
}
124+
109125
#[derive(Debug, Clone, PartialEq, Eq)]
110126
pub enum OfferAmount {
111127
Bitcoin { amount_msats: u64 },

src/lib.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ use io::utils::write_node_metrics;
138138
use lightning::chain::BestBlock;
139139
use lightning::events::bump_transaction::{Input, Wallet as LdkWallet};
140140
use lightning::impl_writeable_tlv_based;
141-
use lightning::ln::chan_utils::{make_funding_redeemscript, FUNDING_TRANSACTION_WITNESS_WEIGHT};
141+
use lightning::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
142142
use lightning::ln::channel_state::{ChannelDetails as LdkChannelDetails, ChannelShutdownState};
143143
use lightning::ln::channelmanager::PaymentId;
144144
use lightning::ln::funding::SpliceContribution;
@@ -1265,29 +1265,27 @@ impl Node {
12651265
const EMPTY_SCRIPT_SIG_WEIGHT: u64 =
12661266
1 /* empty script_sig */ * bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
12671267

1268-
// Used for creating a redeem script for the previous funding txo and the new funding
1269-
// txo. Only needed when selecting which UTXOs to include in the funding tx that would
1270-
// be sufficient to pay for fees. Hence, the value does not matter.
1271-
let dummy_pubkey = PublicKey::from_slice(&[2; 33]).unwrap();
1272-
12731268
let funding_txo = channel_details.funding_txo.ok_or_else(|| {
12741269
log_error!(self.logger, "Failed to splice channel: channel not yet ready",);
12751270
Error::ChannelSplicingFailed
12761271
})?;
12771272

1273+
let funding_output = channel_details.get_funding_output().ok_or_else(|| {
1274+
log_error!(self.logger, "Failed to splice channel: channel not yet ready");
1275+
Error::ChannelSplicingFailed
1276+
})?;
1277+
12781278
let shared_input = Input {
12791279
outpoint: funding_txo.into_bitcoin_outpoint(),
1280-
previous_utxo: bitcoin::TxOut {
1281-
value: Amount::from_sat(channel_details.channel_value_satoshis),
1282-
script_pubkey: make_funding_redeemscript(&dummy_pubkey, &dummy_pubkey)
1283-
.to_p2wsh(),
1284-
},
1280+
previous_utxo: funding_output.clone(),
12851281
satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + FUNDING_TRANSACTION_WITNESS_WEIGHT,
12861282
};
12871283

12881284
let shared_output = bitcoin::TxOut {
12891285
value: shared_input.previous_utxo.value + Amount::from_sat(splice_amount_sats),
1290-
script_pubkey: make_funding_redeemscript(&dummy_pubkey, &dummy_pubkey).to_p2wsh(),
1286+
// will not actually be the exact same script pubkey after splice
1287+
// but it is the same size and good enough for coin selection purposes
1288+
script_pubkey: funding_output.script_pubkey.clone(),
12911289
};
12921290

12931291
let fee_rate = self.fee_estimator.estimate_fee_rate(ConfirmationTarget::ChannelFunding);
@@ -1303,6 +1301,10 @@ impl Node {
13031301
Error::ChannelSplicingFailed
13041302
})?;
13051303

1304+
// insert channel's funding utxo into the wallet so we can later calculate fees
1305+
// correctly when viewing this splice-in.
1306+
self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output)?;
1307+
13061308
let change_address = self.wallet.get_new_internal_address()?;
13071309

13081310
let contribution = SpliceContribution::SpliceIn {
@@ -1398,6 +1400,18 @@ impl Node {
13981400
},
13991401
};
14001402

1403+
let funding_txo = channel_details.funding_txo.ok_or_else(|| {
1404+
log_error!(self.logger, "Failed to splice channel: channel not yet ready",);
1405+
Error::ChannelSplicingFailed
1406+
})?;
1407+
1408+
let funding_output = channel_details.get_funding_output().ok_or_else(|| {
1409+
log_error!(self.logger, "Failed to splice channel: channel not yet ready");
1410+
Error::ChannelSplicingFailed
1411+
})?;
1412+
1413+
self.wallet.insert_txo(funding_txo.into_bitcoin_outpoint(), funding_output)?;
1414+
14011415
self.channel_manager
14021416
.splice_channel(
14031417
&channel_details.channel_id,

src/types.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::pin::Pin;
1111
use std::sync::{Arc, Mutex};
1212

1313
use bitcoin::secp256k1::PublicKey;
14-
use bitcoin::OutPoint;
14+
use bitcoin::{OutPoint, ScriptBuf};
1515
use lightning::chain::chainmonitor;
1616
use lightning::impl_writeable_tlv_based;
1717
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
@@ -356,6 +356,15 @@ pub struct ChannelDetails {
356356
/// state until the splice transaction reaches sufficient confirmations to be locked (and we
357357
/// exchange `splice_locked` messages with our peer).
358358
pub funding_txo: Option<OutPoint>,
359+
/// The witness script that is used to lock the channel's funding output to commitment transactions.
360+
///
361+
/// This field will be `None` if we have not negotiated the funding transaction with our
362+
/// counterparty already.
363+
///
364+
/// When a channel is spliced, this continues to refer to the original pre-splice channel
365+
/// state until the splice transaction reaches sufficient confirmations to be locked (and we
366+
/// exchange `splice_locked` messages with our peer).
367+
pub funding_redeem_script: Option<ScriptBuf>,
359368
/// The position of the funding transaction in the chain. None if the funding transaction has
360369
/// not yet been confirmed and the channel fully opened.
361370
///
@@ -512,6 +521,7 @@ impl From<LdkChannelDetails> for ChannelDetails {
512521
channel_id: value.channel_id,
513522
counterparty_node_id: value.counterparty.node_id,
514523
funding_txo: value.funding_txo.map(|o| o.into_bitcoin_outpoint()),
524+
funding_redeem_script: value.funding_redeem_script,
515525
short_channel_id: value.short_channel_id,
516526
outbound_scid_alias: value.outbound_scid_alias,
517527
inbound_scid_alias: value.inbound_scid_alias,

src/wallet/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
2525
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
2626
use bitcoin::secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey};
2727
use bitcoin::{
28-
Address, Amount, FeeRate, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, Weight,
28+
Address, Amount, FeeRate, OutPoint, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, Weight,
2929
WitnessProgram, WitnessVersion,
3030
};
3131
use lightning::chain::chaininterface::BroadcasterInterface;
@@ -152,6 +152,19 @@ impl Wallet {
152152
Ok(())
153153
}
154154

155+
pub(crate) fn insert_txo(&self, outpoint: OutPoint, txout: TxOut) -> Result<(), Error> {
156+
let mut locked_wallet = self.inner.lock().unwrap();
157+
locked_wallet.insert_txout(outpoint, txout);
158+
159+
let mut locked_persister = self.persister.lock().unwrap();
160+
locked_wallet.persist(&mut locked_persister).map_err(|e| {
161+
log_error!(self.logger, "Failed to persist wallet: {}", e);
162+
Error::PersistenceFailed
163+
})?;
164+
165+
Ok(())
166+
}
167+
155168
fn update_payment_store<'a>(
156169
&self, locked_wallet: &'a mut PersistedWallet<KVStoreWalletPersister>,
157170
) -> Result<(), Error> {

tests/integration_tests_rust.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -926,10 +926,13 @@ async fn concurrent_connections_succeed() {
926926
}
927927
}
928928

929-
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
930-
async fn splice_channel() {
929+
async fn run_splice_channel_test(bitcoind_chain_source: bool) {
931930
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
932-
let chain_source = TestChainSource::Esplora(&electrsd);
931+
let chain_source = if bitcoind_chain_source {
932+
TestChainSource::BitcoindRpcSync(&bitcoind)
933+
} else {
934+
TestChainSource::Esplora(&electrsd)
935+
};
933936
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
934937

935938
let address_a = node_a.onchain_payment().new_address().unwrap();
@@ -994,7 +997,7 @@ async fn splice_channel() {
994997
// Splice-in funds for Node B so that it has outbound liquidity to make a payment
995998
node_b.splice_in(&user_channel_id_b, node_a.node_id(), 4_000_000).unwrap();
996999

997-
expect_splice_pending_event!(node_a, node_b.node_id());
1000+
let txo = expect_splice_pending_event!(node_a, node_b.node_id());
9981001
expect_splice_pending_event!(node_b, node_a.node_id());
9991002

10001003
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
@@ -1005,11 +1008,16 @@ async fn splice_channel() {
10051008
expect_channel_ready_event!(node_a, node_b.node_id());
10061009
expect_channel_ready_event!(node_b, node_a.node_id());
10071010

1008-
let splice_in_fee_sat = 252;
1011+
let expected_splice_in_fee_sat = 252;
1012+
1013+
let payments = node_b.list_payments();
1014+
let payment =
1015+
payments.into_iter().find(|p| p.id == PaymentId(txo.txid.to_byte_array())).unwrap();
1016+
assert_eq!(payment.fee_paid_msat, Some(expected_splice_in_fee_sat * 1_000));
10091017

10101018
assert_eq!(
10111019
node_b.list_balances().total_onchain_balance_sats,
1012-
premine_amount_sat - 4_000_000 - splice_in_fee_sat
1020+
premine_amount_sat - 4_000_000 - expected_splice_in_fee_sat
10131021
);
10141022
assert_eq!(node_b.list_balances().total_lightning_balance_sats, 4_000_000);
10151023

@@ -1032,7 +1040,7 @@ async fn splice_channel() {
10321040
let address = node_a.onchain_payment().new_address().unwrap();
10331041
node_a.splice_out(&user_channel_id_a, node_b.node_id(), &address, amount_msat / 1000).unwrap();
10341042

1035-
expect_splice_pending_event!(node_a, node_b.node_id());
1043+
let txo = expect_splice_pending_event!(node_a, node_b.node_id());
10361044
expect_splice_pending_event!(node_b, node_a.node_id());
10371045

10381046
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
@@ -1043,18 +1051,29 @@ async fn splice_channel() {
10431051
expect_channel_ready_event!(node_a, node_b.node_id());
10441052
expect_channel_ready_event!(node_b, node_a.node_id());
10451053

1046-
let splice_out_fee_sat = 183;
1054+
let expected_splice_out_fee_sat = 183;
1055+
1056+
let payments = node_a.list_payments();
1057+
let payment =
1058+
payments.into_iter().find(|p| p.id == PaymentId(txo.txid.to_byte_array())).unwrap();
1059+
assert_eq!(payment.fee_paid_msat, Some(expected_splice_out_fee_sat * 1_000));
10471060

10481061
assert_eq!(
10491062
node_a.list_balances().total_onchain_balance_sats,
10501063
premine_amount_sat - 4_000_000 - opening_transaction_fee_sat + amount_msat / 1000
10511064
);
10521065
assert_eq!(
10531066
node_a.list_balances().total_lightning_balance_sats,
1054-
4_000_000 - closing_transaction_fee_sat - anchor_output_sat - splice_out_fee_sat
1067+
4_000_000 - closing_transaction_fee_sat - anchor_output_sat - expected_splice_out_fee_sat
10551068
);
10561069
}
10571070

1071+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
1072+
async fn splice_channel() {
1073+
run_splice_channel_test(false).await;
1074+
run_splice_channel_test(true).await;
1075+
}
1076+
10581077
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
10591078
async fn simple_bolt12_send_receive() {
10601079
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)