Skip to content

Commit e1125d3

Browse files
committed
refactor: Clean up BitcoinD init a bit
1 parent c76ed4b commit e1125d3

File tree

5 files changed

+88
-142
lines changed

5 files changed

+88
-142
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ edition = "2021"
1515

1616
bitcoin = { version = "0.28.1", features = ["std", "use-serde"] }
1717
bitcoin-bech32 = "0.12"
18+
base64 = "0.13.0"
1819

1920
# --- LIGHTNING --- #
2021

common/src/cli.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,16 @@ impl BitcoindRpcInfo {
350350
port,
351351
})
352352
}
353+
354+
/// Returns a base64 encoding of "<user>:<pass>" required by the BitcoinD
355+
/// RPC client.
356+
pub fn base64_credentials(&self) -> String {
357+
base64::encode(format!(
358+
"{}:{}",
359+
self.username.clone(),
360+
self.password.clone(),
361+
))
362+
}
353363
}
354364

355365
impl FromStr for BitcoindRpcInfo {

node/src/init.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ impl LexeNode {
121121
let (node, provisioned_secrets) =
122122
fetch_res.context("Failed to fetch provisioned secrets")?;
123123
let root_seed = &provisioned_secrets.root_seed;
124+
let bitcoind = Arc::new(bitcoind);
125+
126+
// Spawn task to refresh feerates
127+
// TODO(max): Handle the handle
128+
let _refresh_fees_handle = bitcoind.spawn_refresh_fees_task();
124129

125130
// Build LexeKeysManager from node init data
126131
let keys_manager = LexeKeysManager::init(rng, &node.node_pk, root_seed)

node/src/lexe/bitcoind/mod.rs

Lines changed: 71 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use lightning_block_sync::rpc::RpcClient;
1919
use lightning_block_sync::{
2020
AsyncBlockSourceResult, BlockHeaderData, BlockSource,
2121
};
22-
use tokio::runtime::Handle;
22+
use tokio::task::JoinHandle;
2323
use tokio::time;
2424
use tracing::{debug, error};
2525

@@ -28,78 +28,44 @@ mod types;
2828
pub use types::*;
2929

3030
const POLL_FEE_ESTIMATE_INTERVAL: Duration = Duration::from_secs(60);
31+
/// The minimum feerate we are allowed to send, as specified by LDK.
32+
const MIN_FEERATE: u32 = 253;
3133

3234
pub struct LexeBitcoind {
33-
bitcoind_rpc_client: Arc<RpcClient>,
34-
host: String,
35-
port: u16,
36-
rpc_user: String,
37-
rpc_password: String,
35+
rpc_client: Arc<RpcClient>,
3836
background_fees: Arc<AtomicU32>,
3937
normal_fees: Arc<AtomicU32>,
4038
high_prio_fees: Arc<AtomicU32>,
41-
handle: Handle,
42-
}
43-
44-
#[derive(Clone, Eq, Hash, PartialEq)]
45-
pub enum Target {
46-
Background,
47-
Normal,
48-
HighPriority,
49-
}
50-
51-
impl BlockSource for &LexeBitcoind {
52-
fn get_header<'a>(
53-
&'a self,
54-
header_hash: &'a BlockHash,
55-
height_hint: Option<u32>,
56-
) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
57-
Box::pin(async move {
58-
self.bitcoind_rpc_client
59-
.get_header(header_hash, height_hint)
60-
.await
61-
})
62-
}
63-
64-
fn get_block<'a>(
65-
&'a self,
66-
header_hash: &'a BlockHash,
67-
) -> AsyncBlockSourceResult<'a, Block> {
68-
Box::pin(async move {
69-
self.bitcoind_rpc_client.get_block(header_hash).await
70-
})
71-
}
72-
73-
fn get_best_block(
74-
&self,
75-
) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)> {
76-
Box::pin(async move { self.bitcoind_rpc_client.get_best_block().await })
77-
}
7839
}
7940

80-
/// The minimum feerate we are allowed to send, as specify by LDK.
81-
const MIN_FEERATE: u32 = 253;
82-
8341
impl LexeBitcoind {
8442
pub async fn init(
8543
bitcoind_rpc: BitcoindRpcInfo,
8644
network: Network,
87-
) -> anyhow::Result<Arc<Self>> {
88-
debug!(%network, "initializing bitcoind client");
89-
90-
let client = LexeBitcoind::new(
91-
bitcoind_rpc.host,
92-
bitcoind_rpc.port,
93-
bitcoind_rpc.username,
94-
bitcoind_rpc.password,
95-
Handle::current(),
96-
)
97-
.await
98-
.context("Failed to connect to bitcoind client")?;
99-
let client = Arc::new(client);
100-
101-
// Check that the bitcoind we've connected to is running the network we
102-
// expect
45+
) -> anyhow::Result<Self> {
46+
debug!(%network, "Initializing bitcoind client");
47+
48+
let http_endpoint = HttpEndpoint::for_host(bitcoind_rpc.host.clone())
49+
.with_port(bitcoind_rpc.port);
50+
let credentials = bitcoind_rpc.base64_credentials();
51+
let rpc_client = RpcClient::new(&credentials, http_endpoint)
52+
.context("Could not initialize RPC client")?;
53+
let rpc_client = Arc::new(rpc_client);
54+
55+
let background_fees = Arc::new(AtomicU32::new(MIN_FEERATE));
56+
let normal_fees = Arc::new(AtomicU32::new(2000));
57+
let high_prio_fees = Arc::new(AtomicU32::new(5000));
58+
59+
let client = Self {
60+
rpc_client,
61+
background_fees,
62+
normal_fees,
63+
high_prio_fees,
64+
};
65+
66+
// Make an initial test call to check that the RPC client is working
67+
// correctly, and also check that the bitcoind we've connected to is
68+
// running the network we expect
10369
let bitcoind_chain = client
10470
.get_blockchain_info()
10571
.await
@@ -124,70 +90,19 @@ impl LexeBitcoind {
12490
Ok(client)
12591
}
12692

127-
// A runtime handle has to be passed in explicitly, otherwise these fns may
128-
// panic when called from the (non-Tokio) background processor thread
129-
async fn new(
130-
host: String,
131-
port: u16,
132-
rpc_user: String,
133-
rpc_password: String,
134-
handle: Handle,
135-
) -> std::io::Result<Self> {
136-
let http_endpoint =
137-
HttpEndpoint::for_host(host.clone()).with_port(port);
138-
let rpc_credentials = base64::encode(format!(
139-
"{}:{}",
140-
rpc_user.clone(),
141-
rpc_password.clone()
142-
));
143-
let bitcoind_rpc_client =
144-
RpcClient::new(&rpc_credentials, http_endpoint)?;
145-
let _dummy = bitcoind_rpc_client
146-
.call_method::<BlockchainInfo>("getblockchaininfo", &[])
147-
.await
148-
.map_err(|_| {
149-
std::io::Error::new(std::io::ErrorKind::PermissionDenied,
150-
"Failed to make initial call to bitcoind - please check your RPC user/password and access settings")
151-
})?;
152-
153-
let background_fees = Arc::new(AtomicU32::new(MIN_FEERATE));
154-
let normal_fees = Arc::new(AtomicU32::new(2000));
155-
let high_prio_fees = Arc::new(AtomicU32::new(5000));
93+
pub fn spawn_refresh_fees_task(&self) -> JoinHandle<()> {
94+
let rpc_client = self.rpc_client.clone();
95+
let background_fees = self.background_fees.clone();
96+
let normal_fees = self.normal_fees.clone();
97+
let high_prio_fees = self.high_prio_fees.clone();
15698

157-
let client = Self {
158-
bitcoind_rpc_client: Arc::new(bitcoind_rpc_client),
159-
host,
160-
port,
161-
rpc_user,
162-
rpc_password,
163-
background_fees,
164-
normal_fees,
165-
high_prio_fees,
166-
handle: handle.clone(),
167-
};
168-
client.poll_for_fee_estimates(
169-
client.background_fees.clone(),
170-
client.normal_fees.clone(),
171-
client.high_prio_fees.clone(),
172-
client.bitcoind_rpc_client.clone(),
173-
);
174-
Ok(client)
175-
}
176-
177-
fn poll_for_fee_estimates(
178-
&self,
179-
background_fees: Arc<AtomicU32>,
180-
normal_fees: Arc<AtomicU32>,
181-
high_prio_fees: Arc<AtomicU32>,
182-
rpc_client: Arc<RpcClient>,
183-
) {
184-
self.handle.spawn(async move {
99+
tokio::spawn(async move {
185100
let mut poll_interval = time::interval(POLL_FEE_ESTIMATE_INTERVAL);
186101

187102
loop {
188103
poll_interval.tick().await;
189104

190-
let poll_res = Self::poll_for_fee_estimates_fallible(
105+
let poll_res = Self::refresh_fees(
191106
background_fees.as_ref(),
192107
normal_fees.as_ref(),
193108
high_prio_fees.as_ref(),
@@ -202,10 +117,10 @@ impl LexeBitcoind {
202117
}
203118
}
204119
}
205-
});
120+
})
206121
}
207122

208-
async fn poll_for_fee_estimates_fallible(
123+
async fn refresh_fees(
209124
background_fees: &AtomicU32,
210125
normal_fees: &AtomicU32,
211126
high_prio_fees: &AtomicU32,
@@ -267,23 +182,12 @@ impl LexeBitcoind {
267182
Ok(())
268183
}
269184

270-
pub fn get_new_rpc_client(&self) -> std::io::Result<RpcClient> {
271-
let http_endpoint =
272-
HttpEndpoint::for_host(self.host.clone()).with_port(self.port);
273-
let rpc_credentials = base64::encode(format!(
274-
"{}:{}",
275-
self.rpc_user.clone(),
276-
self.rpc_password.clone()
277-
));
278-
RpcClient::new(&rpc_credentials, http_endpoint)
279-
}
280-
281185
pub async fn create_raw_transaction(
282186
&self,
283187
outputs: Vec<HashMap<String, f64>>,
284188
) -> anyhow::Result<RawTx> {
285189
let outputs_json = serde_json::json!(outputs);
286-
self.bitcoind_rpc_client
190+
self.rpc_client
287191
.call_method::<RawTx>(
288192
"createrawtransaction",
289193
&[serde_json::json!([]), outputs_json],
@@ -312,7 +216,7 @@ impl LexeBitcoind {
312216
// the same node.
313217
"replaceable": false,
314218
});
315-
self.bitcoind_rpc_client
219+
self.rpc_client
316220
.call_method("fundrawtransaction", &[raw_tx_json, options])
317221
.await
318222
.context("fundrawtransaction RPC call failed")
@@ -323,7 +227,7 @@ impl LexeBitcoind {
323227
raw_tx: RawTx,
324228
) -> anyhow::Result<Txid> {
325229
let raw_tx_json = serde_json::json!(raw_tx.0);
326-
self.bitcoind_rpc_client
230+
self.rpc_client
327231
.call_method::<Txid>("sendrawtransaction", &[raw_tx_json])
328232
.await
329233
.context("sesndrawtransaction RPC call failed")
@@ -334,7 +238,7 @@ impl LexeBitcoind {
334238
tx_hex: String,
335239
) -> anyhow::Result<SignedTx> {
336240
let tx_hex_json = serde_json::json!(tx_hex);
337-
self.bitcoind_rpc_client
241+
self.rpc_client
338242
.call_method("signrawtransactionwithwallet", &[tx_hex_json])
339243
.await
340244
.context("signrawtransactionwithwallet RPC call failed")
@@ -343,7 +247,7 @@ impl LexeBitcoind {
343247
pub async fn get_new_address(&self) -> anyhow::Result<Address> {
344248
let addr_args = vec![serde_json::json!("LDK output address")];
345249
let addr = self
346-
.bitcoind_rpc_client
250+
.rpc_client
347251
.call_method::<NewAddress>("getnewaddress", &addr_args)
348252
.await
349253
.context("getnewaddress RPC call failed")?;
@@ -352,13 +256,38 @@ impl LexeBitcoind {
352256
}
353257

354258
pub async fn get_blockchain_info(&self) -> anyhow::Result<BlockchainInfo> {
355-
self.bitcoind_rpc_client
259+
self.rpc_client
356260
.call_method::<BlockchainInfo>("getblockchaininfo", &[])
357261
.await
358262
.context("getblockchaininfo RPC call failed")
359263
}
360264
}
361265

266+
impl BlockSource for &LexeBitcoind {
267+
fn get_header<'a>(
268+
&'a self,
269+
header_hash: &'a BlockHash,
270+
height_hint: Option<u32>,
271+
) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
272+
Box::pin(async move {
273+
self.rpc_client.get_header(header_hash, height_hint).await
274+
})
275+
}
276+
277+
fn get_block<'a>(
278+
&'a self,
279+
header_hash: &'a BlockHash,
280+
) -> AsyncBlockSourceResult<'a, Block> {
281+
Box::pin(async move { self.rpc_client.get_block(header_hash).await })
282+
}
283+
284+
fn get_best_block(
285+
&self,
286+
) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)> {
287+
Box::pin(async move { self.rpc_client.get_best_block().await })
288+
}
289+
}
290+
362291
impl FeeEstimator for LexeBitcoind {
363292
fn get_est_sat_per_1000_weight(
364293
&self,
@@ -381,13 +310,13 @@ impl FeeEstimator for LexeBitcoind {
381310
impl BroadcasterInterface for LexeBitcoind {
382311
fn broadcast_transaction(&self, tx: &Transaction) {
383312
debug!("Broadcasting transaction");
384-
let bitcoind_rpc_client = self.bitcoind_rpc_client.clone();
313+
let rpc_client = self.rpc_client.clone();
385314
let tx_serialized = serde_json::json!(encode::serialize_hex(tx));
386-
self.handle.spawn(async move {
315+
tokio::spawn(async move {
387316
// This may error due to RL calling `broadcast_transaction` with the
388317
// same transaction multiple times, but the error is
389318
// safe to ignore.
390-
match bitcoind_rpc_client
319+
match rpc_client
391320
.call_method::<Txid>("sendrawtransaction", &[tx_serialized])
392321
.await
393322
{

0 commit comments

Comments
 (0)