Merge pull request #2213 from benthecarman/error-sign-provider-addrs

Allow get_shutdown_scriptpubkey and get_destination_script to return an Error
This commit is contained in:
Matt Corallo 2023-05-02 17:48:05 +00:00 committed by GitHub
commit 101c09f9bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 38 deletions

View file

@ -259,18 +259,18 @@ impl SignerProvider for KeyProvider {
})
}
fn get_destination_script(&self) -> Script {
fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, self.node_secret[31]]).unwrap();
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()
Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script())
}
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let secret_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, self.node_secret[31]]).unwrap();
let pubkey_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &secret_key).serialize());
ShutdownScript::new_p2wpkh(&pubkey_hash)
Ok(ShutdownScript::new_p2wpkh(&pubkey_hash))
}
}

View file

@ -376,18 +376,18 @@ impl SignerProvider for KeyProvider {
))
}
fn get_destination_script(&self) -> Script {
fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()
Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script())
}
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let secret_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
let pubkey_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &secret_key).serialize());
ShutdownScript::new_p2wpkh(&pubkey_hash)
Ok(ShutdownScript::new_p2wpkh(&pubkey_hash))
}
}

View file

@ -141,9 +141,9 @@ impl SignerProvider for KeyProvider {
fn read_chan_signer(&self, _data: &[u8]) -> Result<EnforcingSigner, DecodeError> { unreachable!() }
fn get_destination_script(&self) -> Script { unreachable!() }
fn get_destination_script(&self) -> Result<Script, ()> { unreachable!() }
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!() }
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> { unreachable!() }
}
#[cfg(test)]

View file

@ -543,15 +543,21 @@ pub trait SignerProvider {
/// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
///
/// 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 script pubkey which we will send funds to when closing a channel.
/// If this function returns an error, this will result in a channel failing to open.
///
/// 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_scriptpubkey(&self) -> ShutdownScript;
fn get_destination_script(&self) -> Result<Script, ()>;
/// Get a script pubkey which we will send funds to when closing a channel.
///
/// If this function returns an error, this will result in a channel failing to open or close.
/// In the event of a failure when the counterparty is initiating a close, this can result in a
/// channel force close.
///
/// 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_scriptpubkey(&self) -> Result<ShutdownScript, ()>;
}
/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
@ -1366,12 +1372,12 @@ impl SignerProvider for KeysManager {
InMemorySigner::read(&mut io::Cursor::new(reader), self)
}
fn get_destination_script(&self) -> Script {
self.destination_script.clone()
fn get_destination_script(&self) -> Result<Script, ()> {
Ok(self.destination_script.clone())
}
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
Ok(ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone()))
}
}
@ -1461,11 +1467,11 @@ impl SignerProvider for PhantomKeysManager {
self.inner.read_chan_signer(reader)
}
fn get_destination_script(&self) -> Script {
fn get_destination_script(&self) -> Result<Script, ()> {
self.inner.get_destination_script()
}
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
self.inner.get_shutdown_scriptpubkey()
}
}

View file

@ -986,7 +986,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
Some(signer_provider.get_shutdown_scriptpubkey())
match signer_provider.get_shutdown_scriptpubkey() {
Ok(scriptpubkey) => Some(scriptpubkey),
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned()}),
}
} else { None };
if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
@ -995,6 +998,11 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}
let destination_script = match signer_provider.get_destination_script() {
Ok(script) => script,
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
};
let temporary_channel_id = entropy_source.get_secure_random_bytes();
Ok(Channel {
@ -1021,7 +1029,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
holder_signer,
shutdown_scriptpubkey,
destination_script: signer_provider.get_destination_script(),
destination_script,
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
@ -1333,7 +1341,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
} else { None };
let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
Some(signer_provider.get_shutdown_scriptpubkey())
match signer_provider.get_shutdown_scriptpubkey() {
Ok(scriptpubkey) => Some(scriptpubkey),
Err(_) => return Err(ChannelError::Close("Failed to get upfront shutdown scriptpubkey".to_owned())),
}
} else { None };
if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
@ -1342,6 +1353,11 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}
let destination_script = match signer_provider.get_destination_script() {
Ok(script) => script,
Err(_) => return Err(ChannelError::Close("Failed to get destination script".to_owned())),
};
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
@ -1368,7 +1384,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
holder_signer,
shutdown_scriptpubkey,
destination_script: signer_provider.get_destination_script(),
destination_script,
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
@ -4355,7 +4371,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
Some(_) => false,
None => {
assert!(send_shutdown);
let shutdown_scriptpubkey = signer_provider.get_shutdown_scriptpubkey();
let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
Ok(scriptpubkey) => scriptpubkey,
Err(_) => return Err(ChannelError::Close("Failed to get shutdown scriptpubkey".to_owned())),
};
if !shutdown_scriptpubkey.is_compatible(their_features) {
return Err(ChannelError::Close(format!("Provided a scriptpubkey format not accepted by peer: {}", shutdown_scriptpubkey)));
}
@ -6062,7 +6081,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
let update_shutdown_script = match self.shutdown_scriptpubkey {
Some(_) => false,
None if !chan_closed => {
let shutdown_scriptpubkey = signer_provider.get_shutdown_scriptpubkey();
let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
Ok(scriptpubkey) => scriptpubkey,
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned() }),
};
if !shutdown_scriptpubkey.is_compatible(their_features) {
return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
}
@ -7087,17 +7109,17 @@ mod tests {
fn read_chan_signer(&self, _data: &[u8]) -> Result<Self::Signer, DecodeError> { panic!(); }
fn get_destination_script(&self) -> Script {
fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
let channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&channel_monitor_claim_key_hash[..]).into_script()
Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&channel_monitor_claim_key_hash[..]).into_script())
}
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_close_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
ShutdownScript::new_p2wpkh_from_pubkey(PublicKey::from_secret_key(&secp_ctx, &channel_close_key))
Ok(ShutdownScript::new_p2wpkh_from_pubkey(PublicKey::from_secret_key(&secp_ctx, &channel_close_key)))
}
}

View file

@ -1854,6 +1854,10 @@ where
/// Raises [`APIError::APIMisuseError`] when `channel_value_satoshis` > 2**24 or `push_msat` is
/// greater than `channel_value_satoshis * 1k` or `channel_value_satoshis < 1000`.
///
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be opened due to failing to
/// generate a shutdown scriptpubkey or destination script set by
/// [`SignerProvider::get_shutdown_scriptpubkey`] or [`SignerProvider::get_destination_script`].
///
/// Note that we do not check if you are currently connected to the given peer. If no
/// connection is available, the outbound `open_channel` message may fail to send, resulting in
/// the channel eventually being silently forgotten (dropped on reload).
@ -2098,6 +2102,11 @@ where
///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
/// generate a shutdown scriptpubkey or destination script set by
/// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
/// channel.
///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
@ -2122,6 +2131,11 @@ where
///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
/// generate a shutdown scriptpubkey or destination script set by
/// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
/// channel.
///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal

View file

@ -669,7 +669,7 @@ fn test_unsupported_anysegwit_shutdown_script() {
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
// Check that using an unsupported shutdown script fails and a supported one succeeds.
let supported_shutdown_script = chanmon_cfgs[1].keys_manager.get_shutdown_scriptpubkey();
let supported_shutdown_script = chanmon_cfgs[1].keys_manager.get_shutdown_scriptpubkey().unwrap();
let unsupported_shutdown_script =
ShutdownScript::new_witness_program(WitnessVersion::V16, &[0, 40]).unwrap();
chanmon_cfgs[1].keys_manager

View file

@ -180,8 +180,8 @@ impl SignerProvider for OnlyReadsKeysInterface {
))
}
fn get_destination_script(&self) -> Script { unreachable!(); }
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
fn get_destination_script(&self) -> Result<Script, ()> { Err(()) }
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> { Err(()) }
}
pub struct TestChainMonitor<'a> {
@ -809,14 +809,14 @@ impl SignerProvider for TestKeysInterface {
))
}
fn get_destination_script(&self) -> Script { self.backing.get_destination_script() }
fn get_destination_script(&self) -> Result<Script, ()> { self.backing.get_destination_script() }
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
match &mut *self.expectations.lock().unwrap() {
None => self.backing.get_shutdown_scriptpubkey(),
Some(expectations) => match expectations.pop_front() {
None => panic!("Unexpected get_shutdown_scriptpubkey"),
Some(expectation) => expectation.returns,
Some(expectation) => Ok(expectation.returns),
},
}
}