diff --git a/Cargo.lock b/Cargo.lock index 8f2cc968..91f73cf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -918,9 +918,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-simd" @@ -1068,6 +1068,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata 0.4.5", + "serde", +] + [[package]] name = "bumpalo" version = "3.15.3" @@ -1088,9 +1099,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bytes-utils" @@ -1802,6 +1813,12 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "debugid" version = "0.8.0" @@ -2200,7 +2217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a63bfc6d371d3b51d6094fd96c4c32a084ceefece3b4f4b328f30067d29da064" dependencies = [ "anyhow", - "base64 0.22.0", + "base64 0.22.1", "bytemuck", "extism-convert-macros", "prost 0.12.3", @@ -2228,7 +2245,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c7d16695dc6b72418e23b58c943411a08264332af403ae9870997b4d495c3d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", "serde_json", ] @@ -2638,6 +2655,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "goldenfile" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "672ff1c2f0537cf3f92065ce8aa77e2fc3f2abae2c805eb67f40ceecfbdee428" +dependencies = [ + "scopeguard", + "similar-asserts", + "tempfile", + "yansi", +] + [[package]] name = "google-cloud-auth" version = "0.11.0" @@ -3801,7 +3830,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", "serde_yaml", "sha2", "slog", @@ -4107,6 +4136,7 @@ dependencies = [ "aws-sdk-sqs", "aws-types", "bech32 0.9.1", + "bytes", "clap", "config", "crossterm", @@ -4115,8 +4145,10 @@ dependencies = [ "extism", "file-rotate", "futures", + "futures-util", "gasket", "gasket-prometheus", + "goldenfile", "google-cloud-default", "google-cloud-googleapis", "google-cloud-pubsub", @@ -4134,6 +4166,7 @@ dependencies = [ "net2", "openssl", "pallas", + "port-selector", "r2d2_redis", "regex", "reqwest 0.11.24", @@ -4142,11 +4175,14 @@ dependencies = [ "sqlx", "strum 0.24.1", "strum_macros 0.25.3", + "tempfile", "thiserror", "tokio", + "tokio-tungstenite", "tonic 0.11.0", "tracing", "tracing-subscriber", + "tungstenite", "unicode-truncate", ] @@ -4291,7 +4327,7 @@ version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12dc7f456f61479407f22afd29a58671c148c50c703b16ef09653406d131f0e0" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "hex", "num-rational", "pallas-addresses 0.30.2", @@ -4300,7 +4336,7 @@ dependencies = [ "pallas-primitives 0.30.2", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", ] [[package]] @@ -4787,6 +4823,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "port-selector" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd119ef551a50cd8939f0ff93bd062891f7b0dbb771b4a05df8a9c13aebaff68" +dependencies = [ + "rand", +] + [[package]] name = "portable-atomic" version = "1.6.0" @@ -5272,7 +5317,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", @@ -5568,7 +5613,7 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] @@ -5811,11 +5856,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -5823,7 +5868,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.8.1", + "serde_with_macros 3.11.0", "time", ] @@ -5853,9 +5898,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling 0.20.9", "proc-macro2", @@ -5981,6 +6026,26 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe85670573cd6f0fa97940f26e7e6601213c3b0555246c24234131f88c5709e" +dependencies = [ + "console", + "similar", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -6751,6 +6816,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -6983,6 +7060,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.0.0", + "httparse", + "log", + "rand", + "sha1 0.10.6", + "thiserror", + "utf-8", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -7157,6 +7252,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" @@ -8181,6 +8282,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index d8387ac3..2eff47b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,3 +79,12 @@ extism = { version = "1.2.0", optional = true } mithril-client = { version = "^0.8", optional = true, features = ["fs"] } miette = { version = "7.2.0", features = ["fancy"] } itertools = "0.12.1" +tungstenite = "0.24.0" +tokio-tungstenite = "0.24.0" +futures-util = "0.3" +bytes = "1.7.2" + +[dev-dependencies] +goldenfile = "1.7.3" +tempfile = "3.4" +port-selector = "0.1.6" diff --git a/examples/hydra/daemon.toml b/examples/hydra/daemon.toml new file mode 100644 index 00000000..e860fa89 --- /dev/null +++ b/examples/hydra/daemon.toml @@ -0,0 +1,10 @@ +[source] +type = "Hydra" +hydra_socket_path = "ws://127.0.0.1:4001" +magic = 42 + +[intersect] +type = "Origin" + +[sink] +type = "Stdout" diff --git a/src/bin/oura/main.rs b/src/bin/oura/main.rs index 24420043..c253a8cc 100644 --- a/src/bin/oura/main.rs +++ b/src/bin/oura/main.rs @@ -2,21 +2,21 @@ use clap::Parser; use std::process; mod console; -mod daemon; +mod run_daemon; #[derive(Parser)] #[clap(name = "Oura")] #[clap(bin_name = "oura")] #[clap(author, version, about, long_about = None)] enum Oura { - Daemon(daemon::Args), + Daemon(run_daemon::Args), } fn main() { let args = Oura::parse(); let result = match args { - Oura::Daemon(x) => daemon::run(&x), + Oura::Daemon(x) => run_daemon::run(&x), }; if let Err(err) = &result { diff --git a/src/bin/oura/run_daemon.rs b/src/bin/oura/run_daemon.rs new file mode 100644 index 00000000..1febaf32 --- /dev/null +++ b/src/bin/oura/run_daemon.rs @@ -0,0 +1,85 @@ +use gasket::daemon::Daemon; +use oura::framework::*; +use std::net::SocketAddr; +use std::sync::Arc; +use tracing::info; +use oura::daemon::{run_daemon, ConfigRoot, MetricsConfig}; + +use crate::console; + +fn setup_tracing() { + tracing::subscriber::set_global_default( + tracing_subscriber::FmtSubscriber::builder() + .with_max_level(tracing::Level::DEBUG) + .finish(), + ) + .unwrap(); +} + +async fn serve_prometheus( + daemon: Arc, + metrics: Option, +) -> Result<(), Error> { + if let Some(metrics) = metrics { + info!("starting metrics exporter"); + let runtime = daemon.clone(); + + let addr: SocketAddr = metrics + .address + .as_deref() + .unwrap_or("0.0.0.0:9186") + .parse() + .map_err(Error::parse)?; + + gasket_prometheus::serve(addr, runtime).await; + } + + Ok(()) +} + +pub fn run(args: &Args) -> Result<(), Error> { + if !args.tui { + setup_tracing(); + } + + let config = ConfigRoot::new(&args.config).map_err(Error::config)?; + let metrics = config.metrics.clone(); + + let daemon = run_daemon(config)?; + + info!("oura is running"); + + let daemon = Arc::new(daemon); + + let tokio_rt = tokio::runtime::Builder::new_multi_thread() + .enable_io() + .enable_time() + .build() + .unwrap(); + + let prometheus = tokio_rt.spawn(serve_prometheus(daemon.clone(), metrics)); + let tui = tokio_rt.spawn(console::render(daemon.clone(), args.tui)); + + daemon.block(); + + info!("oura is stopping"); + + daemon.teardown(); + prometheus.abort(); + tui.abort(); + + Ok(()) +} + + +#[derive(clap::Args)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + /// config file to load by the daemon + #[clap(long, value_parser)] + config: Option, + + /// display the terminal UI + #[clap(long, action)] + tui: bool, +} diff --git a/src/bin/oura/daemon.rs b/src/daemon/mod.rs similarity index 61% rename from src/bin/oura/daemon.rs rename to src/daemon/mod.rs index 3da8d9db..3634c115 100644 --- a/src/bin/oura/daemon.rs +++ b/src/daemon/mod.rs @@ -1,11 +1,8 @@ + use gasket::daemon::Daemon; -use oura::{cursor, filters, framework::*, sinks, sources}; +use crate::{cursor, filters, framework::*, sinks, sources}; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; -use std::{sync::Arc, time::Duration}; -use tracing::info; - -use crate::console; +use std::time::Duration; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricsConfig { @@ -13,16 +10,16 @@ pub struct MetricsConfig { } #[derive(Deserialize)] -struct ConfigRoot { - source: sources::Config, - filters: Option>, - sink: sinks::Config, - intersect: IntersectConfig, - finalize: Option, - chain: Option, - retries: Option, - cursor: Option, - metrics: Option, +pub struct ConfigRoot { + pub source: sources::Config, + pub filters: Option>, + pub sink: sinks::Config, + pub intersect: IntersectConfig, + pub finalize: Option, + pub chain: Option, + pub retries: Option, + pub cursor: Option, + pub metrics: Option, } impl ConfigRoot { @@ -94,50 +91,13 @@ fn connect_stages( Ok(runtime) } -fn setup_tracing() { - tracing::subscriber::set_global_default( - tracing_subscriber::FmtSubscriber::builder() - .with_max_level(tracing::Level::DEBUG) - .finish(), - ) - .unwrap(); -} - -async fn serve_prometheus( - daemon: Arc, - metrics: Option, -) -> Result<(), Error> { - if let Some(metrics) = metrics { - info!("starting metrics exporter"); - let runtime = daemon.clone(); - - let addr: SocketAddr = metrics - .address - .as_deref() - .unwrap_or("0.0.0.0:9186") - .parse() - .map_err(Error::parse)?; - - gasket_prometheus::serve(addr, runtime).await; - } - - Ok(()) -} - -pub fn run(args: &Args) -> Result<(), Error> { - if !args.tui { - setup_tracing(); - } - - let config = ConfigRoot::new(&args.config).map_err(Error::config)?; - +pub fn run_daemon(config: ConfigRoot) -> Result { let chain = config.chain.unwrap_or_default(); let intersect = config.intersect; let finalize = config.finalize; let current_dir = std::env::current_dir().unwrap(); let cursor = config.cursor.unwrap_or_default(); let breadcrumbs = cursor.initial_load()?; - let ctx = Context { chain, intersect, @@ -145,56 +105,17 @@ pub fn run(args: &Args) -> Result<(), Error> { current_dir, breadcrumbs, }; - let source = config.source.bootstrapper(&ctx)?; - let filters = config .filters .into_iter() .flatten() .map(|x| x.bootstrapper(&ctx)) .collect::>()?; - let sink = config.sink.bootstrapper(&ctx)?; - let cursor = cursor.bootstrapper(&ctx)?; - let retries = define_gasket_policy(config.retries.as_ref()); - let daemon = connect_stages(source, filters, sink, cursor, retries)?; - - info!("oura is running"); - - let daemon = Arc::new(daemon); - - let tokio_rt = tokio::runtime::Builder::new_multi_thread() - .enable_io() - .enable_time() - .build() - .unwrap(); - - let prometheus = tokio_rt.spawn(serve_prometheus(daemon.clone(), config.metrics)); - let tui = tokio_rt.spawn(console::render(daemon.clone(), args.tui)); - - daemon.block(); - - info!("oura is stopping"); - - daemon.teardown(); - prometheus.abort(); - tui.abort(); - - Ok(()) + Ok(daemon) } -#[derive(clap::Args)] -#[clap(author, version, about, long_about = None)] -pub struct Args { - /// config file to load by the daemon - #[clap(long, value_parser)] - config: Option, - - /// display the terminal UI - #[clap(long, action)] - tui: bool, -} diff --git a/src/lib.rs b/src/lib.rs index 36bfe792..23f77b3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,4 @@ pub mod filters; pub mod framework; pub mod sinks; pub mod sources; +pub mod daemon; diff --git a/src/sources/hydra.rs b/src/sources/hydra.rs new file mode 100644 index 00000000..e9753377 --- /dev/null +++ b/src/sources/hydra.rs @@ -0,0 +1,279 @@ +use tokio::net::TcpStream; +use tokio_tungstenite::MaybeTlsStream; + +use pallas::network::miniprotocols::Point; + +use gasket::framework::*; +use tracing::{debug, error, info, warn}; + +use futures_util::StreamExt; +use tokio_tungstenite::WebSocketStream; +use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; + +use serde::de::{self}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; + +use crate::framework::*; + +#[derive(PartialEq, Debug, Clone)] +pub struct HydraMessage { + pub seq: u64, + pub head_id: Option>, + pub payload: HydraMessagePayload, + pub raw_json: Value, +} + +impl HydraMessage { + fn head_id_or_default(&self) -> Vec { + let dummy_hash = vec![0u8; 28]; + self.head_id.clone().unwrap_or(dummy_hash) + } + + /// As a first implementation, we'll treat the msg seq number + /// as a slot number, and the head id as a block hash. + /// + /// This means all points on the same chain will share the same block hash, but hopefully + /// this shouldn't matter. + fn pseudo_point(&self) -> Point { + Point::Specific(self.seq, self.head_id_or_default()) + } +} + +impl<'de> Deserialize<'de> for HydraMessage { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let map: Value = Deserialize::deserialize(deserializer)?; + + let seq = map + .get("seq") + .ok_or_else(|| de::Error::missing_field("seq"))? + .as_u64() + .ok_or_else(|| de::Error::custom("seq should be a u64"))?; + + let head_id_str: Option<&str> = map + .get("headId") + .map(|v| { + v.as_str() + .ok_or_else(|| de::Error::custom("headId should be a string")) + }) + .transpose()?; + + let head_id = head_id_str + .map(|s| { + hex::decode(s) + .map_err(|_e| serde::de::Error::custom(format!("Expected hex-encoded headId"))) + }) + .transpose()?; + + let payload = HydraMessagePayload::deserialize(&map).map_err(de::Error::custom)?; + let raw_json = map; + + Ok(HydraMessage { + seq, + payload, + raw_json, + head_id, + }) + } +} + +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +#[serde(tag = "tag", rename_all = "PascalCase")] +pub enum HydraMessagePayload { + #[serde(deserialize_with = "deserialize_tx_valid")] + TxValid { tx: Vec }, + + #[serde(other)] + Other, +} + +fn deserialize_tx_valid<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct TxValidJson { + transaction: TxCborJson, + } + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct TxCborJson { + cbor_hex: String, + } + + let msg = TxValidJson::deserialize(deserializer)?; + let cbor = hex::decode(msg.transaction.cbor_hex) + .map_err(|_e| serde::de::Error::custom(format!("Expected hex-encoded cbor")))?; + + Ok(cbor) +} + +type HydraConnection = WebSocketStream>; + +#[derive(Stage)] +#[stage(name = "source", unit = "Message", worker = "Worker")] +pub struct Stage { + config: Config, + + intersect: IntersectConfig, + + pub output: SourceOutputPort, + + #[metric] + ops_count: gasket::metrics::Counter, + + #[metric] + current_slot: gasket::metrics::Gauge, +} + +pub struct Worker { + socket: HydraConnection, + intersect: WorkerIntersect, +} + +/// Worker state for finding the right intersection point +#[derive(Debug, Clone)] +pub enum WorkerIntersect { + SkipUntil(u64, Vec), // Possibility of Point::Origin is excluded + ProcessMessages, +} + +impl Worker { + async fn process(&mut self, stage: &mut Stage, msg: HydraMessage) -> Result<(), WorkerError> { + let point = msg.pseudo_point(); + match &self.intersect { + WorkerIntersect::SkipUntil(slot, hash) => { + let target = Point::Specific(slot.clone(), hash.clone()); + debug!( + "Skipping message {} before (or at) requested intersection {}", + point.slot_or_default(), target.slot_or_default() + ); + + if target == point { + self.intersect = WorkerIntersect::ProcessMessages; + } else if point.slot_or_default() >= target.slot_or_default() { + warn!( + "Skipping message from wrong hydra chain (intersection point hash mismatch) {:?} {:?}", + target, point + ); + } + Ok(()) + } + WorkerIntersect::ProcessMessages => self.process_message(stage, msg).await, + } + } + + /// Helper only to be called by `process` + async fn process_message( + &mut self, + stage: &mut Stage, + next: HydraMessage, + ) -> Result<(), WorkerError> { + let point = next.pseudo_point(); + + // First, apply raw json messages regardless of message type + let json_evt = ChainEvent::Apply(point.clone(), Record::GenericJson(next.raw_json.clone())); + stage.output.send(json_evt.into()).await.or_panic()?; + stage.ops_count.inc(1); + + // Apply CborTx events for any txs + match next.payload { + HydraMessagePayload::TxValid { tx } => { + let evt = ChainEvent::Apply(point.clone(), Record::CborTx(tx)); + stage.output.send(evt.into()).await.or_panic()?; + stage.ops_count.inc(1); + + stage.current_slot.set(point.slot_or_default() as i64); + stage.ops_count.inc(1); + } + _ => (), + }; + Ok(()) + } +} + +fn intersect_from_config(intersect: &IntersectConfig) -> WorkerIntersect { + match intersect { + IntersectConfig::Origin => { + info!("starting from Origin"); + WorkerIntersect::ProcessMessages + } + IntersectConfig::Tip => { + panic!("intersecting tip not currently supported with hydra as source") + } + IntersectConfig::Point(slot, hash_str) => { + info!("intersecting specific point"); + let hash = hex::decode(hash_str).expect("valid hex hash"); + WorkerIntersect::SkipUntil(slot.clone(), hash) + } + IntersectConfig::Breadcrumbs(_) => { + panic!("intersecting breadcrumbs not currently supported with hydra as source") + } + } +} + +#[async_trait::async_trait(?Send)] +impl gasket::framework::Worker for Worker { + async fn bootstrap(stage: &Stage) -> Result { + debug!("connecting to hydra WebSocket"); + + let url = &stage.config.hydra_socket_path; + let (socket, _) = connect_async(url).await.expect("Can't connect"); + let worker = Self { + socket, + intersect: intersect_from_config(&stage.intersect), + }; + + Ok(worker) + } + + async fn schedule(&mut self, _stage: &mut Stage) -> Result, WorkerError> { + let next_msg = self.socket.next().await.transpose().or_restart()?; + + Ok(match next_msg { + Some(message) => WorkSchedule::Unit(message), + None => WorkSchedule::Idle, + }) + } + + async fn execute(&mut self, message: &Message, stage: &mut Stage) -> Result<(), WorkerError> { + match message { + Message::Text(text) => { + debug!("Hydra message: {}", text); + match serde_json::from_str::(text) { + Ok(hydra_message) => { + self.process(stage, hydra_message).await + } + Err(err) => { + error!("Failed to parse Hydra message: {}", err); + Ok(()) + } + } + } + _ => Ok(()), + } + } +} + +#[derive(Deserialize)] +pub struct Config { + pub hydra_socket_path: String, +} + +impl Config { + pub fn bootstrapper(self, ctx: &Context) -> Result { + let stage = Stage { + config: self, + intersect: ctx.intersect.clone(), + output: Default::default(), + ops_count: Default::default(), + current_slot: Default::default(), + }; + + Ok(stage) + } +} diff --git a/src/sources/mod.rs b/src/sources/mod.rs index 87cb3950..cd41bdd7 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -8,6 +8,7 @@ use crate::framework::*; pub mod n2c; pub mod n2n; +pub mod hydra; #[cfg(feature = "u5c")] pub mod u5c; @@ -24,6 +25,8 @@ pub enum Bootstrapper { #[cfg(target_family = "unix")] N2C(n2c::Stage), + Hydra(hydra::Stage), + #[cfg(feature = "u5c")] U5C(u5c::Stage), @@ -42,6 +45,8 @@ impl Bootstrapper { #[cfg(target_family = "unix")] Bootstrapper::N2C(p) => &mut p.output, + Bootstrapper::Hydra(p) => &mut p.output, + #[cfg(feature = "u5c")] Bootstrapper::U5C(p) => &mut p.output, @@ -60,6 +65,8 @@ impl Bootstrapper { #[cfg(target_family = "unix")] Bootstrapper::N2C(x) => gasket::runtime::spawn_stage(x, policy), + Bootstrapper::Hydra(x) => gasket::runtime::spawn_stage(x, policy), + #[cfg(feature = "u5c")] Bootstrapper::U5C(x) => gasket::runtime::spawn_stage(x, policy), @@ -80,6 +87,8 @@ pub enum Config { #[cfg(target_family = "unix")] N2C(n2c::Config), + Hydra(hydra::Config), + #[cfg(feature = "u5c")] U5C(u5c::Config), @@ -98,6 +107,8 @@ impl Config { #[cfg(target_family = "unix")] Config::N2C(c) => Ok(Bootstrapper::N2C(c.bootstrapper(ctx)?)), + Config::Hydra(c) => Ok(Bootstrapper::Hydra(c.bootstrapper(ctx)?)), + #[cfg(feature = "u5c")] Config::U5C(c) => Ok(Bootstrapper::U5C(c.bootstrapper(ctx)?)), diff --git a/tests/daemon.toml b/tests/daemon.toml new file mode 100644 index 00000000..97706a13 --- /dev/null +++ b/tests/daemon.toml @@ -0,0 +1,18 @@ +[source] +type = "Hydra" +hydra_socket_path = "ws://127.0.0.1:4001" +magic = 42 + +[intersect] +type = "Origin" + +[[filters]] +type = "IntoJson" + +[sink] +type = "FileRotate" +max_total_files = 1 +output_format = "JSONL" +output_path = "tests/hydra/logs.txt" +max_bytes_per_file = 5_000_000 +compress_files = true diff --git a/tests/hydra.rs b/tests/hydra.rs new file mode 100644 index 00000000..764da318 --- /dev/null +++ b/tests/hydra.rs @@ -0,0 +1,634 @@ +use std::fs; +use std::path::PathBuf; +use std::sync::mpsc; +use std::time::Duration; + +use serde::Deserialize; +use oura::framework::IntersectConfig; +use anyhow::Result; +use futures_util::SinkExt; +use oura::sources::hydra::{HydraMessage, HydraMessagePayload}; +use oura::sinks::Config::FileRotate; +use serde_json::{json, Value}; +use tokio::net::TcpListener; +use oura::sources::Config::Hydra; +use oura::daemon::{run_daemon, ConfigRoot}; +use gasket::daemon::Daemon; +use goldenfile::Mint; +use std::io::Write; +use tempfile::NamedTempFile; +use tokio::runtime::Runtime; +use tokio::time; +use tokio_tungstenite::accept_async; +use port_selector::random_free_port; +use tokio_tungstenite::tungstenite::protocol::Message; + +type TestResult = Result<(), Box>; + +#[derive(Debug, PartialEq)] +enum LineParseResult { + LineParsed(T), + LineNotParsed, +} + +fn test_events_deserialisation( + expected_msgs: Vec>, + input: &str, +) -> TestResult { + let mut deserialized: Vec> = Vec::new(); + for line in input.lines() { + match serde_json::from_str(&line) { + Ok(msg) => { + deserialized.push(LineParseResult::LineParsed(msg)); + } + _ => { + deserialized.push(LineParseResult::LineNotParsed); + } + } + } + assert_eq!(deserialized, expected_msgs); + Ok(()) +} + +fn test_scenario( + expected_msgs: Vec>, + file: &str, +) -> TestResult { + let mut deserialized: Vec> = Vec::new(); + let input = fs::read_to_string(file)?; + for line in input.lines() { + match serde_json::from_str::(&line) { + Ok(msg) => { + deserialized.push(LineParseResult::LineParsed(msg.payload)); + } + _ => { + deserialized.push(LineParseResult::LineNotParsed); + } + } + } + assert_eq!(deserialized, expected_msgs); + Ok(()) +} + +fn test_event_deserialization(expected: HydraMessage, input: &str) -> TestResult { + let deserialized: HydraMessage = serde_json::from_str(&input)?; + assert_eq!(deserialized, expected); + Ok(()) +} + +#[test] +fn tx_valid_evt() -> TestResult { + let evt = HydraMessage { + seq: 15, + head_id: Some(hex::decode("84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab").unwrap() + .to_vec()), + payload: HydraMessagePayload::TxValid { + tx: hex::decode("84a300d9010281825820635ffa4d3f8b5ccd60a89918866a5bb0776966572324da9a86870f79dcce4aad01018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a0098968082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a039387000200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840c1f23b630cf3d0ffe4186436225906c81bcddb0a27a632696035d4bb2d32e646c81759789c35c940b9695a87a0978a0408cff550c8d8f9ab4ac6d6d29b82a109f5f6") + .unwrap() + .to_vec(), + }, + raw_json: json!( + { "headId": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab" + , "seq": 15 + , "tag": "TxValid" + , "timestamp": "2024-10-03T11:38:45.449663464Z" + , "transaction": + { "cborHex": "84a300d9010281825820635ffa4d3f8b5ccd60a89918866a5bb0776966572324da9a86870f79dcce4aad01018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a0098968082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a039387000200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840c1f23b630cf3d0ffe4186436225906c81bcddb0a27a632696035d4bb2d32e646c81759789c35c940b9695a87a0978a0408cff550c8d8f9ab4ac6d6d29b82a109f5f6" + , "description": "Ledger Cddl Format" + , "txId": "08bb77374329ca28cd3023cace2948d0fc23e2812e8998c966db8b457e6390fe" + , "type": "Witnessed Tx ConwayEra" + } + }), + }; + + let raw_str = r#" + { + "headId": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab", + "seq": 15, + "timestamp": "2024-10-03T11:38:45.449663464Z", + "tag":"TxValid", + "transaction": { + "cborHex": "84a300d9010281825820635ffa4d3f8b5ccd60a89918866a5bb0776966572324da9a86870f79dcce4aad01018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a0098968082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a039387000200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840c1f23b630cf3d0ffe4186436225906c81bcddb0a27a632696035d4bb2d32e646c81759789c35c940b9695a87a0978a0408cff550c8d8f9ab4ac6d6d29b82a109f5f6", + "description": "Ledger Cddl Format", + "txId": "08bb77374329ca28cd3023cace2948d0fc23e2812e8998c966db8b457e6390fe", + "type": "Witnessed Tx ConwayEra" + } + } +"#; + test_event_deserialization(evt, &raw_str) +} + +#[test] +fn peer_connected_evt() -> TestResult { + let evt = HydraMessage { + seq: 0, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "3" + , "seq": 0 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.556003751Z" + }), + }; + + let raw_str = r#" + { + "peer": "3", + "seq": 0, + "tag": "PeerConnected", + "timestamp": "2024-10-08T13:01:20.556003751Z" + } +"#; + test_event_deserialization(evt, &raw_str) +} + +#[test] +fn idle_evt() -> TestResult { + let evt = HydraMessage { + seq: 2, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "headStatus": "Idle" + , "hydraNodeVersion": "0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425" + , "me": + { "vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb" + } + , "seq": 2 + , "tag": "Greetings" + , "timestamp": "2024-10-08T13:04:56.445761285Z" + }), + }; + + let raw_str = r#" + { + "headStatus": "Idle", + "hydraNodeVersion": "0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425", + "me": { + "vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb" + }, + "seq": 2, + "tag": "Greetings", + "timestamp": "2024-10-08T13:04:56.445761285Z" + } +"#; + test_event_deserialization(evt, &raw_str) +} + +#[test] +fn committed_evt() -> TestResult { + let evt = HydraMessage { + seq: 3, + payload: HydraMessagePayload::Other, + head_id: Some( + hex::decode("84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab") + .unwrap() + .to_vec(), + ), + raw_json: json!( + { "headId": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab" + , "party": {"vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"} + , "seq": 3 + , "tag": "Committed" + , "timestamp": "2024-10-08T13:05:56.918549005Z" + , "utxo": {"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0": + {"address": "addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z" + , "datum": null + , "datumhash": null + , "inlineDatum": null + , "referenceScript": null + , "value": {"lovelace": 100000000} + } + } + }), + }; + + let raw_str = r#" + { + "headId": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab", + "party": { + "vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb" + }, + "seq": 3, + "tag": "Committed", + "timestamp": "2024-10-08T13:05:56.918549005Z", + "utxo": { + "c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0": { + "address": "addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z", + "datum": null, + "datumhash": null, + "inlineDatum": null, + "referenceScript": null, + "value": { + "lovelace": 100000000 + } + } + } + } +"#; + test_event_deserialization(evt, &raw_str) +} + +#[test] +fn two_valid_evts() -> TestResult { + let evts = vec![ + LineParseResult::LineParsed(HydraMessage { + seq: 7, + head_id: Some(hex::decode("84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab").unwrap() + .to_vec()), + payload: HydraMessagePayload::TxValid { + tx: hex::decode("84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6") + .unwrap() + .to_vec(), + }, + raw_json: json!( + { "headId": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab" + , "seq": 7 + , "tag": "TxValid" + , "timestamp": "2024-10-08T13:07:18.008847436Z" + , "transaction": + { "cborHex": "84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6" + , "description": "Ledger Cddl Format" + , "txId": "633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1" + , "type": "Witnessed Tx ConwayEra" + } + }), + }), LineParseResult::LineParsed(HydraMessage { + seq: 0, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "3" + , "seq": 0 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.556003751Z" + }), + })]; + + let raw_str = r#"{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":7,"tag":"TxValid","timestamp":"2024-10-08T13:07:18.008847436Z","transaction":{"cborHex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6","description":"Ledger Cddl Format","txId":"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1","type":"Witnessed Tx ConwayEra"}} +{"peer":"3","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.556003751Z"} +"#; + test_events_deserialisation(evts, &raw_str) +} + +#[test] +fn three_valid_evts() -> TestResult { + let evts = vec![ + LineParseResult::LineParsed(HydraMessage { + seq: 0, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "3" + , "seq": 0 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.556003751Z" + }), + }), + LineParseResult::LineParsed(HydraMessage { + seq: 1, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "2" + , "seq": 1 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.559653645Z" + }), + }), + LineParseResult::LineParsed(HydraMessage { + seq: 2, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "headStatus": "Idle" + , "hydraNodeVersion": "0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425" + , "me": + { "vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb" + } + , "seq": 2 + , "tag": "Greetings" + , "timestamp": "2024-10-08T13:04:56.445761285Z" + }), + }), + ]; + + let raw_str = r#"{"peer":"3","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.556003751Z"} +{"peer":"2","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.559653645Z"} +{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:04:56.445761285Z"} +"#; + test_events_deserialisation(evts, &raw_str) +} + +#[test] +fn three_valid_two_invalid_evts() -> TestResult { + let evts = vec![ + LineParseResult::LineParsed(HydraMessage { + seq: 0, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "3" + , "seq": 0 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.556003751Z" + }), + }), + LineParseResult::LineParsed(HydraMessage { + seq: 1, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "peer": "2" + , "seq": 1 + , "tag": "PeerConnected" + , "timestamp": "2024-10-08T13:01:20.559653645Z" + }), + }), + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessage { + seq: 2, + payload: HydraMessagePayload::Other, + head_id: None, + raw_json: json!( + { "headStatus": "Idle" + , "hydraNodeVersion": "0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425" + , "me": + { "vkey": "b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb" + } + , "seq": 2 + , "tag": "Greetings" + , "timestamp": "2024-10-08T13:04:56.445761285Z" + }), + }), + ]; + + let raw_str = r#"{"peer":"3","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.556003751Z"} +{"peer":"2","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.559653645Z"} +1 +2 +{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:04:56.445761285Z"} +"#; + test_events_deserialisation(evts, &raw_str) +} + +#[test] +fn scenario_1() -> TestResult { + let payloads = vec![ + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::TxValid { + tx: hex::decode("84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6") + .unwrap() + .to_vec(), + }), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed + ]; + test_scenario(payloads, "tests/hydra/scenario_1.txt") +} + +#[test] +fn scenario_2() -> TestResult { + let payloads = vec![ + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::TxValid { + tx: hex::decode("84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858407342c0c4de1b55bc9e56c86829a1fb5906e964f109fd698d37d5933ed230b1a878bfee20980bb90b48aa32c472fdd465c2eb770551b84de7041838415faed502f5f6") + .unwrap() + .to_vec(), + }), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::TxValid { + tx: hex::decode("84a300d901028182582065d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e5161701018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a0016e36082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a014810600200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf85840b991c62af8e2b2d06f821fb6064f98c2fc8909b0b2d81435c7e075a61fc92ee6c9224f23d817de35d5529f54034c2ab8dfaded387e99fc525344846bb5dc860af5f6") + .unwrap() + .to_vec(), + }), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::TxValid { + tx: hex::decode("84a300d90102818258207b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c69700018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a00a7d8c082581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a025317c00200a100d9010281825820aa268d154185c9ea06ea73442fd8143c34c1dd543b7142bcb132aac0d1ed6ece5840fc6e2b0750259deedd5a73eeadf481138bf82edc3425614871a0ef09bfcf8cae52a80240fb895a7e6a8ad94d4acb32dffe567ed0d338afcd7878f745737f420df5f6") + .unwrap() + .to_vec(), + }), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::TxValid { + tx: hex::decode("84a300d9010281825820c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d9700018282581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a00c65d4082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a052f83c00200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840ac8f1632d9a636d3627328ffd09cd32e1b654cbf318f0ce499a9870b05530041aa0badf07cd43fec8f1456537ada71227bea8123c1ed641ae3cb22b7313d5f08f5f6") + .unwrap() + .to_vec(), + }), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineParsed(HydraMessagePayload::Other), + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed, + LineParseResult::LineNotParsed + ]; + test_scenario(payloads, "tests/hydra/scenario_2.txt") +} + +#[test] +fn hydra_oura_stdout_scenario_1() { + hydra_oura_stdout_test("scenario_1.txt".to_string(), "golden_1".to_string()) +} + +#[test] +fn hydra_oura_stdout_scenario_2() { + hydra_oura_stdout_test("scenario_2.txt".to_string(), "golden_2".to_string()) +} + +#[test] +fn hydra_restore_from_intersection_success() { + let scenario= fs::read_to_string("tests/hydra/scenario_1.txt").unwrap(); + let intersect = IntersectConfig::Point( + 6, + "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab".to_string() + ); + let events = oura_events_from_mock_chain(scenario, intersect); + + assert_ne!(events.len(), 0); + assert_eq!(events[0].point, json!({"slot": 7, "hash": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab"})); + for e in events { + if e.point == json!({"slot": 6, "hash": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab"}) { + panic!("only events /after/ the intersection should be emitted"); + } + } +} + +#[test] +fn hydra_restore_from_intersection_tip() { + let scenario = fs::read_to_string("tests/hydra/scenario_1.txt").unwrap(); + let intersect = IntersectConfig::Point( + 11, + "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab".to_string() + ); + let events = oura_events_from_mock_chain(scenario, intersect); + assert_eq!(events, vec![]); +} + +#[test] +fn hydra_restore_from_intersection_point_with_dummy_hash_and_shared_slot_1() { + let scenario = fs::read_to_string("tests/hydra/scenario_1.txt").unwrap(); + let intersect = IntersectConfig::Point( + 2, + "00000000000000000000000000000000000000000000000000000000".to_string() + ); + let events = oura_events_from_mock_chain(scenario, intersect); + // It appears the Greetings and HeadIsInitializing messages share the same seq / slot. + assert_eq!(events[0].point, json!({"slot": 2, "hash": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab"})) +} + +#[test] +fn hydra_restore_from_intersection_point_with_shared_slot_2() { + let scenario = fs::read_to_string("tests/hydra/scenario_1.txt").unwrap(); + let intersect = IntersectConfig::Point( + 2, + "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab".to_string() + ); + let events = oura_events_from_mock_chain(scenario, intersect); + assert_eq!(events[0].point, json!({"slot": 3, "hash": "84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab"})) +} + +#[test] +fn hydra_restore_from_intersection_failure() { + let scenario = fs::read_to_string("tests/hydra/scenario_1.txt").unwrap(); + let bad_intersect= IntersectConfig::Point( + 6, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + let events = oura_events_from_mock_chain(scenario, bad_intersect); + assert_eq!(events, vec![]); +} + +/// Wraps the json format of oura::framework::ChainEvent::Apply with just enough +/// structure to test point equality without having to implement the full json +/// deserializers. +#[derive(Debug, Deserialize, PartialEq)] +struct JsonApplyChainEvent { + point: Value, + record: Value, +} + +fn oura_output_from_mock_chain(scenario: String, intersect: IntersectConfig) -> String { + let rt = Runtime::new().unwrap(); + rt.block_on(async move { + let port: u16 = random_free_port().unwrap(); + let addr = format!("127.0.0.1:{}", port); + let server = TcpListener::bind(&addr).await.unwrap(); + let output_file = NamedTempFile::new().unwrap(); + let mut config = test_config(&output_file, &addr); + config.intersect = intersect; + + println!("WebSocket server starting on ws://{}", addr); + + let _ = tokio::spawn(async move { run_oura(config) }); + let _ = mock_hydra_node(server, scenario).await; + + // After the connection is established, give oura time to process + // the chain data before we read it. + time::sleep(Duration::from_secs(3)).await; + fs::read_to_string(&output_file).unwrap() + }) +} + +fn oura_events_from_mock_chain(scenario: String, intersect: IntersectConfig) -> Vec { + oura_output_from_mock_chain(scenario, intersect) + .lines() + .map(|line| serde_json::from_str(line).expect("Invalid JSON line")) + .collect() +} + +// Run: +// cargo test hydra_oura -- --nocapture +// in order to see println +fn hydra_oura_stdout_test(scenario_name: String, golden_name: String) { + let mut mint = Mint::new("tests/hydra"); + let mut golden = mint.new_goldenfile(golden_name.clone()).unwrap(); + + let scenario = fs::read_to_string(format!("tests/hydra/{}", scenario_name)).unwrap(); + let output = oura_output_from_mock_chain(scenario, IntersectConfig::Origin); + + golden.write_all(output.as_bytes()).unwrap(); +} + +/// Will await the first connection, and then return while handling it in the +/// background. +async fn mock_hydra_node(server: TcpListener, mock_data: String) { + async fn handle_connection( + stream: tokio::net::TcpStream, + mock_data: String, + tx: mpsc::Sender, + ) -> Result<()> { + let mut ws_stream = accept_async(stream).await?; + println!("WebSocket server oura connection established"); + + let mut lines = 0; + for line in mock_data.lines() { + ws_stream.send(Message::Text(line.to_string())).await?; + lines += 1; + } + tx.send(lines).unwrap(); + Ok(()) + } + + let (tx, _rx) = mpsc::channel(); + let (stream, _) = server.accept().await.unwrap(); + let _ = tokio::spawn(handle_connection(stream, mock_data , tx)); +} + + +fn test_config(tmp_output_file: &NamedTempFile, socket_path: &String) -> ConfigRoot { + let mut config = ConfigRoot::new(&Some(PathBuf::from("tests/daemon.toml"))).unwrap(); + + if let FileRotate(ref mut file_rotate) = config.sink { + file_rotate.output_path = Some(tmp_output_file.path().to_string_lossy().to_string()); + } else { + panic!("assumed config template to use file_rotate sink"); + } + + if let Hydra(ref mut hydra_config) = config.source { + hydra_config.hydra_socket_path = "ws://".to_string() + socket_path; + } else { + panic!("assumed config template to use hydra source"); + } + + config +} + +fn run_oura(config: ConfigRoot) -> Result { + run_daemon(config).map_err(|e| anyhow::anyhow!(e)) +} diff --git a/tests/hydra/golden_1 b/tests/hydra/golden_1 new file mode 100644 index 00000000..91a4c334 --- /dev/null +++ b/tests/hydra/golden_1 @@ -0,0 +1,14 @@ +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":0},"record":{"peer":"3","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.556003751Z"}} +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":1},"record":{"peer":"2","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.559653645Z"}} +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":2},"record":{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:04:56.445761285Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":2},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","parties":[{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"}],"seq":2,"tag":"HeadIsInitializing","timestamp":"2024-10-08T13:05:47.330461177Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":3},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":3,"tag":"Committed","timestamp":"2024-10-08T13:05:56.918549005Z","utxo":{"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":4},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},"seq":4,"tag":"Committed","timestamp":"2024-10-08T13:06:05.615623261Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":5},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"},"seq":5,"tag":"Committed","timestamp":"2024-10-08T13:06:17.51514695Z","utxo":{"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":6},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":6,"tag":"HeadIsOpen","timestamp":"2024-10-08T13:06:18.687120539Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":7},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":7,"tag":"TxValid","timestamp":"2024-10-08T13:07:18.008847436Z","transaction":{"cborHex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6","description":"Ledger Cddl Format","txId":"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1","type":"Witnessed Tx ConwayEra"}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":7},"record":{"hex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":8},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":8,"signatures":{"multiSignature":["71c368f5f9b124d6b327132fbbf7e92939a88ddcc26e2e06ee17f23318b2bb9c8fa87ebb92d63b6d9cf53ff7612140ea5b7084951ec422d41f909b97f5e64904","51d78ed466a67c4acacf588772d3cc6bf72325e83c318c5ac6a1d90a10ba132a225fa48240570b6254df2c831f952a72213984d0953b22d49a1de1fbfb38540a","993126e89e417fcff9135d7cedc9fa2b46dcf1c9d2ccaa64e5400b782ef40643afe69e73983ee3517492f1816dc99a5dd29002ca9b2d5182bd24e00c9e40b105"]},"snapshot":{"confirmedTransactions":["633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":1,"utxo":{"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:07:18.064534686Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":9},"record":{"contestationDeadline":"2024-10-08T13:07:37.7Z","headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":9,"snapshotNumber":1,"tag":"HeadIsClosed","timestamp":"2024-10-08T13:07:31.814065753Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":10},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":10,"tag":"ReadyToFanout","timestamp":"2024-10-08T13:07:37.807683329Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":11},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":11,"tag":"HeadIsFinalized","timestamp":"2024-10-08T13:07:40.815046135Z","utxo":{"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}}} diff --git a/tests/hydra/golden_2 b/tests/hydra/golden_2 new file mode 100644 index 00000000..5895f165 --- /dev/null +++ b/tests/hydra/golden_2 @@ -0,0 +1,23 @@ +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":0},"record":{"peer":"2","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:19:06.954897681Z"}} +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":1},"record":{"peer":"3","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:19:06.98647342Z"}} +{"event":"apply","point":{"hash":"00000000000000000000000000000000000000000000000000000000","slot":2},"record":{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:21:28.141876427Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":2},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","parties":[{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"}],"seq":2,"tag":"HeadIsInitializing","timestamp":"2024-10-08T13:22:05.725778923Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":3},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":3,"tag":"Committed","timestamp":"2024-10-08T13:22:11.016254447Z","utxo":{"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":4},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},"seq":4,"tag":"Committed","timestamp":"2024-10-08T13:22:18.915120931Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":5},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"},"seq":5,"tag":"Committed","timestamp":"2024-10-08T13:22:30.313144555Z","utxo":{"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":6},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":6,"tag":"HeadIsOpen","timestamp":"2024-10-08T13:22:30.520745142Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":7},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":7,"tag":"TxValid","timestamp":"2024-10-08T13:22:44.316966394Z","transaction":{"cborHex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858407342c0c4de1b55bc9e56c86829a1fb5906e964f109fd698d37d5933ed230b1a878bfee20980bb90b48aa32c472fdd465c2eb770551b84de7041838415faed502f5f6","description":"Ledger Cddl Format","txId":"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617","type":"Witnessed Tx ConwayEra"}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":7},"record":{"hex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858407342c0c4de1b55bc9e56c86829a1fb5906e964f109fd698d37d5933ed230b1a878bfee20980bb90b48aa32c472fdd465c2eb770551b84de7041838415faed502f5f6"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":8},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":8,"signatures":{"multiSignature":["e9eaa5edf35b35872c94d9b11b1074e8dc737a24ad5956a7c982ee7eb3d2bfe31def724b8a8e586e30806b50ed55984e331a3fae986bc12e6e705e1138164400","7058aca64e437169677db810048bbfc1e0714bbf348cd43c37c46f3d836dc251e09ca7d2a2d6c89e87b2e7d7f4b110024ecbcec59f694370079a069df1903d03","e55bfb59691c93dfd6343a034cf9c78b03261e7d51a16e61e8fd7257378b563b8de6568abd88ddeb532d109e3ad33c6454b268b6ee6b29849786ac5691898603"]},"snapshot":{"confirmedTransactions":["65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":1,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:22:44.360477345Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":9},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":9,"tag":"TxValid","timestamp":"2024-10-08T13:23:18.169555579Z","transaction":{"cborHex":"84a300d901028182582065d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e5161701018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a0016e36082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a014810600200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf85840b991c62af8e2b2d06f821fb6064f98c2fc8909b0b2d81435c7e075a61fc92ee6c9224f23d817de35d5529f54034c2ab8dfaded387e99fc525344846bb5dc860af5f6","description":"Ledger Cddl Format","txId":"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af","type":"Witnessed Tx ConwayEra"}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":9},"record":{"hex":"84a300d901028182582065d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e5161701018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a0016e36082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a014810600200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf85840b991c62af8e2b2d06f821fb6064f98c2fc8909b0b2d81435c7e075a61fc92ee6c9224f23d817de35d5529f54034c2ab8dfaded387e99fc525344846bb5dc860af5f6"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":10},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":10,"signatures":{"multiSignature":["5897602f543c3692cfb691119b2e9feb22e2302333b3200f95fa65feffaa0846abeb293afc8dcbd4ba2ac4b0ba1df729730d2e3b5e69a652f99b52fa15729209","d2268afebbbde31886cf8ce1c7b827f92a1c675bc9dee5603d6d87d7c30d9f7ae46d596095a6e03624d2526fe101b026c9c765abb7e2603b06ffb4fa6ecc6b0e","b3342f10a0678c24bff40ab5d394b8d7382419b826bfbbc3c019a8c4fd20d6d4db9eec29d72fb58ceb09dfe6720ae5c8ade77fd49e2a4b7e884beeb93f027b00"]},"snapshot":{"confirmedTransactions":["a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":2,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:23:18.220733933Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":11},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":11,"tag":"TxValid","timestamp":"2024-10-08T13:24:09.770902618Z","transaction":{"cborHex":"84a300d90102818258207b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c69700018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a00a7d8c082581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a025317c00200a100d9010281825820aa268d154185c9ea06ea73442fd8143c34c1dd543b7142bcb132aac0d1ed6ece5840fc6e2b0750259deedd5a73eeadf481138bf82edc3425614871a0ef09bfcf8cae52a80240fb895a7e6a8ad94d4acb32dffe567ed0d338afcd7878f745737f420df5f6","description":"Ledger Cddl Format","txId":"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce","type":"Witnessed Tx ConwayEra"}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":11},"record":{"hex":"84a300d90102818258207b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c69700018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a00a7d8c082581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a025317c00200a100d9010281825820aa268d154185c9ea06ea73442fd8143c34c1dd543b7142bcb132aac0d1ed6ece5840fc6e2b0750259deedd5a73eeadf481138bf82edc3425614871a0ef09bfcf8cae52a80240fb895a7e6a8ad94d4acb32dffe567ed0d338afcd7878f745737f420df5f6"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":12},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":12,"signatures":{"multiSignature":["aa0673de465f1e7f4ca472b85315243ca2327f15778b3625b8a225563fd4dbb083e2ff9e50bb3a4680a3d1a3da89a839ed841f18c3f85770bf669fe268418608","c682e3bc61e3bf78db64a6bfdd48588e3aafcba62bdb507b8f6b56b364985e575d7185505f33597a45782edff46687d67281e65cf19c7dbbbc68b80b4ed92605","7efc7a1846184214ea2d6748f3db82a34c3be0d9af310d685a47f24e2694223b03611111902512c5674729553440c57755f340694589ccc94e093732b9630908"]},"snapshot":{"confirmedTransactions":["de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":3,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:24:09.825917124Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":13},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":13,"tag":"TxValid","timestamp":"2024-10-08T13:24:30.136780836Z","transaction":{"cborHex":"84a300d9010281825820c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d9700018282581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a00c65d4082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a052f83c00200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840ac8f1632d9a636d3627328ffd09cd32e1b654cbf318f0ce499a9870b05530041aa0badf07cd43fec8f1456537ada71227bea8123c1ed641ae3cb22b7313d5f08f5f6","description":"Ledger Cddl Format","txId":"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74","type":"Witnessed Tx ConwayEra"}}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":13},"record":{"hex":"84a300d9010281825820c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d9700018282581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a00c65d4082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a052f83c00200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840ac8f1632d9a636d3627328ffd09cd32e1b654cbf318f0ce499a9870b05530041aa0badf07cd43fec8f1456537ada71227bea8123c1ed641ae3cb22b7313d5f08f5f6"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":14},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":14,"signatures":{"multiSignature":["cdd5844d70c2ad9d6e6981f922757cc5aff7785b425ffcbff83347f2ad9545c646bae9c75f6b65d23aad9452e17704d76cbd5e948408e73277cafa2858cb9104","a1551898f0c4b0f2a9b5a94998a18b9ca962dc92f24d4e5308a0dd449eaf1661c1e075fbc9895ad6f0bbbcd141eb251c9e08493d04adff01125a15906f919701","976e8a5fc707c4e048d266344a2af1a43e94dd7ca29f395d1735c70562ff915af22e9b9829f5058eb02d734e625bd30ada7cbb49060098ad27d6c8a747e9740f"]},"snapshot":{"confirmedTransactions":["bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":4,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":13000000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#1":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":87000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:24:30.186588075Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":15},"record":{"contestationDeadline":"2024-10-08T13:24:42.6Z","headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":15,"snapshotNumber":4,"tag":"HeadIsClosed","timestamp":"2024-10-08T13:24:36.81629911Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":16},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":16,"tag":"ReadyToFanout","timestamp":"2024-10-08T13:24:42.708785607Z"}} +{"event":"apply","point":{"hash":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","slot":17},"record":{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":17,"tag":"HeadIsFinalized","timestamp":"2024-10-08T13:25:02.420848196Z","utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":13000000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#1":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":87000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}}}} diff --git a/tests/hydra/scenario_1.txt b/tests/hydra/scenario_1.txt new file mode 100644 index 00000000..575d1697 --- /dev/null +++ b/tests/hydra/scenario_1.txt @@ -0,0 +1,23 @@ +{"peer":"3","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.556003751Z"} +{"peer":"2","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:01:20.559653645Z"} +{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:04:56.445761285Z"} +1 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","parties":[{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"}],"seq":2,"tag":"HeadIsInitializing","timestamp":"2024-10-08T13:05:47.330461177Z"} +2 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":3,"tag":"Committed","timestamp":"2024-10-08T13:05:56.918549005Z","utxo":{"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},"seq":4,"tag":"Committed","timestamp":"2024-10-08T13:06:05.615623261Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"},"seq":5,"tag":"Committed","timestamp":"2024-10-08T13:06:17.51514695Z","utxo":{"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":6,"tag":"HeadIsOpen","timestamp":"2024-10-08T13:06:18.687120539Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}} +3 +4 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":7,"tag":"TxValid","timestamp":"2024-10-08T13:07:18.008847436Z","transaction":{"cborHex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858401b13ee550f3167a1b94796f2a2f5e22d782d628336a7797c5b798f358fa564dbe92ea75a4e2449eb2cef59c097d8497545ef1e4ea441b88a481194323ae7c608f5f6","description":"Ledger Cddl Format","txId":"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1","type":"Witnessed Tx ConwayEra"}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":8,"signatures":{"multiSignature":["71c368f5f9b124d6b327132fbbf7e92939a88ddcc26e2e06ee17f23318b2bb9c8fa87ebb92d63b6d9cf53ff7612140ea5b7084951ec422d41f909b97f5e64904","51d78ed466a67c4acacf588772d3cc6bf72325e83c318c5ac6a1d90a10ba132a225fa48240570b6254df2c831f952a72213984d0953b22d49a1de1fbfb38540a","993126e89e417fcff9135d7cedc9fa2b46dcf1c9d2ccaa64e5400b782ef40643afe69e73983ee3517492f1816dc99a5dd29002ca9b2d5182bd24e00c9e40b105"]},"snapshot":{"confirmedTransactions":["633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":1,"utxo":{"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:07:18.064534686Z"} +5 +{"contestationDeadline":"2024-10-08T13:07:37.7Z","headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":9,"snapshotNumber":1,"tag":"HeadIsClosed","timestamp":"2024-10-08T13:07:31.814065753Z"} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":10,"tag":"ReadyToFanout","timestamp":"2024-10-08T13:07:37.807683329Z"} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":11,"tag":"HeadIsFinalized","timestamp":"2024-10-08T13:07:40.815046135Z","utxo":{"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"633777d68a85fe989f88aa839aa84743f64d68a931192c41f4df8ed0f16e03d1#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}} +6 +7 +8 +9 +10 diff --git a/tests/hydra/scenario_2.txt b/tests/hydra/scenario_2.txt new file mode 100644 index 00000000..c64b7061 --- /dev/null +++ b/tests/hydra/scenario_2.txt @@ -0,0 +1,30 @@ +{"peer":"2","seq":0,"tag":"PeerConnected","timestamp":"2024-10-08T13:19:06.954897681Z"} +{"peer":"3","seq":1,"tag":"PeerConnected","timestamp":"2024-10-08T13:19:06.98647342Z"} +{"headStatus":"Idle","hydraNodeVersion":"0.19.0-1ffe7c6b505e3f38b5546ae5e5b97de26bc70425","me":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":2,"tag":"Greetings","timestamp":"2024-10-08T13:21:28.141876427Z"} +1 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","parties":[{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"}],"seq":2,"tag":"HeadIsInitializing","timestamp":"2024-10-08T13:22:05.725778923Z"} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"b37aabd81024c043f53a069c91e51a5b52e4ea399ae17ee1fe3cb9c44db707eb"},"seq":3,"tag":"Committed","timestamp":"2024-10-08T13:22:11.016254447Z","utxo":{"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"f68e5624f885d521d2f43c3959a0de70496d5464bd3171aba8248f50d5d72b41"},"seq":4,"tag":"Committed","timestamp":"2024-10-08T13:22:18.915120931Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}}}} +2 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","party":{"vkey":"7abcda7de6d883e7570118c1ccc8ee2e911f2e628a41ab0685ffee15f39bba96"},"seq":5,"tag":"Committed","timestamp":"2024-10-08T13:22:30.313144555Z","utxo":{"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":6,"tag":"HeadIsOpen","timestamp":"2024-10-08T13:22:30.520745142Z","utxo":{"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d20#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":25000000}}}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":7,"tag":"TxValid","timestamp":"2024-10-08T13:22:44.316966394Z","transaction":{"cborHex":"84a300d9010281825820f0a39560ea80ccc68e8dffb6a4a077c8927811f06c5d9058d0fa2d1a8d047d2000018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a001e848082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a015ef3c00200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf858407342c0c4de1b55bc9e56c86829a1fb5906e964f109fd698d37d5933ed230b1a878bfee20980bb90b48aa32c472fdd465c2eb770551b84de7041838415faed502f5f6","description":"Ledger Cddl Format","txId":"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617","type":"Witnessed Tx ConwayEra"}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":8,"signatures":{"multiSignature":["e9eaa5edf35b35872c94d9b11b1074e8dc737a24ad5956a7c982ee7eb3d2bfe31def724b8a8e586e30806b50ed55984e331a3fae986bc12e6e705e1138164400","7058aca64e437169677db810048bbfc1e0714bbf348cd43c37c46f3d836dc251e09ca7d2a2d6c89e87b2e7d7f4b110024ecbcec59f694370079a069df1903d03","e55bfb59691c93dfd6343a034cf9c78b03261e7d51a16e61e8fd7257378b563b8de6568abd88ddeb532d109e3ad33c6454b268b6ee6b29849786ac5691898603"]},"snapshot":{"confirmedTransactions":["65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":1,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":23000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:22:44.360477345Z"} +3 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":9,"tag":"TxValid","timestamp":"2024-10-08T13:23:18.169555579Z","transaction":{"cborHex":"84a300d901028182582065d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e5161701018282581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a0016e36082581d600d45f2b310a98e766cee2ab2f6756c91719bd7b35929cef058365b651a014810600200a100d90102818258200f193a88190f6dace0a3db1e0e50797a6e28cd4b6e289260dc96b5a8d7934bf85840b991c62af8e2b2d06f821fb6064f98c2fc8909b0b2d81435c7e075a61fc92ee6c9224f23d817de35d5529f54034c2ab8dfaded387e99fc525344846bb5dc860af5f6","description":"Ledger Cddl Format","txId":"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af","type":"Witnessed Tx ConwayEra"}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":10,"signatures":{"multiSignature":["5897602f543c3692cfb691119b2e9feb22e2302333b3200f95fa65feffaa0846abeb293afc8dcbd4ba2ac4b0ba1df729730d2e3b5e69a652f99b52fa15729209","d2268afebbbde31886cf8ce1c7b827f92a1c675bc9dee5603d6d87d7c30d9f7ae46d596095a6e03624d2526fe101b026c9c765abb7e2603b06ffb4fa6ecc6b0e","b3342f10a0678c24bff40ab5d394b8d7382419b826bfbbc3c019a8c4fd20d6d4db9eec29d72fb58ceb09dfe6720ae5c8ade77fd49e2a4b7e884beeb93f027b00"]},"snapshot":{"confirmedTransactions":["a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":2,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"7b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c697#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":50000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:23:18.220733933Z"} +4 +5 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":11,"tag":"TxValid","timestamp":"2024-10-08T13:24:09.770902618Z","transaction":{"cborHex":"84a300d90102818258207b27f432e04984dc21ee61e8b1539775cd72cc8669f72cf39aebf6d87e35c69700018282581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a00a7d8c082581d605e4e214a6addd337126b3a61faad5dfe1e4f14f637a8969e3a05eefd1a025317c00200a100d9010281825820aa268d154185c9ea06ea73442fd8143c34c1dd543b7142bcb132aac0d1ed6ece5840fc6e2b0750259deedd5a73eeadf481138bf82edc3425614871a0ef09bfcf8cae52a80240fb895a7e6a8ad94d4acb32dffe567ed0d338afcd7878f745737f420df5f6","description":"Ledger Cddl Format","txId":"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce","type":"Witnessed Tx ConwayEra"}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":12,"signatures":{"multiSignature":["aa0673de465f1e7f4ca472b85315243ca2327f15778b3625b8a225563fd4dbb083e2ff9e50bb3a4680a3d1a3da89a839ed841f18c3f85770bf669fe268418608","c682e3bc61e3bf78db64a6bfdd48588e3aafcba62bdb507b8f6b56b364985e575d7185505f33597a45782edff46687d67281e65cf19c7dbbbc68b80b4ed92605","7efc7a1846184214ea2d6748f3db82a34c3be0d9af310d685a47f24e2694223b03611111902512c5674729553440c57755f340694589ccc94e093732b9630908"]},"snapshot":{"confirmedTransactions":["de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":3,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d97#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":100000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:24:09.825917124Z"} +6 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":13,"tag":"TxValid","timestamp":"2024-10-08T13:24:30.136780836Z","transaction":{"cborHex":"84a300d9010281825820c9a5fb7ca6f55f07facefccb7c5d824eed00ce18719d28ec4c4a2e4041e85d9700018282581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a00c65d4082581d6069830961c6af9095b0f2648dff31fa9545d8f0b6623db865eb78fde81a052f83c00200a100d9010281825820f953b2d6b6f319faa9f8462257eb52ad73e33199c650f0755e279e21882399c05840ac8f1632d9a636d3627328ffd09cd32e1b654cbf318f0ce499a9870b05530041aa0badf07cd43fec8f1456537ada71227bea8123c1ed641ae3cb22b7313d5f08f5f6","description":"Ledger Cddl Format","txId":"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74","type":"Witnessed Tx ConwayEra"}} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":14,"signatures":{"multiSignature":["cdd5844d70c2ad9d6e6981f922757cc5aff7785b425ffcbff83347f2ad9545c646bae9c75f6b65d23aad9452e17704d76cbd5e948408e73277cafa2858cb9104","a1551898f0c4b0f2a9b5a94998a18b9ca962dc92f24d4e5308a0dd449eaf1661c1e075fbc9895ad6f0bbbcd141eb251c9e08493d04adff01125a15906f919701","976e8a5fc707c4e048d266344a2af1a43e94dd7ca29f395d1735c70562ff915af22e9b9829f5058eb02d734e625bd30ada7cbb49060098ad27d6c8a747e9740f"]},"snapshot":{"confirmedTransactions":["bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74"],"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","snapshotNumber":4,"utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":13000000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#1":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":87000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}},"utxoToDecommit":null,"version":0},"tag":"SnapshotConfirmed","timestamp":"2024-10-08T13:24:30.186588075Z"} +{"contestationDeadline":"2024-10-08T13:24:42.6Z","headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":15,"snapshotNumber":4,"tag":"HeadIsClosed","timestamp":"2024-10-08T13:24:36.81629911Z"} +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":16,"tag":"ReadyToFanout","timestamp":"2024-10-08T13:24:42.708785607Z"} +7 +{"headId":"84e657e3dd5241caac75b749195f78684023583736cc08b2896290ab","seq":17,"tag":"HeadIsFinalized","timestamp":"2024-10-08T13:25:02.420848196Z","utxo":{"65d64ade1fa9da5099107e3ab9efeea6f305c3c831ca8b9c8f87594289e51617#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":2000000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#0":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":1500000}},"a8117ebbc21da57e580d95bcda7c316eff492c977e61a68fd0aea251348eb4af#1":{"address":"addr_test1vqx5tu4nzz5cuanvac4t9an4djghrx7hkdvjnnhstqm9kegvm6g6c","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":21500000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#0":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":13000000}},"bf4ba0d28cc39abee99b7b2bda7d104871e3e979ebfdc531ec34c490f85d1d74#1":{"address":"addr_test1vp5cxztpc6hep9ds7fjgmle3l225tk8ske3rmwr9adu0m6qchmx5z","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":87000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#0":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":11000000}},"de33eeedc890f11fab3c1d827974fbc69e96cfdce2418573b30c9e0844a738ce#1":{"address":"addr_test1vp0yug22dtwaxdcjdvaxr74dthlpunc57cm639578gz7algset3fh","datum":null,"datumhash":null,"inlineDatum":null,"referenceScript":null,"value":{"lovelace":39000000}}}} +8 +9 +10 +11