Skip to content

Commit 25ef91c

Browse files
committed
Custom Keysmanager and openchannel_without_peer_addr added
MyKeysManager is a custom keysmanager that can be used to set custom channel secrets. Setting custom channel is essential for interacting with Lnprototest. A method called openchannel_without_peer_addr is also added. This allows creating a channel with a peer that is already connected. This is also needed for Lnprototest tests regarding open channel.
1 parent 3676248 commit 25ef91c

File tree

5 files changed

+362
-16
lines changed

5 files changed

+362
-16
lines changed

src/args.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::str::FromStr;
99

1010
pub(crate) fn parse_startup_args() -> Result<LdkUserInfo, ()> {
1111
if env::args().len() < 3 {
12-
println!("ldk-tutorial-node requires at least 2 arguments: `cargo run [<bitcoind-rpc-username>:<bitcoind-rpc-password>@]<bitcoind-rpc-host>:<bitcoind-rpc-port> ldk_storage_directory_path [<ldk-incoming-peer-listening-port>] [bitcoin-network] [announced-node-name announced-listen-addr*]`");
12+
println!("ldk-tutorial-node requires at least 2 arguments: `cargo run [<bitcoind-rpc-username>:<bitcoind-rpc-password>@]<bitcoind-rpc-host>:<bitcoind-rpc-port> ldk_storage_directory_path [<ldk-incoming-peer-listening-port>] [bitcoin-network] [channel-secrets] [announced-node-name announced-listen-addr*]`");
1313
return Err(());
1414
}
1515
let bitcoind_rpc_info = env::args().skip(1).next().unwrap();
@@ -69,6 +69,17 @@ pub(crate) fn parse_startup_args() -> Result<LdkUserInfo, ()> {
6969
return Err(());
7070
};
7171

72+
let channel_secrets = match env::args().skip(arg_idx + 1).next() {
73+
Some(s) => {
74+
if s.len() != 389 {
75+
panic!("Channel secrets should be seperated by '/' and total string length should be 389");
76+
}
77+
arg_idx += 1;
78+
Some(s)
79+
}
80+
None => None,
81+
};
82+
7283
let ldk_announced_node_name = match env::args().skip(arg_idx + 1).next().as_ref() {
7384
Some(s) => {
7485
if s.len() > 32 {
@@ -106,6 +117,7 @@ pub(crate) fn parse_startup_args() -> Result<LdkUserInfo, ()> {
106117
ldk_announced_listen_addr,
107118
ldk_announced_node_name,
108119
network,
120+
channel_secrets,
109121
})
110122
}
111123

src/cli.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::disk::{self, INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME};
22
use crate::hex_utils;
3+
use crate::keys::MyKeysManager;
34
use crate::{
45
ChainMonitor, ChannelManager, HTLCStatus, InboundPaymentInfoStorage, MillisatAmount,
56
NetworkGraph, OutboundPaymentInfoStorage, PaymentInfo, PeerManager,
@@ -18,7 +19,7 @@ use lightning::ln::types::ChannelId;
1819
use lightning::offers::offer::{self, Offer};
1920
use lightning::routing::gossip::NodeId;
2021
use lightning::routing::router::{PaymentParameters, RouteParameters};
21-
use lightning::sign::{EntropySource, KeysManager};
22+
use lightning::sign::EntropySource;
2223
use lightning::types::payment::{PaymentHash, PaymentPreimage};
2324
use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig};
2425
use lightning::util::persist::KVStore;
@@ -43,11 +44,12 @@ pub(crate) struct LdkUserInfo {
4344
pub(crate) ldk_announced_listen_addr: Vec<SocketAddress>,
4445
pub(crate) ldk_announced_node_name: [u8; 32],
4546
pub(crate) network: Network,
47+
pub(crate) channel_secrets: Option<String>,
4648
}
4749

4850
pub(crate) fn poll_for_user_input(
4951
peer_manager: Arc<PeerManager>, channel_manager: Arc<ChannelManager>,
50-
chain_monitor: Arc<ChainMonitor>, keys_manager: Arc<KeysManager>,
52+
chain_monitor: Arc<ChainMonitor>, keys_manager: Arc<MyKeysManager>,
5153
network_graph: Arc<NetworkGraph>, inbound_payments: Arc<Mutex<InboundPaymentInfoStorage>>,
5254
outbound_payments: Arc<Mutex<OutboundPaymentInfoStorage>>, ldk_data_dir: String,
5355
network: Network, logger: Arc<disk::FilesystemLogger>, fs_store: Arc<FilesystemStore>,
@@ -74,6 +76,54 @@ pub(crate) fn poll_for_user_input(
7476
if let Some(word) = words.next() {
7577
match word {
7678
"help" => help(),
79+
"openchannel_without_peer_addr" => {
80+
// opens a channel with a connected node
81+
let peer_pubkey = words.next();
82+
let channel_value_sat = words.next();
83+
if peer_pubkey.is_none() || channel_value_sat.is_none() {
84+
println!("ERROR: openchannel_without_peer_addr has 2 required arguments: `openchannel_without_peer_addr pubkey channel_amt_satoshis` [--public]");
85+
continue;
86+
}
87+
let peer_pubkey = peer_pubkey.unwrap();
88+
let chan_amt_sat: Result<u64, _> = channel_value_sat.unwrap().parse();
89+
if chan_amt_sat.is_err() {
90+
println!("ERROR: channel amount must be a number");
91+
continue;
92+
}
93+
94+
95+
let (mut announce_channel, mut with_anchors) = (false, false);
96+
while let Some(word) = words.next() {
97+
match word {
98+
"--public" | "--public=true" => announce_channel = true,
99+
"--public=false" => announce_channel = false,
100+
"--with-anchors" | "--with-anchors=true" => with_anchors = true,
101+
"--with-anchors=false" => with_anchors = false,
102+
_ => {
103+
println!("ERROR: invalid boolean flag format. Valid formats: `--option`, `--option=true` `--option=false`");
104+
continue;
105+
},
106+
}
107+
}
108+
let pubkey = peer_pubkey;
109+
let pubkey = hex_utils::to_compressed_pubkey(pubkey);
110+
if open_channel(
111+
pubkey.unwrap(),
112+
chan_amt_sat.unwrap(),
113+
announce_channel,
114+
with_anchors,
115+
channel_manager.clone(),
116+
)
117+
.is_ok()
118+
{
119+
// let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir.clone());
120+
// let _ = disk::persist_channel_peer(
121+
// Path::new(&peer_data_path),
122+
// peer_pubkey_and_ip_addr,
123+
// );
124+
}
125+
},
126+
77127
"openchannel" => {
78128
let peer_pubkey_and_ip_addr = words.next();
79129
let channel_value_sat = words.next();
@@ -848,7 +898,7 @@ fn keysend<E: EntropySource>(
848898

849899
fn get_invoice(
850900
amt_msat: u64, inbound_payments: &mut InboundPaymentInfoStorage,
851-
channel_manager: &ChannelManager, keys_manager: Arc<KeysManager>, network: Network,
901+
channel_manager: &ChannelManager, keys_manager: Arc<MyKeysManager>, network: Network,
852902
expiry_secs: u32, logger: Arc<disk::FilesystemLogger>,
853903
) {
854904
let currency = match network {

src/keys.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
use std::sync::Arc;
2+
3+
use bitcoin::secp256k1::{Secp256k1, SecretKey};
4+
use lightning::sign::{InMemorySigner, NodeSigner, OutputSpender, SignerProvider};
5+
6+
use lightning::sign::{EntropySource, KeysManager};
7+
use lightning_invoice::RawBolt11Invoice;
8+
9+
pub struct MyKeys {
10+
pub keys_manager: Arc<MyKeysManager>,
11+
}
12+
13+
impl MyKeys {
14+
pub fn new(seed: [u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
15+
MyKeys {
16+
keys_manager: Arc::new(MyKeysManager::new(
17+
&seed,
18+
starting_time_secs,
19+
starting_time_nanos,
20+
)),
21+
}
22+
}
23+
24+
pub fn with_channel_keys(seed: [u8; 32], starting_time_secs: u64, starting_time_nanos: u32, channels_keys: String) -> Self {
25+
let keys = channels_keys.split('/').collect::<Vec<_>>();
26+
27+
let mut manager =
28+
MyKeysManager::new(&seed, starting_time_secs, starting_time_nanos);
29+
manager.set_channel_keys(
30+
keys[0].to_string(),
31+
keys[1].to_string(),
32+
keys[2].to_string(),
33+
keys[3].to_string(),
34+
keys[4].to_string(),
35+
keys[5].to_string(),
36+
);
37+
MyKeys {
38+
keys_manager: Arc::new(manager),
39+
}
40+
}
41+
42+
pub fn inner(&self) -> Arc<MyKeysManager> {
43+
self.keys_manager.clone()
44+
}
45+
}
46+
47+
/// MyKeysManager is a custom keysmanager that allows the use of custom channel secrets.
48+
pub struct MyKeysManager {
49+
pub(crate) inner: KeysManager,
50+
51+
funding_key: Option<SecretKey>,
52+
revocation_base_secret: Option<SecretKey>,
53+
payment_base_secret: Option<SecretKey>,
54+
delayed_payment_base_secret: Option<SecretKey>,
55+
htlc_base_secret: Option<SecretKey>,
56+
shachain_seed: Option<[u8; 32]>,
57+
}
58+
59+
impl MyKeysManager {
60+
pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
61+
let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos);
62+
Self {
63+
inner,
64+
funding_key: None,
65+
revocation_base_secret: None,
66+
payment_base_secret: None,
67+
delayed_payment_base_secret: None,
68+
htlc_base_secret: None,
69+
shachain_seed: None,
70+
}
71+
}
72+
73+
pub fn get_node_secret_key(&self) -> SecretKey {
74+
self.inner.get_node_secret_key()
75+
}
76+
77+
pub fn set_channel_keys(
78+
&mut self,
79+
funding_key: String,
80+
revocation_base_secret: String,
81+
payment_base_secret: String,
82+
delayed_payment_base_secret: String,
83+
htlc_base_secret: String,
84+
_shachain_seed: String,
85+
) {
86+
use std::str::FromStr;
87+
88+
self.funding_key = Some(SecretKey::from_str(&funding_key).unwrap());
89+
self.revocation_base_secret = Some(SecretKey::from_str(&revocation_base_secret).unwrap());
90+
self.payment_base_secret = Some(SecretKey::from_str(&payment_base_secret).unwrap());
91+
self.delayed_payment_base_secret =
92+
Some(SecretKey::from_str(&delayed_payment_base_secret).unwrap());
93+
self.htlc_base_secret = Some(SecretKey::from_str(&htlc_base_secret).unwrap());
94+
self.shachain_seed = Some(self.inner.get_secure_random_bytes())
95+
}
96+
}
97+
98+
impl EntropySource for MyKeysManager {
99+
fn get_secure_random_bytes(&self) -> [u8; 32] {
100+
self.inner.get_secure_random_bytes()
101+
}
102+
}
103+
104+
impl NodeSigner for MyKeysManager {
105+
fn ecdh(
106+
&self,
107+
recipient: lightning::sign::Recipient,
108+
other_key: &bitcoin::secp256k1::PublicKey,
109+
tweak: Option<&bitcoin::secp256k1::Scalar>,
110+
) -> Result<bitcoin::secp256k1::ecdh::SharedSecret, ()> {
111+
self.inner.ecdh(recipient, other_key, tweak)
112+
}
113+
114+
fn get_inbound_payment_key_material(&self) -> lightning::sign::KeyMaterial {
115+
self.inner.get_inbound_payment_key_material()
116+
}
117+
118+
fn get_node_id(
119+
&self,
120+
recipient: lightning::sign::Recipient,
121+
) -> Result<bitcoin::secp256k1::PublicKey, ()> {
122+
self.inner.get_node_id(recipient)
123+
}
124+
125+
fn sign_bolt12_invoice(
126+
&self,
127+
invoice: &lightning::offers::invoice::UnsignedBolt12Invoice,
128+
) -> Result<bitcoin::secp256k1::schnorr::Signature, ()> {
129+
self.inner.sign_bolt12_invoice(invoice)
130+
}
131+
132+
fn sign_bolt12_invoice_request(
133+
&self,
134+
invoice_request: &lightning::offers::invoice_request::UnsignedInvoiceRequest,
135+
) -> Result<bitcoin::secp256k1::schnorr::Signature, ()> {
136+
self.inner.sign_bolt12_invoice_request(invoice_request)
137+
}
138+
139+
fn sign_gossip_message(
140+
&self,
141+
msg: lightning::ln::msgs::UnsignedGossipMessage,
142+
) -> Result<bitcoin::secp256k1::ecdsa::Signature, ()> {
143+
self.inner.sign_gossip_message(msg)
144+
}
145+
146+
fn sign_invoice(
147+
&self,
148+
invoice: &RawBolt11Invoice,
149+
recipient: lightning::sign::Recipient,
150+
) -> Result<bitcoin::secp256k1::ecdsa::RecoverableSignature, ()> {
151+
self.inner.sign_invoice(invoice, recipient)
152+
}
153+
}
154+
155+
156+
impl OutputSpender for MyKeysManager {
157+
fn spend_spendable_outputs<C: bitcoin::secp256k1::Signing>(
158+
&self,
159+
descriptors: &[&lightning::sign::SpendableOutputDescriptor],
160+
outputs: Vec<bitcoin::TxOut>,
161+
change_destination_script: bitcoin::ScriptBuf,
162+
feerate_sat_per_1000_weight: u32,
163+
locktime: Option<bitcoin::absolute::LockTime>,
164+
secp_ctx: &bitcoin::secp256k1::Secp256k1<C>,
165+
) -> Result<bitcoin::Transaction, ()> {
166+
self.inner.spend_spendable_outputs(
167+
descriptors,
168+
outputs,
169+
change_destination_script,
170+
feerate_sat_per_1000_weight,
171+
locktime,
172+
secp_ctx,
173+
)
174+
}
175+
}
176+
177+
impl SignerProvider for MyKeysManager {
178+
type EcdsaSigner = InMemorySigner;
179+
180+
fn derive_channel_signer(
181+
&self,
182+
channel_value_satoshis: u64,
183+
channel_keys_id: [u8; 32],
184+
) -> Self::EcdsaSigner {
185+
if self.funding_key.is_some() {
186+
let commitment_seed = [
187+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
188+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
189+
];
190+
return InMemorySigner::new(
191+
&Secp256k1::new(),
192+
self.funding_key.unwrap(),
193+
self.revocation_base_secret.unwrap(),
194+
self.payment_base_secret.unwrap(),
195+
self.delayed_payment_base_secret.unwrap(),
196+
self.htlc_base_secret.unwrap(),
197+
commitment_seed,
198+
channel_value_satoshis,
199+
channel_keys_id,
200+
self.shachain_seed.unwrap(),
201+
);
202+
}
203+
self.inner
204+
.derive_channel_signer(channel_value_satoshis, channel_keys_id)
205+
}
206+
207+
fn generate_channel_keys_id(
208+
&self,
209+
inbound: bool,
210+
channel_value_satoshis: u64,
211+
user_channel_id: u128,
212+
) -> [u8; 32] {
213+
self.inner
214+
.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
215+
}
216+
217+
fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Result<bitcoin::ScriptBuf, ()> {
218+
self.inner.get_destination_script(channel_keys_id)
219+
}
220+
221+
fn get_shutdown_scriptpubkey(&self) -> Result<lightning::ln::script::ShutdownScript, ()> {
222+
self.inner.get_shutdown_scriptpubkey()
223+
}
224+
225+
fn read_chan_signer(
226+
&self,
227+
reader: &[u8],
228+
) -> Result<Self::EcdsaSigner, lightning::ln::msgs::DecodeError> {
229+
self.inner.read_chan_signer(reader)
230+
}
231+
}

0 commit comments

Comments
 (0)