Repository navigation
Bump BDK wallet dependency to 3.2 #1182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
48b6c59
458e434
03a911d
5bc67b7
32f0c52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -919,14 +919,98 @@ impl Filter for ElectrumRuntimeClient { | |
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use std::io::{BufRead, BufReader, Write}; | ||
| use std::net::TcpListener; | ||
| use std::sync::atomic::{AtomicUsize, Ordering}; | ||
| use std::sync::mpsc; | ||
| use std::sync::{mpsc, Barrier}; | ||
| use std::thread; | ||
|
|
||
| use bitcoin::blockdata::constants::genesis_block; | ||
|
|
||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn clean_eof_wakes_concurrent_electrum_requests() { | ||
| const REQUEST_COUNT: usize = 8; | ||
| for _ in 0..10 { | ||
| let listener = TcpListener::bind("127.0.0.1:0").unwrap(); | ||
| let server_address = listener.local_addr().unwrap(); | ||
| let server = thread::spawn(move || { | ||
| let (stream, _) = listener.accept().unwrap(); | ||
| let mut reader = BufReader::new(stream); | ||
| let mut version_request = String::new(); | ||
| assert!(reader.read_line(&mut version_request).unwrap() > 0); | ||
| let version_request: serde_json::Value = | ||
| serde_json::from_str(&version_request).unwrap(); | ||
| let version_response = serde_json::json!({ | ||
| "jsonrpc": "2.0", | ||
| "id": version_request["id"], | ||
| "result": ["test-server", "1.4"], | ||
| }); | ||
| writeln!(reader.get_mut(), "{version_response}").unwrap(); | ||
| reader.get_mut().flush().unwrap(); | ||
| reader.get_mut().set_read_timeout(Some(Duration::from_millis(250))).unwrap(); | ||
|
|
||
| let mut requests_received = 0; | ||
| while requests_received < 2 { | ||
| let mut request = String::new(); | ||
| match reader.read_line(&mut request) { | ||
| Ok(0) => break, | ||
| Ok(_) => requests_received += 1, | ||
| Err(e) | ||
| if matches!( | ||
| e.kind(), | ||
| std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut | ||
| ) => | ||
| { | ||
| break | ||
| }, | ||
| Err(e) => panic!("failed to read Electrum request: {e}"), | ||
| } | ||
| } | ||
| if requests_received > 1 { | ||
| thread::sleep(Duration::from_millis(50)); | ||
| } | ||
| requests_received | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we close the entire socket while request data may still be unread. This can send a TCP reset, which the old client already handles correctly, allowing the test to pass without exercising the EOF bug. We should shut down only the server’s sending side first, then read the remaining requests before fully closing the socket. |
||
|
|
||
| let config = ElectrumConfigBuilder::new().retry(0).build(); | ||
| let client = Arc::new( | ||
| ElectrumClient::from_config(&format!("tcp://{server_address}"), config).unwrap(), | ||
| ); | ||
| let start = Arc::new(Barrier::new(REQUEST_COUNT + 1)); | ||
| let (finished_sender, finished_receiver) = mpsc::channel(); | ||
| let mut requests = Vec::new(); | ||
| for _ in 0..REQUEST_COUNT { | ||
| let client = Arc::clone(&client); | ||
| let start = Arc::clone(&start); | ||
| let finished_sender = finished_sender.clone(); | ||
| requests.push(thread::spawn(move || { | ||
| start.wait(); | ||
| let result = | ||
| client.raw_call("server.ping", Vec::<electrum_client::Param>::new()); | ||
| finished_sender.send(result.is_err()).unwrap(); | ||
| })); | ||
| } | ||
| start.wait(); | ||
| let requests_received = server.join().unwrap(); | ||
|
|
||
| for _ in 0..REQUEST_COUNT { | ||
| let failed_as_expected = finished_receiver | ||
| .recv_timeout(Duration::from_secs(5)) | ||
| .expect("clean EOF must wake every concurrent Electrum request"); | ||
| assert!(failed_as_expected); | ||
| } | ||
| for request in requests { | ||
| request.join().unwrap(); | ||
| } | ||
| if requests_received > 1 { | ||
| return; | ||
| } | ||
| } | ||
| panic!("failed to issue concurrent Electrum requests"); | ||
| } | ||
|
|
||
| struct RecordingConfirm { | ||
| calls: AtomicUsize, | ||
| relevant_txid: Txid, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s also ensure multiple requests are fully sent before shutting down the server’s sending side. Otherwise, the remaining requests could fail while writing, allowing the test to pass without verifying that EOF wakes multiple requests waiting for responses.