X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=2da9e575047f68ec805fe0089642cdd90169ce92;hb=c828ff42c006abf3ceffe552dfe510f9f3538e11;hp=9714e992d5912276f004d964f10eb5e6b10ce235;hpb=0821fb3de07b8567ec007a2fa6e246c424264401;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 9714e992..2da9e575 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -26,6 +26,7 @@ use bitcoin::hash_types::WPubkeyHash; use bitcoin::secp256k1::key::{SecretKey, PublicKey}; use bitcoin::secp256k1::{Secp256k1, Signature, Signing}; +use bitcoin::secp256k1::recovery::RecoverableSignature; use bitcoin::secp256k1; use util::{byte_utils, transaction_utils}; @@ -33,12 +34,13 @@ use util::ser::{Writeable, Writer, Readable}; use chain::transaction::OutPoint; use ln::chan_utils; -use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction}; +use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction}; use ln::msgs::UnsignedChannelAnnouncement; +use ln::script::ShutdownScript; -use std::collections::HashSet; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::io::Error; +use prelude::*; +use core::sync::atomic::{AtomicUsize, Ordering}; +use io::{self, Error}; use ln::msgs::{DecodeError, MAX_VALUE_MSAT}; /// Information about a spendable output to a P2WSH script. See @@ -71,6 +73,16 @@ impl DelayedPaymentOutputDescriptor { pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1; } +impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { + (0, outpoint, required), + (2, per_commitment_point, required), + (4, to_self_delay, required), + (6, output, required), + (8, revocation_pubkey, required), + (10, channel_keys_id, required), + (12, channel_value_satoshis, required), +}); + /// Information about a spendable output to our "payment key". See /// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this. #[derive(Clone, Debug, PartialEq)] @@ -92,6 +104,12 @@ impl StaticPaymentOutputDescriptor { // redeemscript push length. pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34; } +impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { + (0, outpoint, required), + (2, output, required), + (4, channel_keys_id, required), + (6, channel_value_satoshis, required), +}); /// 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 @@ -101,8 +119,8 @@ impl StaticPaymentOutputDescriptor { #[derive(Clone, Debug, PartialEq)] pub enum SpendableOutputDescriptor { /// An output to a script which was provided via KeysInterface directly, either from - /// `get_destination_script()` or `get_shutdown_pubkey()`, thus you should already know how to - /// spend it. No secret keys are provided as rust-lightning was never given any key. + /// `get_destination_script()` or `get_shutdown_scriptpubkey()`, thus you should already know + /// how to spend it. No secret keys are provided as rust-lightning was never given any key. /// These may include outputs from a transaction punishing our counterparty or claiming an HTLC /// on-chain using the payment preimage or after it has timed out. StaticOutput { @@ -150,62 +168,15 @@ pub enum SpendableOutputDescriptor { StaticPaymentOutput(StaticPaymentOutputDescriptor), } -impl Writeable for SpendableOutputDescriptor { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => { - 0u8.write(writer)?; - outpoint.write(writer)?; - output.write(writer)?; - }, - &SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) => { - 1u8.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::StaticPaymentOutput(ref descriptor) => { - 2u8.write(writer)?; - descriptor.outpoint.write(writer)?; - descriptor.output.write(writer)?; - descriptor.channel_keys_id.write(writer)?; - descriptor.channel_value_satoshis.write(writer)?; - }, - } - Ok(()) - } -} - -impl Readable for SpendableOutputDescriptor { - fn read(reader: &mut R) -> Result { - match Readable::read(reader)? { - 0u8 => Ok(SpendableOutputDescriptor::StaticOutput { - outpoint: Readable::read(reader)?, - output: Readable::read(reader)?, - }), - 1u8 => Ok(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor { - outpoint: Readable::read(reader)?, - per_commitment_point: Readable::read(reader)?, - to_self_delay: Readable::read(reader)?, - output: Readable::read(reader)?, - revocation_pubkey: Readable::read(reader)?, - channel_keys_id: Readable::read(reader)?, - channel_value_satoshis: Readable::read(reader)?, - })), - 2u8 => Ok(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor { - outpoint: Readable::read(reader)?, - output: Readable::read(reader)?, - channel_keys_id: Readable::read(reader)?, - channel_value_satoshis: Readable::read(reader)?, - })), - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(SpendableOutputDescriptor, + (0, StaticOutput) => { + (0, outpoint, required), + (2, output, required), + }, +; + (1, DelayedPaymentOutput), + (2, StaticPaymentOutput), +); /// A trait to sign lightning channel transactions as described in BOLT 3. /// @@ -226,7 +197,7 @@ impl Readable for SpendableOutputDescriptor { /// of LN security model, orthogonal of key management issues. // TODO: We should remove Clone by instead requesting a new Sign copy when we create // ChannelMonitors instead of expecting to clone the one out of the Channel into the monitors. -pub trait BaseSign : Send { +pub trait BaseSign { /// Gets the per-commitment point for a specific commitment number /// /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards. @@ -241,6 +212,13 @@ pub trait BaseSign : Send { /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards. // TODO: return a Result so we can signal a validation error fn release_commitment_secret(&self, idx: u64) -> [u8; 32]; + /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs. + /// + /// This is required in order for the signer to make sure that releasing a commitment + /// secret won't leave us without a broadcastable holder transaction. + /// Policy checks should be implemented in this function, including checking the amount + /// sent to us and checking the HTLCs. + fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>; /// Gets the holder's channel public keys and basepoints fn pubkeys(&self) -> &ChannelPublicKeys; /// Gets an arbitrary identifier describing the set of keys which are provided back to you in @@ -251,9 +229,17 @@ pub trait BaseSign : Send { /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions. /// /// Note that if signing fails or is rejected, the channel will be force-closed. + /// + /// Policy checks should be implemented in this function, including checking the amount + /// sent to us and checking the HTLCs. // // TODO: Document the things someone using this interface should enforce before signing. fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()>; + /// Validate the counterparty's revocation. + /// + /// This is required in order for the signer to make sure that the state has moved + /// forward and it is safe to sign the next counterparty commitment. + fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>; /// Create a signatures for a holder's commitment transaction and its claiming HTLC transactions. /// This will only ever be called with a non-revoked commitment_tx. This will be called with the @@ -278,12 +264,28 @@ pub trait BaseSign : Send { #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))] fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()>; - /// Create a signature for the given input in a transaction spending an HTLC or commitment - /// transaction output when our counterparty broadcasts an old state. + /// Create a signature for the given input in a transaction spending an HTLC transaction output + /// or a commitment transaction `to_local` output when our counterparty broadcasts an old state. + /// + /// A justice transaction may claim multiple outputs at the same time if timelocks are + /// similar, but only a signature for the input at index `input` should be signed for here. + /// It may be called multiple times for same output(s) if a fee-bump is needed with regards + /// to an upcoming timelock expiration. + /// + /// Amount is value of the output spent by this input, committed to in the BIP 143 signature. + /// + /// per_commitment_key is revocation secret which was provided by our counterparty when they + /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does + /// not allow the spending of any funds by itself (you need our holder revocation_secret to do + /// so). + fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1) -> Result; + + /// Create a signature for the given input in a transaction spending a commitment transaction + /// HTLC output when our counterparty broadcasts an old state. /// - /// A justice transaction may claim multiples outputs at the same time if timelocks are + /// A justice transaction may claim multiple outputs at the same time if timelocks are /// similar, but only a signature for the input at index `input` should be signed for here. - /// It may be called multiples time for same output(s) if a fee-bump is needed with regards + /// It may be called multiple times for same output(s) if a fee-bump is needed with regards /// to an upcoming timelock expiration. /// /// Amount is value of the output spent by this input, committed to in the BIP 143 signature. @@ -293,10 +295,9 @@ pub trait BaseSign : Send { /// not allow the spending of any funds by itself (you need our holder revocation_secret to do /// so). /// - /// htlc holds HTLC elements (hash, timelock) if the output being spent is a HTLC output, thus - /// changing the format of the witness script (which is committed to in the BIP 143 - /// signatures). - fn sign_justice_transaction(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &Option, secp_ctx: &Secp256k1) -> Result; + /// htlc holds HTLC elements (hash, timelock), thus changing the format of the witness script + /// (which is committed to in the BIP 143 signatures). + fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result; /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment /// transaction, either offered or received. @@ -321,7 +322,7 @@ pub trait BaseSign : Send { /// /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have /// chosen to forgo their output as dust. - fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result; + fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result; /// Signs a channel announcement message with our funding key, proving it comes from one /// of the channel participants. @@ -353,7 +354,7 @@ pub trait Sign: BaseSign + Writeable + Clone { } /// A trait to describe an object which can get user secrets and key material. -pub trait KeysInterface: Send + Sync { +pub trait KeysInterface { /// A type which implements Sign which will be returned by get_channel_signer. type Signer : Sign; @@ -366,12 +367,11 @@ pub trait KeysInterface: Send + Sync { /// This method should return a different value each time it is called, to avoid linking /// on-chain funds across channels as controlled to the same user. fn get_destination_script(&self) -> Script; - /// Get a public key which we will send funds to (in the form of a P2WPKH output) when closing - /// a channel. + /// Get a script pubkey which we will send funds to when closing a channel. /// /// This method should return a different value each time it is called, to avoid linking /// on-chain funds across channels as controlled to the same user. - fn get_shutdown_pubkey(&self) -> PublicKey; + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript; /// Get a new set of Sign for per-channel secrets. These MUST be unique even if you /// restarted with some stale data! /// @@ -391,6 +391,12 @@ pub trait KeysInterface: Send + Sync { /// contain no versioning scheme. You may wish to include your own version prefix and ensure /// you've read all of the provided bytes to ensure no corruption occurred. fn read_chan_signer(&self, reader: &[u8]) -> Result; + + /// Sign an invoice's preimage (note that this is the preimage of the invoice, not the HTLC's + /// preimage). By parameterizing by the preimage instead of the hash, we allow implementors of + /// this trait to parse the invoice and make sure they're signing what they expect, rather than + /// blindly signing the hash. + fn sign_invoice(&self, invoice_preimage: Vec) -> Result; } #[derive(Clone)] @@ -567,6 +573,10 @@ impl BaseSign for InMemorySigner { 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 } @@ -586,16 +596,17 @@ impl BaseSign for InMemorySigner { 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 = match chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key) { - Ok(s) => s, - Err(_) => return Err(()), - }; + 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)); } Ok((commitment_sig, htlc_sigs)) } + fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> { + Ok(()) + } + fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey); @@ -617,32 +628,27 @@ impl BaseSign for InMemorySigner { Ok((sig, htlc_sigs)) } - fn sign_justice_transaction(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &Option, secp_ctx: &Secp256k1) -> Result { - let revocation_key = match chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key) { - Ok(revocation_key) => revocation_key, - Err(_) => return Err(()) - }; + fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1) -> Result { + let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?; let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); - let revocation_pubkey = match chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) { - Ok(revocation_pubkey) => revocation_pubkey, - Err(_) => return Err(()) + let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?; + let witness_script = { + let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint).map_err(|_| ())?; + chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey) }; - let witness_script = if let &Some(ref htlc) = htlc { - let counterparty_htlcpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint) { - Ok(counterparty_htlcpubkey) => counterparty_htlcpubkey, - Err(_) => return Err(()) - }; - let holder_htlcpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint) { - Ok(holder_htlcpubkey) => holder_htlcpubkey, - Err(_) => return Err(()) - }; + let mut sighash_parts = bip143::SigHashCache::new(justice_tx); + let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]); + return Ok(secp_ctx.sign(&sighash, &revocation_key)) + } + + fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { + let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?; + let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); + let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?; + let witness_script = { + let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint).map_err(|_| ())?; + let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint).map_err(|_| ())?; chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey) - } else { - let counterparty_delayedpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint) { - Ok(counterparty_delayedpubkey) => counterparty_delayedpubkey, - Err(_) => return Err(()) - }; - chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey) }; let mut sighash_parts = bip143::SigHashCache::new(justice_tx); let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]); @@ -665,17 +671,10 @@ impl BaseSign for InMemorySigner { Err(()) } - fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { - if closing_tx.input.len() != 1 { return Err(()); } - if closing_tx.input[0].witness.len() != 0 { return Err(()); } - if closing_tx.output.len() > 2 { return Err(()); } - + fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result { 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 sighash = hash_to_message!(&bip143::SigHashCache::new(closing_tx) - .signature_hash(0, &channel_funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]); - Ok(secp_ctx.sign(&sighash, &self.funding_key)) + Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx)) } fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result { @@ -690,10 +689,15 @@ impl BaseSign for InMemorySigner { } } +const SERIALIZATION_VERSION: u8 = 1; +const MIN_SERIALIZATION_VERSION: u8 = 1; + impl Sign for InMemorySigner {} impl Writeable for InMemorySigner { fn write(&self, writer: &mut W) -> Result<(), Error> { + write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); + self.funding_key.write(writer)?; self.revocation_base_key.write(writer)?; self.payment_key.write(writer)?; @@ -704,12 +708,16 @@ impl Writeable for InMemorySigner { self.channel_value_satoshis.write(writer)?; self.channel_keys_id.write(writer)?; + write_tlv_fields!(writer, {}); + Ok(()) } } impl Readable for InMemorySigner { - fn read(reader: &mut R) -> Result { + fn read(reader: &mut R) -> Result { + let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION); + let funding_key = Readable::read(reader)?; let revocation_base_key = Readable::read(reader)?; let payment_key = Readable::read(reader)?; @@ -725,6 +733,8 @@ impl Readable for InMemorySigner { &htlc_base_key); let keys_id = Readable::read(reader)?; + read_tlv_fields!(reader, {}); + Ok(InMemorySigner { funding_key, revocation_base_key, @@ -843,7 +853,7 @@ impl KeysManager { /// onchain output detection for which a corresponding delayed_payment_key must be derived. pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner { let chan_id = byte_utils::slice_to_be64(¶ms[0..8]); - assert!(chan_id <= std::u32::MAX as u64); // Otherwise the params field wasn't created by us + assert!(chan_id <= core::u32::MAX as u64); // Otherwise the params field wasn't created by us let mut unique_start = Sha256::engine(); unique_start.input(params); unique_start.input(&self.seed); @@ -1019,13 +1029,13 @@ impl KeysInterface for KeysManager { self.destination_script.clone() } - fn get_shutdown_pubkey(&self) -> PublicKey { - self.shutdown_pubkey.clone() + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { + ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone()) } fn get_channel_signer(&self, _inbound: bool, channel_value_satoshis: u64) -> Self::Signer { let child_ix = self.channel_child_index.fetch_add(1, Ordering::AcqRel); - assert!(child_ix <= std::u32::MAX as usize); + assert!(child_ix <= core::u32::MAX as usize); let mut id = [0; 32]; id[0..8].copy_from_slice(&byte_utils::be64_to_array(child_ix as u64)); id[8..16].copy_from_slice(&byte_utils::be64_to_array(self.starting_time_nanos as u64)); @@ -1045,7 +1055,11 @@ impl KeysInterface for KeysManager { } fn read_chan_signer(&self, reader: &[u8]) -> Result { - InMemorySigner::read(&mut std::io::Cursor::new(reader)) + InMemorySigner::read(&mut io::Cursor::new(reader)) + } + + fn sign_invoice(&self, invoice_preimage: Vec) -> Result { + Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&invoice_preimage)), &self.get_node_secret())) } } @@ -1053,4 +1067,4 @@ impl KeysInterface for KeysManager { #[test] pub fn dyn_sign() { let _signer: Box; -} \ No newline at end of file +}