Skip to content

Commit f1eb463

Browse files
authored
Merge pull request #225 from TheBlueMatt/2018-10-214-redo
Keys Interface Simplification
2 parents 70b026c + 5180686 commit f1eb463

File tree

8 files changed

+516
-194
lines changed

8 files changed

+516
-194
lines changed

fuzz/fuzz_targets/full_stack_target.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ extern crate secp256k1;
55

66
use bitcoin::blockdata::block::BlockHeader;
77
use bitcoin::blockdata::transaction::{Transaction, TxOut};
8-
use bitcoin::blockdata::script::Script;
8+
use bitcoin::blockdata::script::{Builder, Script};
9+
use bitcoin::blockdata::opcodes;
910
use bitcoin::network::constants::Network;
1011
use bitcoin::network::serialize::{deserialize, serialize, BitcoinHash};
11-
use bitcoin::util::hash::Sha256dHash;
12+
use bitcoin::util::hash::{Sha256dHash, Hash160};
1213

1314
use crypto::digest::Digest;
1415

1516
use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator,ChainWatchInterfaceUtil};
1617
use lightning::chain::transaction::OutPoint;
18+
use lightning::chain::keysinterface::{ChannelKeys, KeysInterface};
1719
use lightning::ln::channelmonitor;
1820
use lightning::ln::channelmanager::{ChannelManager, PaymentFailReason};
1921
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
@@ -196,6 +198,50 @@ impl<'a> Drop for MoneyLossDetector<'a> {
196198
}
197199
}
198200

201+
struct KeyProvider {
202+
node_secret: SecretKey,
203+
}
204+
impl KeysInterface for KeyProvider {
205+
fn get_node_secret(&self) -> SecretKey {
206+
self.node_secret.clone()
207+
}
208+
209+
fn get_destination_script(&self) -> Script {
210+
let secp_ctx = Secp256k1::signing_only();
211+
let channel_monitor_claim_key = SecretKey::from_slice(&secp_ctx, &hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
212+
let our_channel_monitor_claim_key_hash = Hash160::from_data(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
213+
Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()
214+
}
215+
216+
fn get_shutdown_pubkey(&self) -> PublicKey {
217+
let secp_ctx = Secp256k1::signing_only();
218+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap())
219+
}
220+
221+
fn get_channel_keys(&self, inbound: bool) -> ChannelKeys {
222+
let secp_ctx = Secp256k1::without_caps();
223+
if inbound {
224+
ChannelKeys {
225+
funding_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
226+
revocation_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
227+
payment_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
228+
delayed_payment_base_key: SecretKey::from_slice(&secp_ctx, &[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, 4, 0]).unwrap(),
229+
htlc_base_key: SecretKey::from_slice(&secp_ctx, &[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, 5, 0]).unwrap(),
230+
commitment_seed: [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, 0],
231+
}
232+
} else {
233+
ChannelKeys {
234+
funding_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
235+
revocation_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
236+
payment_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
237+
delayed_payment_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
238+
htlc_base_key: SecretKey::from_slice(&secp_ctx, &[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, 0]).unwrap(),
239+
commitment_seed: [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, 0],
240+
}
241+
}
242+
}
243+
}
244+
199245
#[inline]
200246
pub fn do_test(data: &[u8], logger: &Arc<Logger>) {
201247
reset_rng_state();
@@ -236,8 +282,9 @@ pub fn do_test(data: &[u8], logger: &Arc<Logger>) {
236282
let broadcast = Arc::new(TestBroadcaster{});
237283
let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone());
238284

239-
let channelmanager = ChannelManager::new(our_network_key, slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger)).unwrap();
240-
let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &our_network_key), watch.clone(), Arc::clone(&logger)));
285+
let keys_manager = Arc::new(KeyProvider { node_secret: our_network_key.clone() });
286+
let channelmanager = ChannelManager::new(slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone()).unwrap();
287+
let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()), watch.clone(), Arc::clone(&logger)));
241288

242289
let peers = RefCell::new([false; 256]);
243290
let mut loss_detector = MoneyLossDetector::new(&peers, channelmanager.clone(), monitor.clone(), PeerManager::new(MessageHandler {

src/chain/keysinterface.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//! keysinterface provides keys into rust-lightning and defines some useful enums which describe
2+
//! spendable on-chain outputs which the user owns and is responsible for using just as any other
3+
//! on-chain output which is theirs.
4+
5+
use bitcoin::blockdata::transaction::{OutPoint, TxOut};
6+
use bitcoin::blockdata::script::{Script, Builder};
7+
use bitcoin::blockdata::opcodes;
8+
use bitcoin::network::constants::Network;
9+
use bitcoin::util::hash::Hash160;
10+
use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};
11+
12+
use secp256k1::key::{SecretKey, PublicKey};
13+
use secp256k1::Secp256k1;
14+
use secp256k1;
15+
16+
use crypto::hkdf::{hkdf_extract,hkdf_expand};
17+
18+
use util::sha2::Sha256;
19+
use util::logger::Logger;
20+
21+
use std::sync::Arc;
22+
23+
/// When on-chain outputs are created by rust-lightning an event is generated which informs the
24+
/// user thereof. This enum describes the format of the output and provides the OutPoint.
25+
pub enum SpendableOutputDescriptor {
26+
/// Outpoint with an output to a script which was provided via KeysInterface, thus you should
27+
/// have stored somewhere how to spend script_pubkey!
28+
/// Outputs from a justice tx, claim tx or preimage tx
29+
StaticOutput {
30+
/// The outpoint spendable by user wallet
31+
outpoint: OutPoint,
32+
/// The output which is referenced by the given outpoint
33+
output: TxOut,
34+
},
35+
/// Outpoint commits to a P2WSH, should be spend by the following witness :
36+
/// <local_delayedsig> 0 <witnessScript>
37+
/// With input nSequence set to_self_delay.
38+
/// Outputs from a HTLC-Success/Timeout tx
39+
DynamicOutput {
40+
/// Outpoint spendable by user wallet
41+
outpoint: OutPoint,
42+
/// local_delayedkey = delayed_payment_basepoint_secret + SHA256(per_commitment_point || delayed_payment_basepoint
43+
local_delayedkey: SecretKey,
44+
/// witness redeemScript encumbering output
45+
witness_script: Script,
46+
/// nSequence input must commit to self_delay to satisfy script's OP_CSV
47+
to_self_delay: u16,
48+
}
49+
}
50+
51+
/// A trait to describe an object which can get user secrets and key material.
52+
pub trait KeysInterface: Send + Sync {
53+
/// Get node secret key (aka node_id or network_key)
54+
fn get_node_secret(&self) -> SecretKey;
55+
/// Get destination redeemScript to encumber static protocol exit points.
56+
fn get_destination_script(&self) -> Script;
57+
/// Get shutdown_pubkey to use as PublicKey at channel closure
58+
fn get_shutdown_pubkey(&self) -> PublicKey;
59+
/// Get a new set of ChannelKeys for per-channel secrets. These MUST be unique even if you
60+
/// restarted with some stale data!
61+
fn get_channel_keys(&self, inbound: bool) -> ChannelKeys;
62+
}
63+
64+
/// Set of lightning keys needed to operate a channel as described in BOLT 3
65+
#[derive(Clone)]
66+
pub struct ChannelKeys {
67+
/// Private key of anchor tx
68+
pub funding_key: SecretKey,
69+
/// Local secret key for blinded revocation pubkey
70+
pub revocation_base_key: SecretKey,
71+
/// Local secret key used in commitment tx htlc outputs
72+
pub payment_base_key: SecretKey,
73+
/// Local secret key used in HTLC tx
74+
pub delayed_payment_base_key: SecretKey,
75+
/// Local htlc secret key used in commitment tx htlc outputs
76+
pub htlc_base_key: SecretKey,
77+
/// Commitment seed
78+
pub commitment_seed: [u8; 32],
79+
}
80+
81+
impl ChannelKeys {
82+
/// Generate a set of lightning keys needed to operate a channel by HKDF-expanding a given
83+
/// random 32-byte seed
84+
pub fn new_from_seed(seed: &[u8; 32]) -> ChannelKeys {
85+
let mut prk = [0; 32];
86+
hkdf_extract(Sha256::new(), b"rust-lightning key gen salt", seed, &mut prk);
87+
let secp_ctx = Secp256k1::without_caps();
88+
89+
let mut okm = [0; 32];
90+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning funding key info", &mut okm);
91+
let funding_key = SecretKey::from_slice(&secp_ctx, &okm).expect("Sha256 is broken");
92+
93+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning revocation base key info", &mut okm);
94+
let revocation_base_key = SecretKey::from_slice(&secp_ctx, &okm).expect("Sha256 is broken");
95+
96+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning payment base key info", &mut okm);
97+
let payment_base_key = SecretKey::from_slice(&secp_ctx, &okm).expect("Sha256 is broken");
98+
99+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning delayed payment base key info", &mut okm);
100+
let delayed_payment_base_key = SecretKey::from_slice(&secp_ctx, &okm).expect("Sha256 is broken");
101+
102+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning htlc base key info", &mut okm);
103+
let htlc_base_key = SecretKey::from_slice(&secp_ctx, &okm).expect("Sha256 is broken");
104+
105+
hkdf_expand(Sha256::new(), &prk, b"rust-lightning local commitment seed info", &mut okm);
106+
107+
ChannelKeys {
108+
funding_key: funding_key,
109+
revocation_base_key: revocation_base_key,
110+
payment_base_key: payment_base_key,
111+
delayed_payment_base_key: delayed_payment_base_key,
112+
htlc_base_key: htlc_base_key,
113+
commitment_seed: okm
114+
}
115+
}
116+
}
117+
118+
/// Simple KeysInterface implementor that takes a 32-byte seed for use as a BIP 32 extended key
119+
/// and derives keys from that.
120+
///
121+
/// Your node_id is seed/0'
122+
/// ChannelMonitor closes may use seed/1'
123+
/// Cooperative closes may use seed/2'
124+
/// The two close keys may be needed to claim on-chain funds!
125+
pub struct KeysManager {
126+
secp_ctx: Secp256k1<secp256k1::All>,
127+
node_secret: SecretKey,
128+
destination_script: Script,
129+
shutdown_pubkey: PublicKey,
130+
channel_master_key: ExtendedPrivKey,
131+
132+
logger: Arc<Logger>,
133+
}
134+
135+
impl KeysManager {
136+
/// Constructs a KeysManager from a 32-byte seed. If the seed is in some way biased (eg your
137+
/// RNG is busted) this may panic.
138+
pub fn new(seed: &[u8; 32], network: Network, logger: Arc<Logger>) -> KeysManager {
139+
let secp_ctx = Secp256k1::new();
140+
match ExtendedPrivKey::new_master(&secp_ctx, network.clone(), seed) {
141+
Ok(master_key) => {
142+
let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0)).expect("Your RNG is busted").secret_key;
143+
let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1)) {
144+
Ok(destination_key) => {
145+
let pubkey_hash160 = Hash160::from_data(&ExtendedPubKey::from_private(&secp_ctx, &destination_key).public_key.serialize()[..]);
146+
Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0)
147+
.push_slice(pubkey_hash160.as_bytes())
148+
.into_script()
149+
},
150+
Err(_) => panic!("Your RNG is busted"),
151+
};
152+
let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2)) {
153+
Ok(shutdown_key) => ExtendedPubKey::from_private(&secp_ctx, &shutdown_key).public_key,
154+
Err(_) => panic!("Your RNG is busted"),
155+
};
156+
let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3)).expect("Your RNG is busted");
157+
KeysManager {
158+
secp_ctx,
159+
node_secret,
160+
destination_script,
161+
shutdown_pubkey,
162+
channel_master_key,
163+
164+
logger,
165+
}
166+
},
167+
Err(_) => panic!("Your rng is busted"),
168+
}
169+
}
170+
}
171+
172+
impl KeysInterface for KeysManager {
173+
fn get_node_secret(&self) -> SecretKey {
174+
self.node_secret.clone()
175+
}
176+
177+
fn get_destination_script(&self) -> Script {
178+
self.destination_script.clone()
179+
}
180+
181+
fn get_shutdown_pubkey(&self) -> PublicKey {
182+
self.shutdown_pubkey.clone()
183+
}
184+
185+
fn get_channel_keys(&self, _inbound: bool) -> ChannelKeys {
186+
let channel_pubkey = ExtendedPubKey::from_private(&self.secp_ctx, &self. channel_master_key);
187+
let mut seed = [0; 32];
188+
for (arr, slice) in seed.iter_mut().zip((&channel_pubkey.public_key.serialize()[0..32]).iter()) {
189+
*arr = *slice;
190+
}
191+
ChannelKeys::new_from_seed(&seed)
192+
}
193+
}

src/chain/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
33
pub mod chaininterface;
44
pub mod transaction;
5+
pub mod keysinterface;

0 commit comments

Comments
 (0)