Page MenuHomePhabricator

D17936.id53564.diff
No OneTemporary

D17936.id53564.diff

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

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)

Event Timeline