Repository: ideal-lab5/idn-sdk
Pull request #125: Feat: mpsc channel
Author: login: driemworks
Created: 2025-03-04T16:58:15Z | Merged: 2025-03-10T13:37:18Z

This replaces the `SharedState` struct with an mpsc channel, where it uses a `TracingUnboundedSender` to write well-formatted pulses from the gossipsub topic. Pulses can be read with a corresponding `TracingUnboundedReceiver`

- close #59 
- enhances test coverage for the gossipsub implementation
- ~~update tarpaulin config to output html reports and update gitignore to ignore it~~
- migrates dependencies to top level Cargo.toml

Linked issue #59: Gossipsub: use mpsc channel to write pulses
- use an mpsc channel instead of the shared state to store pulses
- the shared state can potentially be locked which could be problematic

Diff:
diff --git a/.gitignore b/.gitignore
index 4e2f0cb8..d370a235 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,5 +39,4 @@ rls*.log
 runtime/wasm/target/
 substrate.code-workspace
 target*/
-*.profraw
-tarpaulin-report.html
\ No newline at end of file
+*.profraw
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 3ab9eec7..37251cef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1600,68 +1600,31 @@ dependencies = [
  "percent-encoding",
 ]
 
-[[package]]
-name = "frame-benchmarking"
-version = "38.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01bdd47c2d541b38bd892da647d1e972c9d85b4ecd7094ad64f7600175da54d"
-dependencies = [
- "frame-support 38.2.0",
- "frame-support-procedural 30.0.6",
- "frame-system 38.0.0",
- "linregress",
- "log",
- "parity-scale-codec",
- "paste",
- "scale-info",
- "serde",
- "sp-api 34.0.0",
- "sp-application-crypto 38.0.0",
- "sp-core 34.0.0",
- "sp-io 38.0.0",
- "sp-runtime 39.0.5",
- "sp-runtime-interface 28.0.0",
- "sp-storage 21.0.0",
- "static_assertions",
-]
-
 [[package]]
 name = "frame-benchmarking"
 version = "39.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8c221b23f5cc5990830c4010bc01eac1cf401327e2bec362b0d28cb261809958"
 dependencies = [
- "frame-support 39.0.0",
- "frame-support-procedural 31.0.1",
- "frame-system 39.1.0",
+ "frame-support",
+ "frame-support-procedural",
+ "frame-system",
  "linregress",
  "log",
  "parity-scale-codec",
  "paste",
  "scale-info",
  "serde",
- "sp-api 35.0.0",
- "sp-application-crypto 39.0.0",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
  "sp-runtime-interface 29.0.0",
  "sp-storage 22.0.0",
  "static_assertions",
 ]
 
-[[package]]
-name = "frame-metadata"
-version = "16.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692"
-dependencies = [
- "cfg-if",
- "parity-scale-codec",
- "scale-info",
- "serde",
-]
-
 [[package]]
 name = "frame-metadata"
 version = "18.0.0"
@@ -1674,48 +1637,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "frame-support"
-version = "38.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7dd8b9f161a8289e3b9fe6c1068519358dbff2270d38097a923d3d1b4459dca"
-dependencies = [
- "aquamarine",
- "array-bytes",
- "bitflags 1.3.2",
- "docify",
- "environmental",
- "frame-metadata 16.0.0",
- "frame-support-procedural 30.0.6",
- "impl-trait-for-tuples",
- "k256",
- "log",
- "macro_magic",
- "parity-scale-codec",
- "paste",
- "scale-info",
- "serde",
- "serde_json",
- "smallvec",
- "sp-api 34.0.0",
- "sp-arithmetic",
- "sp-core 34.0.0",
- "sp-crypto-hashing-proc-macro",
- "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-genesis-builder 0.15.1",
- "sp-inherents 34.0.0",
- "sp-io 38.0.0",
- "sp-metadata-ir 0.7.0",
- "sp-runtime 39.0.5",
- "sp-staking 36.0.0",
- "sp-state-machine 0.43.0",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-tracing 17.0.1",
- "sp-weights",
- "static_assertions",
- "tt-call",
-]
-
 [[package]]
 name = "frame-support"
 version = "39.0.0"
@@ -1728,8 +1649,8 @@ dependencies = [
  "bitflags 1.3.2",
  "docify",
  "environmental",
- "frame-metadata 18.0.0",
- "frame-support-procedural 31.0.1",
+ "frame-metadata",
+ "frame-support-procedural",
  "impl-trait-for-tuples",
  "k256",
  "log",
@@ -1740,47 +1661,26 @@ dependencies = [
  "serde",
  "serde_json",
  "smallvec",
- "sp-api 35.0.0",
+ "sp-api",
  "sp-arithmetic",
- "sp-core 35.0.0",
+ "sp-core",
  "sp-crypto-hashing-proc-macro",
  "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-genesis-builder 0.16.0",
- "sp-inherents 35.0.0",
- "sp-io 39.0.0",
- "sp-metadata-ir 0.8.0",
- "sp-runtime 40.1.0",
- "sp-staking 37.0.0",
- "sp-state-machine 0.44.0",
+ "sp-genesis-builder",
+ "sp-inherents",
+ "sp-io",
+ "sp-metadata-ir",
+ "sp-runtime",
+ "sp-staking",
+ "sp-state-machine",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "sp-tracing 17.0.1",
- "sp-trie 38.0.0",
+ "sp-trie",
  "sp-weights",
  "static_assertions",
  "tt-call",
 ]
 
-[[package]]
-name = "frame-support-procedural"
-version = "30.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8da784d943f2a945be923ab081a7c0837355b38045c50945d7ec1a138e2f3c52"
-dependencies = [
- "Inflector",
- "cfg-expr",
- "derive-syn-parse",
- "docify",
- "expander",
- "frame-support-procedural-tools",
- "itertools 0.11.0",
- "macro_magic",
- "proc-macro-warning",
- "proc-macro2",
- "quote",
- "sp-crypto-hashing",
- "syn 2.0.98",
-]
-
 [[package]]
 name = "frame-support-procedural"
 version = "31.0.1"
@@ -1826,27 +1726,6 @@ dependencies = [
  "syn 2.0.98",
 ]
 
-[[package]]
-name = "frame-system"
-version = "38.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c7fa02f8c305496d2ae52edaecdb9d165f11afa965e05686d7d7dd1ce93611"
-dependencies = [
- "cfg-if",
- "docify",
- "frame-support 38.2.0",
- "log",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-core 34.0.0",
- "sp-io 38.0.0",
- "sp-runtime 39.0.5",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-version 37.0.0",
- "sp-weights",
-]
-
 [[package]]
 name = "frame-system"
 version = "39.1.0"
@@ -1855,16 +1734,16 @@ checksum = "b97b74455a72cc924b8b8e8a1dee05de90d3714d1723b0ff54b9e6976aa009ac"
 dependencies = [
  "cfg-if",
  "docify",
- "frame-support 39.0.0",
+ "frame-support",
  "log",
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-version 38.0.0",
+ "sp-version",
  "sp-weights",
 ]
 
@@ -2610,15 +2489,6 @@ dependencies = [
  "xmltree",
 ]
 
-[[package]]
-name = "impl-codec"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f"
-dependencies = [
- "parity-scale-codec",
-]
-
 [[package]]
 name = "impl-codec"
 version = "0.7.1"
@@ -2636,16 +2506,7 @@ checksum = "803d15461ab0dcc56706adf266158acbc44ccf719bf7d0af30705f58b90a4b8c"
 dependencies = [
  "integer-sqrt",
  "num-traits",
- "uint 0.10.0",
-]
-
-[[package]]
-name = "impl-serde"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd"
-dependencies = [
- "serde",
+ "uint",
 ]
 
 [[package]]
@@ -3726,51 +3587,51 @@ version = "21.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f8b3dc7ead2fafe65a65d1589017f25fd1c3e020dea3371c210ebdcfd0c11f5"
 dependencies = [
- "frame-benchmarking 39.0.0",
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
  "log",
  "parity-scale-codec",
  "scale-info",
- "sp-api 35.0.0",
+ "sp-api",
  "sp-arithmetic",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
 ]
 
 [[package]]
 name = "pallet-balances"
-version = "40.0.0"
+version = "40.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5800e106650e41577d852e003a212b82998bb2fd1e348ed08436ef620fd094f"
+checksum = "8b3d18caaae72674d6536d339c1ae5b5063b55de5813a43ee045847d28c0a128"
 dependencies = [
  "docify",
- "frame-benchmarking 39.0.0",
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
  "log",
  "parity-scale-codec",
  "scale-info",
- "sp-runtime 40.1.0",
+ "sp-runtime",
 ]
 
 [[package]]
 name = "pallet-idn-manager"
 version = "0.0.1-dev"
 dependencies = [
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-support",
+ "frame-system",
  "idn-traits",
  "pallet-balances",
  "pallet-xcm",
  "parity-scale-codec",
  "scale-info",
- "sp-api 35.0.0",
+ "sp-api",
  "sp-arithmetic",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "staging-xcm",
  "staging-xcm-builder",
@@ -3784,9 +3645,9 @@ dependencies = [
  "ark-ec",
  "ark-serialize",
  "ark-std",
- "frame-benchmarking 38.0.0",
- "frame-support 38.2.0",
- "frame-system 38.0.0",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
  "hex",
  "log",
  "parity-scale-codec",
@@ -3796,11 +3657,11 @@ dependencies = [
  "sha2 0.10.8",
  "sp-ark-bls12-381",
  "sp-consensus-randomness-beacon",
- "sp-core 34.0.0",
- "sp-inherents 34.0.0",
- "sp-io 38.0.0",
- "sp-keystore 0.40.0",
- "sp-runtime 39.0.5",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-keystore",
+ "sp-runtime",
  "timelock",
 ]
 
@@ -3810,15 +3671,15 @@ version = "39.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ee68d52c4f65063624f02ac83691ca6e46b471c8eb3bfb6f60b441f24a23ab"
 dependencies = [
- "frame-benchmarking 39.0.0",
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
 ]
 
 [[package]]
@@ -3828,16 +3689,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "165300c258d4f6fd325d3195e3d68b976891d5710a6f0ac636b935906c22891c"
 dependencies = [
  "bounded-collections",
- "frame-benchmarking 39.0.0",
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
  "staging-xcm",
  "staging-xcm-builder",
  "staging-xcm-executor",
@@ -4036,8 +3897,8 @@ checksum = "fe728468f0519d4ae802cae85b21a50072730fb93ad47bedb34fbc01fa62f125"
 dependencies = [
  "parity-scale-codec",
  "scale-info",
- "sp-core 35.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-runtime",
 ]
 
 [[package]]
@@ -4052,8 +3913,8 @@ dependencies = [
  "polkadot-core-primitives",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-runtime",
  "sp-weights",
 ]
 
@@ -4200,19 +4061,6 @@ dependencies = [
  "syn 2.0.98",
 ]
 
-[[package]]
-name = "primitive-types"
-version = "0.12.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2"
-dependencies = [
- "fixed-hash",
- "impl-codec 0.6.0",
- "impl-serde 0.4.0",
- "scale-info",
- "uint 0.9.5",
-]
-
 [[package]]
 name = "primitive-types"
 version = "0.13.1"
@@ -4220,11 +4068,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5"
 dependencies = [
  "fixed-hash",
- "impl-codec 0.7.1",
+ "impl-codec",
  "impl-num-traits",
- "impl-serde 0.5.0",
+ "impl-serde",
  "scale-info",
- "uint 0.10.0",
+ "uint",
 ]
 
 [[package]]
@@ -4280,6 +4128,20 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "prometheus"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1"
+dependencies = [
+ "cfg-if",
+ "fnv",
+ "lazy_static",
+ "memchr",
+ "parking_lot",
+ "thiserror 1.0.69",
+]
+
 [[package]]
 name = "prometheus-client"
 version = "0.22.3"
@@ -4835,20 +4697,34 @@ dependencies = [
  "ark-serialize",
  "async-std",
  "futures",
- "futures-timer",
  "libp2p",
  "log",
  "parity-scale-codec",
  "prost",
  "prost-build",
- "prost-types",
  "rand 0.8.5",
+ "sc-utils",
  "scale-info",
  "serde",
  "sp-ark-bls12-381",
  "tokio",
 ]
 
+[[package]]
+name = "sc-utils"
+version = "18.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63860daa5455a5f3689bec3e94aad823746bac0f4166761786435043ab01a27"
+dependencies = [
+ "async-channel 1.9.0",
+ "futures",
+ "futures-timer",
+ "log",
+ "parking_lot",
+ "prometheus",
+ "sp-arithmetic",
+]
+
 [[package]]
 name = "scale-info"
 version = "2.11.6"
@@ -5207,29 +5083,6 @@ dependencies = [
  "sha1",
 ]
 
-[[package]]
-name = "sp-api"
-version = "34.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75"
-dependencies = [
- "docify",
- "hash-db",
- "log",
- "parity-scale-codec",
- "scale-info",
- "sp-api-proc-macro 20.0.0",
- "sp-core 34.0.0",
- "sp-externalities 0.29.0",
- "sp-metadata-ir 0.7.0",
- "sp-runtime 39.0.5",
- "sp-runtime-interface 28.0.0",
- "sp-state-machine 0.43.0",
- "sp-trie 37.0.0",
- "sp-version 37.0.0",
- "thiserror 1.0.69",
-]
-
 [[package]]
 name = "sp-api"
 version = "35.0.0"
@@ -5241,33 +5094,18 @@ dependencies = [
  "log",
  "parity-scale-codec",
  "scale-info",
- "sp-api-proc-macro 21.0.0",
- "sp-core 35.0.0",
+ "sp-api-proc-macro",
+ "sp-core",
  "sp-externalities 0.30.0",
- "sp-metadata-ir 0.8.0",
- "sp-runtime 40.1.0",
+ "sp-metadata-ir",
+ "sp-runtime",
  "sp-runtime-interface 29.0.0",
- "sp-state-machine 0.44.0",
- "sp-trie 38.0.0",
- "sp-version 38.0.0",
+ "sp-state-machine",
+ "sp-trie",
+ "sp-version",
  "thiserror 1.0.69",
 ]
 
-[[package]]
-name = "sp-api-proc-macro"
-version = "20.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9aadf9e97e694f0e343978aa632938c5de309cbcc8afed4136cb71596737278"
-dependencies = [
- "Inflector",
- "blake2",
- "expander",
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn 2.0.98",
-]
-
 [[package]]
 name = "sp-api-proc-macro"
 version = "21.0.0"
@@ -5283,19 +5121,6 @@ dependencies = [
  "syn 2.0.98",
 ]
 
-[[package]]
-name = "sp-application-crypto"
-version = "38.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d8133012faa5f75b2f0b1619d9f720c1424ac477152c143e5f7dbde2fe1a958"
-dependencies = [
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-core 34.0.0",
- "sp-io 38.0.0",
-]
-
 [[package]]
 name = "sp-application-crypto"
 version = "39.0.0"
@@ -5305,8 +5130,8 @@ dependencies = [
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
+ "sp-core",
+ "sp-io",
 ]
 
 [[package]]
@@ -5340,60 +5165,10 @@ version = "0.0.1-dev"
 dependencies = [
  "array-bytes",
  "async-trait",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-inherents 34.0.0",
+ "sp-inherents",
  "tokio",
 ]
 
-[[package]]
-name = "sp-core"
-version = "34.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c961a5e33fb2962fa775c044ceba43df9c6f917e2c35d63bfe23738468fa76a7"
-dependencies = [
- "array-bytes",
- "bitflags 1.3.2",
- "blake2",
- "bounded-collections",
- "bs58",
- "dyn-clonable",
- "ed25519-zebra",
- "futures",
- "hash-db",
- "hash256-std-hasher",
- "impl-serde 0.4.0",
- "itertools 0.11.0",
- "k256",
- "libsecp256k1",
- "log",
- "merlin",
- "parity-bip39",
- "parity-scale-codec",
- "parking_lot",
- "paste",
- "primitive-types 0.12.2",
- "rand 0.8.5",
- "scale-info",
- "schnorrkel",
- "secp256k1",
- "secrecy",
- "serde",
- "sp-crypto-hashing",
- "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-externalities 0.29.0",
- "sp-runtime-interface 28.0.0",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-storage 21.0.0",
- "ss58-registry",
- "substrate-bip39",
- "thiserror 1.0.69",
- "tracing",
- "w3f-bls",
- "zeroize",
-]
-
 [[package]]
 name = "sp-core"
 version = "35.0.0"
@@ -5410,7 +5185,7 @@ dependencies = [
  "futures",
  "hash-db",
  "hash256-std-hasher",
- "impl-serde 0.5.0",
+ "impl-serde",
  "itertools 0.11.0",
  "k256",
  "libsecp256k1",
@@ -5420,7 +5195,7 @@ dependencies = [
  "parity-scale-codec",
  "parking_lot",
  "paste",
- "primitive-types 0.13.1",
+ "primitive-types",
  "rand 0.8.5",
  "scale-info",
  "schnorrkel",
@@ -5444,7 +5219,7 @@ dependencies = [
 [[package]]
 name = "sp-crypto-ec-utils"
 version = "0.10.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "ark-bls12-377",
  "ark-bls12-377-ext",
@@ -5500,7 +5275,7 @@ dependencies = [
 [[package]]
 name = "sp-debug-derive"
 version = "14.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5510,24 +5285,13 @@ dependencies = [
 [[package]]
 name = "sp-externalities"
 version = "0.25.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "environmental",
  "parity-scale-codec",
  "sp-storage 19.0.0",
 ]
 
-[[package]]
-name = "sp-externalities"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30"
-dependencies = [
- "environmental",
- "parity-scale-codec",
- "sp-storage 21.0.0",
-]
-
 [[package]]
 name = "sp-externalities"
 version = "0.30.0"
@@ -5539,19 +5303,6 @@ dependencies = [
  "sp-storage 22.0.0",
 ]
 
-[[package]]
-name = "sp-genesis-builder"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8"
-dependencies = [
- "parity-scale-codec",
- "scale-info",
- "serde_json",
- "sp-api 34.0.0",
- "sp-runtime 39.0.5",
-]
-
 [[package]]
 name = "sp-genesis-builder"
 version = "0.16.0"
@@ -5561,22 +5312,8 @@ dependencies = [
  "parity-scale-codec",
  "scale-info",
  "serde_json",
- "sp-api 35.0.0",
- "sp-runtime 40.1.0",
-]
-
-[[package]]
-name = "sp-inherents"
-version = "34.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57"
-dependencies = [
- "async-trait",
- "impl-trait-for-tuples",
- "parity-scale-codec",
- "scale-info",
- "sp-runtime 39.0.5",
- "thiserror 1.0.69",
+ "sp-api",
+ "sp-runtime",
 ]
 
 [[package]]
@@ -5589,37 +5326,10 @@ dependencies = [
  "impl-trait-for-tuples",
  "parity-scale-codec",
  "scale-info",
- "sp-runtime 40.1.0",
+ "sp-runtime",
  "thiserror 1.0.69",
 ]
 
-[[package]]
-name = "sp-io"
-version = "38.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ef7eb561bb4839cc8424ce58c5ea236cbcca83f26fcc0426d8decfe8aa97d4"
-dependencies = [
- "bytes",
- "docify",
- "ed25519-dalek",
- "libsecp256k1",
- "log",
- "parity-scale-codec",
- "polkavm-derive 0.9.1",
- "rustversion",
- "secp256k1",
- "sp-core 34.0.0",
- "sp-crypto-hashing",
- "sp-externalities 0.29.0",
- "sp-keystore 0.40.0",
- "sp-runtime-interface 28.0.0",
- "sp-state-machine 0.43.0",
- "sp-tracing 17.0.1",
- "sp-trie 37.0.0",
- "tracing",
- "tracing-core",
-]
-
 [[package]]
 name = "sp-io"
 version = "39.0.0"
@@ -5635,30 +5345,18 @@ dependencies = [
  "polkavm-derive 0.9.1",
  "rustversion",
  "secp256k1",
- "sp-core 35.0.0",
+ "sp-core",
  "sp-crypto-hashing",
  "sp-externalities 0.30.0",
- "sp-keystore 0.41.0",
+ "sp-keystore",
  "sp-runtime-interface 29.0.0",
- "sp-state-machine 0.44.0",
+ "sp-state-machine",
  "sp-tracing 17.0.1",
- "sp-trie 38.0.0",
+ "sp-trie",
  "tracing",
  "tracing-core",
 ]
 
-[[package]]
-name = "sp-keystore"
-version = "0.40.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42"
-dependencies = [
- "parity-scale-codec",
- "parking_lot",
- "sp-core 34.0.0",
- "sp-externalities 0.29.0",
-]
-
 [[package]]
 name = "sp-keystore"
 version = "0.41.0"
@@ -5667,28 +5365,17 @@ checksum = "c1d41475fcdf253f9f0da839564c1b7f8a95c6a293ddfffd6e48e3671e76f33b"
 dependencies = [
  "parity-scale-codec",
  "parking_lot",
- "sp-core 35.0.0",
+ "sp-core",
  "sp-externalities 0.30.0",
 ]
 
-[[package]]
-name = "sp-metadata-ir"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869"
-dependencies = [
- "frame-metadata 16.0.0",
- "parity-scale-codec",
- "scale-info",
-]
-
 [[package]]
 name = "sp-metadata-ir"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "427be4e8e6a33cb8ffc8c91f8834b9c6f563daf246e8f0da16e9e0db3db55f5a"
 dependencies = [
- "frame-metadata 18.0.0",
+ "frame-metadata",
  "parity-scale-codec",
  "scale-info",
 ]
@@ -5703,33 +5390,6 @@ dependencies = [
  "regex",
 ]
 
-[[package]]
-name = "sp-runtime"
-version = "39.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e00503b83cf48fffe48746b91b9b832d6785d4e2eeb0941558371eac6baac6"
-dependencies = [
- "docify",
- "either",
- "hash256-std-hasher",
- "impl-trait-for-tuples",
- "log",
- "num-traits",
- "parity-scale-codec",
- "paste",
- "rand 0.8.5",
- "scale-info",
- "serde",
- "simple-mermaid",
- "sp-application-crypto 38.0.0",
- "sp-arithmetic",
- "sp-core 34.0.0",
- "sp-io 38.0.0",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-weights",
- "tracing",
-]
-
 [[package]]
 name = "sp-runtime"
 version = "40.1.0"
@@ -5749,12 +5409,12 @@ dependencies = [
  "scale-info",
  "serde",
  "simple-mermaid",
- "sp-application-crypto 39.0.0",
+ "sp-application-crypto",
  "sp-arithmetic",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
+ "sp-core",
+ "sp-io",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-trie 38.0.0",
+ "sp-trie",
  "sp-weights",
  "tracing",
  "tuplex",
@@ -5763,13 +5423,13 @@ dependencies = [
 [[package]]
 name = "sp-runtime-interface"
 version = "24.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "bytes",
  "impl-trait-for-tuples",
  "parity-scale-codec",
  "polkavm-derive 0.18.0",
- "primitive-types 0.13.1",
+ "primitive-types",
  "sp-externalities 0.25.0",
  "sp-runtime-interface-proc-macro 17.0.0",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)",
@@ -5779,26 +5439,6 @@ dependencies = [
  "static_assertions",
 ]
 
-[[package]]
-name = "sp-runtime-interface"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "985eb981f40c689c6a0012c937b68ed58dabb4341d06f2dfe4dfd5ed72fa4017"
-dependencies = [
- "bytes",
- "impl-trait-for-tuples",
- "parity-scale-codec",
- "polkavm-derive 0.9.1",
- "primitive-types 0.12.2",
- "sp-externalities 0.29.0",
- "sp-runtime-interface-proc-macro 18.0.0",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-storage 21.0.0",
- "sp-tracing 17.0.1",
- "sp-wasm-interface 21.0.1",
- "static_assertions",
-]
-
 [[package]]
 name = "sp-runtime-interface"
 version = "29.0.0"
@@ -5809,7 +5449,7 @@ dependencies = [
  "impl-trait-for-tuples",
  "parity-scale-codec",
  "polkavm-derive 0.9.1",
- "primitive-types 0.13.1",
+ "primitive-types",
  "sp-externalities 0.30.0",
  "sp-runtime-interface-proc-macro 18.0.0",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -5822,7 +5462,7 @@ dependencies = [
 [[package]]
 name = "sp-runtime-interface-proc-macro"
 version = "17.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "Inflector",
  "expander",
@@ -5846,20 +5486,6 @@ dependencies = [
  "syn 2.0.98",
 ]
 
-[[package]]
-name = "sp-staking"
-version = "36.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a73eedb4b85f4cd420d31764827546aa22f82ce1646d0fd258993d051de7a90"
-dependencies = [
- "impl-trait-for-tuples",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-core 34.0.0",
- "sp-runtime 39.0.5",
-]
-
 [[package]]
 name = "sp-staking"
 version = "37.0.0"
@@ -5870,29 +5496,8 @@ dependencies = [
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-core 35.0.0",
- "sp-runtime 40.1.0",
-]
-
-[[package]]
-name = "sp-state-machine"
-version = "0.43.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "930104d6ae882626e8880d9b1578da9300655d337a3ffb45e130c608b6c89660"
-dependencies = [
- "hash-db",
- "log",
- "parity-scale-codec",
- "parking_lot",
- "rand 0.8.5",
- "smallvec",
- "sp-core 34.0.0",
- "sp-externalities 0.29.0",
- "sp-panic-handler",
- "sp-trie 37.0.0",
- "thiserror 1.0.69",
- "tracing",
- "trie-db",
+ "sp-core",
+ "sp-runtime",
 ]
 
 [[package]]
@@ -5907,10 +5512,10 @@ dependencies = [
  "parking_lot",
  "rand 0.8.5",
  "smallvec",
- "sp-core 35.0.0",
+ "sp-core",
  "sp-externalities 0.30.0",
  "sp-panic-handler",
- "sp-trie 38.0.0",
+ "sp-trie",
  "thiserror 1.0.69",
  "tracing",
  "trie-db",
@@ -5925,40 +5530,27 @@ checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834"
 [[package]]
 name = "sp-std"
 version = "14.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 
 [[package]]
 name = "sp-storage"
 version = "19.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
- "impl-serde 0.5.0",
+ "impl-serde",
  "parity-scale-codec",
  "ref-cast",
  "serde",
  "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)",
 ]
 
-[[package]]
-name = "sp-storage"
-version = "21.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8"
-dependencies = [
- "impl-serde 0.4.0",
- "parity-scale-codec",
- "ref-cast",
- "serde",
- "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "sp-storage"
 version = "22.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee3b70ca340e41cde9d2e069d354508a6e37a6573d66f7cc38f11549002f64ec"
 dependencies = [
- "impl-serde 0.5.0",
+ "impl-serde",
  "parity-scale-codec",
  "ref-cast",
  "serde",
@@ -5968,7 +5560,7 @@ dependencies = [
 [[package]]
 name = "sp-tracing"
 version = "16.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "parity-scale-codec",
  "tracing",
@@ -5988,30 +5580,6 @@ dependencies = [
  "tracing-subscriber",
 ]
 
-[[package]]
-name = "sp-trie"
-version = "37.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6282aef9f4b6ecd95a67a45bcdb67a71f4a4155c09a53c10add4ffe823db18cd"
-dependencies = [
- "ahash",
- "hash-db",
- "lazy_static",
- "memory-db",
- "nohash-hasher",
- "parity-scale-codec",
- "parking_lot",
- "rand 0.8.5",
- "scale-info",
- "schnellru",
- "sp-core 34.0.0",
- "sp-externalities 0.29.0",
- "thiserror 1.0.69",
- "tracing",
- "trie-db",
- "trie-root",
-]
-
 [[package]]
 name = "sp-trie"
 version = "38.0.0"
@@ -6027,7 +5595,7 @@ dependencies = [
  "rand 0.8.5",
  "scale-info",
  "schnellru",
- "sp-core 35.0.0",
+ "sp-core",
  "sp-externalities 0.30.0",
  "thiserror 1.0.69",
  "tracing",
@@ -6035,54 +5603,24 @@ dependencies = [
  "trie-root",
 ]
 
-[[package]]
-name = "sp-version"
-version = "37.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1"
-dependencies = [
- "impl-serde 0.4.0",
- "parity-scale-codec",
- "parity-wasm",
- "scale-info",
- "serde",
- "sp-crypto-hashing-proc-macro",
- "sp-runtime 39.0.5",
- "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-version-proc-macro 14.0.0",
- "thiserror 1.0.69",
-]
-
 [[package]]
 name = "sp-version"
 version = "38.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b7561e88742bc47b2f5fbdcab77a1cd98cf04117a3e163c1aadd58b9a592a18"
 dependencies = [
- "impl-serde 0.5.0",
+ "impl-serde",
  "parity-scale-codec",
  "parity-wasm",
  "scale-info",
  "serde",
  "sp-crypto-hashing-proc-macro",
- "sp-runtime 40.1.0",
+ "sp-runtime",
  "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sp-version-proc-macro 15.0.0",
+ "sp-version-proc-macro",
  "thiserror 1.0.69",
 ]
 
-[[package]]
-name = "sp-version-proc-macro"
-version = "14.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7"
-dependencies = [
- "parity-scale-codec",
- "proc-macro2",
- "quote",
- "syn 2.0.98",
-]
-
 [[package]]
 name = "sp-version-proc-macro"
 version = "15.0.0"
@@ -6099,7 +5637,7 @@ dependencies = [
 [[package]]
 name = "sp-wasm-interface"
 version = "20.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#b9b73eb2758edc8af140fea60fdc51f890eefc48"
+source = "git+https://github.com/paritytech/polkadot-sdk#3dc3a11cd68762c2e5feb0beba0b61f448c4fc92"
 dependencies = [
  "anyhow",
  "impl-trait-for-tuples",
@@ -6181,14 +5719,14 @@ dependencies = [
  "bounded-collections",
  "derivative",
  "environmental",
- "frame-support 39.0.0",
+ "frame-support",
  "hex-literal",
  "impl-trait-for-tuples",
  "log",
  "parity-scale-codec",
  "scale-info",
  "serde",
- "sp-runtime 40.1.0",
+ "sp-runtime",
  "sp-weights",
  "xcm-procedural",
 ]
@@ -6199,8 +5737,8 @@ version = "18.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1cce13bf3abbda42464069f92c21c8cd64eeb089b43cde3c3aca5f90cf485d7b"
 dependencies = [
- "frame-support 39.0.0",
- "frame-system 39.1.0",
+ "frame-support",
+ "frame-system",
  "impl-trait-for-tuples",
  "log",
  "pallet-asset-conversion",
@@ -6209,8 +5747,8 @@ dependencies = [
  "polkadot-parachain-primitives",
  "scale-info",
  "sp-arithmetic",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-io",
+ "sp-runtime",
  "sp-weights",
  "staging-xcm",
  "staging-xcm-executor",
@@ -6223,15 +5761,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e07e7020a321d74513c3d6e4015008de56e2a362758fd9615f6837b29b01a9ee"
 dependencies = [
  "environmental",
- "frame-benchmarking 39.0.0",
- "frame-support 39.0.0",
+ "frame-benchmarking",
+ "frame-support",
  "impl-trait-for-tuples",
  "parity-scale-codec",
  "scale-info",
  "sp-arithmetic",
- "sp-core 35.0.0",
- "sp-io 39.0.0",
- "sp-runtime 40.1.0",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
  "sp-weights",
  "staging-xcm",
  "tracing",
@@ -6697,18 +6235,6 @@ version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
 
-[[package]]
-name = "uint"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52"
-dependencies = [
- "byteorder",
- "crunchy",
- "hex",
- "static_assertions",
-]
-
 [[package]]
 name = "uint"
 version = "0.10.0"
@@ -7365,10 +6891,10 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e27196c596b9175eb068f7e34da0be09e26806ae69cb8cbc837f26dff494ef1e"
 dependencies = [
- "frame-support 39.0.0",
+ "frame-support",
  "parity-scale-codec",
  "scale-info",
- "sp-api 35.0.0",
+ "sp-api",
  "sp-weights",
  "staging-xcm",
  "staging-xcm-executor",
diff --git a/Cargo.toml b/Cargo.toml
index 16327213..e01599c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,34 +45,56 @@ extra-unused-type-parameters = { level = "allow", priority = 2 }     # stylistic
 default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic
 
 [workspace.dependencies]
-# Async & Concurrency
+# Async, Concurrency, & Networking
 async-trait = "0.1.85"
+futures = "0.3"
+futures-timer = "3.0.3"
+libp2p = { version = "0.55.0", features = [
+    "gossipsub",
+    "noise",
+    "tcp",
+    "ping",
+    "dns",
+    "async-std",
+    "tokio",
+    "websocket",
+    "yamux",
+] }
+thiserror = { version = "1.0.64" }
 tokio = { version = "^1", features = ["full"] }
 
 # Serialization
+array-bytes = { version = "6.2.2", default-features = false }
+hex = { version = "0.4", features = ["serde"], default-features = false }
 serde = { version = "1.0.197", default-features = false }
+serde_json = { version = "1.0.132", default-features = false }
 
 # Polkadot & Substrate
-frame-benchmarking = { version = "38.0.0", default-features = false }
-frame-support = { version = "38.0.0", default-features = false }
-frame-system = { version = "38.0.0", default-features = false }
-sp-core = { version = "34.0.0", default-features = false }
-sp-inherents = { version = "34.0.0", default-features = false }
-sp-io = { version = "38.0.0", default-features = false }
-sp-keystore = { version = "0.34.0", default-features = false }
-sp-runtime = { version = "39.0.3", default-features = false }
+frame-benchmarking = { version = "39.0.0", default-features = false }
+frame-executive = { version = "39.0.0", default-features = false }
+frame-support = { version = "39.0.0", default-features = false }
+frame-system = { version = "39.1.0", default-features = false }
+sc-consensus-randomness-beacon = { path = "./client/consensus/randomness-beacon", default-features = false }
+sc-utils = { version = "18.0.0", default-features = false }
+sp-consensus-randomness-beacon = { path = "./primitives/consensus/randomness-beacon", default-features = false }
+sp-core = { version = "35.0.0", default-features = false }
+sp-inherents = { version = "35.0.0", default-features = false }
+sp-io = { version = "39.0.0", default-features = false }
+sp-keystore = { version = "0.41.0", default-features = false }
+sp-runtime = { version = "40.1.0", default-features = false }
 
-# Arkworks (crypto)
+# Arkworks & Crypto
 sp-ark-bls12-381 = { git = "https://github.com/paritytech/arkworks-substrate", version = "0.4.2", default-features = false }
 ark-bls12-381 = { version = "0.4.0", features = ["curve"], default-features = false }
 ark-serialize = { version = "0.4.0", features = ["derive"], default-features = false }
 ark-ec = { version = "0.4.0", default-features = false }
 ark-std = { version = "0.4.0", default-features = false }
+sha2 = { version = "0.10.8" }
+timelock = { version = "0.0.1", default-features = false }
 
 # Logging & Utilities
 log = { version = "0.4.21", default-features = false }
 
-
 # Parity SCALE Codec
 codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
 scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
diff --git a/client/consensus/randomness-beacon/Cargo.toml b/client/consensus/randomness-beacon/Cargo.toml
index ae2a08c8..aecbaceb 100644
--- a/client/consensus/randomness-beacon/Cargo.toml
+++ b/client/consensus/randomness-beacon/Cargo.toml
@@ -14,22 +14,13 @@ workspace = true
 [dependencies]
 codec.workspace = true
 serde = { workspace = true, features = ["derive"] }
-libp2p = { version = "0.55.0", features = [
-    "gossipsub",
-    "noise",
-    "tcp",
-    "ping",
-    "dns",
-    "async-std",
-    "tokio",
-    "websocket",
-    "yamux",
-] }
+tokio.workspace = true
+libp2p.workspace = true
 prost = "0.13.4"
-prost-types = "0.13.4"
 log = { workspace = true }
-futures = "0.3"
+futures.workspace = true
 scale-info.workspace = true
+sc-utils.workspace = true
 
 # arkworks dependencies
 sp-ark-bls12-381 = { workspace = true, optional = true }
@@ -41,10 +32,10 @@ prost-build = "0.13.4"
 
 [dev-dependencies]
 async-std = "1.13.0"
-futures-timer = "3.0.3"
 rand = "0.8.5"
-tokio = { version = "1.43.0", features = ["full"] }
+tokio.workspace = true
 
+ 
 [features]
 e2e = []
 host-arkworks = ["sp-ark-bls12-381/std"]
\ No newline at end of file
diff --git a/client/consensus/randomness-beacon/src/gossipsub.rs b/client/consensus/randomness-beacon/src/gossipsub.rs
index 0f9a9771..964fb5f0 100644
--- a/client/consensus/randomness-beacon/src/gossipsub.rs
+++ b/client/consensus/randomness-beacon/src/gossipsub.rs
@@ -27,8 +27,8 @@
 //!
 //! ## Examples
 //!
-//! ```
-//! use sc_consensus_randomness_beacon::gossipsub::{GossipsubNetwork, GossipsubState};
+//! ``` no_run
+//! use sc_consensus_randomness_beacon::gossipsub::GossipsubNetwork;
 //! use sc_consensus_randomness_beacon::types::*;
 //! use futures::StreamExt;
 //! use libp2p::{
@@ -40,6 +40,7 @@
 //! 		swarm::{Swarm, SwarmEvent},
 //! 		Multiaddr, SwarmBuilder,
 //! };
+//! use sc_utils::mpsc::tracing_unbounded;
 //! use prost::Message;
 //! use std::sync::{Arc, Mutex};
 //!
@@ -54,11 +55,11 @@
 //! 		.parse()
 //! 		.expect("The string is a well-formatted multiaddress. qed.");
 //! let local_identity: Keypair = Keypair::generate_ed25519();
-//! let state = Arc::new(Mutex::new(GossipsubState { pulses: vec![] }));
+//! let (tx, rx) = tracing_unbounded("drand-notification-channel", 100000);
 //! let gossipsub_config = GossipsubConfig::default();
-//! let mut gossipsub = GossipsubNetwork::new(&local_identity, state.clone(), gossipsub_config).unwrap();
+//! let mut gossipsub = GossipsubNetwork::new(&local_identity, gossipsub_config, tx, None).unwrap();
 //! tokio::spawn(async move {
-//! 	if let Err(e) = gossipsub.run(topic_str, vec![&maddr1, &maddr2], None).await {
+//! 	if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 //! 		log::error!("Failed to run gossipsub network: {:?}", e);
 //! 	}
 //! });
@@ -75,7 +76,7 @@ use libp2p::{
 	Multiaddr, SwarmBuilder,
 };
 use prost::Message;
-use std::sync::{Arc, Mutex};
+use sc_utils::mpsc::TracingUnboundedSender;
 
 /// The default address instructing libp2p to choose a random open port on the local machine
 const RAND_LISTEN_ADDR: &str = "/ip4/0.0.0.0/tcp/0";
@@ -85,46 +86,24 @@ const RAND_LISTEN_ADDR: &str = "/ip4/0.0.0.0/tcp/0";
 pub enum Error {
 	/// The signature buffer expects 48 bytes, but more were provided
 	SignatureBufferCapacityExceeded,
+	/// The message did not follow the expected format
+	UnexpectedMessageFormat,
 	/// The provided gossipsub behaviour is invalid
 	InvalidGossipsubNetworkBehaviour,
-	/// The peer could not be dialed.
-	PeerUnreachable { who: Multiaddr },
+	/// The multiaddress is invalid (likely the protocol is not supported)
+	InvalidMultiaddress { who: Multiaddr },
 	/// The swarm could not listen on the given port
 	SwarmListenFailure,
-	/// The swarm could not subscribe to the topic.
-	GossipsubSubscriptionFailed,
-	/// The Mutex is locked and can not be accessed.
-	StateLocked,
-}
-
-/// The Gossipsub State tracks the pulses ingested from a randomness beacon
-/// during some given block's lifetime (i.e. the new pulses observed)
-#[derive(Debug, Clone, PartialEq)]
-pub struct GossipsubState {
-	/// An (unbounded) vec of pulses
-	pub pulses: Vec<OpaquePulse>,
 }
 
-impl GossipsubState {
-	/// Append a new pulse to the pulses vec. It returns Ok() if successful, otherwise gives an
-	/// error.
-	/// * `pulse`: The pulse to append
-	fn add_pulse(&mut self, pulse: Pulse) -> Result<(), Error> {
-		let opaque = pulse.try_into().map_err(|_| Error::SignatureBufferCapacityExceeded)?;
-		self.pulses.push(opaque);
-		Ok(())
-	}
-}
-
-/// A shared Gossipsub state between threads
-pub type SharedState = Arc<Mutex<GossipsubState>>;
-
 /// A gossipsub network with any behaviour and shared state
 pub struct GossipsubNetwork {
 	/// The behaviour config for the swam
 	swarm: Swarm<GossipsubBehaviour>,
-	/// The shared state
-	state: SharedState,
+	/// The mpsc channel sender
+	sender: TracingUnboundedSender<OpaquePulse>,
+	/// The number of peers the node is connected to
+	pub(crate) connected_peers: u8,
 }
 
 impl GossipsubNetwork {
@@ -134,17 +113,21 @@ impl GossipsubNetwork {
 	/// transport layer.
 	///
 	/// * `key`: A libp2p keypair
-	/// * `state`: The shared state
 	/// * `gossipsub_config`: A gossipsub config
+	/// * `sender`: A `TracingUnboundedSender` that can send an `OpaquePulse`
+	/// * `listen_addr`: An optional address to listen on. If None, a random local port is assigned.
 	pub fn new(
 		key: &Keypair,
-		state: SharedState,
 		gossipsub_config: GossipsubConfig,
+		sender: TracingUnboundedSender<OpaquePulse>,
+		listen_addr: Option<&Multiaddr>,
 	) -> Result<Self, Error> {
 		let message_authenticity = MessageAuthenticity::Signed(key.clone());
 		let gossipsub = GossipsubBehaviour::new(message_authenticity, gossipsub_config)
 			.map_err(|_| Error::InvalidGossipsubNetworkBehaviour)?;
-		let swarm = SwarmBuilder::with_existing_identity(key.clone())
+		// setup a libp2p swarm with tcp transport, using noise protocol for encryption
+		// and yamux for multiplexing
+		let mut swarm = SwarmBuilder::with_existing_identity(key.clone())
 			.with_tokio()
 			.with_tcp(
 				libp2p::tcp::Config::default(),
@@ -156,36 +139,31 @@ impl GossipsubNetwork {
 			.expect("The behaviour is well defined.")
 			.build();
 
-		Ok(Self { swarm, state })
+		// fallback to a randomly assigned open port if one was not provided
+		let fallback = &RAND_LISTEN_ADDR.parse().expect("The multiaddress is well-formatted;QED.");
+		let listen_addr = listen_addr.unwrap_or(fallback);
+
+		swarm.listen_on(listen_addr.clone()).map_err(|_| Error::SwarmListenFailure)?;
+
+		Ok(Self { swarm, sender, connected_peers: 0 })
 	}
 
 	/// Start the gossipsub network.
 	/// It waits for peers to establish a connection, then writes well-formed messages received
 	/// from the gossipsub topic to the shared state.
 	///
-	/// * `topic_str`: The gossipsub topic to subscribe to
-	/// * `peers`: A list of peers to dial
-	/// * `listen_addr`: An address to listen on. If None, then a random local port is assigned.
-	pub async fn run(
-		&mut self,
-		topic_str: &str,
-		peers: Vec<&Multiaddr>,
-		listen_addr: Option<&Multiaddr>,
-	) -> Result<(), Error> {
-		// fallback to a randomly assigned open port if one was not provided
-		let fallback = &RAND_LISTEN_ADDR.parse().expect("The multiaddress is well-formatted;QED.");
-		let listen_addr = listen_addr.unwrap_or(fallback);
-		self.swarm
-			.listen_on(listen_addr.clone())
-			.map_err(|_| Error::SwarmListenFailure)?;
-
-		for peer in &peers {
-			self.swarm
-				.dial((*peer).clone())
-				.map_err(|_| Error::PeerUnreachable { who: (*peer).clone() })?;
+	/// * `topic_str`: The gossipsub topic to subscribe to.
+	/// * `peers`: A list of peers to dial.
+	pub async fn run(&mut self, topic_str: &str, peers: Vec<Multiaddr>) -> Result<(), Error> {
+		if !peers.is_empty() {
+			for peer in &peers {
+				self.swarm.dial((*peer).clone()).map_err(|_| {
+					return Error::InvalidMultiaddress { who: (*peer).clone() };
+				})?;
+			}
+			self.wait_for_peers(peers.len()).await;
 		}
 
-		self.wait_for_peers(peers.len()).await;
 		self.subscribe(topic_str).await
 	}
 
@@ -199,6 +177,7 @@ impl GossipsubNetwork {
 				connected_peers += 1;
 			}
 		}
+		self.connected_peers = connected_peers as u8;
 	}
 
 	/// Create a subscription to a gossipsub topic.
@@ -208,33 +187,26 @@ impl GossipsubNetwork {
 	/// * `topic_str`: The gossipsub topic to subscribe to.
 	async fn subscribe(&mut self, topic_str: &str) -> Result<(), Error> {
 		let topic = IdentTopic::new(topic_str);
-
+		// *SRLabs: The error can never be encountered
+		// Q: Can we use an expect, or is this unsafe?
+		// Ref: https://docs.rs/libpp-gossipsub/0.48.0/src/libp2p_gossipsub/behaviour.rs.html#532
+		// The error can only occur if the subscription filter rejects it, but we specify no filter.
 		self.swarm
 			.behaviour_mut()
 			.subscribe(&topic)
-			.map_err(|_| Error::GossipsubSubscriptionFailed)?;
+			.expect("The libp2p gossipsub behavior has no subscription filter.");
 
 		loop {
 			match self.swarm.next().await {
-				Some(SwarmEvent::Behaviour(gossipsub::Event::Message {
-					propagation_source,
-					message_id,
-					message,
-				})) => {
-					// ignore non-decodable messages
-					if let Ok(pulse) = Pulse::decode(&*message.data) {
-						// The state could be locked here: https://github.com/ideal-lab5/idn-sdk/issues/59
-						if let Ok(mut state) = self.state.lock() {
-							state.add_pulse(pulse.clone())?;
-							log::info!(
-								"Received pulse for round {:?} with message id: {} from peer: {:?}",
-								pulse.round,
-								message_id,
-								propagation_source
-							);
-						}
+				Some(SwarmEvent::Behaviour(gossipsub::Event::Message { message, .. })) => {
+					match try_handle_pulse(&message.data) {
+						Ok(pulse) => {
+							self.sender.unbounded_send(pulse.clone()).unwrap();
+						},
+						Err(_) => {
+							// handle non-decodable messages: https://github.com/ideal-lab5/idn-sdk/issues/60
+						},
 					}
-					// handle non-decodable messages: https://github.com/ideal-lab5/idn-sdk/issues/60
 				},
 				_ => {
 					// ignore all other events
@@ -244,107 +216,149 @@ impl GossipsubNetwork {
 	}
 }
 
-#[cfg(feature = "e2e")]
-#[cfg(test)]
-mod tests {
-	use super::*;
-	use std::sync::{Arc, Mutex};
-	use tokio::time::{sleep, Duration};
-
-	fn build_node() -> (GossipsubNetwork, SharedState) {
-		let local_identity: Keypair = Keypair::generate_ed25519();
-		let state = Arc::new(Mutex::new(GossipsubState { pulses: vec![] }));
-		let gossipsub_config = GossipsubConfig::default();
-		(GossipsubNetwork::new(&local_identity, state.clone(), gossipsub_config).unwrap(), state)
-	}
-
-	#[tokio::test]
-	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
-		let topic_str: &str =
-			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
-
-		let maddr1: libp2p::Multiaddr =
-			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
-				.parse()
-				.expect("The string is a well-formatted multiaddress. qed.");
-
-		let maddr2: libp2p::Multiaddr =
-        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
-            .parse()
-            .expect("The string is a well-formatted multiaddress. qed.");
+pub(crate) fn try_handle_pulse(data: &[u8]) -> Result<OpaquePulse, Error> {
+	let pulse = Pulse::decode(data).map_err(|_| Error::UnexpectedMessageFormat)?;
+	let pulse: OpaquePulse =
+		pulse.try_into().map_err(|_| Error::SignatureBufferCapacityExceeded)?;
 
-		let (mut gossipsub, state) = build_node();
-		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![&maddr1, &maddr2], None).await {
-				log::error!("Failed to run gossipsub network: {:?}", e);
-			}
-		});
-
-		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() >= 1);
-	}
+	Ok(pulse)
 }
 
-#[cfg(not(feature = "e2e"))]
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use libp2p::gossipsub::{ConfigBuilder, ValidationMode};
-	use std::sync::{Arc, Mutex};
+	use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
 	use tokio::time::{sleep, Duration};
 
-	fn build_node() -> (GossipsubNetwork, SharedState) {
-		let local_identity: Keypair = Keypair::generate_ed25519();
-		let state = Arc::new(Mutex::new(GossipsubState { pulses: vec![] }));
-		let gossipsub_config = GossipsubConfig::default();
-		(GossipsubNetwork::new(&local_identity, state.clone(), gossipsub_config).unwrap(), state)
+	#[test]
+	fn can_convert_valid_data_to_opaque_pulse() {
+		let pulse = Pulse {
+			round: 14475418,
+			signature: [
+				146, 37, 87, 193, 37, 144, 182, 61, 73, 122, 248, 242, 242, 43, 61, 28, 75, 93, 37,
+				95, 131, 38, 3, 203, 216, 6, 213, 241, 244, 90, 162, 208, 90, 104, 76, 235, 84, 49,
+				223, 95, 22, 186, 113, 163, 202, 195, 230, 117,
+			]
+			.to_vec(),
+		};
+		let opaque: OpaquePulse = pulse.clone().try_into().unwrap();
+		let mut data = Vec::new();
+		pulse.encode(&mut data).unwrap();
+
+		let actual_opaque = try_handle_pulse(&data).unwrap();
+		assert_eq!(opaque, actual_opaque, "The output should match the input");
 	}
 
 	#[test]
-	fn can_add_pulse_to_state() {
-		let mut state = GossipsubState { pulses: vec![] };
-		let pulse = Pulse { round: 0, signature: ::prost::alloc::vec![1;48] };
-		let res = state.add_pulse(pulse);
-		assert!(res.is_ok());
-		assert_eq!(state.pulses.len(), 1);
+	fn can_fail_when_data_not_decodable_to_pulse() {
+		let res = try_handle_pulse(&[1; 32]);
+		assert!(res.is_err());
+		assert_eq!(
+			res,
+			Err(Error::UnexpectedMessageFormat),
+			"There should be an `UnexpectedMessageFormat` error."
+		);
 	}
 
 	#[test]
-	fn can_not_pulse_to_state_if_sig_buff_too_big() {
-		let mut state = GossipsubState { pulses: vec![] };
-		let pulse = Pulse { round: 0, signature: ::prost::alloc::vec![1;49] };
-		let res = state.add_pulse(pulse);
+	fn can_fail_when_pulse_signature_exceeds_buffer() {
+		let pulse = Pulse {
+			round: 14475418,
+			signature: [
+				146, 37, 87, 193, 37, 144, 182, 61, 73, 122, 248, 242, 242, 43, 61, 28, 75, 93, 37,
+				95, 131, 38, 3, 203, 216, 6, 213, 241, 244, 90, 162, 208, 90, 104, 76, 235, 84, 49,
+				223, 95, 22, 186, 113, 163, 202, 195, 230, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			]
+			.to_vec(),
+		};
+
+		let expected_error = Error::SignatureBufferCapacityExceeded;
+
+		let mut data = Vec::new();
+		pulse.encode(&mut data).unwrap();
+
+		let res = try_handle_pulse(&data);
 		assert!(res.is_err());
-		assert_eq!(state.pulses.len(), 0);
+		assert_eq!(
+			res,
+			Err(expected_error),
+			"There should be an `SignatureBufferCapacityExceeded` error."
+		);
 	}
 
-	#[tokio::test]
-	async fn can_fail_on_invalid_gossipsub_network_behaviour() {
+	fn build_node() -> (GossipsubNetwork, TracingUnboundedReceiver<OpaquePulse>) {
 		let local_identity: Keypair = Keypair::generate_ed25519();
-		let state = Arc::new(Mutex::new(GossipsubState { pulses: vec![] }));
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, rx) = tracing_unbounded("drand-notification-channel", 100000);
+		(GossipsubNetwork::new(&local_identity, gossipsub_config, tx, None).unwrap(), rx)
+	}
 
-		let gossipsub_config = ConfigBuilder::default()
-			.validation_mode(ValidationMode::Anonymous)
+	#[tokio::test]
+	async fn can_not_build_node_with_invalid_gossipsub_behavior() {
+		// supply a signing key but set anon validation, an invalid config
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = libp2p::gossipsub::ConfigBuilder::default()
+			.validation_mode(libp2p::gossipsub::ValidationMode::Anonymous)
 			.build()
 			.unwrap();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res = GossipsubNetwork::new(&local_identity, gossipsub_config, tx, None);
+		assert!(res.is_err());
+	}
 
-		let result = GossipsubNetwork::new(&local_identity, state, gossipsub_config);
+	#[tokio::test]
+	async fn can_build_new_node() {
+		let (node, _rx) = build_node();
+		assert!(node.connected_peers == 0, "There should be no connected peers.");
+	}
 
-		assert!(result.is_err());
-		assert!(
-			matches!(result, Err(Error::InvalidGossipsubNetworkBehaviour)),
-			"Expected InvalidGossipsubNetworkBehaviour error"
-		);
+	#[tokio::test]
+	async fn can_build_new_node_with_listen_addr() {
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let listen_addr: Multiaddr = "/ip4/0.0.0.0/tcp/4001".parse().unwrap();
+		let node = GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&listen_addr))
+			.unwrap();
+		assert!(node.connected_peers == 0, "There should be no connected peers.");
 	}
 
 	#[tokio::test]
-	async fn can_create_new_gossipsub_network() {
-		let (_gossipsub, state) = build_node();
-		let data_lock = state.lock().unwrap();
-		assert!(data_lock.pulses.is_empty());
+	async fn can_build_node_and_run_without_peers() {
+		let topic_str = "test";
+		let (mut node, _rx) = build_node();
+
+		let mut is_err: bool = false;
+
+		tokio::spawn(async move {
+			if let Err(_e) = node.run(topic_str, vec![]).await {
+				is_err = true;
+			}
+		});
+
+		sleep(Duration::from_secs(1)).await;
+
+		assert!(!is_err, "There should be no errors.");
+	}
+
+	#[tokio::test]
+	async fn can_build_node_and_fail_with_random_peers() {
+		let topic_str = "test";
+		let (mut node, _rx) = build_node();
+
+		let fake_peer: Multiaddr = Multiaddr::empty().with_p2p(libp2p::PeerId::random()).unwrap();
+
+		let mut is_err: bool = false;
+
+		tokio::spawn(async move {
+			if let Err(_e) = node.run(topic_str, vec![fake_peer]).await {
+				is_err = true;
+			}
+		});
+
+		sleep(Duration::from_secs(2)).await;
+
+		assert!(!is_err, "There should not be an error.");
 	}
 
 	#[tokio::test]
@@ -354,29 +368,23 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
-		let topic_str: &str =
-			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
-
-		let (mut gossipsub, state) = build_node();
-		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
-				log::error!("Failed to run gossipsub network: {:?}", e);
-			}
-		});
-
-		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
 	}
 }
diff --git a/pallets/randomness-beacon/Cargo.toml b/pallets/randomness-beacon/Cargo.toml
index b8496944..1d111d1c 100644
--- a/pallets/randomness-beacon/Cargo.toml
+++ b/pallets/randomness-beacon/Cargo.toml
@@ -18,30 +18,29 @@ targets = ["x86_64-unknown-linux-gnu"]
 codec.workspace = true
 scale-info.workspace = true
 serde = { workspace = true, features = ["derive"] }
-sha2 = { version = "0.10.8" }
+sha2.workspace = true
 log.workspace = true
-hex = { version = "0.4", features = ["serde"], default-features = false }
+hex.workspace = true
 # frame deps
 frame-benchmarking = { workspace = true, optional = true }
 frame-support.workspace = true
 frame-system.workspace = true
-sc-consensus-randomness-beacon = { path = "../../client/consensus/randomness-beacon", default-features = false }
-sp-consensus-randomness-beacon = { path = "../../primitives/consensus/randomness-beacon", default-features = false }
+sc-consensus-randomness-beacon.workspace = true
+sp-consensus-randomness-beacon.workspace = true
 sp-core.workspace = true
 sp-io.workspace = true
 sp-runtime.workspace = true
-# arkworks dependencies
+# crypto dependencies
 sp-ark-bls12-381 = { workspace = true, optional = true }
 ark-bls12-381.workspace = true
 ark-serialize.workspace = true
 ark-ec.workspace = true
 ark-std.workspace = true
-timelock = { version = "0.0.1", default-features = false }
-
+timelock.workspace = true
 
 [dev-dependencies]
-sp-keystore = { version = "0.40.0",  default-features = false }
-sp-inherents = { version = "34.0.0" }
+sp-keystore.workspace = true
+sp-inherents.workspace = true
 
 [features]
 default = ["std"]
diff --git a/primitives/consensus/randomness-beacon/Cargo.toml b/primitives/consensus/randomness-beacon/Cargo.toml
index 6afbd9a3..c7dbd1c8 100644
--- a/primitives/consensus/randomness-beacon/Cargo.toml
+++ b/primitives/consensus/randomness-beacon/Cargo.toml
@@ -16,9 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 async-trait = { optional = true, workspace = true }
-codec.workspace = true
-scale-info.workspace = true
-serde = { workspace = true, features = ["derive"] }
 sp-inherents.workspace = true
 
 
@@ -30,11 +27,5 @@ tokio.workspace = true
 default = ["std"]
 std = [
 	"async-trait",
-	"codec/std",
 	"sp-inherents/std",
-	"scale-info/std",
-	"serde/std"
 ]
-
-# Serde support without relying on std features.
-serde = ["scale-info/serde"]
diff --git a/primitives/consensus/randomness-beacon/src/inherents.rs b/primitives/consensus/randomness-beacon/src/inherents.rs
index 03332b02..34a61ca9 100644
--- a/primitives/consensus/randomness-beacon/src/inherents.rs
+++ b/primitives/consensus/randomness-beacon/src/inherents.rs
@@ -84,4 +84,13 @@ mod tests {
 		let data = inherent_data.get_data::<Vec<Vec<u8>>>(&INHERENT_IDENTIFIER).unwrap().unwrap();
 		assert_eq!(extra_data, data)
 	}
+
+	#[tokio::test]
+	pub async fn try_handle_error_returns_none() {
+		let extra_data = vec![vec![1]];
+		let mut inherent_data = InherentData::new();
+		let provider = RandInherentDataProvider::new(extra_data.clone());
+		let res = provider.try_handle_error(&[1u8; 8], &vec![1]).await;
+		assert!(res.is_none());
+	}
 }


Reviews:
user: login: juangirini
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T08:34:32Z
user: login: juangirini
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T08:43:24Z
user: login: driemworks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T13:38:59Z
user: login: driemworks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:15:48Z
user: login: carloskiron
body: @driemworks looks good overall imo. Nice tests, my only comment would be to differentiate those tests with actual dependency to the drand network to be more like integration or e2e tests
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T15:34:58Z
user: login: colemanirby
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T19:06:57Z
user: login: colemanirby
body: Looks good to me. The tarpaulin stuff has been removed in the most recent PR.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T19:08:07Z
user: login: driemworks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T20:25:38Z
user: login: juangirini
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T09:28:26Z
user: login: driemworks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T13:29:44Z

Review comments:
diff_hunk: @@ -156,36 +139,31 @@ impl GossipsubNetwork {
 			.expect("The behaviour is well defined.")
 			.build();
 
-		Ok(Self { swarm, state })
+		// fallback to a randomly assigned open port if one was not provided
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: juangirini
body: I had the same comment a while ago :D this is more like a hard-coded fallback rather than a random value
created_at: 2025-03-05T08:34:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: juangirini
body: This is quite a lot of waiting, why is that? and why did you need to increase it?
created_at: 2025-03-05T08:43:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: driemworks
body: This test actually connects to drand's  gossipsub. Waiting 7 seconds ensure we get enough pulses. It's probably not necessary to wait that long but it provides a strong guarantee that we would have consumed at least two messages from the topic.
created_at: 2025-03-05T13:38:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -156,36 +139,31 @@ impl GossipsubNetwork {
 			.expect("The behaviour is well defined.")
 			.build();
 
-		Ok(Self { swarm, state })
+		// fallback to a randomly assigned open port if one was not provided
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: driemworks
body: resolving, as discussed
created_at: 2025-03-06T22:15:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: colemanirby
body: Is this something that we could do with a mock?
created_at: 2025-03-07T19:06:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: driemworks
body: It can be mocked, but I still think it's good to have an e2e test that consumes from the real drand.
created_at: 2025-03-07T20:25:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: juangirini
body: We should avoid flaky tests, relaying on third party services makes tests non-deterministic.
Even on e2e tests it's preferable to have third party interactions mocked as @colemanirby has suggested.
Testing that we can ingest from drand looks more like a smoke test to me, and could be tested on isolation of the rest of the e2e journey. Maybe right before deployment. Also as critical as it is for IDN to consume from Drand, this should be monitored on every ingestion on testnet/mainnet 
created_at: 2025-03-10T09:28:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,29 +368,54 @@ mod tests {
 				.parse()
 				.unwrap();
 
-		let (mut gossipsub, _s) = build_node();
-
-		let res = gossipsub.run("test", vec![], Some(&fake_listen_addr)).await;
+		let local_identity: Keypair = Keypair::generate_ed25519();
+		let gossipsub_config = GossipsubConfig::default();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let res =
+			GossipsubNetwork::new(&local_identity, gossipsub_config, tx, Some(&fake_listen_addr));
 		assert!(res.is_err());
 		assert!(matches!(res, Err(Error::SwarmListenFailure)), "Expected SwarmListenFailure error");
 	}
 
 	#[tokio::test]
-	async fn can_fail_to_subscribe_to_topic_with_no_peers() {
+	async fn test_gossipsub_network_listen_failure() {
+		let key = Keypair::generate_ed25519();
+		let (tx, _rx) = tracing_unbounded("drand-notification-channel", 100000);
+		let config = GossipsubConfig::default();
+		let invalid_addr: Multiaddr = Multiaddr::empty();
+
+		let result = GossipsubNetwork::new(&key, config, tx, Some(&invalid_addr));
+		assert!(result.is_err(), "Expected failure due to invalid listen address");
+	}
+
+	#[cfg(feature = "e2e ")]
+	#[tokio::test]
+	async fn can_subscribe_to_topic_and_deserialize_pulses_when_peers_connected() {
 		let topic_str: &str =
 			"/drand/pubsub/v0.0.0/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
 
-		let (mut gossipsub, state) = build_node();
+		let maddr1: libp2p::Multiaddr =
+			"/ip4/184.72.27.233/tcp/44544/p2p/12D3KooWBhAkxEn3XE7QanogjGrhyKBMC5GeM3JUTqz54HqS6VHG"
+				.parse()
+				.expect("The string is a well-formatted multiaddress. qed.");
+
+		let maddr2: libp2p::Multiaddr =
+        "/ip4/54.193.191.250/tcp/44544/p2p/12D3KooWQqDi3D3KLfDjWATQUUE4o5aSshwBFi9JM36wqEPMPD5y"
+            .parse()
+            .expect("The string is a well-formatted multiaddress. qed.");
+
+		let (mut gossipsub, mut rx) = build_node();
+
 		tokio::spawn(async move {
-			if let Err(e) = gossipsub.run(topic_str, vec![], None).await {
+			if let Err(e) = gossipsub.run(topic_str, vec![maddr1, maddr2]).await {
 				log::error!("Failed to run gossipsub network: {:?}", e);
 			}
 		});
 
 		// Sleep for 6 secs
-		sleep(Duration::from_millis(6000)).await;
-		let data_lock = state.lock().unwrap();
-		let pulses = &data_lock.pulses;
-		assert!(pulses.len() == 0);
+		sleep(Duration::from_millis(7000)).await;
path: client/consensus/randomness-beacon/src/gossipsub.rs
user: login: driemworks
body: test removed
created_at: 2025-03-10T13:29:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: driemworks
created_at: 2025-03-07T20:07:59Z
body: @colemanirby  Gotcha, I removed the changes to the tarpaulin config.
author_association: CONTRIBUTOR
user: login: juangirini
created_at: 2025-03-10T09:39:56Z
body: > @driemworks looks good overall imo. Nice tests, my only comment would be to differentiate those tests with actual dependency to the drand network to be more like integration or e2e tests

I don't see why would we need to verify that drand produces pulses every time we run our tests, even if it is an e2e test. Interacting with the real dependency instead of a mock does not allow us to test different scenarios.

Checking that drand produces pulses as part of our monitoring service does make sense. We could have that on testnet and mainnet.

If we really want to have that test as part of our test suite we could have it as a smote test to run before deployment, just to make sure that we haven't added any typo to the topic or something like that.

cc @driemworks 
author_association: CONTRIBUTOR
user: login: driemworks
created_at: 2025-03-10T13:15:24Z
body: The test has been removed: https://github.com/ideal-lab5/idn-sdk/issues/131
author_association: CONTRIBUTOR