use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
-use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys, KeysInterface};
+use chain::keysinterface::{SpendableOutputDescriptor, StaticCounterpartyPaymentOutputDescriptor, DynamicP2WSHOutputDescriptor, ChannelKeys, KeysInterface};
use util::logger::Logger;
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
use util::byte_utils;
break;
} else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH {
+ spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
per_commitment_point: broadcasted_holder_revokable_script.1,
to_self_delay: self.on_holder_tx_csv,
revocation_pubkey: broadcasted_holder_revokable_script.2.clone(),
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
- });
+ }));
break;
}
} else if self.counterparty_payment_script == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+ spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
- });
+ }));
break;
} else if outp.script_pubkey == self.shutdown_script {
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
use std::io::Error;
use ln::msgs::DecodeError;
+/// Information about a spendable output to a P2WSH script. See
+/// SpendableOutputDescriptor::DynamicOutputP2WSH for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct DynamicP2WSHOutputDescriptor {
+ /// The outpoint which is spendable
+ pub outpoint: OutPoint,
+ /// Per commitment point to derive delayed_payment_key by key holder
+ pub per_commitment_point: PublicKey,
+ /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
+ /// the witness_script.
+ pub to_self_delay: u16,
+ /// The output which is referenced by the given outpoint
+ pub output: TxOut,
+ /// The revocation_pubkey used to derive witnessScript
+ pub revocation_pubkey: PublicKey,
+ /// Arbitrary identification information returned by a call to
+ /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+ /// the channel to spend the output.
+ pub channel_keys_id: [u8; 32],
+ /// The value of the channel which this output originated from, possibly indirectly.
+ pub channel_value_satoshis: u64,
+}
+impl DynamicP2WSHOutputDescriptor {
+ /// The maximum length a well-formed witness spending one of these should have.
+ // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+ // redeemscript push length.
+ pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
+}
+
+/// Information about a spendable output to our "payment key". See
+/// SpendableOutputDescriptor::StaticOutputCounterpartyPayment for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct StaticCounterpartyPaymentOutputDescriptor {
+ /// The outpoint which is spendable
+ pub outpoint: OutPoint,
+ /// The output which is reference by the given outpoint
+ pub output: TxOut,
+ /// Arbitrary identification information returned by a call to
+ /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+ /// the channel to spend the output.
+ pub channel_keys_id: [u8; 32],
+ /// The value of the channel which this transactions spends.
+ pub channel_value_satoshis: u64,
+}
+impl StaticCounterpartyPaymentOutputDescriptor {
+ /// The maximum length a well-formed witness spending one of these should have.
+ // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+ // redeemscript push length.
+ pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
+}
+
/// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
/// claim at any point in the future) an event is generated which you must track and be able to
/// spend on-chain. The information needed to do this is provided in this enum, including the
/// chan_utils::get_revokeable_redeemscript.
//
// TODO: we need to expose utility methods in KeyManager to do all the relevant derivation.
- DynamicOutputP2WSH {
- /// The outpoint which is spendable
- outpoint: OutPoint,
- /// Per commitment point to derive delayed_payment_key by key holder
- per_commitment_point: PublicKey,
- /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
- /// the witness_script.
- to_self_delay: u16,
- /// The output which is referenced by the given outpoint
- output: TxOut,
- /// The revocation_pubkey used to derive witnessScript
- revocation_pubkey: PublicKey,
- /// Arbitrary identification information returned by a call to
- /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
- /// the channel to spend the output.
- channel_keys_id: [u8; 32],
- /// The value of the channel which this output originated from, possibly indirectly.
- channel_value_satoshis: u64,
- },
+ DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor),
/// An output to a P2WPKH, spendable exclusively by our payment key (ie the private key which
/// corresponds to the public key in ChannelKeys::pubkeys().payment_point).
/// The witness in the spending input, is, thus, simply:
///
/// These are generally the result of our counterparty having broadcast the current state,
/// allowing us to claim the non-HTLC-encumbered outputs immediately.
- StaticOutputCounterpartyPayment {
- /// The outpoint which is spendable
- outpoint: OutPoint,
- /// The output which is reference by the given outpoint
- output: TxOut,
- /// Arbitrary identification information returned by a call to
- /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
- /// the channel to spend the output.
- channel_keys_id: [u8; 32],
- /// The value of the channel which this transactions spends.
- channel_value_satoshis: u64,
- }
+ StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor),
}
impl Writeable for SpendableOutputDescriptor {
outpoint.write(writer)?;
output.write(writer)?;
},
- &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis } => {
+ &SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
1u8.write(writer)?;
- outpoint.write(writer)?;
- per_commitment_point.write(writer)?;
- to_self_delay.write(writer)?;
- output.write(writer)?;
- revocation_pubkey.write(writer)?;
- channel_keys_id.write(writer)?;
- channel_value_satoshis.write(writer)?;
+ descriptor.outpoint.write(writer)?;
+ descriptor.per_commitment_point.write(writer)?;
+ descriptor.to_self_delay.write(writer)?;
+ descriptor.output.write(writer)?;
+ descriptor.revocation_pubkey.write(writer)?;
+ descriptor.channel_keys_id.write(writer)?;
+ descriptor.channel_value_satoshis.write(writer)?;
},
- &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
+ &SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
2u8.write(writer)?;
- outpoint.write(writer)?;
- output.write(writer)?;
- channel_keys_id.write(writer)?;
- channel_value_satoshis.write(writer)?;
+ descriptor.outpoint.write(writer)?;
+ descriptor.output.write(writer)?;
+ descriptor.channel_keys_id.write(writer)?;
+ descriptor.channel_value_satoshis.write(writer)?;
},
}
Ok(())
outpoint: Readable::read(reader)?,
output: Readable::read(reader)?,
}),
- 1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH {
+ 1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH(DynamicP2WSHOutputDescriptor {
outpoint: Readable::read(reader)?,
per_commitment_point: Readable::read(reader)?,
to_self_delay: Readable::read(reader)?,
revocation_pubkey: Readable::read(reader)?,
channel_keys_id: Readable::read(reader)?,
channel_value_satoshis: Readable::read(reader)?,
- }),
- 2u8 => Ok(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+ })),
+ 2u8 => Ok(SpendableOutputDescriptor::StaticOutputCounterpartyPayment(StaticCounterpartyPaymentOutputDescriptor {
outpoint: Readable::read(reader)?,
output: Readable::read(reader)?,
channel_keys_id: Readable::read(reader)?,
channel_value_satoshis: Readable::read(reader)?,
- }),
+ })),
_ => Err(DecodeError::InvalidValue),
}
}
}
}
+/// The maximum length of a script returned by get_revokeable_redeemscript.
+// Calculated as 6 bytes of opcodes, 1 byte push plus 2 bytes for contest_delay, and two public
+// keys of 33 bytes (+ 1 push).
+pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 3 + 34*2;
+
/// A script either spendable by the revocation
/// key or the broadcaster_delayed_payment_key and satisfying the relative-locktime OP_CSV constrain.
/// Encumbering a `to_holder` output on a commitment transaction or 2nd-stage HTLC transactions.
pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u16, broadcaster_delayed_payment_key: &PublicKey) -> Script {
- Builder::new().push_opcode(opcodes::all::OP_IF)
+ let res = Builder::new().push_opcode(opcodes::all::OP_IF)
.push_slice(&revocation_key.serialize())
.push_opcode(opcodes::all::OP_ELSE)
.push_int(contest_delay as i64)
.push_slice(&broadcaster_delayed_payment_key.serialize())
.push_opcode(opcodes::all::OP_ENDIF)
.push_opcode(opcodes::all::OP_CHECKSIG)
- .into_script()
+ .into_script();
+ debug_assert!(res.len() <= REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH);
+ res
}
#[derive(Clone, PartialEq)]
Event::SpendableOutputs { ref outputs } => {
for outp in outputs {
match *outp {
- SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
- assert_eq!(channel_value_satoshis, $chan_value);
+ SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
+ assert_eq!(descriptor.channel_value_satoshis, $chan_value);
let input = TxIn {
- previous_output: outpoint.into_bitcoin_outpoint(),
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
script_sig: Script::new(),
sequence: 0,
witness: Vec::new(),
};
let outp = TxOut {
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
- value: output.value,
+ value: descriptor.output.value,
};
let mut spend_tx = Transaction {
version: 2,
};
spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
let secp_ctx = Secp256k1::new();
- let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
+ let keys = $keysinterface.derive_channel_keys($chan_value, &descriptor.channel_keys_id);
let remotepubkey = keys.pubkeys().payment_point;
let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
- let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
+ let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, descriptor.output.value, SigHashType::All)[..]).unwrap();
let remotesig = secp_ctx.sign(&sighash, &keys.inner.payment_key);
spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec());
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec());
txn.push(spend_tx);
},
- SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis } => {
- assert_eq!(channel_value_satoshis, $chan_value);
+ SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
+ assert_eq!(descriptor.channel_value_satoshis, $chan_value);
let input = TxIn {
- previous_output: outpoint.into_bitcoin_outpoint(),
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
script_sig: Script::new(),
- sequence: *to_self_delay as u32,
+ sequence: descriptor.to_self_delay as u32,
witness: Vec::new(),
};
let outp = TxOut {
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
- value: output.value,
+ value: descriptor.output.value,
};
let mut spend_tx = Transaction {
version: 2,
output: vec![outp],
};
let secp_ctx = Secp256k1::new();
- let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
- if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &keys.inner.delayed_payment_base_key) {
+ let keys = $keysinterface.derive_channel_keys($chan_value, &descriptor.channel_keys_id);
+ if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &keys.inner.delayed_payment_base_key) {
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
- let witness_script = chan_utils::get_revokeable_redeemscript(revocation_pubkey, *to_self_delay, &delayed_payment_pubkey);
+ let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
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
- let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
+ let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, descriptor.output.value, SigHashType::All)[..]).unwrap();
let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
spend_tx.input[0].witness.push(local_delayedsig.serialize_der().to_vec());
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
&SpendableOutputDescriptor::StaticOutput { ref outpoint, .. } => {
write!(f, "StaticOutput {}:{} marked for spending", outpoint.txid, outpoint.index)?;
}
- &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, .. } => {
- write!(f, "DynamicOutputP2WSH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+ &SpendableOutputDescriptor::DynamicOutputP2WSH(ref descriptor) => {
+ write!(f, "DynamicOutputP2WSH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
}
- &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, .. } => {
- write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+ &SpendableOutputDescriptor::StaticOutputCounterpartyPayment(ref descriptor) => {
+ write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
}
}
Ok(())