Skip to content

Commit 3cdbbf5

Browse files
committed
Immutable BlockSource interface
Querying a BlockSource is a logically immutable operation. Use non-mut references in its interface to reflect this, which allows for users to hold multiple references if desired.
1 parent 0a0f87c commit 3cdbbf5

File tree

7 files changed

+59
-54
lines changed

7 files changed

+59
-54
lines changed

lightning-block-sync/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ rpc-client = [ "serde", "serde_json", "chunked_transfer" ]
2020
[dependencies]
2121
bitcoin = "0.27"
2222
lightning = { version = "0.0.106", path = "../lightning" }
23+
futures = { version = "0.3" }
2324
tokio = { version = "1.0", features = [ "io-util", "net", "time" ], optional = true }
2425
serde = { version = "1.0", features = ["derive"], optional = true }
2526
serde_json = { version = "1.0", optional = true }

lightning-block-sync/src/init.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use lightning::chain;
1616
/// start when there are no chain listeners to sync yet.
1717
///
1818
/// [`SpvClient`]: crate::SpvClient
19-
pub async fn validate_best_block_header<B: BlockSource>(block_source: &mut B) ->
19+
pub async fn validate_best_block_header<B: BlockSource>(block_source: &B) ->
2020
BlockSourceResult<ValidatedBlockHeader> {
2121
let (best_block_hash, best_block_height) = block_source.get_best_block().await?;
2222
block_source
@@ -67,7 +67,7 @@ BlockSourceResult<ValidatedBlockHeader> {
6767
/// C: chain::Filter,
6868
/// P: chainmonitor::Persist<S>,
6969
/// >(
70-
/// block_source: &mut B,
70+
/// block_source: &B,
7171
/// chain_monitor: &ChainMonitor<S, &C, &T, &F, &L, &P>,
7272
/// config: UserConfig,
7373
/// keys_manager: &K,
@@ -122,7 +122,7 @@ BlockSourceResult<ValidatedBlockHeader> {
122122
/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
123123
/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
124124
pub async fn synchronize_listeners<'a, B: BlockSource, C: Cache, L: chain::Listen + ?Sized>(
125-
block_source: &mut B,
125+
block_source: &B,
126126
network: Network,
127127
header_cache: &mut C,
128128
mut chain_listeners: Vec<(BlockHash, &'a L)>,

lightning-block-sync/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,19 @@ pub trait BlockSource : Sync + Send {
6161
///
6262
/// Implementations that cannot find headers based on the hash should return a `Transient` error
6363
/// when `height_hint` is `None`.
64-
fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
64+
fn get_header<'a>(&'a self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData>;
6565

6666
/// Returns the block for a given hash. A headers-only block source should return a `Transient`
6767
/// error.
68-
fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block>;
68+
fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block>;
6969

7070
/// Returns the hash of the best block and, optionally, its height.
7171
///
7272
/// When polling a block source, [`Poll`] implementations may pass the height to [`get_header`]
7373
/// to allow for a more efficient lookup.
7474
///
7575
/// [`get_header`]: Self::get_header
76-
fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
76+
fn get_best_block<'a>(&'a self) -> AsyncBlockSourceResult<(BlockHash, Option<u32>)>;
7777
}
7878

7979
/// Result type for `BlockSource` requests.

lightning-block-sync/src/poll.rs

+22-22
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use bitcoin::blockdata::block::Block;
66
use bitcoin::hash_types::BlockHash;
77
use bitcoin::network::constants::Network;
88

9-
use std::ops::DerefMut;
9+
use std::ops::Deref;
1010

1111
/// The `Poll` trait defines behavior for polling block sources for a chain tip and retrieving
1212
/// related chain data. It serves as an adapter for `BlockSource`.
@@ -17,15 +17,15 @@ use std::ops::DerefMut;
1717
/// [`ChainPoller`]: ../struct.ChainPoller.html
1818
pub trait Poll {
1919
/// Returns a chain tip in terms of its relationship to the provided chain tip.
20-
fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
20+
fn poll_chain_tip<'a>(&'a self, best_known_chain_tip: ValidatedBlockHeader) ->
2121
AsyncBlockSourceResult<'a, ChainTip>;
2222

2323
/// Returns the header that preceded the given header in the chain.
24-
fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
24+
fn look_up_previous_header<'a>(&'a self, header: &'a ValidatedBlockHeader) ->
2525
AsyncBlockSourceResult<'a, ValidatedBlockHeader>;
2626

2727
/// Returns the block associated with the given header.
28-
fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
28+
fn fetch_block<'a>(&'a self, header: &'a ValidatedBlockHeader) ->
2929
AsyncBlockSourceResult<'a, ValidatedBlock>;
3030
}
3131

@@ -170,12 +170,12 @@ mod sealed {
170170
///
171171
/// Other `Poll` implementations should be built using `ChainPoller` as it provides the simplest way
172172
/// of validating chain data and checking consistency.
173-
pub struct ChainPoller<B: DerefMut<Target=T> + Sized, T: BlockSource> {
173+
pub struct ChainPoller<B: Deref<Target=T> + Sized, T: BlockSource> {
174174
block_source: B,
175175
network: Network,
176176
}
177177

178-
impl<B: DerefMut<Target=T> + Sized, T: BlockSource> ChainPoller<B, T> {
178+
impl<B: Deref<Target=T> + Sized, T: BlockSource> ChainPoller<B, T> {
179179
/// Creates a new poller for the given block source.
180180
///
181181
/// If the `network` parameter is mainnet, then the difficulty between blocks is checked for
@@ -185,8 +185,8 @@ impl<B: DerefMut<Target=T> + Sized, T: BlockSource> ChainPoller<B, T> {
185185
}
186186
}
187187

188-
impl<B: DerefMut<Target=T> + Sized + Send + Sync, T: BlockSource> Poll for ChainPoller<B, T> {
189-
fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
188+
impl<B: Deref<Target=T> + Sized + Send + Sync, T: BlockSource> Poll for ChainPoller<B, T> {
189+
fn poll_chain_tip<'a>(&'a self, best_known_chain_tip: ValidatedBlockHeader) ->
190190
AsyncBlockSourceResult<'a, ChainTip>
191191
{
192192
Box::pin(async move {
@@ -206,7 +206,7 @@ impl<B: DerefMut<Target=T> + Sized + Send + Sync, T: BlockSource> Poll for Chain
206206
})
207207
}
208208

209-
fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
209+
fn look_up_previous_header<'a>(&'a self, header: &'a ValidatedBlockHeader) ->
210210
AsyncBlockSourceResult<'a, ValidatedBlockHeader>
211211
{
212212
Box::pin(async move {
@@ -225,7 +225,7 @@ impl<B: DerefMut<Target=T> + Sized + Send + Sync, T: BlockSource> Poll for Chain
225225
})
226226
}
227227

228-
fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
228+
fn fetch_block<'a>(&'a self, header: &'a ValidatedBlockHeader) ->
229229
AsyncBlockSourceResult<'a, ValidatedBlock>
230230
{
231231
Box::pin(async move {
@@ -249,7 +249,7 @@ mod tests {
249249
let best_known_chain_tip = chain.tip();
250250
chain.disconnect_tip();
251251

252-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
252+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
253253
match poller.poll_chain_tip(best_known_chain_tip).await {
254254
Err(e) => {
255255
assert_eq!(e.kind(), BlockSourceErrorKind::Transient);
@@ -261,10 +261,10 @@ mod tests {
261261

262262
#[tokio::test]
263263
async fn poll_chain_without_headers() {
264-
let mut chain = Blockchain::default().with_height(1).without_headers();
264+
let chain = Blockchain::default().with_height(1).without_headers();
265265
let best_known_chain_tip = chain.at_height(0);
266266

267-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
267+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
268268
match poller.poll_chain_tip(best_known_chain_tip).await {
269269
Err(e) => {
270270
assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
@@ -283,7 +283,7 @@ mod tests {
283283
chain.blocks.last_mut().unwrap().header.bits =
284284
BlockHeader::compact_target_from_u256(&Uint256::from_be_bytes([0; 32]));
285285

286-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
286+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
287287
match poller.poll_chain_tip(best_known_chain_tip).await {
288288
Err(e) => {
289289
assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
@@ -295,10 +295,10 @@ mod tests {
295295

296296
#[tokio::test]
297297
async fn poll_chain_with_malformed_headers() {
298-
let mut chain = Blockchain::default().with_height(1).malformed_headers();
298+
let chain = Blockchain::default().with_height(1).malformed_headers();
299299
let best_known_chain_tip = chain.at_height(0);
300300

301-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
301+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
302302
match poller.poll_chain_tip(best_known_chain_tip).await {
303303
Err(e) => {
304304
assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
@@ -310,10 +310,10 @@ mod tests {
310310

311311
#[tokio::test]
312312
async fn poll_chain_with_common_tip() {
313-
let mut chain = Blockchain::default().with_height(0);
313+
let chain = Blockchain::default().with_height(0);
314314
let best_known_chain_tip = chain.tip();
315315

316-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
316+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
317317
match poller.poll_chain_tip(best_known_chain_tip).await {
318318
Err(e) => panic!("Unexpected error: {:?}", e),
319319
Ok(tip) => assert_eq!(tip, ChainTip::Common),
@@ -330,7 +330,7 @@ mod tests {
330330
let worse_chain_tip = chain.tip();
331331
assert_eq!(best_known_chain_tip.chainwork, worse_chain_tip.chainwork);
332332

333-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
333+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
334334
match poller.poll_chain_tip(best_known_chain_tip).await {
335335
Err(e) => panic!("Unexpected error: {:?}", e),
336336
Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
@@ -345,7 +345,7 @@ mod tests {
345345
chain.disconnect_tip();
346346
let worse_chain_tip = chain.tip();
347347

348-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
348+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
349349
match poller.poll_chain_tip(best_known_chain_tip).await {
350350
Err(e) => panic!("Unexpected error: {:?}", e),
351351
Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
@@ -354,12 +354,12 @@ mod tests {
354354

355355
#[tokio::test]
356356
async fn poll_chain_with_better_tip() {
357-
let mut chain = Blockchain::default().with_height(1);
357+
let chain = Blockchain::default().with_height(1);
358358
let best_known_chain_tip = chain.at_height(0);
359359

360360
let better_chain_tip = chain.tip();
361361

362-
let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
362+
let poller = ChainPoller::new(&chain, Network::Bitcoin);
363363
match poller.poll_chain_tip(best_known_chain_tip).await {
364364
Err(e) => panic!("Unexpected error: {:?}", e),
365365
Ok(tip) => assert_eq!(tip, ChainTip::Better(better_chain_tip)),

lightning-block-sync/src/rest.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,51 @@ use bitcoin::blockdata::block::Block;
88
use bitcoin::hash_types::BlockHash;
99
use bitcoin::hashes::hex::ToHex;
1010

11+
use futures::lock::Mutex;
12+
1113
use std::convert::TryFrom;
1214
use std::convert::TryInto;
1315

1416
/// A simple REST client for requesting resources using HTTP `GET`.
1517
pub struct RestClient {
1618
endpoint: HttpEndpoint,
17-
client: HttpClient,
19+
client: Mutex<HttpClient>,
1820
}
1921

2022
impl RestClient {
2123
/// Creates a new REST client connected to the given endpoint.
2224
///
2325
/// The endpoint should contain the REST path component (e.g., http://127.0.0.1:8332/rest).
2426
pub fn new(endpoint: HttpEndpoint) -> std::io::Result<Self> {
25-
let client = HttpClient::connect(&endpoint)?;
27+
let client = Mutex::new(HttpClient::connect(&endpoint)?);
2628
Ok(Self { endpoint, client })
2729
}
2830

2931
/// Requests a resource encoded in `F` format and interpreted as type `T`.
30-
pub async fn request_resource<F, T>(&mut self, resource_path: &str) -> std::io::Result<T>
32+
pub async fn request_resource<F, T>(&self, resource_path: &str) -> std::io::Result<T>
3133
where F: TryFrom<Vec<u8>, Error = std::io::Error> + TryInto<T, Error = std::io::Error> {
3234
let host = format!("{}:{}", self.endpoint.host(), self.endpoint.port());
3335
let uri = format!("{}/{}", self.endpoint.path().trim_end_matches("/"), resource_path);
34-
self.client.get::<F>(&uri, &host).await?.try_into()
36+
self.client.lock().await.get::<F>(&uri, &host).await?.try_into()
3537
}
3638
}
3739

3840
impl BlockSource for RestClient {
39-
fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, _height: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
41+
fn get_header<'a>(&'a self, header_hash: &'a BlockHash, _height: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
4042
Box::pin(async move {
4143
let resource_path = format!("headers/1/{}.json", header_hash.to_hex());
4244
Ok(self.request_resource::<JsonResponse, _>(&resource_path).await?)
4345
})
4446
}
4547

46-
fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> {
48+
fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> {
4749
Box::pin(async move {
4850
let resource_path = format!("block/{}.bin", header_hash.to_hex());
4951
Ok(self.request_resource::<BinaryResponse, _>(&resource_path).await?)
5052
})
5153
}
5254

53-
fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<'a, (BlockHash, Option<u32>)> {
55+
fn get_best_block<'a>(&'a self) -> AsyncBlockSourceResult<'a, (BlockHash, Option<u32>)> {
5456
Box::pin(async move {
5557
Ok(self.request_resource::<JsonResponse, _>("chaininfo.json").await?)
5658
})
@@ -81,7 +83,7 @@ mod tests {
8183
#[tokio::test]
8284
async fn request_unknown_resource() {
8385
let server = HttpServer::responding_with_not_found();
84-
let mut client = RestClient::new(server.endpoint()).unwrap();
86+
let client = RestClient::new(server.endpoint()).unwrap();
8587

8688
match client.request_resource::<BinaryResponse, u32>("/").await {
8789
Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::Other),
@@ -92,7 +94,7 @@ mod tests {
9294
#[tokio::test]
9395
async fn request_malformed_resource() {
9496
let server = HttpServer::responding_with_ok(MessageBody::Content("foo"));
95-
let mut client = RestClient::new(server.endpoint()).unwrap();
97+
let client = RestClient::new(server.endpoint()).unwrap();
9698

9799
match client.request_resource::<BinaryResponse, u32>("/").await {
98100
Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::InvalidData),
@@ -103,7 +105,7 @@ mod tests {
103105
#[tokio::test]
104106
async fn request_valid_resource() {
105107
let server = HttpServer::responding_with_ok(MessageBody::Content(42));
106-
let mut client = RestClient::new(server.endpoint()).unwrap();
108+
let client = RestClient::new(server.endpoint()).unwrap();
107109

108110
match client.request_resource::<BinaryResponse, u32>("/").await {
109111
Err(e) => panic!("Unexpected error: {:?}", e),

0 commit comments

Comments
 (0)