mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-05-30 15:51:06 +00:00
Squashed diff from mh-backend-shard
This commit is contained in:
Generated
+175
-28
@@ -11,7 +11,7 @@ dependencies = [
|
|||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix_derive",
|
"actix_derive",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -33,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "90673465c6187bd0829116b02be465dc0195a74d7719f76ffff0effef934a92e"
|
checksum = "90673465c6187bd0829116b02be465dc0195a74d7719f76ffff0effef934a92e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
@@ -54,9 +54,9 @@ dependencies = [
|
|||||||
"actix-tls",
|
"actix-tls",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash",
|
"ahash",
|
||||||
"base64",
|
"base64 0.13.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
@@ -195,7 +195,7 @@ dependencies = [
|
|||||||
"actix-web-codegen",
|
"actix-web-codegen",
|
||||||
"ahash",
|
"ahash",
|
||||||
"awc",
|
"awc",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"either",
|
"either",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
@@ -224,7 +224,7 @@ dependencies = [
|
|||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
@@ -273,6 +273,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -281,9 +287,9 @@ checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
@@ -312,8 +318,8 @@ dependencies = [
|
|||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
"base64",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -334,6 +340,12 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
|
checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@@ -394,6 +406,12 @@ version = "1.3.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -406,7 +424,7 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d"
|
checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -648,6 +666,21 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@@ -655,6 +688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
|
checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -663,12 +697,35 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
|
checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.13"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
|
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@@ -690,13 +747,17 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
|
checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro-nested",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -738,7 +799,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
|
checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -787,7 +848,7 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
|
checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
@@ -798,7 +859,7 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737"
|
checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"http",
|
"http",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite 0.2.6",
|
||||||
]
|
]
|
||||||
@@ -821,7 +882,7 @@ version = "0.14.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
|
checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -845,7 +906,7 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"hyper",
|
"hyper",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1106,9 +1167,9 @@ checksum = "2ac6fe3538f701e339953a3ebbe4f39941aababa8a3f6964635b24ab526daeac"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-scale-codec"
|
name = "parity-scale-codec"
|
||||||
version = "2.0.1"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cd3dab59b5cf4bc81069ade0fc470341a1ef3ad5fa73e5a8943bed2ec12b2e8"
|
checksum = "e0f518afaa5a47d0d6386229b0a6e01e86427291d643aa4cabb4992219f504f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitvec",
|
"bitvec",
|
||||||
@@ -1240,6 +1301,12 @@ version = "0.5.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-nested"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@@ -1384,8 +1451,8 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4"
|
checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1556,6 +1623,51 @@ version = "0.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shard"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"actix",
|
||||||
|
"actix-http",
|
||||||
|
"actix-web",
|
||||||
|
"actix-web-actors",
|
||||||
|
"anyhow",
|
||||||
|
"bincode",
|
||||||
|
"bytes 1.0.1",
|
||||||
|
"clap",
|
||||||
|
"log",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"shared",
|
||||||
|
"simple_logger",
|
||||||
|
"soketto",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tokio-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"actix",
|
||||||
|
"actix-http",
|
||||||
|
"actix-web",
|
||||||
|
"actix-web-actors",
|
||||||
|
"bincode",
|
||||||
|
"bytes 1.0.1",
|
||||||
|
"fnv",
|
||||||
|
"hex",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
"primitive-types",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -1602,6 +1714,21 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soketto"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5c71ed3d54db0a699f4948e1bb3e45b450fa31fe602621dee6680361d569c88"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.12.3",
|
||||||
|
"bytes 0.5.6",
|
||||||
|
"futures",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
"rand 0.7.3",
|
||||||
|
"sha-1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "standback"
|
name = "standback"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -1698,22 +1825,18 @@ dependencies = [
|
|||||||
"actix-web",
|
"actix-web",
|
||||||
"actix-web-actors",
|
"actix-web-actors",
|
||||||
"bincode",
|
"bincode",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"ctor",
|
"ctor",
|
||||||
"fnv",
|
|
||||||
"hex",
|
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"primitive-types",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"shared",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"thiserror",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1839,7 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
|
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
"mio",
|
||||||
@@ -1848,9 +1971,21 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite 0.2.6",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
|
"tokio-macros",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-native-tls"
|
name = "tokio-native-tls"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -1861,14 +1996,26 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite 0.2.6",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
|
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes 1.0.1",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite 0.2.6",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"core",
|
"core",
|
||||||
|
"shared",
|
||||||
|
"shard",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
@@ -13,15 +13,11 @@ actix-http = "3.0.0-beta.4"
|
|||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
bytes = "1.0.1"
|
bytes = "1.0.1"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
fnv = "1.0.7"
|
|
||||||
hex = "0.4.3"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = ["raw_value"] }
|
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||||
thiserror = "1.0.24"
|
shared = { path = "../shared" }
|
||||||
primitive-types = { version = "0.9.0", features = ["serde"] }
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simple_logger = "1.11.0"
|
simple_logger = "1.11.0"
|
||||||
num-traits = "0.2"
|
|
||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
reqwest = { version = "0.11.1", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.1", features = ["blocking", "json"] }
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web_actors::ws::{CloseCode, CloseReason};
|
|
||||||
use ctor::ctor;
|
use ctor::ctor;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use crate::shard::connector::ShardConnector;
|
||||||
use crate::chain::{self, Chain, ChainId, Label};
|
use crate::chain::{self, Chain, ChainId, Label};
|
||||||
use crate::feed::connector::{Connected, FeedConnector, FeedId};
|
use crate::feed::connector::{Connected, FeedConnector, FeedId};
|
||||||
use crate::feed::{self, FeedMessageSerializer};
|
use crate::feed::{self, FeedMessageSerializer};
|
||||||
use crate::node::connector::{Mute, NodeConnector};
|
use crate::node::connector::NodeConnector;
|
||||||
use crate::types::{ConnId, NodeDetails};
|
use shared::ws::MuteReason;
|
||||||
use crate::util::{DenseMap, Hash};
|
use shared::shard::ShardConnId;
|
||||||
|
use shared::types::{ConnId, NodeDetails};
|
||||||
|
use shared::util::{DenseMap, Hash};
|
||||||
|
|
||||||
pub struct Aggregator {
|
pub struct Aggregator {
|
||||||
genesis_hashes: HashMap<Hash, ChainId>,
|
genesis_hashes: HashMap<Hash, ChainId>,
|
||||||
@@ -124,10 +126,24 @@ pub struct AddNode {
|
|||||||
pub node: NodeDetails,
|
pub node: NodeDetails,
|
||||||
/// Genesis [`Hash`] of the chain the node is being added to.
|
/// Genesis [`Hash`] of the chain the node is being added to.
|
||||||
pub genesis_hash: Hash,
|
pub genesis_hash: Hash,
|
||||||
|
/// Source from which this node is being added (Direct | Shard)
|
||||||
|
pub source: NodeSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum NodeSource {
|
||||||
|
Direct {
|
||||||
/// Connection id used by the node connector for multiplexing parachains
|
/// Connection id used by the node connector for multiplexing parachains
|
||||||
pub conn_id: ConnId,
|
conn_id: ConnId,
|
||||||
/// Address of the NodeConnector actor
|
/// Address of the NodeConnector actor
|
||||||
pub node_connector: Addr<NodeConnector>,
|
node_connector: Addr<NodeConnector>,
|
||||||
|
},
|
||||||
|
// TODO
|
||||||
|
Shard {
|
||||||
|
/// `ShardConnId` that identifies the node connection within a shard.
|
||||||
|
sid: ShardConnId,
|
||||||
|
/// Address to the ShardConnector actor
|
||||||
|
shard_connector: Addr<ShardConnector>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message sent from the Chain to the Aggregator when the Chain loses all nodes
|
/// Message sent from the Chain to the Aggregator when the Chain loses all nodes
|
||||||
@@ -183,25 +199,36 @@ pub struct NodeCount(pub ChainId, pub usize);
|
|||||||
#[rtype(result = "usize")]
|
#[rtype(result = "usize")]
|
||||||
pub struct GetHealth;
|
pub struct GetHealth;
|
||||||
|
|
||||||
|
impl NodeSource {
|
||||||
|
pub fn mute(&self, reason: MuteReason) {
|
||||||
|
match self {
|
||||||
|
NodeSource::Direct { node_connector, .. } => {
|
||||||
|
node_connector.do_send(reason);
|
||||||
|
},
|
||||||
|
// TODO
|
||||||
|
NodeSource::Shard { shard_connector, .. } => {
|
||||||
|
// shard_connector.do_send(Mute { reason });
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler<AddNode> for Aggregator {
|
impl Handler<AddNode> for Aggregator {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
||||||
if self.denylist.contains(&*msg.node.chain) {
|
if self.denylist.contains(&*msg.node.chain) {
|
||||||
log::warn!(target: "Aggregator::AddNode", "'{}' is on the denylist.", msg.node.chain);
|
log::warn!(target: "Aggregator::AddNode", "'{}' is on the denylist.", msg.node.chain);
|
||||||
let AddNode { node_connector, .. } = msg;
|
|
||||||
let reason = CloseReason {
|
msg.source.mute(MuteReason::Denied);
|
||||||
code: CloseCode::Abnormal,
|
|
||||||
description: Some("Denied".into()),
|
|
||||||
};
|
|
||||||
node_connector.do_send(Mute { reason });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let AddNode {
|
let AddNode {
|
||||||
node,
|
node,
|
||||||
genesis_hash,
|
genesis_hash,
|
||||||
conn_id,
|
source,
|
||||||
node_connector,
|
// conn_id,
|
||||||
|
// node_connector,
|
||||||
} = msg;
|
} = msg;
|
||||||
log::trace!(target: "Aggregator::AddNode", "New node connected. Chain '{}'", node.chain);
|
log::trace!(target: "Aggregator::AddNode", "New node connected. Chain '{}'", node.chain);
|
||||||
|
|
||||||
@@ -213,16 +240,12 @@ impl Handler<AddNode> for Aggregator {
|
|||||||
if chain.nodes < chain.max_nodes {
|
if chain.nodes < chain.max_nodes {
|
||||||
chain.addr.do_send(chain::AddNode {
|
chain.addr.do_send(chain::AddNode {
|
||||||
node,
|
node,
|
||||||
conn_id,
|
source,
|
||||||
node_connector,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log::warn!(target: "Aggregator::AddNode", "Chain {} is over quota ({})", chain.label, chain.max_nodes);
|
log::warn!(target: "Aggregator::AddNode", "Chain {} is over quota ({})", chain.label, chain.max_nodes);
|
||||||
let reason = CloseReason {
|
|
||||||
code: CloseCode::Again,
|
source.mute(MuteReason::Overquota);
|
||||||
description: Some("Overquota".into()),
|
|
||||||
};
|
|
||||||
node_connector.do_send(Mute { reason });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-76
@@ -3,16 +3,13 @@ use rustc_hash::FxHashMap;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::aggregator::{Aggregator, DropChain, NodeCount, RenameChain};
|
use crate::aggregator::{Aggregator, DropChain, NodeCount, NodeSource, RenameChain};
|
||||||
use crate::feed::connector::{FeedConnector, FeedId, Subscribed, Unsubscribed};
|
use crate::feed::connector::{FeedConnector, FeedId, Subscribed, Unsubscribed};
|
||||||
use crate::feed::{self, FeedMessageSerializer};
|
use crate::feed::{self, FeedMessageSerializer};
|
||||||
use crate::node::{
|
use crate::node::Node;
|
||||||
connector::{Initialize, NodeConnector},
|
use shared::types::{Block, NodeDetails, NodeId, NodeLocation, Timestamp};
|
||||||
message::Payload,
|
use shared::util::{now, DenseMap, NumStats};
|
||||||
Node,
|
use shared::node::Payload;
|
||||||
};
|
|
||||||
use crate::types::{Block, BlockNumber, ConnId, NodeDetails, NodeId, NodeLocation, Timestamp};
|
|
||||||
use crate::util::{now, DenseMap, NumStats};
|
|
||||||
|
|
||||||
const STALE_TIMEOUT: u64 = 2 * 60 * 1000; // 2 minutes
|
const STALE_TIMEOUT: u64 = 2 * 60 * 1000; // 2 minutes
|
||||||
|
|
||||||
@@ -204,10 +201,8 @@ impl Actor for Chain {
|
|||||||
pub struct AddNode {
|
pub struct AddNode {
|
||||||
/// Details of the node being added to the aggregator
|
/// Details of the node being added to the aggregator
|
||||||
pub node: NodeDetails,
|
pub node: NodeDetails,
|
||||||
/// Connection id used by the node connector for multiplexing parachains
|
/// Source from which this node is being added (Direct | Shard)
|
||||||
pub conn_id: ConnId,
|
pub source: NodeSource,
|
||||||
/// Address of the NodeConnector actor to which we send [`Initialize`] or [`Mute`] messages.
|
|
||||||
pub node_connector: Addr<NodeConnector>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message sent from the NodeConnector to the Chain when it receives new telemetry data
|
/// Message sent from the NodeConnector to the Chain when it receives new telemetry data
|
||||||
@@ -249,14 +244,38 @@ pub struct LocateNode {
|
|||||||
pub location: Arc<NodeLocation>,
|
pub location: Arc<NodeLocation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NodeSource {
|
||||||
|
pub fn init(self, nid: NodeId, chain: Addr<Chain>) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeSource::Direct { conn_id, node_connector } => {
|
||||||
|
node_connector
|
||||||
|
.try_send(crate::node::connector::Initialize {
|
||||||
|
nid,
|
||||||
|
conn_id,
|
||||||
|
chain,
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
},
|
||||||
|
NodeSource::Shard { sid, shard_connector } => {
|
||||||
|
shard_connector
|
||||||
|
.try_send(crate::shard::connector::Initialize {
|
||||||
|
nid,
|
||||||
|
sid,
|
||||||
|
chain,
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler<AddNode> for Chain {
|
impl Handler<AddNode> for Chain {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
||||||
let AddNode {
|
let AddNode {
|
||||||
node,
|
node,
|
||||||
conn_id,
|
source,
|
||||||
node_connector,
|
|
||||||
} = msg;
|
} = msg;
|
||||||
log::trace!(target: "Chain::AddNode", "New node connected. Chain '{}', node count goes from {} to {}", node.chain, self.nodes.len(), self.nodes.len() + 1);
|
log::trace!(target: "Chain::AddNode", "New node connected. Chain '{}', node count goes from {} to {}", node.chain, self.nodes.len(), self.nodes.len() + 1);
|
||||||
self.increment_label_count(&node.chain);
|
self.increment_label_count(&node.chain);
|
||||||
@@ -264,14 +283,7 @@ impl Handler<AddNode> for Chain {
|
|||||||
let nid = self.nodes.add(Node::new(node));
|
let nid = self.nodes.add(Node::new(node));
|
||||||
let chain = ctx.address();
|
let chain = ctx.address();
|
||||||
|
|
||||||
if node_connector
|
if source.init(nid, chain) {
|
||||||
.try_send(Initialize {
|
|
||||||
nid,
|
|
||||||
conn_id,
|
|
||||||
chain,
|
|
||||||
})
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
self.nodes.remove(nid);
|
self.nodes.remove(nid);
|
||||||
} else if let Some(node) = self.nodes.get(nid) {
|
} else if let Some(node) = self.nodes.get(nid) {
|
||||||
self.serializer.push(feed::AddedNode(nid, node));
|
self.serializer.push(feed::AddedNode(nid, node));
|
||||||
@@ -355,60 +367,60 @@ impl Handler<UpdateNode> for Chain {
|
|||||||
self.serializer.push(feed::NodeIOUpdate(nid, io));
|
self.serializer.push(feed::NodeIOUpdate(nid, io));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Payload::AfgAuthoritySet(authority) => {
|
// Payload::AfgAuthoritySet(authority) => {
|
||||||
node.set_validator_address(authority.authority_id.clone());
|
// node.set_validator_address(authority.authority_id.clone());
|
||||||
self.broadcast();
|
// self.broadcast();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
Payload::AfgFinalized(finalized) => {
|
// Payload::AfgFinalized(finalized) => {
|
||||||
if let Ok(finalized_number) = finalized.finalized_number.parse::<BlockNumber>()
|
// if let Ok(finalized_number) = finalized.finalized_number.parse::<BlockNumber>()
|
||||||
{
|
// {
|
||||||
if let Some(addr) = node.details().validator.clone() {
|
// if let Some(addr) = node.details().validator.clone() {
|
||||||
self.serializer.push(feed::AfgFinalized(
|
// self.serializer.push(feed::AfgFinalized(
|
||||||
addr,
|
// addr,
|
||||||
finalized_number,
|
// finalized_number,
|
||||||
finalized.finalized_hash,
|
// finalized.finalized_hash,
|
||||||
));
|
// ));
|
||||||
self.broadcast_finality();
|
// self.broadcast_finality();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
Payload::AfgReceivedPrecommit(precommit) => {
|
// Payload::AfgReceivedPrecommit(precommit) => {
|
||||||
if let Ok(finalized_number) =
|
// if let Ok(finalized_number) =
|
||||||
precommit.received.target_number.parse::<BlockNumber>()
|
// precommit.received.target_number.parse::<BlockNumber>()
|
||||||
{
|
// {
|
||||||
if let Some(addr) = node.details().validator.clone() {
|
// if let Some(addr) = node.details().validator.clone() {
|
||||||
let voter = precommit.received.voter.clone();
|
// let voter = precommit.received.voter.clone();
|
||||||
self.serializer.push(feed::AfgReceivedPrecommit(
|
// self.serializer.push(feed::AfgReceivedPrecommit(
|
||||||
addr,
|
// addr,
|
||||||
finalized_number,
|
// finalized_number,
|
||||||
precommit.received.target_hash,
|
// precommit.received.target_hash,
|
||||||
voter,
|
// voter,
|
||||||
));
|
// ));
|
||||||
self.broadcast_finality();
|
// self.broadcast_finality();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
Payload::AfgReceivedPrevote(prevote) => {
|
// Payload::AfgReceivedPrevote(prevote) => {
|
||||||
if let Ok(finalized_number) =
|
// if let Ok(finalized_number) =
|
||||||
prevote.received.target_number.parse::<BlockNumber>()
|
// prevote.received.target_number.parse::<BlockNumber>()
|
||||||
{
|
// {
|
||||||
if let Some(addr) = node.details().validator.clone() {
|
// if let Some(addr) = node.details().validator.clone() {
|
||||||
let voter = prevote.received.voter.clone();
|
// let voter = prevote.received.voter.clone();
|
||||||
self.serializer.push(feed::AfgReceivedPrevote(
|
// self.serializer.push(feed::AfgReceivedPrevote(
|
||||||
addr,
|
// addr,
|
||||||
finalized_number,
|
// finalized_number,
|
||||||
prevote.received.target_hash,
|
// prevote.received.target_hash,
|
||||||
voter,
|
// voter,
|
||||||
));
|
// ));
|
||||||
self.broadcast_finality();
|
// self.broadcast_finality();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
Payload::AfgReceivedCommit(_) => {}
|
// Payload::AfgReceivedCommit(_) => {}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-23
@@ -3,20 +3,33 @@ use serde::Serialize;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
use crate::types::{
|
|
||||||
Address, BlockDetails, BlockHash, BlockNumber, NodeHardware, NodeIO, NodeId, NodeStats,
|
|
||||||
Timestamp,
|
|
||||||
};
|
|
||||||
use serde_json::to_writer;
|
use serde_json::to_writer;
|
||||||
|
use shared::types::{
|
||||||
|
Address, BlockDetails, BlockHash, BlockNumber, NodeHardware, NodeIO, NodeId, NodeStats,
|
||||||
|
Timestamp, NodeDetails,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
|
|
||||||
use connector::Serialized;
|
use connector::Serialized;
|
||||||
|
|
||||||
pub trait FeedMessage: Serialize {
|
pub trait FeedMessage {
|
||||||
const ACTION: u8;
|
const ACTION: u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait FeedMessageWrite: FeedMessage {
|
||||||
|
fn write_to_feed(&self, ser: &mut FeedMessageSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FeedMessageWrite for T
|
||||||
|
where
|
||||||
|
T: FeedMessage + Serialize,
|
||||||
|
{
|
||||||
|
fn write_to_feed(&self, ser: &mut FeedMessageSerializer) {
|
||||||
|
ser.write(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FeedMessageSerializer {
|
pub struct FeedMessageSerializer {
|
||||||
/// Current buffer,
|
/// Current buffer,
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
@@ -33,7 +46,7 @@ impl FeedMessageSerializer {
|
|||||||
|
|
||||||
pub fn push<Message>(&mut self, msg: Message)
|
pub fn push<Message>(&mut self, msg: Message)
|
||||||
where
|
where
|
||||||
Message: FeedMessage,
|
Message: FeedMessageWrite,
|
||||||
{
|
{
|
||||||
let glue = match self.buffer.len() {
|
let glue = match self.buffer.len() {
|
||||||
0 => b'[',
|
0 => b'[',
|
||||||
@@ -41,9 +54,16 @@ impl FeedMessageSerializer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.buffer.push(glue);
|
self.buffer.push(glue);
|
||||||
let _ = to_writer(&mut self.buffer, &Message::ACTION);
|
self.write(&Message::ACTION);
|
||||||
self.buffer.push(b',');
|
self.buffer.push(b',');
|
||||||
let _ = to_writer(&mut self.buffer, &msg);
|
msg.write_to_feed(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<S>(&mut self, value: &S)
|
||||||
|
where
|
||||||
|
S: Serialize,
|
||||||
|
{
|
||||||
|
let _ = to_writer(&mut self.buffer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(&mut self) -> Option<Serialized> {
|
pub fn finalize(&mut self) -> Option<Serialized> {
|
||||||
@@ -175,21 +195,28 @@ pub struct AfgAuthoritySet(
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct StaleNode(pub NodeId);
|
pub struct StaleNode(pub NodeId);
|
||||||
|
|
||||||
impl Serialize for AddedNode<'_> {
|
impl FeedMessageWrite for AddedNode<'_> {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn write_to_feed(&self, ser: &mut FeedMessageSerializer) {
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let AddedNode(nid, node) = self;
|
let AddedNode(nid, node) = self;
|
||||||
let mut tup = serializer.serialize_tuple(8)?;
|
|
||||||
tup.serialize_element(nid)?;
|
let details = node.details();
|
||||||
tup.serialize_element(node.details())?;
|
let details = (
|
||||||
tup.serialize_element(node.stats())?;
|
&details.name,
|
||||||
tup.serialize_element(node.io())?;
|
&details.implementation,
|
||||||
tup.serialize_element(node.hardware())?;
|
&details.version,
|
||||||
tup.serialize_element(node.block_details())?;
|
&details.validator,
|
||||||
tup.serialize_element(&node.location())?;
|
&details.network_id,
|
||||||
tup.serialize_element(&node.startup_time())?;
|
);
|
||||||
tup.end()
|
|
||||||
|
ser.write(&(
|
||||||
|
nid,
|
||||||
|
details,
|
||||||
|
node.stats(),
|
||||||
|
node.io(),
|
||||||
|
node.hardware(),
|
||||||
|
node.block_details(),
|
||||||
|
&node.location(),
|
||||||
|
&node.startup_time(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::aggregator::{Aggregator, Connect, Disconnect, NoMoreFinality, SendFinality, Subscribe};
|
use crate::aggregator::{Aggregator, Connect, Disconnect, NoMoreFinality, SendFinality, Subscribe};
|
||||||
use crate::chain::Unsubscribe;
|
use crate::chain::Unsubscribe;
|
||||||
use crate::feed::{FeedMessageSerializer, Pong};
|
use crate::feed::{FeedMessageSerializer, Pong};
|
||||||
use crate::util::fnv;
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use shared::util::fnv;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
pub type FeedId = usize;
|
pub type FeedId = usize;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use rustc_hash::FxHashMap;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::chain::{Chain, LocateNode};
|
use crate::chain::{Chain, LocateNode};
|
||||||
use crate::types::{NodeId, NodeLocation};
|
use shared::types::{NodeId, NodeLocation};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Locator {
|
pub struct Locator {
|
||||||
@@ -12,21 +12,20 @@ use simple_logger::SimpleLogger;
|
|||||||
mod aggregator;
|
mod aggregator;
|
||||||
mod chain;
|
mod chain;
|
||||||
mod feed;
|
mod feed;
|
||||||
|
mod location;
|
||||||
mod node;
|
mod node;
|
||||||
mod shard;
|
mod shard;
|
||||||
mod types;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
use aggregator::{Aggregator, GetHealth};
|
use aggregator::{Aggregator, GetHealth};
|
||||||
use feed::connector::FeedConnector;
|
use feed::connector::FeedConnector;
|
||||||
|
use location::{Locator, LocatorFactory};
|
||||||
use node::connector::NodeConnector;
|
use node::connector::NodeConnector;
|
||||||
use shard::connector::ShardConnector;
|
use shard::connector::ShardConnector;
|
||||||
use util::{Locator, LocatorFactory};
|
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
const NAME: &str = "Substrate Telemetry Backend";
|
const NAME: &str = "Substrate Telemetry Backend Core";
|
||||||
const ABOUT: &str = "This is the Telemetry Backend that injects and provide the data sent by Substrate/Polkadot nodes";
|
const ABOUT: &str = "This is the Telemetry Backend Core that injects and provide the data sent by Substrate/Polkadot nodes";
|
||||||
|
|
||||||
#[derive(Clap, Debug)]
|
#[derive(Clap, Debug)]
|
||||||
#[clap(name = NAME, version = VERSION, author = AUTHORS, about = ABOUT)]
|
#[clap(name = NAME, version = VERSION, author = AUTHORS, about = ABOUT)]
|
||||||
@@ -109,17 +108,21 @@ async fn shard_route(
|
|||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
stream: web::Payload,
|
stream: web::Payload,
|
||||||
aggregator: web::Data<Addr<Aggregator>>,
|
aggregator: web::Data<Addr<Aggregator>>,
|
||||||
|
locator: web::Data<Addr<Locator>>,
|
||||||
path: web::Path<Box<str>>,
|
path: web::Path<Box<str>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let hash_str = path.into_inner();
|
let hash_str = path.into_inner();
|
||||||
let genesis_hash = hash_str.parse()?;
|
let genesis_hash = hash_str.parse()?;
|
||||||
|
|
||||||
|
println!("Genesis hash {}", genesis_hash);
|
||||||
|
|
||||||
let mut res = ws::handshake(&req)?;
|
let mut res = ws::handshake(&req)?;
|
||||||
|
|
||||||
let aggregator = aggregator.get_ref().clone();
|
let aggregator = aggregator.get_ref().clone();
|
||||||
|
let locator = locator.get_ref().clone().recipient();
|
||||||
|
|
||||||
Ok(res.streaming(ws::WebsocketContext::with_codec(
|
Ok(res.streaming(ws::WebsocketContext::with_codec(
|
||||||
ShardConnector::new(aggregator, genesis_hash),
|
ShardConnector::new(aggregator, locator, genesis_hash),
|
||||||
stream,
|
stream,
|
||||||
Codec::new().max_size(10 * 1024 * 1024), // 10mb frame limit
|
Codec::new().max_size(10 * 1024 * 1024), // 10mb frame limit
|
||||||
)))
|
)))
|
||||||
@@ -171,7 +174,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let aggregator = Aggregator::new(denylist).start();
|
let aggregator = Aggregator::new(denylist).start();
|
||||||
let factory = LocatorFactory::new();
|
let factory = LocatorFactory::new();
|
||||||
let locator = SyncArbiter::start(4, move || factory.create());
|
let locator = SyncArbiter::start(4, move || factory.create());
|
||||||
log::info!("Starting telemetry version: {}", env!("CARGO_PKG_VERSION"));
|
log::info!("Starting Telemetry Core version: {}", env!("CARGO_PKG_VERSION"));
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::NormalizePath::default())
|
.wrap(middleware::NormalizePath::default())
|
||||||
@@ -179,6 +182,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.data(locator.clone())
|
.data(locator.clone())
|
||||||
.service(node_route)
|
.service(node_route)
|
||||||
.service(feed_route)
|
.service(feed_route)
|
||||||
|
.service(shard_route)
|
||||||
.service(health)
|
.service(health)
|
||||||
})
|
})
|
||||||
.bind(opts.socket)?
|
.bind(opts.socket)?
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::types::{
|
use shared::types::{
|
||||||
Block, BlockDetails, NodeDetails, NodeHardware, NodeIO, NodeId, NodeLocation, NodeStats,
|
Block, BlockDetails, NodeDetails, NodeHardware, NodeIO, NodeId, NodeLocation, NodeStats,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
};
|
};
|
||||||
use crate::util::now;
|
use shared::util::now;
|
||||||
|
use shared::node::SystemInterval;
|
||||||
|
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
pub mod message;
|
|
||||||
|
|
||||||
use message::SystemInterval;
|
|
||||||
|
|
||||||
/// Minimum time between block below broadcasting updates to the browser gets throttled, in ms.
|
/// Minimum time between block below broadcasting updates to the browser gets throttled, in ms.
|
||||||
const THROTTLE_THRESHOLD: u64 = 100;
|
const THROTTLE_THRESHOLD: u64 = 100;
|
||||||
|
|||||||
@@ -1,25 +1,22 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::mem;
|
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::aggregator::{AddNode, Aggregator};
|
use crate::aggregator::{AddNode, Aggregator, NodeSource};
|
||||||
use crate::chain::{Chain, RemoveNode, UpdateNode};
|
use crate::chain::{Chain, RemoveNode, UpdateNode};
|
||||||
use crate::node::message::{NodeMessage, Payload};
|
use crate::location::LocateRequest;
|
||||||
use crate::node::NodeId;
|
use crate::node::NodeId;
|
||||||
use crate::types::ConnId;
|
|
||||||
use crate::util::LocateRequest;
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_http::ws::Item;
|
|
||||||
use actix_web_actors::ws::{self, CloseReason};
|
use actix_web_actors::ws::{self, CloseReason};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::Bytes;
|
||||||
|
use shared::types::ConnId;
|
||||||
|
use shared::ws::{MultipartHandler, WsMessage, MuteReason};
|
||||||
|
use shared::node::{NodeMessage, Payload};
|
||||||
|
|
||||||
/// How often heartbeat pings are sent
|
/// How often heartbeat pings are sent
|
||||||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20);
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20);
|
||||||
/// How long before lack of client response causes a timeout
|
/// How long before lack of client response causes a timeout
|
||||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
/// Continuation buffer limit, 10mb
|
|
||||||
const CONT_BUF_LIMIT: usize = 10 * 1024 * 1024;
|
|
||||||
|
|
||||||
pub struct NodeConnector {
|
pub struct NodeConnector {
|
||||||
/// Multiplexing connections by id
|
/// Multiplexing connections by id
|
||||||
@@ -32,8 +29,8 @@ pub struct NodeConnector {
|
|||||||
ip: Option<Ipv4Addr>,
|
ip: Option<Ipv4Addr>,
|
||||||
/// Actix address of location services
|
/// Actix address of location services
|
||||||
locator: Recipient<LocateRequest>,
|
locator: Recipient<LocateRequest>,
|
||||||
/// Buffer for constructing continuation messages
|
/// Helper for handling continuation messages
|
||||||
contbuf: BytesMut,
|
multipart: MultipartHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnMultiplex {
|
enum ConnMultiplex {
|
||||||
@@ -85,7 +82,7 @@ impl NodeConnector {
|
|||||||
aggregator,
|
aggregator,
|
||||||
ip,
|
ip,
|
||||||
locator,
|
locator,
|
||||||
contbuf: BytesMut::new(),
|
multipart: MultipartHandler::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +120,10 @@ impl NodeConnector {
|
|||||||
self.aggregator.do_send(AddNode {
|
self.aggregator.do_send(AddNode {
|
||||||
node: connected.node,
|
node: connected.node,
|
||||||
genesis_hash: connected.genesis_hash,
|
genesis_hash: connected.genesis_hash,
|
||||||
|
source: NodeSource::Direct {
|
||||||
conn_id,
|
conn_id,
|
||||||
node_connector: ctx.address(),
|
node_connector: ctx.address(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if backlog.len() >= 10 {
|
if backlog.len() >= 10 {
|
||||||
@@ -136,42 +135,14 @@ impl NodeConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_frame(&mut self, bytes: &[u8]) {
|
|
||||||
if !self.contbuf.is_empty() {
|
|
||||||
log::error!("Unused continuation buffer");
|
|
||||||
self.contbuf.clear();
|
|
||||||
}
|
|
||||||
self.continue_frame(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn continue_frame(&mut self, bytes: &[u8]) {
|
impl Handler<MuteReason> for NodeConnector {
|
||||||
if self.contbuf.len() + bytes.len() <= CONT_BUF_LIMIT {
|
|
||||||
self.contbuf.extend_from_slice(&bytes);
|
|
||||||
} else {
|
|
||||||
log::error!("Continuation buffer overflow");
|
|
||||||
self.contbuf = BytesMut::new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_frame(&mut self) -> Bytes {
|
|
||||||
mem::replace(&mut self.contbuf, BytesMut::new()).freeze()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Message)]
|
|
||||||
#[rtype(result = "()")]
|
|
||||||
pub struct Mute {
|
|
||||||
pub reason: CloseReason,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<Mute> for NodeConnector {
|
|
||||||
type Result = ();
|
type Result = ();
|
||||||
fn handle(&mut self, msg: Mute, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: MuteReason, ctx: &mut Self::Context) {
|
||||||
let Mute { reason } = msg;
|
log::debug!(target: "NodeConnector::Mute", "Muting a node. Reason: {:?}", msg);
|
||||||
log::debug!(target: "NodeConnector::Mute", "Muting a node. Reason: {:?}", reason.description);
|
|
||||||
|
|
||||||
ctx.close(Some(reason));
|
ctx.close(Some(msg.into()));
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,34 +192,18 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for NodeConnector {
|
|||||||
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
|
|
||||||
let data = match msg {
|
let data = match msg.map(|msg| self.multipart.handle(msg)) {
|
||||||
Ok(ws::Message::Ping(msg)) => {
|
Ok(WsMessage::Nop) => return,
|
||||||
|
Ok(WsMessage::Ping(msg)) => {
|
||||||
ctx.pong(&msg);
|
ctx.pong(&msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(ws::Message::Pong(_)) => return,
|
Ok(WsMessage::Data(data)) => data,
|
||||||
Ok(ws::Message::Text(text)) => text.into_bytes(),
|
Ok(WsMessage::Close(reason)) => {
|
||||||
Ok(ws::Message::Binary(data)) => data,
|
|
||||||
Ok(ws::Message::Close(reason)) => {
|
|
||||||
ctx.close(reason);
|
ctx.close(reason);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(ws::Message::Nop) => return,
|
|
||||||
Ok(ws::Message::Continuation(cont)) => match cont {
|
|
||||||
Item::FirstText(bytes) | Item::FirstBinary(bytes) => {
|
|
||||||
self.start_frame(&bytes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Item::Continue(bytes) => {
|
|
||||||
self.continue_frame(&bytes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Item::Last(bytes) => {
|
|
||||||
self.continue_frame(&bytes);
|
|
||||||
self.finish_frame()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!("{:?}", error);
|
log::error!("{:?}", error);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
|
|||||||
@@ -1,13 +1 @@
|
|||||||
use crate::node::message::Payload;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
|
|
||||||
/// Alias for the ID of the node connection
|
|
||||||
type ShardConnId = usize;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct ShardMessage {
|
|
||||||
pub conn_id: ShardConnId,
|
|
||||||
pub payload: Payload,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
use std::mem;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
use crate::aggregator::{AddNode, Aggregator};
|
use crate::aggregator::{AddNode, Aggregator, NodeSource};
|
||||||
use crate::chain::{Chain, RemoveNode, UpdateNode};
|
use crate::chain::{Chain, RemoveNode, UpdateNode};
|
||||||
use crate::shard::ShardMessage;
|
use crate::location::LocateRequest;
|
||||||
use crate::types::NodeId;
|
|
||||||
use crate::util::{DenseMap, Hash};
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_http::ws::Item;
|
|
||||||
use actix_web_actors::ws::{self, CloseReason};
|
use actix_web_actors::ws::{self, CloseReason};
|
||||||
use bincode::Options;
|
use bincode::Options;
|
||||||
use bytes::{Bytes, BytesMut};
|
use shared::types::NodeId;
|
||||||
|
use shared::util::Hash;
|
||||||
|
use shared::ws::{MultipartHandler, WsMessage};
|
||||||
|
use shared::shard::{ShardMessage, ShardConnId, BackendMessage};
|
||||||
|
|
||||||
/// How often heartbeat pings are sent
|
/// How often heartbeat pings are sent
|
||||||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20);
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20);
|
||||||
/// How long before lack of client response causes a timeout
|
/// How long before lack of client response causes a timeout
|
||||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
/// Continuation buffer limit, 10mb
|
|
||||||
const CONT_BUF_LIMIT: usize = 10 * 1024 * 1024;
|
|
||||||
|
|
||||||
pub struct ShardConnector {
|
pub struct ShardConnector {
|
||||||
/// Client must send ping at least once every 60 seconds (CLIENT_TIMEOUT),
|
/// Client must send ping at least once every 60 seconds (CLIENT_TIMEOUT),
|
||||||
@@ -26,12 +25,16 @@ pub struct ShardConnector {
|
|||||||
aggregator: Addr<Aggregator>,
|
aggregator: Addr<Aggregator>,
|
||||||
/// Genesis hash of the chain this connection will be submitting data for
|
/// Genesis hash of the chain this connection will be submitting data for
|
||||||
genesis_hash: Hash,
|
genesis_hash: Hash,
|
||||||
/// Chain address to which this multiplex connector is delegating messages
|
/// Chain address to which this shard connector is delegating messages
|
||||||
chain: Option<Addr<Chain>>,
|
chain: Option<Addr<Chain>>,
|
||||||
/// Mapping `ShardConnId` to `NodeId`
|
/// Transient mapping of `ShardConnId` to external IP address.
|
||||||
nodes: DenseMap<NodeId>,
|
ips: BTreeMap<ShardConnId, Ipv4Addr>,
|
||||||
/// Buffer for constructing continuation messages
|
/// Mapping of `ShardConnId` to initialized `NodeId`s.
|
||||||
contbuf: BytesMut,
|
nodes: BTreeMap<ShardConnId, NodeId>,
|
||||||
|
/// Actix address of location services
|
||||||
|
locator: Recipient<LocateRequest>,
|
||||||
|
/// Container for handling continuation messages
|
||||||
|
multipart: MultipartHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for ShardConnector {
|
impl Actor for ShardConnector {
|
||||||
@@ -43,7 +46,7 @@ impl Actor for ShardConnector {
|
|||||||
|
|
||||||
fn stopped(&mut self, _: &mut Self::Context) {
|
fn stopped(&mut self, _: &mut Self::Context) {
|
||||||
if let Some(ref chain) = self.chain {
|
if let Some(ref chain) = self.chain {
|
||||||
for (_, nid) in self.nodes.iter() {
|
for nid in self.nodes.values() {
|
||||||
chain.do_send(RemoveNode(*nid))
|
chain.do_send(RemoveNode(*nid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,17 +54,31 @@ impl Actor for ShardConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShardConnector {
|
impl ShardConnector {
|
||||||
pub fn new(aggregator: Addr<Aggregator>, genesis_hash: Hash) -> Self {
|
pub fn new(
|
||||||
|
aggregator: Addr<Aggregator>,
|
||||||
|
locator: Recipient<LocateRequest>,
|
||||||
|
genesis_hash: Hash,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
hb: Instant::now(),
|
hb: Instant::now(),
|
||||||
aggregator,
|
aggregator,
|
||||||
genesis_hash,
|
genesis_hash,
|
||||||
chain: None,
|
chain: None,
|
||||||
nodes: DenseMap::new(),
|
ips: BTreeMap::new(),
|
||||||
contbuf: BytesMut::new(),
|
nodes: BTreeMap::new(),
|
||||||
|
locator,
|
||||||
|
multipart: MultipartHandler::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shard_send(msg: BackendMessage, ctx: &mut <Self as Actor>::Context) {
|
||||||
|
let bytes = bincode::options().serialize(&msg).expect("Must be able to serialize to vec; qed");
|
||||||
|
|
||||||
|
println!("Sending back {} bytes", bytes.len());
|
||||||
|
|
||||||
|
ctx.binary(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
fn heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
|
fn heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
|
||||||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||||
// check client heartbeats
|
// check client heartbeats
|
||||||
@@ -77,30 +94,66 @@ impl ShardConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(&mut self, msg: ShardMessage, ctx: &mut <Self as Actor>::Context) {
|
fn handle_message(&mut self, msg: ShardMessage, ctx: &mut <Self as Actor>::Context) {
|
||||||
let ShardMessage { conn_id, payload } = msg;
|
println!("{:?}", msg);
|
||||||
|
|
||||||
// TODO: get `NodeId` for `ShardConnId` and proxy payload to `self.chain`.
|
match msg {
|
||||||
|
ShardMessage::AddNode { ip, node, sid } => {
|
||||||
|
if let Some(ip) = ip {
|
||||||
|
self.ips.insert(sid, ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_frame(&mut self, bytes: &[u8]) {
|
self.aggregator.do_send(AddNode {
|
||||||
if !self.contbuf.is_empty() {
|
node,
|
||||||
log::error!("Unused continuation buffer");
|
genesis_hash: self.genesis_hash,
|
||||||
self.contbuf.clear();
|
source: NodeSource::Shard {
|
||||||
|
sid,
|
||||||
|
shard_connector: ctx.address(),
|
||||||
}
|
}
|
||||||
self.continue_frame(bytes);
|
});
|
||||||
|
},
|
||||||
|
ShardMessage::UpdateNode { nid, payload } => {
|
||||||
|
if let Some(chain) = self.chain.as_ref() {
|
||||||
|
chain.do_send(UpdateNode {
|
||||||
|
nid,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn continue_frame(&mut self, bytes: &[u8]) {
|
|
||||||
if self.contbuf.len() + bytes.len() <= CONT_BUF_LIMIT {
|
|
||||||
self.contbuf.extend_from_slice(&bytes);
|
|
||||||
} else {
|
|
||||||
log::error!("Continuation buffer overflow");
|
|
||||||
self.contbuf = BytesMut::new();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_frame(&mut self) -> Bytes {
|
#[derive(Message)]
|
||||||
mem::replace(&mut self.contbuf, BytesMut::new()).freeze()
|
#[rtype(result = "()")]
|
||||||
|
pub struct Initialize {
|
||||||
|
pub nid: NodeId,
|
||||||
|
pub sid: ShardConnId,
|
||||||
|
pub chain: Addr<Chain>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Initialize> for ShardConnector {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Initialize, ctx: &mut Self::Context) {
|
||||||
|
let Initialize {
|
||||||
|
nid,
|
||||||
|
sid,
|
||||||
|
chain,
|
||||||
|
} = msg;
|
||||||
|
log::trace!(target: "ShardConnector::Initialize", "Initializing a node, nid={}, on conn_id={}", nid, 0);
|
||||||
|
|
||||||
|
if self.chain.is_none() {
|
||||||
|
self.chain = Some(chain.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let be_msg = BackendMessage::Initialize { sid, nid };
|
||||||
|
|
||||||
|
Self::shard_send(be_msg, ctx);
|
||||||
|
|
||||||
|
// Acquire the node's physical location
|
||||||
|
if let Some(ip) = self.ips.remove(&sid) {
|
||||||
|
let _ = self.locator.do_send(LocateRequest { ip, nid, chain });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,34 +161,18 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for ShardConnector {
|
|||||||
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
|
|
||||||
let data = match msg {
|
let data = match msg.map(|msg| self.multipart.handle(msg)) {
|
||||||
Ok(ws::Message::Ping(msg)) => {
|
Ok(WsMessage::Nop) => return,
|
||||||
|
Ok(WsMessage::Ping(msg)) => {
|
||||||
ctx.pong(&msg);
|
ctx.pong(&msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(ws::Message::Pong(_)) => return,
|
Ok(WsMessage::Data(data)) => data,
|
||||||
Ok(ws::Message::Text(text)) => text.into_bytes(),
|
Ok(WsMessage::Close(reason)) => {
|
||||||
Ok(ws::Message::Binary(data)) => data,
|
|
||||||
Ok(ws::Message::Close(reason)) => {
|
|
||||||
ctx.close(reason);
|
ctx.close(reason);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(ws::Message::Nop) => return,
|
|
||||||
Ok(ws::Message::Continuation(cont)) => match cont {
|
|
||||||
Item::FirstText(bytes) | Item::FirstBinary(bytes) => {
|
|
||||||
self.start_frame(&bytes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Item::Continue(bytes) => {
|
|
||||||
self.continue_frame(&bytes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Item::Last(bytes) => {
|
|
||||||
self.continue_frame(&bytes);
|
|
||||||
self.finish_frame()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!("{:?}", error);
|
log::error!("{:?}", error);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
@@ -145,12 +182,12 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for ShardConnector {
|
|||||||
|
|
||||||
match bincode::options().deserialize(&data) {
|
match bincode::options().deserialize(&data) {
|
||||||
Ok(msg) => self.handle_message(msg, ctx),
|
Ok(msg) => self.handle_message(msg, ctx),
|
||||||
#[cfg(debug)]
|
// #[cfg(debug)]
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("Failed to parse shard message: {}", err,)
|
log::warn!("Failed to parse shard message: {}", err,)
|
||||||
}
|
}
|
||||||
#[cfg(not(debug))]
|
// #[cfg(not(debug))]
|
||||||
Err(_) => (),
|
// Err(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
use std::fmt::{self, Debug, Display};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use actix_web::error::ResponseError;
|
|
||||||
use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
|
|
||||||
|
|
||||||
const HASH_BYTES: usize = 32;
|
|
||||||
|
|
||||||
/// Newtype wrapper for 32-byte hash values, implementing readable `Debug` and `serde::Deserialize`.
|
|
||||||
// We could use primitive_types::H256 here, but opted for a custom type to avoid more dependencies.
|
|
||||||
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct Hash([u8; HASH_BYTES]);
|
|
||||||
|
|
||||||
struct HashVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for HashVisitor {
|
|
||||||
type Value = Hash;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("hexidecimal string of 32 bytes beginning with 0x")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
value
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| de::Error::invalid_value(Unexpected::Str(value), &self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Hash {
|
|
||||||
type Err = HashParseError;
|
|
||||||
|
|
||||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
||||||
if !value.starts_with("0x") {
|
|
||||||
return Err(HashParseError::InvalidPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut hash = [0; HASH_BYTES];
|
|
||||||
|
|
||||||
hex::decode_to_slice(&value[2..], &mut hash).map_err(HashParseError::HexError)?;
|
|
||||||
|
|
||||||
Ok(Hash(hash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Hash {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Hash, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_str(HashVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Hash {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str("0x")?;
|
|
||||||
|
|
||||||
let mut ascii = [0; HASH_BYTES * 2];
|
|
||||||
|
|
||||||
hex::encode_to_slice(self.0, &mut ascii)
|
|
||||||
.expect("Encoding 32 bytes into 64 bytes of ascii; qed");
|
|
||||||
|
|
||||||
f.write_str(std::str::from_utf8(&ascii).expect("ASCII hex encoded bytes canot fail; qed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Hash {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum HashParseError {
|
|
||||||
HexError(hex::FromHexError),
|
|
||||||
InvalidPrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for HashParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
Debug::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseError for HashParseError {}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "shard"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies Ltd. <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix = "0.11.1"
|
||||||
|
actix-web = { version = "4.0.0-beta.4", default-features = false }
|
||||||
|
actix-web-actors = "4.0.0-beta.3"
|
||||||
|
actix-http = "3.0.0-beta.4"
|
||||||
|
anyhow = "1.0.40"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
bytes = "1.0.1"
|
||||||
|
clap = "3.0.0-beta.2"
|
||||||
|
log = "0.4"
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
simple_logger = "1.11.0"
|
||||||
|
soketto = "0.4.2"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
tokio-util = { version = "0.6", features = ["compat"] }
|
||||||
|
tokio-stream = { version = "0.1", features = ["net"] }
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::fmt;
|
||||||
|
// use std::sync::mpsc::{self, Sender};
|
||||||
|
|
||||||
|
use actix::prelude::*;
|
||||||
|
use actix_http::http::Uri;
|
||||||
|
use bincode::Options;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use shared::util::{Hash, DenseMap};
|
||||||
|
use shared::types::{ConnId, NodeDetails, NodeId};
|
||||||
|
use shared::node::Payload;
|
||||||
|
use shared::shard::{ShardConnId, ShardMessage, BackendMessage};
|
||||||
|
use soketto::handshake::{Client, ServerResponse};
|
||||||
|
use crate::node::{NodeConnector, Initialize};
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio::sync::mpsc::{self, UnboundedSender};
|
||||||
|
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
|
||||||
|
|
||||||
|
type WsSender = soketto::Sender<Compat<TcpStream>>;
|
||||||
|
type WsReceiver = soketto::Receiver<Compat<TcpStream>>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Aggregator {
|
||||||
|
url: Uri,
|
||||||
|
chains: FxHashMap<Hash, UnboundedSender<ChainMessage>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for Aggregator {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aggregator {
|
||||||
|
pub fn new(url: Uri) -> Self {
|
||||||
|
Aggregator {
|
||||||
|
url,
|
||||||
|
chains: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chain {
|
||||||
|
/// Base URL of Backend Core
|
||||||
|
url: Uri,
|
||||||
|
/// Genesis hash of the chain, required to construct the URL to connect to the Backend Core
|
||||||
|
genesis_hash: Hash,
|
||||||
|
/// Dense mapping of SharedConnId -> Addr<NodeConnector> + multiplexing ConnId sent from the node.
|
||||||
|
nodes: DenseMap<(Addr<NodeConnector>, ConnId)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chain {
|
||||||
|
pub fn new(url: Uri, genesis_hash: Hash) -> Self {
|
||||||
|
Chain {
|
||||||
|
url,
|
||||||
|
genesis_hash,
|
||||||
|
nodes: DenseMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn(mut self) -> UnboundedSender<ChainMessage> {
|
||||||
|
let (tx_ret, mut rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
let tx = tx_ret.clone();
|
||||||
|
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
let mut sender = match self.connect(tx.clone()).await {
|
||||||
|
Ok(pair) => pair,
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to connect to Backend Core: {:?}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// tokio::task::spawn(async move {
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match rx.recv().await {
|
||||||
|
Some(ChainMessage::AddNode(msg)) => {
|
||||||
|
println!("Add node {:?}", msg);
|
||||||
|
|
||||||
|
let AddNode { node, ip, conn_id, node_connector, .. } = msg;
|
||||||
|
let sid = self.nodes.add((node_connector, conn_id)) as ShardConnId;
|
||||||
|
|
||||||
|
let bytes = bincode::options().serialize(&ShardMessage::AddNode {
|
||||||
|
ip,
|
||||||
|
node,
|
||||||
|
sid,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
println!("Sending {} bytes", bytes.len());
|
||||||
|
|
||||||
|
let _ = sender.send_binary_mut(bytes).await;
|
||||||
|
let _ = sender.flush().await;
|
||||||
|
},
|
||||||
|
Some(ChainMessage::UpdateNode(nid, payload)) => {
|
||||||
|
let msg = ShardMessage::UpdateNode {
|
||||||
|
nid,
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Serialize {:?}", msg);
|
||||||
|
|
||||||
|
let bytes = bincode::options().serialize(&msg).unwrap();
|
||||||
|
|
||||||
|
println!("Sending update: {} bytes", bytes.len());
|
||||||
|
|
||||||
|
let _ = sender.send_binary_mut(bytes).await;
|
||||||
|
let _ = sender.flush().await;
|
||||||
|
},
|
||||||
|
Some(ChainMessage::Backend(BackendMessage::Initialize { sid, nid })) => {
|
||||||
|
if let Some((addr, conn_id)) = self.nodes.get(sid as usize) {
|
||||||
|
addr.do_send(Initialize {
|
||||||
|
nid,
|
||||||
|
conn_id: *conn_id,
|
||||||
|
chain: tx.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(ChainMessage::Backend(BackendMessage::Mute { sid, reason })) => {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// let mut client = Client::new(socket.compat(), host, &path);
|
||||||
|
|
||||||
|
// let (mut sender, mut receiver) = match client.handshake().await? {
|
||||||
|
// ServerResponse::Accepted { .. } => client.into_builder().finish(),
|
||||||
|
// ServerResponse::Redirect { status_code, location } => unimplemented!("follow location URL"),
|
||||||
|
// ServerResponse::Rejected { status_code } => unimplemented!("handle failure")
|
||||||
|
// };
|
||||||
|
});
|
||||||
|
|
||||||
|
tx_ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect(&self, tx: UnboundedSender<ChainMessage>) -> anyhow::Result<WsSender> {
|
||||||
|
let host = self.url.host().unwrap_or("127.0.0.1");
|
||||||
|
let port = self.url.port_u16().unwrap_or(8000);
|
||||||
|
let path = format!("{}{}", self.url.path(), self.genesis_hash);
|
||||||
|
|
||||||
|
let socket = TcpStream::connect((host, port)).await?;
|
||||||
|
|
||||||
|
socket.set_nodelay(true).unwrap();
|
||||||
|
|
||||||
|
let mut client = Client::new(socket.compat(), host, &path);
|
||||||
|
|
||||||
|
let (sender, receiver) = match client.handshake().await? {
|
||||||
|
ServerResponse::Accepted { .. } => client.into_builder().finish(),
|
||||||
|
ServerResponse::Redirect { status_code, .. } |
|
||||||
|
ServerResponse::Rejected { status_code } => {
|
||||||
|
return Err(anyhow::anyhow!("Failed to connect, status code: {}", status_code));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn read(tx: UnboundedSender<ChainMessage>, mut receiver: WsReceiver) -> anyhow::Result<()> {
|
||||||
|
let mut data = Vec::with_capacity(128);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
data.clear();
|
||||||
|
|
||||||
|
receiver.receive_data(&mut data).await?;
|
||||||
|
|
||||||
|
println!("Received {} bytes from Backend Core", data.len());
|
||||||
|
|
||||||
|
match bincode::options().deserialize(&data) {
|
||||||
|
Ok(msg) => tx.send(ChainMessage::Backend(msg))?,
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to read message from Backend Core: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::task::spawn(read(tx, receiver));
|
||||||
|
|
||||||
|
Ok(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for Chain {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct AddNode {
|
||||||
|
pub ip: Option<Ipv4Addr>,
|
||||||
|
pub genesis_hash: Hash,
|
||||||
|
pub node: NodeDetails,
|
||||||
|
pub conn_id: ConnId,
|
||||||
|
pub node_connector: Addr<NodeConnector>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ChainMessage {
|
||||||
|
AddNode(AddNode),
|
||||||
|
UpdateNode(NodeId, Payload),
|
||||||
|
Backend(BackendMessage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AddNode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("AddNode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<AddNode> for Aggregator {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
||||||
|
let AddNode { genesis_hash, .. } = msg;
|
||||||
|
|
||||||
|
let url = &self.url;
|
||||||
|
let chain = self
|
||||||
|
.chains
|
||||||
|
.entry(genesis_hash)
|
||||||
|
.or_insert_with(move || Chain::new(url.clone(), genesis_hash).spawn());
|
||||||
|
|
||||||
|
if let Err(err) = chain.send(ChainMessage::AddNode(msg)) {
|
||||||
|
let msg = err.0;
|
||||||
|
log::error!("Failed to add node to chain, shutting down chain");
|
||||||
|
self.chains.remove(&genesis_hash);
|
||||||
|
// TODO: Send a message back to clean up node connections
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<AddNode> for Chain {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: AddNode, ctx: &mut Self::Context) {
|
||||||
|
let AddNode { ip, node_connector, .. } = msg;
|
||||||
|
|
||||||
|
println!("Node connected to {}: {:?}", self.genesis_hash, ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use actix::prelude::*;
|
||||||
|
use actix_http::ws::Codec;
|
||||||
|
use actix_http::http::Uri;
|
||||||
|
use actix_web::{get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
use actix_web_actors::ws;
|
||||||
|
use clap::Clap;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
mod aggregator;
|
||||||
|
mod node;
|
||||||
|
|
||||||
|
use aggregator::Aggregator;
|
||||||
|
use node::NodeConnector;
|
||||||
|
|
||||||
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
|
const NAME: &str = "Substrate Telemetry Backend Shard";
|
||||||
|
const ABOUT: &str = "This is the Telemetry Backend Shard that forwards the data sent by Substrate/Polkadot nodes to the Backend Core";
|
||||||
|
|
||||||
|
#[derive(Clap, Debug)]
|
||||||
|
#[clap(name = NAME, version = VERSION, author = AUTHORS, about = ABOUT)]
|
||||||
|
struct Opts {
|
||||||
|
#[clap(
|
||||||
|
short = 'l',
|
||||||
|
long = "listen",
|
||||||
|
default_value = "127.0.0.1:8001",
|
||||||
|
about = "This is the socket address Telemetry is listening to. This is restricted to localhost (127.0.0.1) by default and should be fine for most use cases. If you are using Telemetry in a container, you likely want to set this to '0.0.0.0:8000'"
|
||||||
|
)]
|
||||||
|
socket: std::net::SocketAddr,
|
||||||
|
#[clap(
|
||||||
|
arg_enum,
|
||||||
|
required = false,
|
||||||
|
long = "log",
|
||||||
|
default_value = "info",
|
||||||
|
about = "Log level."
|
||||||
|
)]
|
||||||
|
log_level: LogLevel,
|
||||||
|
#[clap(
|
||||||
|
short = 'c',
|
||||||
|
long = "core",
|
||||||
|
default_value = "ws://127.0.0.1:8000/shard_submit/",
|
||||||
|
about = "Url to the Backend Core endpoint accepting shard connections"
|
||||||
|
)]
|
||||||
|
core_url: Uri,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clap, Debug, PartialEq)]
|
||||||
|
enum LogLevel {
|
||||||
|
Error,
|
||||||
|
Warn,
|
||||||
|
Info,
|
||||||
|
Debug,
|
||||||
|
Trace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&LogLevel> for log::LevelFilter {
|
||||||
|
fn from(log_level: &LogLevel) -> Self {
|
||||||
|
match log_level {
|
||||||
|
LogLevel::Error => log::LevelFilter::Error,
|
||||||
|
LogLevel::Warn => log::LevelFilter::Warn,
|
||||||
|
LogLevel::Info => log::LevelFilter::Info,
|
||||||
|
LogLevel::Debug => log::LevelFilter::Debug,
|
||||||
|
LogLevel::Trace => log::LevelFilter::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entry point for connecting nodes
|
||||||
|
#[get("/submit")]
|
||||||
|
async fn node_route(
|
||||||
|
req: HttpRequest,
|
||||||
|
stream: web::Payload,
|
||||||
|
aggregator: web::Data<Addr<Aggregator>>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let ip = req
|
||||||
|
.connection_info()
|
||||||
|
.realip_remote_addr()
|
||||||
|
.and_then(|mut addr| {
|
||||||
|
if let Some(port_idx) = addr.find(':') {
|
||||||
|
addr = &addr[..port_idx];
|
||||||
|
}
|
||||||
|
addr.parse::<Ipv4Addr>().ok()
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut res = ws::handshake(&req)?;
|
||||||
|
let aggregator = aggregator.get_ref().clone();
|
||||||
|
|
||||||
|
Ok(res.streaming(ws::WebsocketContext::with_codec(
|
||||||
|
NodeConnector::new(aggregator, ip),
|
||||||
|
stream,
|
||||||
|
Codec::new().max_size(10 * 1024 * 1024), // 10mb frame limit
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Telemetry entry point. Listening by default on 127.0.0.1:8000.
|
||||||
|
/// This can be changed using the `PORT` and `BIND` ENV variables.
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
let opts = Opts::parse();
|
||||||
|
let log_level = &opts.log_level;
|
||||||
|
SimpleLogger::new()
|
||||||
|
.with_level(log_level.into())
|
||||||
|
.init()
|
||||||
|
.expect("Must be able to start a logger");
|
||||||
|
|
||||||
|
println!("URL? {:?} {:?}", opts.core_url.host(), opts.core_url.port_u16());
|
||||||
|
|
||||||
|
let aggregator = Aggregator::new(opts.core_url).start();
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Starting Telemetry Shard version: {}",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
);
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.wrap(middleware::NormalizePath::default())
|
||||||
|
.data(aggregator.clone())
|
||||||
|
.service(node_route)
|
||||||
|
})
|
||||||
|
.bind(opts.socket)?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::aggregator::{AddNode, Aggregator, ChainMessage};
|
||||||
|
// use crate::chain::{Chain, RemoveNode, UpdateNode};
|
||||||
|
use actix::prelude::*;
|
||||||
|
use actix_web_actors::ws::{self, CloseReason};
|
||||||
|
use shared::node::{NodeMessage, Payload};
|
||||||
|
use shared::types::{ConnId, NodeId};
|
||||||
|
use shared::ws::{MultipartHandler, WsMessage};
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
|
/// How often heartbeat pings are sent
|
||||||
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20);
|
||||||
|
/// How long before lack of client response causes a timeout
|
||||||
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
|
pub struct NodeConnector {
|
||||||
|
/// Multiplexing connections by id
|
||||||
|
multiplex: BTreeMap<ConnId, ConnMultiplex>,
|
||||||
|
/// Client must send ping at least once every 60 seconds (CLIENT_TIMEOUT),
|
||||||
|
hb: Instant,
|
||||||
|
/// Aggregator actor address
|
||||||
|
aggregator: Addr<Aggregator>,
|
||||||
|
/// IP address of the node this connector is responsible for
|
||||||
|
ip: Option<Ipv4Addr>,
|
||||||
|
/// Helper for handling continuation messages
|
||||||
|
multipart: MultipartHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnMultiplex {
|
||||||
|
Connected {
|
||||||
|
/// Id of the node this multiplex connector is responsible for handling
|
||||||
|
nid: NodeId,
|
||||||
|
/// Chain address to which this multiplex connector is delegating messages
|
||||||
|
chain: UnboundedSender<ChainMessage>,
|
||||||
|
},
|
||||||
|
Waiting {
|
||||||
|
/// Backlog of messages to be sent once we get a recipient handle to the chain
|
||||||
|
backlog: Vec<Payload>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConnMultiplex {
|
||||||
|
fn default() -> Self {
|
||||||
|
ConnMultiplex::Waiting {
|
||||||
|
backlog: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for NodeConnector {
|
||||||
|
type Context = ws::WebsocketContext<Self>;
|
||||||
|
|
||||||
|
fn started(&mut self, ctx: &mut Self::Context) {
|
||||||
|
self.heartbeat(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stopped(&mut self, _: &mut Self::Context) {
|
||||||
|
// for mx in self.multiplex.values() {
|
||||||
|
// if let ConnMultiplex::Connected { chain, nid } = mx {
|
||||||
|
// chain.do_send(RemoveNode(*nid));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeConnector {
|
||||||
|
pub fn new(aggregator: Addr<Aggregator>, ip: Option<Ipv4Addr>) -> Self {
|
||||||
|
Self {
|
||||||
|
multiplex: BTreeMap::new(),
|
||||||
|
hb: Instant::now(),
|
||||||
|
aggregator,
|
||||||
|
ip,
|
||||||
|
multipart: MultipartHandler::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
|
||||||
|
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||||
|
// check client heartbeats
|
||||||
|
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
|
||||||
|
// stop actor
|
||||||
|
ctx.close(Some(CloseReason {
|
||||||
|
code: ws::CloseCode::Abnormal,
|
||||||
|
description: Some("Missed heartbeat".into()),
|
||||||
|
}));
|
||||||
|
ctx.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message(
|
||||||
|
&mut self,
|
||||||
|
msg: NodeMessage,
|
||||||
|
ctx: &mut <Self as Actor>::Context,
|
||||||
|
) {
|
||||||
|
let conn_id = msg.id();
|
||||||
|
let payload = msg.into();
|
||||||
|
|
||||||
|
match self.multiplex.entry(conn_id).or_default() {
|
||||||
|
ConnMultiplex::Connected { nid, chain } => {
|
||||||
|
// TODO: error handle
|
||||||
|
let _ = chain.send(ChainMessage::UpdateNode(*nid, payload));
|
||||||
|
}
|
||||||
|
ConnMultiplex::Waiting { backlog } => {
|
||||||
|
if let Payload::SystemConnected(connected) = payload {
|
||||||
|
println!("Node connected {:?}", connected.node);
|
||||||
|
self.aggregator.do_send(AddNode {
|
||||||
|
genesis_hash: connected.genesis_hash,
|
||||||
|
ip: self.ip,
|
||||||
|
node: connected.node,
|
||||||
|
conn_id,
|
||||||
|
node_connector: ctx.address(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if backlog.len() >= 10 {
|
||||||
|
backlog.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
backlog.push(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct Initialize {
|
||||||
|
pub nid: NodeId,
|
||||||
|
pub conn_id: ConnId,
|
||||||
|
pub chain: UnboundedSender<ChainMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Initialize> for NodeConnector {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Initialize, _: &mut Self::Context) {
|
||||||
|
let Initialize {
|
||||||
|
nid,
|
||||||
|
conn_id,
|
||||||
|
chain,
|
||||||
|
} = msg;
|
||||||
|
log::trace!(target: "NodeConnector::Initialize", "Initializing a node, nid={}, on conn_id={}", nid, conn_id);
|
||||||
|
let mx = self.multiplex.entry(conn_id).or_default();
|
||||||
|
|
||||||
|
if let ConnMultiplex::Waiting { backlog } = mx {
|
||||||
|
for payload in backlog.drain(..) {
|
||||||
|
// TODO: error handle.
|
||||||
|
let _ = chain.send(ChainMessage::UpdateNode(nid, payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
*mx = ConnMultiplex::Connected {
|
||||||
|
nid,
|
||||||
|
chain,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for NodeConnector {
|
||||||
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
self.hb = Instant::now();
|
||||||
|
|
||||||
|
let data = match msg.map(|msg| self.multipart.handle(msg)) {
|
||||||
|
Ok(WsMessage::Nop) => return,
|
||||||
|
Ok(WsMessage::Ping(msg)) => {
|
||||||
|
ctx.pong(&msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(WsMessage::Data(data)) => data,
|
||||||
|
Ok(WsMessage::Close(reason)) => {
|
||||||
|
ctx.close(reason);
|
||||||
|
ctx.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("{:?}", error);
|
||||||
|
ctx.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match serde_json::from_slice(&data) {
|
||||||
|
Ok(msg) => self.handle_message(msg, ctx),
|
||||||
|
#[cfg(debug)]
|
||||||
|
Err(err) => {
|
||||||
|
let data: &[u8] = data.get(..512).unwrap_or_else(|| &data);
|
||||||
|
log::warn!(
|
||||||
|
"Failed to parse node message: {} {}",
|
||||||
|
err,
|
||||||
|
std::str::from_utf8(data).unwrap_or_else(|_| "INVALID UTF8")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(not(debug))]
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies Ltd. <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix = "0.11.1"
|
||||||
|
actix-web = { version = "4.0.0-beta.4", default-features = false }
|
||||||
|
actix-web-actors = "4.0.0-beta.3"
|
||||||
|
actix-http = "3.0.0-beta.4"
|
||||||
|
bytes = "1.0.1"
|
||||||
|
fnv = "1.0.7"
|
||||||
|
hex = "0.4.3"
|
||||||
|
log = "0.4"
|
||||||
|
num-traits = "0.2"
|
||||||
|
primitive-types = { version = "0.9.0", features = ["serde"] }
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||||
|
thiserror = "1.0.24"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bincode = "1.3.3"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod node;
|
||||||
|
pub mod shard;
|
||||||
|
pub mod types;
|
||||||
|
pub mod util;
|
||||||
|
pub mod ws;
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
use crate::node::NodeDetails;
|
use crate::types::{Block, BlockHash, BlockNumber, ConnId, NodeDetails};
|
||||||
use crate::types::{Block, BlockHash, BlockNumber, ConnId};
|
use crate::util::{Hash, NullAny};
|
||||||
use crate::util::Hash;
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use serde::de::IgnoredAny;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::Deserialize;
|
use serde::ser::Serializer;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Message)]
|
#[derive(Deserialize, Debug, Message)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
@@ -49,33 +48,52 @@ pub enum Payload {
|
|||||||
#[serde(rename = "notify.finalized")]
|
#[serde(rename = "notify.finalized")]
|
||||||
NotifyFinalized(Finalized),
|
NotifyFinalized(Finalized),
|
||||||
#[serde(rename = "txpool.import")]
|
#[serde(rename = "txpool.import")]
|
||||||
TxPoolImport(IgnoredAny),
|
TxPoolImport(NullAny),
|
||||||
#[serde(rename = "afg.finalized")]
|
// #[serde(rename = "afg.finalized")]
|
||||||
AfgFinalized(AfgFinalized),
|
// AfgFinalized(AfgFinalized),
|
||||||
#[serde(rename = "afg.received_precommit")]
|
// #[serde(rename = "afg.received_precommit")]
|
||||||
AfgReceivedPrecommit(AfgReceivedPrecommit),
|
// AfgReceivedPrecommit(AfgReceivedPrecommit),
|
||||||
#[serde(rename = "afg.received_prevote")]
|
// #[serde(rename = "afg.received_prevote")]
|
||||||
AfgReceivedPrevote(AfgReceivedPrevote),
|
// AfgReceivedPrevote(AfgReceivedPrevote),
|
||||||
#[serde(rename = "afg.received_commit")]
|
// #[serde(rename = "afg.received_commit")]
|
||||||
AfgReceivedCommit(AfgReceivedCommit),
|
// AfgReceivedCommit(AfgReceivedCommit),
|
||||||
#[serde(rename = "afg.authority_set")]
|
// #[serde(rename = "afg.authority_set")]
|
||||||
AfgAuthoritySet(AfgAuthoritySet),
|
// AfgAuthoritySet(AfgAuthoritySet),
|
||||||
#[serde(rename = "afg.finalized_blocks_up_to")]
|
// #[serde(rename = "afg.finalized_blocks_up_to")]
|
||||||
AfgFinalizedBlocksUpTo(IgnoredAny),
|
// AfgFinalizedBlocksUpTo(NullAny),
|
||||||
#[serde(rename = "aura.pre_sealed_block")]
|
// #[serde(rename = "aura.pre_sealed_block")]
|
||||||
AuraPreSealedBlock(IgnoredAny),
|
// AuraPreSealedBlock(NullAny),
|
||||||
#[serde(rename = "prepared_block_for_proposing")]
|
#[serde(rename = "prepared_block_for_proposing")]
|
||||||
PreparedBlockForProposing(IgnoredAny),
|
PreparedBlockForProposing(NullAny),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
impl Serialize for Payload {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
use Payload::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
SystemConnected(val) => serializer.serialize_newtype_variant("Payload", 0, "system.connected", val),
|
||||||
|
SystemInterval(val) => serializer.serialize_newtype_variant("Payload", 1, "system.interval", val),
|
||||||
|
BlockImport(val) => serializer.serialize_newtype_variant("Payload", 3, "block.import", val),
|
||||||
|
NotifyFinalized(val) => serializer.serialize_newtype_variant("Payload", 4, "notify.finalized", val),
|
||||||
|
TxPoolImport(_) => serializer.serialize_unit_variant("Payload", 3, "txpool.import"),
|
||||||
|
PreparedBlockForProposing(_) => serializer.serialize_unit_variant("Payload", 4, "prepared_block_for_proposing"),
|
||||||
|
_ => unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct SystemConnected {
|
pub struct SystemConnected {
|
||||||
pub genesis_hash: Hash,
|
pub genesis_hash: Hash,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub node: NodeDetails,
|
pub node: NodeDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct SystemInterval {
|
pub struct SystemInterval {
|
||||||
pub peers: Option<u64>,
|
pub peers: Option<u64>,
|
||||||
pub txcount: Option<u64>,
|
pub txcount: Option<u64>,
|
||||||
@@ -88,60 +106,51 @@ pub struct SystemInterval {
|
|||||||
pub used_state_cache_size: Option<f32>,
|
pub used_state_cache_size: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Finalized {
|
pub struct Finalized {
|
||||||
#[serde(rename = "best")]
|
#[serde(rename = "best")]
|
||||||
pub hash: BlockHash,
|
pub hash: BlockHash,
|
||||||
pub height: Box<str>,
|
pub height: Box<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct AfgAuthoritySet {
|
pub struct AfgAuthoritySet {
|
||||||
pub authority_id: Box<str>,
|
pub authority_id: Box<str>,
|
||||||
pub authorities: Box<str>,
|
pub authorities: Box<str>,
|
||||||
pub authority_set_id: Box<str>,
|
pub authority_set_id: Box<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct AfgFinalized {
|
pub struct AfgFinalized {
|
||||||
pub finalized_hash: BlockHash,
|
pub finalized_hash: BlockHash,
|
||||||
pub finalized_number: Box<str>,
|
pub finalized_number: Box<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct AfgReceived {
|
pub struct AfgReceived {
|
||||||
pub target_hash: BlockHash,
|
pub target_hash: BlockHash,
|
||||||
pub target_number: Box<str>,
|
pub target_number: Box<str>,
|
||||||
pub voter: Option<Box<str>>,
|
pub voter: Option<Box<str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct AfgReceivedPrecommit {
|
pub struct AfgReceivedPrecommit {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub received: AfgReceived,
|
pub received: AfgReceived,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct AfgReceivedPrevote {
|
pub struct AfgReceivedPrevote {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub received: AfgReceived,
|
pub received: AfgReceived,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct AfgReceivedCommit {
|
pub struct AfgReceivedCommit {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub received: AfgReceived,
|
pub received: AfgReceived,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Block {
|
|
||||||
hash: BlockHash::from([0; 32]),
|
|
||||||
height: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Payload {
|
impl Payload {
|
||||||
pub fn best_block(&self) -> Option<&Block> {
|
pub fn best_block(&self) -> Option<&Block> {
|
||||||
match self {
|
match self {
|
||||||
@@ -169,6 +178,7 @@ impl Payload {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use bincode::Options;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_v1() {
|
fn message_v1() {
|
||||||
@@ -193,4 +203,16 @@ mod tests {
|
|||||||
"message did not match variant V2",
|
"message did not match variant V2",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bincode_block_zero() {
|
||||||
|
let raw = Block::zero();
|
||||||
|
|
||||||
|
let bytes = bincode::options().serialize(&raw).unwrap();
|
||||||
|
|
||||||
|
let deserialized: Block = bincode::options().deserialize(&bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw.hash, deserialized.hash);
|
||||||
|
assert_eq!(raw.height, deserialized.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use crate::ws::MuteReason;
|
||||||
|
use crate::node::Payload;
|
||||||
|
use crate::types::{NodeId, NodeDetails};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Alias for the ID of the node connection
|
||||||
|
pub type ShardConnId = u32;
|
||||||
|
|
||||||
|
/// Message sent from the shard to the backend core
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub enum ShardMessage {
|
||||||
|
/// Get a connection id for a new node, passing IPv4
|
||||||
|
AddNode {
|
||||||
|
ip: Option<Ipv4Addr>,
|
||||||
|
node: NodeDetails,
|
||||||
|
sid: ShardConnId,
|
||||||
|
},
|
||||||
|
/// Send a message payload for a given node
|
||||||
|
UpdateNode {
|
||||||
|
nid: NodeId,
|
||||||
|
payload: Payload,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message sent form the backend core to the shard
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub enum BackendMessage {
|
||||||
|
Initialize {
|
||||||
|
sid: ShardConnId,
|
||||||
|
nid: NodeId,
|
||||||
|
},
|
||||||
|
Mute {
|
||||||
|
sid: ShardConnId,
|
||||||
|
reason: MuteReason,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use serde::ser::{Serialize, SerializeTuple, Serializer};
|
use serde::ser::{SerializeTuple, Serializer};
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::util::{now, MeanList};
|
use crate::util::{now, MeanList};
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ pub type Timestamp = u64;
|
|||||||
pub type Address = Box<str>;
|
pub type Address = Box<str>;
|
||||||
pub use primitive_types::H256 as BlockHash;
|
pub use primitive_types::H256 as BlockHash;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct NodeDetails {
|
pub struct NodeDetails {
|
||||||
pub chain: Box<str>,
|
pub chain: Box<str>,
|
||||||
pub name: Box<str>,
|
pub name: Box<str>,
|
||||||
@@ -32,13 +32,22 @@ pub struct NodeIO {
|
|||||||
pub used_state_cache_size: MeanList<f32>,
|
pub used_state_cache_size: MeanList<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
#[serde(rename = "best")]
|
#[serde(rename = "best")]
|
||||||
pub hash: BlockHash,
|
pub hash: BlockHash,
|
||||||
pub height: BlockNumber,
|
pub height: BlockNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Block {
|
||||||
|
hash: BlockHash::from([0; 32]),
|
||||||
|
height: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct BlockDetails {
|
pub struct BlockDetails {
|
||||||
pub block: Block,
|
pub block: Block,
|
||||||
@@ -75,20 +84,20 @@ pub struct NodeLocation {
|
|||||||
pub city: Box<str>,
|
pub city: Box<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for NodeDetails {
|
// impl Serialize for NodeDetails {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
// where
|
||||||
S: Serializer,
|
// S: Serializer,
|
||||||
{
|
// {
|
||||||
let mut tup = serializer.serialize_tuple(6)?;
|
// let mut tup = serializer.serialize_tuple(6)?;
|
||||||
tup.serialize_element(&self.name)?;
|
// tup.serialize_element(&self.name)?;
|
||||||
tup.serialize_element(&self.implementation)?;
|
// tup.serialize_element(&self.implementation)?;
|
||||||
tup.serialize_element(&self.version)?;
|
// tup.serialize_element(&self.version)?;
|
||||||
tup.serialize_element(&self.validator)?;
|
// tup.serialize_element(&self.validator)?;
|
||||||
tup.serialize_element(&self.network_id)?;
|
// tup.serialize_element(&self.network_id)?;
|
||||||
tup.end()
|
// tup.end()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl Serialize for NodeStats {
|
impl Serialize for NodeStats {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
mod dense_map;
|
mod dense_map;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod location;
|
|
||||||
mod mean_list;
|
mod mean_list;
|
||||||
|
mod null;
|
||||||
mod num_stats;
|
mod num_stats;
|
||||||
|
|
||||||
pub use dense_map::DenseMap;
|
pub use dense_map::DenseMap;
|
||||||
pub use hash::Hash;
|
pub use hash::Hash;
|
||||||
pub use location::{LocateRequest, Locator, LocatorFactory};
|
|
||||||
pub use mean_list::MeanList;
|
pub use mean_list::MeanList;
|
||||||
|
pub use null::NullAny;
|
||||||
pub use num_stats::NumStats;
|
pub use num_stats::NumStats;
|
||||||
|
|
||||||
pub fn fnv<D: AsRef<[u8]>>(data: D) -> u64 {
|
pub fn fnv<D: AsRef<[u8]>>(data: D) -> u64 {
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
use std::fmt::{self, Debug, Display};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use actix_web::error::ResponseError;
|
||||||
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor, SeqAccess};
|
||||||
|
|
||||||
|
const HASH_BYTES: usize = 32;
|
||||||
|
|
||||||
|
/// Newtype wrapper for 32-byte hash values, implementing readable `Debug` and `serde::Deserialize`.
|
||||||
|
// We could use primitive_types::H256 here, but opted for a custom type to avoid more dependencies.
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Hash([u8; HASH_BYTES]);
|
||||||
|
|
||||||
|
struct HashVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for HashVisitor {
|
||||||
|
type Value = Hash;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("byte array of length 32, or hexidecimal string of 32 bytes beginning with 0x")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
value
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| de::Error::invalid_value(Unexpected::Str(value), &self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if value.len() == HASH_BYTES {
|
||||||
|
let mut hash = [0; HASH_BYTES];
|
||||||
|
|
||||||
|
hash.copy_from_slice(value);
|
||||||
|
|
||||||
|
return Ok(Hash(hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
Hash::from_ascii(value)
|
||||||
|
.map_err(|_| de::Error::invalid_value(Unexpected::Bytes(value), &self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut hash = [0u8; HASH_BYTES];
|
||||||
|
|
||||||
|
for (i, byte) in hash.iter_mut().enumerate() {
|
||||||
|
match seq.next_element()? {
|
||||||
|
Some(b) => *byte = b,
|
||||||
|
None => return Err(de::Error::invalid_length(i, &"an array of 32 bytes"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if seq.next_element::<u8>()?.is_some() {
|
||||||
|
return Err(de::Error::invalid_length(33, &"an array of 32 bytes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Hash(hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash {
|
||||||
|
pub fn from_ascii(value: &[u8]) -> Result<Self, HashParseError> {
|
||||||
|
if !value.starts_with(b"0x") {
|
||||||
|
return Err(HashParseError::InvalidPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hash = [0; HASH_BYTES];
|
||||||
|
|
||||||
|
hex::decode_to_slice(&value[2..], &mut hash).map_err(HashParseError::HexError)?;
|
||||||
|
|
||||||
|
Ok(Hash(hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Hash {
|
||||||
|
type Err = HashParseError;
|
||||||
|
|
||||||
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
|
Hash::from_ascii(value.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Hash {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Hash, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_bytes(HashVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Hash {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_bytes(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Hash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("0x")?;
|
||||||
|
|
||||||
|
let mut ascii = [0; HASH_BYTES * 2];
|
||||||
|
|
||||||
|
hex::encode_to_slice(self.0, &mut ascii)
|
||||||
|
.expect("Encoding 32 bytes into 64 bytes of ascii; qed");
|
||||||
|
|
||||||
|
f.write_str(std::str::from_utf8(&ascii).expect("ASCII hex encoded bytes canot fail; qed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Hash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum HashParseError {
|
||||||
|
HexError(hex::FromHexError),
|
||||||
|
InvalidPrefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HashParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
Debug::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for HashParseError {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Hash;
|
||||||
|
use bincode::Options;
|
||||||
|
|
||||||
|
const DUMMY: Hash = {
|
||||||
|
let mut hash = [0; 32];
|
||||||
|
hash[0] = 0xDE;
|
||||||
|
hash[1] = 0xAD;
|
||||||
|
hash[2] = 0xBE;
|
||||||
|
hash[3] = 0xEF;
|
||||||
|
Hash(hash)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_hash_str() {
|
||||||
|
let json = r#""0xdeadBEEF00000000000000000000000000000000000000000000000000000000""#;
|
||||||
|
|
||||||
|
let hash: Hash = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hash, DUMMY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_array() {
|
||||||
|
let json = r#"[222,173,190,239,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#;
|
||||||
|
|
||||||
|
let hash: Hash = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hash, DUMMY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_array_too_short() {
|
||||||
|
let json = r#"[222,173,190,239,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#;
|
||||||
|
|
||||||
|
let res = serde_json::from_str::<Hash>(json);
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_array_too_long() {
|
||||||
|
let json = r#"[222,173,190,239,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#;
|
||||||
|
|
||||||
|
let res = serde_json::from_str::<Hash>(json);
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bincode() {
|
||||||
|
let bytes = bincode::options().serialize(&DUMMY).unwrap();
|
||||||
|
|
||||||
|
let mut expected = [0; 33];
|
||||||
|
|
||||||
|
expected[0] = 32; // length
|
||||||
|
expected[1..].copy_from_slice(&DUMMY.0);
|
||||||
|
|
||||||
|
assert_eq!(bytes, &expected);
|
||||||
|
|
||||||
|
let deserialized: Hash = bincode::options().deserialize(&bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(DUMMY, deserialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
use serde::de::{Deserialize, Deserializer, IgnoredAny};
|
||||||
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
|
||||||
|
/// Alternative to `serde::de::IgnoreAny` that implements `Serialize`.
|
||||||
|
/// Will serialize to `null` in JSON, or empty data in bincode.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct NullAny;
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for NullAny {
|
||||||
|
#[inline]
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<NullAny, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// `bincode` is going to throw an error here as it does not support `IgnoredAny`.
|
||||||
|
//
|
||||||
|
// When using `bincode` `NullAny` will always serialize to unit (aka no data), so
|
||||||
|
// this safely becomes a no-op.
|
||||||
|
let _ = deserializer.deserialize_ignored_any(IgnoredAny);
|
||||||
|
|
||||||
|
Ok(NullAny)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for NullAny {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_unit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::NullAny;
|
||||||
|
use bincode::Options;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
struct Dummy {
|
||||||
|
ignore: NullAny,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_null() {
|
||||||
|
let dummy: Dummy = serde_json::from_str(r#"{"ignore":null}"#).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(dummy, Dummy { ignore: NullAny });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_struct() {
|
||||||
|
let dummy: Dummy = serde_json::from_str(r#"{"ignore":{"foo":"bar"}}"#).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(dummy, Dummy { ignore: NullAny });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_struct_invalid() {
|
||||||
|
let dummy = serde_json::from_str::<Dummy>(r#"{"ignore":{"foo":"bar"}"#);
|
||||||
|
|
||||||
|
assert!(dummy.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_json_vec_any() {
|
||||||
|
let raw = [NullAny; 10];
|
||||||
|
let json = r#"[null,true,false,10,{},[],[null],{"foo":"bar"},[9,9,9],"ten"]"#;
|
||||||
|
|
||||||
|
let deserialized: Vec<NullAny> = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&raw[..], &deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_json_null() {
|
||||||
|
let dummy = Dummy { ignore: NullAny };
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&dummy).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(json, r#"{"ignore":null}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bincode_vec() {
|
||||||
|
let raw = vec![NullAny; 10];
|
||||||
|
|
||||||
|
let bytes = bincode::options().serialize(&raw).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(bytes, &[10u8]);
|
||||||
|
|
||||||
|
let deserialized: Vec<NullAny> = bincode::options().deserialize(&bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bincode_tuple() {
|
||||||
|
let raw = (NullAny, "Hello world".to_string());
|
||||||
|
|
||||||
|
let bytes = bincode::options().serialize(&raw).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(bytes, b"\x0BHello world"); // 0B = 11 = length of string
|
||||||
|
|
||||||
|
let deserialized: (NullAny, String) = bincode::options().deserialize(&bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn json_vec() {
|
||||||
|
let raw = vec![NullAny; 10];
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&raw).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(json, "[null,null,null,null,null,null,null,null,null,null]");
|
||||||
|
|
||||||
|
let deserialized: Vec<NullAny> = serde_json::from_str(&json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn json_tuple() {
|
||||||
|
let raw = (NullAny, "Hello world".to_string());
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&raw).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(json, r#"[null,"Hello world"]"#);
|
||||||
|
|
||||||
|
let deserialized: (NullAny, String) = serde_json::from_str(&json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw, deserialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use actix_http::ws::Item;
|
||||||
|
use actix_web_actors::ws::{self, CloseReason, CloseCode};
|
||||||
|
use bytes::{Bytes, BytesMut};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use actix::prelude::Message;
|
||||||
|
|
||||||
|
/// Helper that will buffer continuation messages from actix
|
||||||
|
/// until completion, capping at 10mb.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MultipartHandler {
|
||||||
|
buf: BytesMut,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message to signal that a node should be muted for a reason that's
|
||||||
|
/// cheap to transfer between Actors or over the wire for shards.
|
||||||
|
#[derive(Serialize, Deserialize, Message, Clone, Copy, Debug)]
|
||||||
|
#[rtype("()")]
|
||||||
|
pub enum MuteReason {
|
||||||
|
/// Node was denied connection for any arbitrary reason,
|
||||||
|
/// and should not attempt to reconnect.
|
||||||
|
Denied,
|
||||||
|
/// Node was denied because the chain it belongs to is currently
|
||||||
|
/// at the limit of allowed nodes, and it may attempt to reconnect.
|
||||||
|
Overquota,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MuteReason> for CloseReason {
|
||||||
|
fn from(mute: MuteReason) -> CloseReason {
|
||||||
|
match mute {
|
||||||
|
MuteReason::Denied => CloseReason {
|
||||||
|
code: CloseCode::Abnormal,
|
||||||
|
description: Some("Denied".into()),
|
||||||
|
},
|
||||||
|
MuteReason::Overquota => CloseReason {
|
||||||
|
code: CloseCode::Again,
|
||||||
|
description: Some("Overquota".into()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Continuation buffer limit, 10mb
|
||||||
|
const CONT_BUF_LIMIT: usize = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
pub enum WsMessage {
|
||||||
|
Nop,
|
||||||
|
Ping(Bytes),
|
||||||
|
Data(Bytes),
|
||||||
|
Close(Option<CloseReason>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultipartHandler {
|
||||||
|
pub fn handle(&mut self, msg: ws::Message) -> WsMessage {
|
||||||
|
match msg {
|
||||||
|
ws::Message::Ping(msg) => WsMessage::Ping(msg),
|
||||||
|
ws::Message::Pong(_) => WsMessage::Nop,
|
||||||
|
ws::Message::Text(text) => WsMessage::Data(text.into_bytes()),
|
||||||
|
ws::Message::Binary(data) => WsMessage::Data(data),
|
||||||
|
ws::Message::Close(reason) => WsMessage::Close(reason),
|
||||||
|
ws::Message::Nop => WsMessage::Nop,
|
||||||
|
ws::Message::Continuation(cont) => match cont {
|
||||||
|
Item::FirstText(bytes) | Item::FirstBinary(bytes) => {
|
||||||
|
self.start_frame(&bytes);
|
||||||
|
WsMessage::Nop
|
||||||
|
}
|
||||||
|
Item::Continue(bytes) => {
|
||||||
|
self.continue_frame(&bytes);
|
||||||
|
WsMessage::Nop
|
||||||
|
}
|
||||||
|
Item::Last(bytes) => {
|
||||||
|
self.continue_frame(&bytes);
|
||||||
|
WsMessage::Data(self.finish_frame())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_frame(&mut self, bytes: &[u8]) {
|
||||||
|
if !self.buf.is_empty() {
|
||||||
|
log::error!("Unused continuation buffer");
|
||||||
|
self.buf.clear();
|
||||||
|
}
|
||||||
|
self.continue_frame(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn continue_frame(&mut self, bytes: &[u8]) {
|
||||||
|
if self.buf.len() + bytes.len() <= CONT_BUF_LIMIT {
|
||||||
|
self.buf.extend_from_slice(&bytes);
|
||||||
|
} else {
|
||||||
|
log::error!("Continuation buffer overflow");
|
||||||
|
self.buf = BytesMut::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_frame(&mut self) -> Bytes {
|
||||||
|
std::mem::replace(&mut self.buf, BytesMut::new()).freeze()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user