Skip to content

Commit 215f4f2

Browse files
committed
Changes to create fresh destination scripts
Make channel_keys_id optional Check output spent script with both old and new destination script Add a parameter for new_style_derivation Derive key from channel_keys_id for spending side Using new derivation index if the bool value is not set Create new master key for fresh destination script Update keysinterface impl in fuzz
1 parent e43cfe1 commit 215f4f2

File tree

6 files changed

+52
-20
lines changed

6 files changed

+52
-20
lines changed

fuzz/src/chanmon_consistency.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl KeysInterface for KeyProvider {
169169
KeyMaterial([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, self.node_id])
170170
}
171171

172-
fn get_destination_script(&self) -> Script {
172+
fn get_destination_script(&self, _channel_keys_id : [u8; 32]) -> Script {
173173
let secp_ctx = Secp256k1::signing_only();
174174
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_id]).unwrap();
175175
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());

fuzz/src/full_stack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ impl KeysInterface for KeyProvider {
273273
self.inbound_payment_key.clone()
274274
}
275275

276-
fn get_destination_script(&self) -> Script {
276+
fn get_destination_script(&self, _channel_keys_id : [u8; 32]) -> Script {
277277
let secp_ctx = Secp256k1::signing_only();
278278
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
279279
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());

lightning/src/chain/channelmonitor.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2922,6 +2922,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
29222922
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
29232923
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
29242924
output: outp.clone(),
2925+
channel_keys_id: Some(self.channel_keys_id),
29252926
});
29262927
break;
29272928
}
@@ -2952,6 +2953,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
29522953
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
29532954
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
29542955
output: outp.clone(),
2956+
channel_keys_id: Some(self.channel_keys_id),
29552957
});
29562958
break;
29572959
}

lightning/src/chain/keysinterface.rs

+41-12
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ pub enum SpendableOutputDescriptor {
137137
outpoint: OutPoint,
138138
/// The output which is referenced by the given outpoint.
139139
output: TxOut,
140+
/// Arbitrary identification information returned by a call to
141+
/// `Sign::channel_keys_id()`. This may be useful in re-deriving keys used in
142+
/// the channel to spend the output.
143+
channel_keys_id: Option<[u8; 32]>
140144
},
141145
/// An output to a P2WSH script which can be spent with a single signature after a CSV delay.
142146
///
@@ -181,6 +185,7 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
181185
(0, StaticOutput) => {
182186
(0, outpoint, required),
183187
(2, output, required),
188+
(3, channel_keys_id, option),
184189
},
185190
;
186191
(1, DelayedPaymentOutput),
@@ -406,7 +411,7 @@ pub trait KeysInterface {
406411
///
407412
/// This method should return a different value each time it is called, to avoid linking
408413
/// on-chain funds across channels as controlled to the same user.
409-
fn get_destination_script(&self) -> Script;
414+
fn get_destination_script(&self, channel_keys_id : [u8; 32]) -> Script;
410415
/// Get a script pubkey which we will send funds to when closing a channel.
411416
///
412417
/// This method should return a different value each time it is called, to avoid linking
@@ -846,7 +851,7 @@ pub struct KeysManager {
846851
node_secret: SecretKey,
847852
inbound_payment_key: KeyMaterial,
848853
destination_script: Script,
849-
shutdown_pubkey: PublicKey,
854+
shutdown_pubkey: ExtendedPubKey,
850855
channel_master_key: ExtendedPrivKey,
851856
channel_child_index: AtomicUsize,
852857

@@ -857,6 +862,7 @@ pub struct KeysManager {
857862
seed: [u8; 32],
858863
starting_time_secs: u64,
859864
starting_time_nanos: u32,
865+
new_style_derivation: bool,
860866
}
861867

862868
impl KeysManager {
@@ -895,7 +901,7 @@ impl KeysManager {
895901
Err(_) => panic!("Your RNG is busted"),
896902
};
897903
let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2).unwrap()) {
898-
Ok(shutdown_key) => ExtendedPubKey::from_private(&secp_ctx, &shutdown_key).public_key.key,
904+
Ok(shutdown_key) => ExtendedPubKey::from_private(&secp_ctx, &shutdown_key),
899905
Err(_) => panic!("Your RNG is busted"),
900906
};
901907
let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted");
@@ -913,7 +919,6 @@ impl KeysManager {
913919
secp_ctx,
914920
node_secret,
915921
inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes),
916-
917922
destination_script,
918923
shutdown_pubkey,
919924

@@ -927,6 +932,7 @@ impl KeysManager {
927932
seed: *seed,
928933
starting_time_secs,
929934
starting_time_nanos,
935+
new_style_derivation: true,
930936
};
931937
let secp_seed = res.get_secure_random_bytes();
932938
res.secp_ctx.seeded_randomize(&secp_seed);
@@ -1031,7 +1037,7 @@ impl KeysManager {
10311037
input_value += descriptor.output.value;
10321038
if !output_set.insert(descriptor.outpoint) { return Err(()); }
10331039
},
1034-
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
1040+
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output, .. } => {
10351041
input.push(TxIn {
10361042
previous_output: outpoint.into_bitcoin_outpoint(),
10371043
script_sig: Script::new(),
@@ -1074,12 +1080,17 @@ impl KeysManager {
10741080
}
10751081
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?;
10761082
},
1077-
SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
1083+
SpendableOutputDescriptor::StaticOutput { ref output, channel_keys_id,.. } => {
1084+
1085+
let destination_script = channel_keys_id.map_or_else(|| self.destination_script.clone(),|s| self.get_destination_script(s));
10781086
let derivation_idx = if output.script_pubkey == self.destination_script {
10791087
1
1088+
} else if output.script_pubkey == destination_script && channel_keys_id.is_some() {
1089+
byte_utils::slice_to_be64(&channel_keys_id.unwrap()[0..8]) as u32
10801090
} else {
10811091
2
10821092
};
1093+
10831094
let secret = {
10841095
// Note that when we aren't serializing the key, network doesn't matter
10851096
match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
@@ -1094,7 +1105,7 @@ impl KeysManager {
10941105
};
10951106
let pubkey = ExtendedPubKey::from_private(&secp_ctx, &secret).public_key;
10961107
if derivation_idx == 2 {
1097-
assert_eq!(pubkey.key, self.shutdown_pubkey);
1108+
assert_eq!(pubkey.key, self.shutdown_pubkey.public_key.key);
10981109
}
10991110
let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
11001111
let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey();
@@ -1134,12 +1145,30 @@ impl KeysInterface for KeysManager {
11341145
self.inbound_payment_key.clone()
11351146
}
11361147

1137-
fn get_destination_script(&self) -> Script {
1138-
self.destination_script.clone()
1148+
fn get_destination_script(&self, channel_keys_id : [u8; 32]) -> Script {
1149+
let derivation_idx = if self.new_style_derivation {
1150+
byte_utils::slice_to_be64(&channel_keys_id[0..8])
1151+
} else {
1152+
3
1153+
};
1154+
match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
1155+
Ok(master_key) => {
1156+
match master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(derivation_idx as u32).expect("key space exhausted")){
1157+
Ok(destination_key) => {
1158+
let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_private(&self.secp_ctx, &destination_key).public_key.to_bytes());
1159+
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0)
1160+
.push_slice(&wpubkey_hash.into_inner())
1161+
.into_script()
1162+
},
1163+
Err(_) => panic!("Your RNG is busted"),
1164+
}
1165+
}
1166+
Err(_) => panic!("Your RNG is busted")
1167+
}
11391168
}
11401169

11411170
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
1142-
ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
1171+
ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.public_key.key.clone())
11431172
}
11441173

11451174
fn get_channel_signer(&self, _inbound: bool, channel_value_satoshis: u64) -> Self::Signer {
@@ -1218,8 +1247,8 @@ impl KeysInterface for PhantomKeysManager {
12181247
self.inbound_payment_key.clone()
12191248
}
12201249

1221-
fn get_destination_script(&self) -> Script {
1222-
self.inner.get_destination_script()
1250+
fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Script {
1251+
self.inner.get_destination_script(channel_keys_id)
12231252
}
12241253

12251254
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {

lightning/src/ln/channel.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ impl<Signer: Sign> Channel<Signer> {
846846
return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
847847
}
848848
}
849-
849+
let channel_keys_id = holder_signer.channel_keys_id();
850850
Ok(Channel {
851851
user_id,
852852
config: config.channel_options.clone(),
@@ -862,7 +862,7 @@ impl<Signer: Sign> Channel<Signer> {
862862

863863
holder_signer,
864864
shutdown_scriptpubkey,
865-
destination_script: keys_provider.get_destination_script(),
865+
destination_script: keys_provider.get_destination_script(channel_keys_id),
866866

867867
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
868868
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
@@ -1148,6 +1148,7 @@ impl<Signer: Sign> Channel<Signer> {
11481148

11491149
let mut secp_ctx = Secp256k1::new();
11501150
secp_ctx.seeded_randomize(&keys_provider.get_secure_random_bytes());
1151+
let channel_keys_id = holder_signer.channel_keys_id();
11511152

11521153
let chan = Channel {
11531154
user_id,
@@ -1163,7 +1164,7 @@ impl<Signer: Sign> Channel<Signer> {
11631164

11641165
holder_signer,
11651166
shutdown_scriptpubkey,
1166-
destination_script: keys_provider.get_destination_script(),
1167+
destination_script: keys_provider.get_destination_script(channel_keys_id),
11671168

11681169
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
11691170
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
@@ -6249,7 +6250,7 @@ mod tests {
62496250

62506251
fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
62516252
fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
6252-
fn get_destination_script(&self) -> Script {
6253+
fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Script {
62536254
let secp_ctx = Secp256k1::signing_only();
62546255
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
62556256
let channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());

lightning/src/util/test_utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl keysinterface::KeysInterface for OnlyReadsKeysInterface {
7272

7373
fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { unreachable!(); }
7474
fn get_inbound_payment_key_material(&self) -> KeyMaterial { unreachable!(); }
75-
fn get_destination_script(&self) -> Script { unreachable!(); }
75+
fn get_destination_script(&self, _channel_keys_id : [u8; 32]) -> Script { unreachable!(); }
7676
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
7777
fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> EnforcingSigner { unreachable!(); }
7878
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
@@ -487,7 +487,7 @@ impl keysinterface::KeysInterface for TestKeysInterface {
487487
fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial {
488488
self.backing.get_inbound_payment_key_material()
489489
}
490-
fn get_destination_script(&self) -> Script { self.backing.get_destination_script() }
490+
fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Script { self.backing.get_destination_script(channel_keys_id) }
491491

492492
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
493493
match &mut *self.expectations.lock().unwrap() {

0 commit comments

Comments
 (0)