Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13711249
D17936.id53564.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
38 KB
Subscribers
None
D17936.id53564.diff
View Options
diff --git a/Cargo.lock b/Cargo.lock
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,7 +10,7 @@
"eyre",
"http 1.2.0",
"stable-eyre",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
]
[[package]]
@@ -166,7 +166,7 @@
"log",
"pin-project-lite",
"tokio",
- "tungstenite",
+ "tungstenite 0.24.0",
]
[[package]]
@@ -271,7 +271,7 @@
"sha1",
"sync_wrapper 1.0.2",
"tokio",
- "tokio-tungstenite",
+ "tokio-tungstenite 0.24.0",
"tower 0.5.1",
"tower-layer",
"tower-service",
@@ -474,7 +474,9 @@
"bitcoinsuite-core 0.1.0",
"bytes",
"chronik-proto",
+ "futures-util",
"hex",
+ "lazy_static",
"pretty_assertions",
"prost",
"prost-build",
@@ -484,6 +486,9 @@
"serde_json",
"thiserror 1.0.69",
"tokio",
+ "tokio-tungstenite 0.26.2",
+ "tungstenite 0.26.2",
+ "url",
]
[[package]]
@@ -513,7 +518,7 @@
"serde",
"serde_json",
"sha2",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
]
[[package]]
@@ -569,7 +574,7 @@
"itertools 0.10.5",
"pretty_assertions",
"serde",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
]
[[package]]
@@ -706,7 +711,7 @@
"seahash",
"serde",
"tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"topo_sort",
]
@@ -737,7 +742,7 @@
"prost",
"rustls",
"serde_json",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"tokio",
"tower-http 0.5.2",
"tower-service",
@@ -765,7 +770,7 @@
"prost",
"prost-build",
"tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"tokio",
]
@@ -809,7 +814,7 @@
"pyo3",
"serde",
"tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"toml 0.8.19",
"versions",
]
@@ -845,7 +850,7 @@
"chronik-util",
"cxx",
"cxx-build",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"tokio",
]
@@ -1151,7 +1156,7 @@
"ecash-secp256k1",
"ripemd",
"sha2",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"wasm-bindgen",
]
@@ -1162,7 +1167,7 @@
"bincode 1.3.3",
"bitcoin_hashes 0.14.0",
"ecash-secp256k1-sys",
- "getrandom",
+ "getrandom 0.2.15",
"hex_lit",
"rand 0.8.5",
"rand_core 0.6.4",
@@ -1279,7 +1284,7 @@
"regex",
"serde",
"serde_json",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
"tokio",
"toml 0.5.11",
"tower-http 0.3.5",
@@ -1488,10 +1493,22 @@
"cfg-if",
"js-sys",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
+[[package]]
+name = "getrandom"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
+
[[package]]
name = "gimli"
version = "0.28.1"
@@ -2304,7 +2321,7 @@
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
@@ -2571,7 +2588,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
- "zerocopy",
+ "zerocopy 0.7.35",
]
[[package]]
@@ -2780,6 +2797,12 @@
"proc-macro2",
]
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
[[package]]
name = "radium"
version = "0.5.3"
@@ -2806,10 +2829,21 @@
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
+ "rand_chacha 0.3.1",
"rand_core 0.6.4",
]
+[[package]]
+name = "rand"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+ "zerocopy 0.8.24",
+]
+
[[package]]
name = "rand_chacha"
version = "0.3.1"
@@ -2820,6 +2854,16 @@
"rand_core 0.6.4",
]
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
+]
+
[[package]]
name = "rand_core"
version = "0.3.1"
@@ -2841,7 +2885,16 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.2",
]
[[package]]
@@ -2868,7 +2921,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
"libredox",
"thiserror 1.0.69",
]
@@ -2959,7 +3012,7 @@
dependencies = [
"cc",
"cfg-if",
- "getrandom",
+ "getrandom 0.2.15",
"libc",
"spin",
"untrusted",
@@ -3489,11 +3542,11 @@
[[package]]
name = "thiserror"
-version = "2.0.4"
+version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
- "thiserror-impl 2.0.4",
+ "thiserror-impl 2.0.12",
]
[[package]]
@@ -3509,9 +3562,9 @@
[[package]]
name = "thiserror-impl"
-version = "2.0.4"
+version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
@@ -3586,7 +3639,19 @@
"futures-util",
"log",
"tokio",
- "tungstenite",
+ "tungstenite 0.24.0",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
+dependencies = [
+ "futures-util",
+ "log",
+ "tokio",
+ "tungstenite 0.26.2",
]
[[package]]
@@ -3780,6 +3845,23 @@
"utf-8",
]
+[[package]]
+name = "tungstenite"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand 0.9.0",
+ "sha1",
+ "thiserror 2.0.12",
+ "utf-8",
+]
+
[[package]]
name = "typenum"
version = "1.17.0"
@@ -3900,6 +3982,15 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
@@ -4220,6 +4311,15 @@
"windows-sys 0.48.0",
]
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
[[package]]
name = "write16"
version = "1.0.0"
@@ -4275,7 +4375,16 @@
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
- "zerocopy-derive",
+ "zerocopy-derive 0.7.35",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
+dependencies = [
+ "zerocopy-derive 0.8.24",
]
[[package]]
@@ -4289,6 +4398,17 @@
"syn 2.0.90",
]
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
[[package]]
name = "zerofrom"
version = "0.1.5"
diff --git a/modules/bitcoinsuite-chronik-client/Cargo.toml b/modules/bitcoinsuite-chronik-client/Cargo.toml
--- a/modules/bitcoinsuite-chronik-client/Cargo.toml
+++ b/modules/bitcoinsuite-chronik-client/Cargo.toml
@@ -40,6 +40,20 @@
# Serializes and deserializes Rust data
serde = "1.0.217"
+# Async streams utilities
+futures-util = "0.3"
+
+# Lazy static initializers
+lazy_static = "1.4"
+
+# URL parsing and manipulation
+url = "2.5"
+
+# WebSocket client implementation
+tokio-tungstenite = "0.26.2"
+
+tungstenite = "0.26.2"
+
[build-dependencies]
# Build Protobuf structs
prost-build = "0.11"
diff --git a/modules/bitcoinsuite-chronik-client/src/lib.rs b/modules/bitcoinsuite-chronik-client/src/lib.rs
--- a/modules/bitcoinsuite-chronik-client/src/lib.rs
+++ b/modules/bitcoinsuite-chronik-client/src/lib.rs
@@ -3,12 +3,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
use std::fmt::Display;
-use abc_rust_error::{Result, WrapErr};
+use abc_rust_error::{Report, Result, WrapErr};
use bitcoinsuite_core::hash::{Hashed, Sha256d};
use bytes::Bytes;
pub use chronik_proto::proto;
+use futures_util::{SinkExt, StreamExt};
+use prost::Message;
use reqwest::{header::CONTENT_TYPE, StatusCode};
use thiserror::Error;
+use tokio_tungstenite::WebSocketStream;
+use tungstenite::protocol::Message as WsMessage;
use crate::ChronikClientError::*;
@@ -68,6 +72,17 @@
#[error("Invalid protobuf: {0}")]
InvalidProtobuf(String),
}
+pub struct WsEndpoint {
+ pub ws: WebSocketStream<
+ tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
+ >,
+ pub subs: WsSubscriptions,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct WsSubscriptions {
+ pub blocks: bool,
+}
impl ChronikClient {
pub fn new(url: String) -> Result<Self> {
@@ -253,6 +268,26 @@
Self::_handle_response(response).await
}
+ pub async fn ws(&self) -> Result<WsEndpoint> {
+ let (ws, response) =
+ tokio_tungstenite::connect_async(&self.ws_url).await?;
+
+ if response.status()
+ != tungstenite::http::StatusCode::SWITCHING_PROTOCOLS
+ {
+ abc_rust_error::bail!(
+ "WebSocket connection failed: expected status 101 (Switching \
+ Protocols), got {}",
+ response.status()
+ )
+ } else {
+ Ok(WsEndpoint {
+ ws,
+ subs: WsSubscriptions::default(),
+ })
+ }
+ }
+
async fn _get<MResponse: prost::Message + Default>(
&self,
url_suffix: &str,
@@ -441,6 +476,66 @@
}
}
+impl WsEndpoint {
+ pub async fn subscribe_to_blocks(&mut self) -> Result<(), Report> {
+ let msg = proto::WsSub {
+ is_unsub: false,
+ sub_type: Some(proto::ws_sub::SubType::Blocks(
+ proto::WsSubBlocks {},
+ )),
+ };
+ self.ws
+ .send(tokio_tungstenite::tungstenite::Message::Binary(
+ Bytes::from(msg.encode_to_vec()),
+ ))
+ .await
+ .wrap_err(HttpRequestError)?;
+ self.subs.blocks = true;
+ Ok(())
+ }
+
+ pub async fn unsubscribe_from_blocks(&mut self) -> Result<(), Report> {
+ let msg = proto::WsSub {
+ is_unsub: true,
+ sub_type: Some(proto::ws_sub::SubType::Blocks(
+ proto::WsSubBlocks {},
+ )),
+ };
+ self.ws
+ .send(tokio_tungstenite::tungstenite::Message::Binary(
+ Bytes::from(msg.encode_to_vec()),
+ ))
+ .await
+ .wrap_err(HttpRequestError)?;
+ self.subs.blocks = false;
+
+ Ok(())
+ }
+
+ pub async fn recv(&mut self) -> Result<Option<proto::WsMsg>, Report> {
+ while let Some(msg) = self.ws.next().await {
+ match msg {
+ Ok(WsMessage::Binary(data)) => {
+ return Ok(Some(proto::WsMsg::decode(data)?))
+ }
+ Ok(WsMessage::Ping(data)) => {
+ self.ws.send(WsMessage::Pong(data)).await?;
+ }
+ Ok(WsMessage::Close(_)) => return Ok(None),
+ Ok(WsMessage::Pong(_)) => {}
+ Err(err) => return Err(err.into()),
+ Ok(unexpected_message) => {
+ abc_rust_error::bail!(
+ "Unexpected WebSocket message type received {:?}",
+ unexpected_message
+ );
+ }
+ }
+ }
+ Ok(None)
+ }
+}
+
#[cfg(test)]
mod tests {
use abc_rust_error::Result;
diff --git a/modules/bitcoinsuite-chronik-client/tests/websocket.rs b/modules/bitcoinsuite-chronik-client/tests/websocket.rs
new file mode 100644
--- /dev/null
+++ b/modules/bitcoinsuite-chronik-client/tests/websocket.rs
@@ -0,0 +1,573 @@
+// Copyright (c) 2025 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+use std::sync::{
+ atomic::{AtomicI32, Ordering},
+ Arc,
+};
+
+use async_trait::async_trait;
+use bitcoinsuite_chronik_client::{
+ handler::{IpcHandler, IpcReader},
+ test_runner::{handle_test_info, spin_child_process},
+ ChronikClient, WsEndpoint,
+};
+use bitcoinsuite_core::block::BlockHash;
+use chronik_proto::proto::{
+ ws_msg::MsgType, BlockMsgType::*, CoinbaseData, MsgBlock, TxOutput, WsMsg,
+};
+use serde_json::Value;
+use tokio::sync::Mutex;
+
+#[derive(Default)]
+struct WebsocketIPC {
+ pub chronik_url: Mutex<String>,
+ pub counter: AtomicI32,
+ pub finalized_block_blockhash: Mutex<String>,
+ pub finalized_height: Mutex<i64>,
+ pub block_timestamp: Mutex<i64>,
+ pub next_blockhash: Mutex<String>,
+ pub endpoint: Mutex<Option<WsEndpoint>>,
+ pub coinbase_out_scriptpubkey: Mutex<String>,
+ pub coinbase_out_value: Mutex<i64>,
+ pub coinbase_scriptsig: Mutex<String>,
+}
+
+macro_rules! use_endpoint {
+ ($mutex:expr, $var:ident => $body:expr) => {{
+ let mut guard = $mutex.lock().await;
+ if let Some($var) = &mut *guard {
+ $body
+ } else {
+ return Err(abc_rust_error::Report::msg(
+ "Expected Some, found None",
+ ));
+ }
+ }};
+}
+
+#[async_trait]
+impl IpcReader for WebsocketIPC {
+ async fn on_rx(
+ &self,
+ handler: &mut IpcHandler,
+ json_data: Value,
+ ) -> Result<(), abc_rust_error::Report> {
+ let values_to_match: Vec<&str> = vec![
+ "test_info",
+ "finalized_block_blockhash",
+ "finalized_height",
+ "block_timestamp",
+ "next_blockhash",
+ "coinbase_out_scriptpubkey",
+ "coinbase_out_value",
+ "coinbase_scriptsig",
+ ];
+
+ for key in values_to_match {
+ if let Some(value) = json_data.get(key) {
+ match key {
+ "test_info" => {
+ *self.chronik_url.lock().await =
+ handle_test_info(value).expect(
+ "Failed to extract chronik URL from test_info
+ message",
+ );
+ }
+ "finalized_block_blockhash" => {
+ if let Some(blockhash) = value.as_str() {
+ *self.finalized_block_blockhash.lock().await =
+ blockhash.to_string();
+ }
+ }
+ "finalized_height" => {
+ if let Some(height) = value.as_i64() {
+ *self.finalized_height.lock().await = height;
+ }
+ }
+ "block_timestamp" => {
+ if let Some(timestamp) = value.as_i64() {
+ *self.block_timestamp.lock().await = timestamp;
+ }
+ }
+ "next_blockhash" => {
+ if let Some(blockhash) = value.as_str() {
+ *self.next_blockhash.lock().await =
+ blockhash.to_string();
+ }
+ }
+ "coinbase_out_scriptpubkey" => {
+ if let Some(coinbase_out_spk) = value.as_str() {
+ *self.coinbase_out_scriptpubkey.lock().await =
+ coinbase_out_spk.to_string();
+ }
+ }
+ "coinbase_out_value" => {
+ if let Some(coinbase_out_value) = value.as_i64() {
+ *self.coinbase_out_value.lock().await =
+ coinbase_out_value;
+ }
+ }
+ "coinbase_scriptsig" => {
+ if let Some(coinbase_sig) = value.as_str() {
+ *self.coinbase_scriptsig.lock().await =
+ coinbase_sig.to_string();
+ }
+ }
+ _ => {
+ println!("Unhandled key: {}", key);
+ }
+ }
+ }
+ }
+
+ if json_data.get("status").and_then(|status| status.as_str())
+ == Some("ready")
+ {
+ let chronik_url = (*self.chronik_url.lock().await).clone();
+ match self.counter.load(Ordering::SeqCst) {
+ // New regtest chain.
+ 0 | 1 => {
+ *self.endpoint.lock().await =
+ Some(ChronikClient::new(chronik_url)?.ws().await?);
+ use_endpoint!(self.endpoint,
+ ep => ep.subscribe_to_blocks().await)?;
+ assert_eq!(
+ self.endpoint
+ .lock()
+ .await
+ .as_ref()
+ .unwrap()
+ .subs
+ .blocks,
+ true
+ );
+ use_endpoint!(self.endpoint,
+ ep => ep.unsubscribe_from_blocks().await)?;
+ assert_eq!(
+ self.endpoint
+ .lock()
+ .await
+ .as_ref()
+ .unwrap()
+ .subs
+ .blocks,
+ false
+ );
+ use_endpoint!(self.endpoint,
+ ep => ep.subscribe_to_blocks().await)?;
+ }
+ // After a block is avalanche finalized
+ 2 => {
+ let finalized_block_message = use_endpoint!(self.endpoint,
+ ep => ep.recv().await)?;
+ assert_eq!(
+ finalized_block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkFinalized.into(),
+ block_hash: (self
+ .finalized_block_blockhash
+ .lock()
+ .await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After some txs have been broadcast
+ 3 => {
+ // Part of the structure to keep count clean
+ // for future iterations. Tx testing will be
+ // conducted here, but we are not testing it
+ // in this diff.
+ }
+ // After a block is mined
+ 4 => {
+ let block_connected_msg =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_connected_msg,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkConnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After this block is finalized by Avalanche
+ 5 => {
+ let block_connected_msg =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_connected_msg,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkFinalized.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After this block is parked
+ 6 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkDisconnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: Some(CoinbaseData {
+ coinbase_scriptsig: hex::decode(
+ &*self.coinbase_scriptsig.lock().await
+ )?,
+ coinbase_outputs: vec![TxOutput {
+ sats: self
+ .coinbase_out_value
+ .lock()
+ .await
+ .clone(),
+ output_script: hex::decode(
+ &*self
+ .coinbase_out_scriptpubkey
+ .lock()
+ .await
+ )?,
+ spent_by: None,
+ token: None,
+ plugins: Default::default()
+ }]
+ })
+ }))
+ })
+ );
+ }
+ // After this block is unparked
+ 7 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkConnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After this block is invalidated
+ 8 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkDisconnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: Some(CoinbaseData {
+ coinbase_scriptsig: hex::decode(
+ &*self.coinbase_scriptsig.lock().await
+ )?,
+ coinbase_outputs: vec![TxOutput {
+ sats: self
+ .coinbase_out_value
+ .lock()
+ .await
+ .clone(),
+ output_script: hex::decode(
+ &*self
+ .coinbase_out_scriptpubkey
+ .lock()
+ .await
+ )?,
+ spent_by: None,
+ token: None,
+ plugins: Default::default()
+ }]
+ })
+ }))
+ })
+ );
+ }
+ // After this block is reconsidered'
+ 9 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkConnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 1,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After a tx is broadcast with outputs of each type
+ 10 => {
+ // Part of the structure to keep count clean
+ // for future iterations. Tx testing will be
+ // conducted here, but we are not testing it
+ // in this diff.
+ }
+ // After a block is mined
+ 11 => {
+ let next_blockhash =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ next_blockhash,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkConnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 2,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: None,
+ }))
+ })
+ );
+ }
+ // After this block is avalanche parked
+ 12 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkDisconnected.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 2,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: Some(CoinbaseData {
+ coinbase_scriptsig: hex::decode(
+ &*self.coinbase_scriptsig.lock().await
+ )?,
+ coinbase_outputs: vec![TxOutput {
+ sats: self
+ .coinbase_out_value
+ .lock()
+ .await
+ .clone(),
+ output_script: hex::decode(
+ &*self
+ .coinbase_out_scriptpubkey
+ .lock()
+ .await
+ )?,
+ spent_by: None,
+ token: None,
+ plugins: Default::default()
+ }]
+ })
+ }))
+ })
+ );
+ }
+ // After this block is avalanche invalidated
+ 13 => {
+ let block_message =
+ use_endpoint!(self.endpoint, ep => ep.recv().await)?;
+ assert_eq!(
+ block_message,
+ Some(WsMsg {
+ msg_type: Some(MsgType::Block(MsgBlock {
+ msg_type: BlkInvalidated.into(),
+ block_hash: (self.next_blockhash.lock().await)
+ .parse::<BlockHash>()?
+ .to_vec(),
+ block_height: *self
+ .finalized_height
+ .lock()
+ .await
+ as i32
+ + 2,
+ block_timestamp: *self
+ .block_timestamp
+ .lock()
+ .await,
+ coinbase_data: Some(CoinbaseData {
+ coinbase_scriptsig: hex::decode(
+ &*self.coinbase_scriptsig.lock().await
+ )?,
+ coinbase_outputs: vec![TxOutput {
+ sats: self
+ .coinbase_out_value
+ .lock()
+ .await
+ .clone(),
+ output_script: hex::decode(
+ &*self
+ .coinbase_out_scriptpubkey
+ .lock()
+ .await
+ )?,
+ spent_by: None,
+ token: None,
+ plugins: Default::default()
+ }]
+ })
+ }))
+ })
+ );
+ use_endpoint!(self.endpoint,
+ ep => ep.unsubscribe_from_blocks().await)?;
+ assert_eq!(
+ self.endpoint
+ .lock()
+ .await
+ .as_ref()
+ .unwrap()
+ .subs
+ .blocks,
+ false
+ );
+ }
+ // After we have unsubscribed to all and another block is found'
+ 14 => {
+ // We get no new msgs after unsubscribing from blocks and
+ // txs, even after block connected
+ // Had we stayed subscribed, we would have expected to
+ // receive Tx Confirmed msgs for the
+ // mixedTx in last step and block connected for new block
+ }
+ _ => {
+ handler.send_message("stop").await?;
+ unreachable!(
+ "An unexpected ready message was sent from the
+ setup framework, causing the counter to
+ increment beyond a valid match arm."
+ );
+ }
+ }
+ self.counter.fetch_add(1, Ordering::SeqCst);
+ handler.send_message("next").await?;
+ return Ok(());
+ }
+ Ok(())
+ }
+}
+
+#[tokio::test]
+pub async fn websocket() -> Result<(), abc_rust_error::Report> {
+ let python_script = "websocket";
+
+ let ipc_reader = Arc::new(WebsocketIPC::default());
+
+ spin_child_process(python_script, ipc_reader.clone()).await?;
+
+ Ok(())
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 26, 11:10 (10 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573368
Default Alt Text
D17936.id53564.diff (38 KB)
Attached To
D17936: [chronik] Add block subscription functionality to websocket.rs within bitcoinsuite-chronik-client
Event Timeline
Log In to Comment