-impl ChannelKeys for InMemoryChannelKeys {
- fn funding_key(&self) -> &SecretKey { &self.funding_key }
- fn revocation_base_key(&self) -> &SecretKey { &self.revocation_base_key }
- fn payment_base_key(&self) -> &SecretKey { &self.payment_base_key }
- fn delayed_payment_base_key(&self) -> &SecretKey { &self.delayed_payment_base_key }
- fn htlc_base_key(&self) -> &SecretKey { &self.htlc_base_key }
- fn commitment_seed(&self) -> &[u8; 32] { &self.commitment_seed }
-
- fn sign_remote_commitment<T: secp256k1::Signing>(&self, channel_value_satoshis: u64, channel_funding_script: &Script, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
- if commitment_tx.input.len() != 1 { return Err(()); }
- let commitment_sighash = hash_to_message!(&bip143::SighashComponents::new(&commitment_tx).sighash_all(&commitment_tx.input[0], &channel_funding_script, channel_value_satoshis)[..]);
- let commitment_sig = secp_ctx.sign(&commitment_sighash, &self.funding_key);
-
- let commitment_txid = commitment_tx.txid();
-
- let mut htlc_sigs = Vec::with_capacity(htlcs.len());
- for ref htlc in htlcs {
- if let Some(_) = htlc.transaction_output_index {
- let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, feerate_per_kw, to_self_delay, htlc, &keys.a_delayed_payment_key, &keys.revocation_key);
- let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
- let htlc_sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
- let our_htlc_key = match chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key) {
- Ok(s) => s,
- Err(_) => return Err(()),
- };
- htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &our_htlc_key));
- }
+impl InMemorySigner {
+ /// Create a new InMemorySigner
+ pub fn new<C: Signing>(
+ secp_ctx: &Secp256k1<C>,
+ funding_key: SecretKey,
+ revocation_base_key: SecretKey,
+ payment_key: SecretKey,
+ delayed_payment_base_key: SecretKey,
+ htlc_base_key: SecretKey,
+ commitment_seed: [u8; 32],
+ channel_value_satoshis: u64,
+ channel_keys_id: [u8; 32]) -> InMemorySigner {
+ let holder_channel_pubkeys =
+ InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key,
+ &payment_key, &delayed_payment_base_key,
+ &htlc_base_key);
+ InMemorySigner {
+ funding_key,
+ revocation_base_key,
+ payment_key,
+ delayed_payment_base_key,
+ htlc_base_key,
+ commitment_seed,
+ channel_value_satoshis,
+ holder_channel_pubkeys,
+ channel_parameters: None,
+ channel_keys_id,
+ }
+ }
+
+ fn make_holder_keys<C: Signing>(secp_ctx: &Secp256k1<C>,
+ funding_key: &SecretKey,
+ revocation_base_key: &SecretKey,
+ payment_key: &SecretKey,
+ delayed_payment_base_key: &SecretKey,
+ htlc_base_key: &SecretKey) -> ChannelPublicKeys {
+ let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s);
+ ChannelPublicKeys {
+ funding_pubkey: from_secret(&funding_key),
+ revocation_basepoint: from_secret(&revocation_base_key),
+ payment_point: from_secret(&payment_key),
+ delayed_payment_basepoint: from_secret(&delayed_payment_base_key),
+ htlc_basepoint: from_secret(&htlc_base_key),
+ }
+ }
+
+ /// Counterparty pubkeys.
+ /// Will panic if ready_channel wasn't called.
+ pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
+
+ /// The contest_delay value specified by our counterparty and applied on holder-broadcastable
+ /// transactions, ie the amount of time that we have to wait to recover our funds if we
+ /// broadcast a transaction.
+ /// Will panic if ready_channel wasn't called.
+ pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
+
+ /// The contest_delay value specified by us and applied on transactions broadcastable
+ /// by our counterparty, ie the amount of time that they have to wait to recover their funds
+ /// if they broadcast a transaction.
+ /// Will panic if ready_channel wasn't called.
+ pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
+
+ /// Whether the holder is the initiator
+ /// Will panic if ready_channel wasn't called.
+ pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
+
+ /// Funding outpoint
+ /// Will panic if ready_channel wasn't called.
+ pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
+
+ /// Obtain a ChannelTransactionParameters for this channel, to be used when verifying or
+ /// building transactions.
+ ///
+ /// Will panic if ready_channel wasn't called.
+ pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
+ self.channel_parameters.as_ref().unwrap()
+ }
+
+ /// Sign the single input of spend_tx at index `input_idx` which spends the output
+ /// described by descriptor, returning the witness stack for the input.
+ ///
+ /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
+ /// or is not spending the outpoint described by `descriptor.outpoint`.
+ pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+ // TODO: We really should be taking the SigHashCache as a parameter here instead of
+ // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+ // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+ // bindings updates to support SigHashCache objects).
+ if spend_tx.input.len() <= input_idx { return Err(()); }
+ if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+ if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+
+ let remotepubkey = self.pubkeys().payment_point;
+ let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
+ let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
+ let remotesig = secp_ctx.sign(&sighash, &self.payment_key);
+
+ let mut witness = Vec::with_capacity(2);
+ witness.push(remotesig.serialize_der().to_vec());
+ witness[0].push(SigHashType::All as u8);
+ witness.push(remotepubkey.serialize().to_vec());
+ Ok(witness)
+ }
+
+ /// Sign the single input of spend_tx at index `input_idx` which spends the output
+ /// described by descriptor, returning the witness stack for the input.
+ ///
+ /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
+ /// is not spending the outpoint described by `descriptor.outpoint`, or does not have a
+ /// sequence set to `descriptor.to_self_delay`.
+ pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+ // TODO: We really should be taking the SigHashCache as a parameter here instead of
+ // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+ // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+ // bindings updates to support SigHashCache objects).
+ if spend_tx.input.len() <= input_idx { return Err(()); }
+ if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+ if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+ if spend_tx.input[input_idx].sequence != descriptor.to_self_delay as u32 { return Err(()); }
+
+ let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key)
+ .expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted.");
+ let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
+ let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
+ let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
+ let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
+
+ let mut witness = Vec::with_capacity(3);
+ witness.push(local_delayedsig.serialize_der().to_vec());
+ witness[0].push(SigHashType::All as u8);
+ witness.push(vec!()); //MINIMALIF
+ witness.push(witness_script.clone().into_bytes());
+ Ok(witness)
+ }
+}
+
+impl BaseSign for InMemorySigner {
+ fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey {
+ let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap();
+ PublicKey::from_secret_key(secp_ctx, &commitment_secret)
+ }
+
+ fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
+ chan_utils::build_commitment_secret(&self.commitment_seed, idx)
+ }
+
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
+ Ok(())
+ }
+
+ fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
+ fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
+
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ let trusted_tx = commitment_tx.trust();
+ let keys = trusted_tx.keys();
+
+ let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
+ let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
+
+ let built_tx = trusted_tx.built_transaction();
+ let commitment_sig = built_tx.sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
+ let commitment_txid = built_tx.txid;
+
+ let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
+ for htlc in commitment_tx.htlcs() {
+ let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
+ let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
+ let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, SigHashType::All)[..]);
+ let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
+ htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &holder_htlc_key));