Skip to content

Commit 414e622

Browse files
authored
Merge pull request #789 from TheBlueMatt/2021-02-chansigner-util-fns
Add util fn for creating a Transaction from spendable outputs
2 parents 2088f4b + 95d0fe9 commit 414e622

File tree

7 files changed

+435
-202
lines changed

7 files changed

+435
-202
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
4343
use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
4444
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
4545
use chain::transaction::{OutPoint, TransactionData};
46-
use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys, KeysInterface};
46+
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ChannelKeys, KeysInterface};
4747
use util::logger::Logger;
4848
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
4949
use util::byte_utils;
@@ -2201,24 +2201,24 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
22012201
break;
22022202
} else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
22032203
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
2204-
spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH {
2204+
spendable_output = Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
22052205
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
22062206
per_commitment_point: broadcasted_holder_revokable_script.1,
22072207
to_self_delay: self.on_holder_tx_csv,
22082208
output: outp.clone(),
22092209
revocation_pubkey: broadcasted_holder_revokable_script.2.clone(),
22102210
channel_keys_id: self.channel_keys_id,
22112211
channel_value_satoshis: self.channel_value_satoshis,
2212-
});
2212+
}));
22132213
break;
22142214
}
22152215
} else if self.counterparty_payment_script == outp.script_pubkey {
2216-
spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
2216+
spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
22172217
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
22182218
output: outp.clone(),
22192219
channel_keys_id: self.channel_keys_id,
22202220
channel_value_satoshis: self.channel_value_satoshis,
2221-
});
2221+
}));
22222222
break;
22232223
} else if outp.script_pubkey == self.shutdown_script {
22242224
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {

lightning/src/chain/keysinterface.rs

Lines changed: 253 additions & 64 deletions
Large diffs are not rendered by default.

lightning/src/ln/chan_utils.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,16 @@ impl TxCreationKeys {
384384
}
385385
}
386386

387+
/// The maximum length of a script returned by get_revokeable_redeemscript.
388+
// Calculated as 6 bytes of opcodes, 1 byte push plus 2 bytes for contest_delay, and two public
389+
// keys of 33 bytes (+ 1 push).
390+
pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 3 + 34*2;
391+
387392
/// A script either spendable by the revocation
388393
/// key or the broadcaster_delayed_payment_key and satisfying the relative-locktime OP_CSV constrain.
389394
/// Encumbering a `to_holder` output on a commitment transaction or 2nd-stage HTLC transactions.
390395
pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u16, broadcaster_delayed_payment_key: &PublicKey) -> Script {
391-
Builder::new().push_opcode(opcodes::all::OP_IF)
396+
let res = Builder::new().push_opcode(opcodes::all::OP_IF)
392397
.push_slice(&revocation_key.serialize())
393398
.push_opcode(opcodes::all::OP_ELSE)
394399
.push_int(contest_delay as i64)
@@ -397,7 +402,9 @@ pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u1
397402
.push_slice(&broadcaster_delayed_payment_key.serialize())
398403
.push_opcode(opcodes::all::OP_ENDIF)
399404
.push_opcode(opcodes::all::OP_CHECKSIG)
400-
.into_script()
405+
.into_script();
406+
debug_assert!(res.len() <= REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH);
407+
res
401408
}
402409

403410
#[derive(Clone, PartialEq)]

lightning/src/ln/functional_tests.rs

Lines changed: 33 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use chain::Watch;
1515
use chain::channelmonitor;
1616
use chain::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
1717
use chain::transaction::OutPoint;
18-
use chain::keysinterface::{ChannelKeys, KeysInterface, SpendableOutputDescriptor};
18+
use chain::keysinterface::{ChannelKeys, KeysInterface};
1919
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
2020
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure, BREAKDOWN_TIMEOUT};
2121
use ln::channel::{Channel, ChannelError};
@@ -33,12 +33,8 @@ use util::config::UserConfig;
3333

3434
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
3535
use bitcoin::hash_types::{Txid, BlockHash};
36-
use bitcoin::util::bip143;
37-
use bitcoin::util::address::Address;
38-
use bitcoin::util::bip32::{ChildNumber, ExtendedPubKey, ExtendedPrivKey};
3936
use bitcoin::blockdata::block::{Block, BlockHeader};
40-
use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType};
41-
use bitcoin::blockdata::script::{Builder, Script};
37+
use bitcoin::blockdata::script::Builder;
4238
use bitcoin::blockdata::opcodes;
4339
use bitcoin::blockdata::constants::genesis_block;
4440
use bitcoin::network::constants::Network;
@@ -4655,122 +4651,27 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() {
46554651
macro_rules! check_spendable_outputs {
46564652
($node: expr, $der_idx: expr, $keysinterface: expr, $chan_value: expr) => {
46574653
{
4658-
let events = $node.chain_monitor.chain_monitor.get_and_clear_pending_events();
4654+
let mut events = $node.chain_monitor.chain_monitor.get_and_clear_pending_events();
46594655
let mut txn = Vec::new();
4660-
for event in events {
4656+
let mut all_outputs = Vec::new();
4657+
let secp_ctx = Secp256k1::new();
4658+
for event in events.drain(..) {
46614659
match event {
4662-
Event::SpendableOutputs { ref outputs } => {
4663-
for outp in outputs {
4664-
match *outp {
4665-
SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
4666-
assert_eq!(channel_value_satoshis, $chan_value);
4667-
let input = TxIn {
4668-
previous_output: outpoint.into_bitcoin_outpoint(),
4669-
script_sig: Script::new(),
4670-
sequence: 0,
4671-
witness: Vec::new(),
4672-
};
4673-
let outp = TxOut {
4674-
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
4675-
value: output.value,
4676-
};
4677-
let mut spend_tx = Transaction {
4678-
version: 2,
4679-
lock_time: 0,
4680-
input: vec![input],
4681-
output: vec![outp],
4682-
};
4683-
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
4684-
let secp_ctx = Secp256k1::new();
4685-
let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
4686-
let remotepubkey = keys.pubkeys().payment_point;
4687-
let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
4688-
let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
4689-
let remotesig = secp_ctx.sign(&sighash, &keys.inner.payment_key);
4690-
spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec());
4691-
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
4692-
spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec());
4693-
txn.push(spend_tx);
4694-
},
4695-
SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis } => {
4696-
assert_eq!(channel_value_satoshis, $chan_value);
4697-
let input = TxIn {
4698-
previous_output: outpoint.into_bitcoin_outpoint(),
4699-
script_sig: Script::new(),
4700-
sequence: *to_self_delay as u32,
4701-
witness: Vec::new(),
4702-
};
4703-
let outp = TxOut {
4704-
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
4705-
value: output.value,
4706-
};
4707-
let mut spend_tx = Transaction {
4708-
version: 2,
4709-
lock_time: 0,
4710-
input: vec![input],
4711-
output: vec![outp],
4712-
};
4713-
let secp_ctx = Secp256k1::new();
4714-
let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
4715-
if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &keys.inner.delayed_payment_base_key) {
4716-
4717-
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
4718-
let witness_script = chan_utils::get_revokeable_redeemscript(revocation_pubkey, *to_self_delay, &delayed_payment_pubkey);
4719-
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 1 + witness_script.len() + 1 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
4720-
let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
4721-
let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
4722-
spend_tx.input[0].witness.push(local_delayedsig.serialize_der().to_vec());
4723-
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
4724-
spend_tx.input[0].witness.push(vec!()); //MINIMALIF
4725-
spend_tx.input[0].witness.push(witness_script.clone().into_bytes());
4726-
} else { panic!() }
4727-
txn.push(spend_tx);
4728-
},
4729-
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
4730-
let secp_ctx = Secp256k1::new();
4731-
let input = TxIn {
4732-
previous_output: outpoint.into_bitcoin_outpoint(),
4733-
script_sig: Script::new(),
4734-
sequence: 0,
4735-
witness: Vec::new(),
4736-
};
4737-
let outp = TxOut {
4738-
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
4739-
value: output.value,
4740-
};
4741-
let mut spend_tx = Transaction {
4742-
version: 2,
4743-
lock_time: 0,
4744-
input: vec![input],
4745-
output: vec![outp.clone()],
4746-
};
4747-
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
4748-
let secret = {
4749-
match ExtendedPrivKey::new_master(Network::Testnet, &$node.node_seed) {
4750-
Ok(master_key) => {
4751-
match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx($der_idx).expect("key space exhausted")) {
4752-
Ok(key) => key,
4753-
Err(_) => panic!("Your RNG is busted"),
4754-
}
4755-
}
4756-
Err(_) => panic!("Your rng is busted"),
4757-
}
4758-
};
4759-
let pubkey = ExtendedPubKey::from_private(&secp_ctx, &secret).public_key;
4760-
let witness_script = Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
4761-
let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
4762-
let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
4763-
spend_tx.input[0].witness.push(sig.serialize_der().to_vec());
4764-
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
4765-
spend_tx.input[0].witness.push(pubkey.key.serialize().to_vec());
4766-
txn.push(spend_tx);
4767-
},
4768-
}
4660+
Event::SpendableOutputs { mut outputs } => {
4661+
for outp in outputs.drain(..) {
4662+
let mut outputs = vec![outp];
4663+
txn.push($keysinterface.backing.spend_spendable_outputs(&outputs, Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx).unwrap());
4664+
all_outputs.push(outputs.pop().unwrap());
47694665
}
47704666
},
47714667
_ => panic!("Unexpected event"),
47724668
};
47734669
}
4670+
if all_outputs.len() > 1 {
4671+
if let Ok(tx) = $keysinterface.backing.spend_spendable_outputs(&all_outputs, Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx) {
4672+
txn.push(tx);
4673+
}
4674+
}
47744675
txn
47754676
}
47764677
}
@@ -4860,9 +4761,10 @@ fn test_claim_on_remote_revoked_sizeable_push_msat() {
48604761
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1, 1, true, header.block_hash());
48614762

48624763
let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
4863-
assert_eq!(spend_txn.len(), 2);
4764+
assert_eq!(spend_txn.len(), 3);
48644765
check_spends!(spend_txn[0], revoked_local_txn[0]); // to_remote output on revoked remote commitment_tx
48654766
check_spends!(spend_txn[1], node_txn[0]);
4767+
check_spends!(spend_txn[2], revoked_local_txn[0], node_txn[0]); // Both outputs
48664768
}
48674769

48684770
#[test]
@@ -4957,8 +4859,10 @@ fn test_static_spendable_outputs_timeout_tx() {
49574859
expect_payment_failed!(nodes[1], our_payment_hash, true);
49584860

49594861
let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
4960-
assert_eq!(spend_txn.len(), 2); // SpendableOutput: remote_commitment_tx.to_remote, timeout_tx.output
4862+
assert_eq!(spend_txn.len(), 3); // SpendableOutput: remote_commitment_tx.to_remote, timeout_tx.output
4863+
check_spends!(spend_txn[0], commitment_tx[0]);
49614864
check_spends!(spend_txn[1], node_txn[0]);
4865+
check_spends!(spend_txn[2], node_txn[0], commitment_tx[0]); // All outputs
49624866
}
49634867

49644868
#[test]
@@ -5135,11 +5039,12 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_success_tx() {
51355039

51365040
// Check A's ChannelMonitor was able to generate the right spendable output descriptor
51375041
let spend_txn = check_spendable_outputs!(nodes[0], 1, node_cfgs[0].keys_manager, 100000);
5138-
assert_eq!(spend_txn.len(), 2);
5042+
assert_eq!(spend_txn.len(), 3);
51395043
assert_eq!(spend_txn[0].input.len(), 1);
51405044
check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx
51415045
assert_ne!(spend_txn[0].input[0].previous_output, revoked_htlc_txn[0].input[0].previous_output);
51425046
check_spends!(spend_txn[1], node_txn[1]); // spending justice tx output on the htlc success tx
5047+
check_spends!(spend_txn[2], revoked_local_txn[0], node_txn[1]); // Both outputs
51435048
}
51445049

51455050
#[test]
@@ -5372,6 +5277,7 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
53725277

53735278
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000).0;
53745279
let local_txn = get_local_commitment_txn!(nodes[1], chan_1.2);
5280+
assert_eq!(local_txn.len(), 1);
53755281
assert_eq!(local_txn[0].input.len(), 1);
53765282
check_spends!(local_txn[0], chan_1.3);
53775283

@@ -5392,10 +5298,13 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
53925298
}
53935299
let node_txn = {
53945300
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
5301+
assert_eq!(node_txn.len(), 3);
5302+
assert_eq!(node_txn[0], node_txn[2]);
5303+
assert_eq!(node_txn[1], local_txn[0]);
53955304
assert_eq!(node_txn[0].input.len(), 1);
53965305
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
53975306
check_spends!(node_txn[0], local_txn[0]);
5398-
vec![node_txn[0].clone(), node_txn[2].clone()]
5307+
vec![node_txn[0].clone()]
53995308
};
54005309

54015310
let header_201 = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
@@ -5404,9 +5313,8 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
54045313

54055314
// Verify that B is able to spend its own HTLC-Success tx thanks to spendable output event given back by its ChannelMonitor
54065315
let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
5407-
assert_eq!(spend_txn.len(), 2);
5316+
assert_eq!(spend_txn.len(), 1);
54085317
check_spends!(spend_txn[0], node_txn[0]);
5409-
check_spends!(spend_txn[1], node_txn[1]);
54105318
}
54115319

54125320
fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, announce_latest: bool) {
@@ -5701,9 +5609,10 @@ fn test_dynamic_spendable_outputs_local_htlc_timeout_tx() {
57015609

57025610
// Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor
57035611
let spend_txn = check_spendable_outputs!(nodes[0], 1, node_cfgs[0].keys_manager, 100000);
5704-
assert_eq!(spend_txn.len(), 2);
5612+
assert_eq!(spend_txn.len(), 3);
57055613
check_spends!(spend_txn[0], local_txn[0]);
57065614
check_spends!(spend_txn[1], htlc_timeout);
5615+
check_spends!(spend_txn[2], local_txn[0], htlc_timeout);
57075616
}
57085617

57095618
#[test]
@@ -5771,9 +5680,10 @@ fn test_key_derivation_params() {
57715680
// Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor
57725681
let new_keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet);
57735682
let spend_txn = check_spendable_outputs!(nodes[0], 1, new_keys_manager, 100000);
5774-
assert_eq!(spend_txn.len(), 2);
5683+
assert_eq!(spend_txn.len(), 3);
57755684
check_spends!(spend_txn[0], local_txn_1[0]);
57765685
check_spends!(spend_txn[1], htlc_timeout);
5686+
check_spends!(spend_txn[2], local_txn_1[0], htlc_timeout);
57775687
}
57785688

57795689
#[test]

lightning/src/util/macro_logger.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ impl<'a> std::fmt::Display for DebugSpendable<'a> {
138138
&SpendableOutputDescriptor::StaticOutput { ref outpoint, .. } => {
139139
write!(f, "StaticOutput {}:{} marked for spending", outpoint.txid, outpoint.index)?;
140140
}
141-
&SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, .. } => {
142-
write!(f, "DynamicOutputP2WSH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
141+
&SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) => {
142+
write!(f, "DelayedPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
143143
}
144-
&SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, .. } => {
145-
write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
144+
&SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => {
145+
write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
146146
}
147147
}
148148
Ok(())

lightning/src/util/test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ impl Logger for TestLogger {
419419
}
420420

421421
pub struct TestKeysInterface {
422-
backing: keysinterface::KeysManager,
422+
pub backing: keysinterface::KeysManager,
423423
pub override_session_priv: Mutex<Option<[u8; 32]>>,
424424
pub override_channel_id_priv: Mutex<Option<[u8; 32]>>,
425425
pub disable_revocation_policy_check: bool,
@@ -475,7 +475,7 @@ impl TestKeysInterface {
475475
pub fn new(seed: &[u8; 32], network: Network) -> Self {
476476
let now = Duration::from_secs(genesis_block(network).header.time as u64);
477477
Self {
478-
backing: keysinterface::KeysManager::new(seed, network, now.as_secs(), now.subsec_nanos()),
478+
backing: keysinterface::KeysManager::new(seed, now.as_secs(), now.subsec_nanos()),
479479
override_session_priv: Mutex::new(None),
480480
override_channel_id_priv: Mutex::new(None),
481481
disable_revocation_policy_check: false,

0 commit comments

Comments
 (0)