Merge branch 'main' into query-interval

This commit is contained in:
Bruno Valente 2024-08-27 16:22:15 +08:00
commit e126cfb629
53 changed files with 1464 additions and 207 deletions

304
Cargo.lock generated
View File

@ -181,6 +181,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -838,6 +844,7 @@ dependencies = [
name = "channel" name = "channel"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"metrics",
"tokio", "tokio",
] ]
@ -1431,6 +1438,17 @@ dependencies = [
"powerfmt", "powerfmt",
] ]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "derive_builder" name = "derive_builder"
version = "0.9.0" version = "0.9.0"
@ -1467,6 +1485,12 @@ dependencies = [
"syn 2.0.68", "syn 2.0.68",
] ]
[[package]]
name = "destructure_traitobject"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@ -1636,7 +1660,7 @@ dependencies = [
"rust_decimal", "rust_decimal",
"serde", "serde",
"thiserror", "thiserror",
"time", "time 0.3.36",
] ]
[[package]] [[package]]
@ -2406,6 +2430,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -3012,6 +3051,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.29" version = "0.14.29"
@ -3080,6 +3125,19 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.60" version = "0.1.60"
@ -3263,6 +3321,19 @@ dependencies = [
"hashbrown 0.14.5", "hashbrown 0.14.5",
] ]
[[package]]
name = "influx_db_client"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ef03268010ccf98c178eed83aa7377b7f6531f8ec8d43a256902c24cadac60"
dependencies = [
"bytes",
"futures",
"reqwest",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "inout" name = "inout"
version = "0.1.3" version = "0.1.3"
@ -4415,9 +4486,45 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [ dependencies = [
"serde",
"value-bag", "value-bag",
] ]
[[package]]
name = "log-mdc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
[[package]]
name = "log4rs"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6"
dependencies = [
"anyhow",
"arc-swap",
"chrono",
"derivative",
"flate2",
"fnv",
"humantime",
"libc",
"log",
"log-mdc",
"once_cell",
"parking_lot 0.12.3",
"rand 0.8.5",
"serde",
"serde-value",
"serde_json",
"serde_yaml",
"thiserror",
"thread-id",
"typemap-ors",
"winapi",
]
[[package]] [[package]]
name = "log_entry_sync" name = "log_entry_sync"
version = "0.1.0" version = "0.1.0"
@ -4432,6 +4539,8 @@ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"jsonrpsee", "jsonrpsee",
"lazy_static",
"metrics",
"serde_json", "serde_json",
"shared_types", "shared_types",
"storage", "storage",
@ -4511,6 +4620,25 @@ dependencies = [
"tiny-keccak", "tiny-keccak",
] ]
[[package]]
name = "metrics"
version = "0.1.0"
source = "git+https://github.com/Conflux-Chain/conflux-rust.git?rev=992ebc5483d937c8f6b883e266f8ed2a67a7fa9a#992ebc5483d937c8f6b883e266f8ed2a67a7fa9a"
dependencies = [
"chrono",
"futures",
"influx_db_client",
"lazy_static",
"log",
"log4rs",
"parking_lot 0.11.2",
"rand 0.7.3",
"serde",
"time 0.1.45",
"timer",
"tokio",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@ -4692,6 +4820,23 @@ dependencies = [
"unsigned-varint", "unsigned-varint",
] ]
[[package]]
name = "native-tls"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "netlink-packet-core" name = "netlink-packet-core"
version = "0.4.2" version = "0.4.2"
@ -4989,18 +5134,65 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "openssl"
version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.68",
]
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.1.5" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.4.3" version = "0.4.3"
@ -6141,10 +6333,12 @@ dependencies = [
"http-body", "http-body",
"hyper", "hyper",
"hyper-rustls 0.24.2", "hyper-rustls 0.24.2",
"hyper-tls",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
"mime", "mime",
"native-tls",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite 0.2.14", "pin-project-lite 0.2.14",
@ -6156,6 +6350,7 @@ dependencies = [
"sync_wrapper", "sync_wrapper",
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-native-tls",
"tokio-rustls 0.24.1", "tokio-rustls 0.24.1",
"tower-service", "tower-service",
"url", "url",
@ -6291,6 +6486,7 @@ dependencies = [
"file_location_cache", "file_location_cache",
"futures", "futures",
"lazy_static", "lazy_static",
"metrics",
"miner", "miner",
"network", "network",
"pruner", "pruner",
@ -6319,6 +6515,7 @@ dependencies = [
"jsonrpsee", "jsonrpsee",
"merkle_light", "merkle_light",
"merkle_tree", "merkle_tree",
"metrics",
"miner", "miner",
"network", "network",
"serde", "serde",
@ -6670,6 +6867,16 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.203"
@ -6713,6 +6920,19 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.2.6",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]] [[package]]
name = "sha-1" name = "sha-1"
version = "0.9.8" version = "0.9.8"
@ -6844,7 +7064,7 @@ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"thiserror", "thiserror",
"time", "time 0.3.36",
] ]
[[package]] [[package]]
@ -7164,9 +7384,11 @@ dependencies = [
"duration-str", "duration-str",
"eth2_ssz", "eth2_ssz",
"file_location_cache", "file_location_cache",
"lazy_static",
"libp2p", "libp2p",
"log_entry_sync", "log_entry_sync",
"merkle_light", "merkle_light",
"metrics",
"network", "network",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
@ -7309,6 +7531,16 @@ dependencies = [
"syn 2.0.68", "syn 2.0.68",
] ]
[[package]]
name = "thread-id"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.8" version = "1.1.8"
@ -7329,6 +7561,17 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.36"
@ -7360,6 +7603,15 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "timer"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b"
dependencies = [
"chrono",
]
[[package]] [[package]]
name = "tiny-keccak" name = "tiny-keccak"
version = "2.0.2" version = "2.0.2"
@ -7434,6 +7686,16 @@ dependencies = [
"syn 2.0.68", "syn 2.0.68",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.23.4" version = "0.23.4"
@ -7592,7 +7854,7 @@ checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"thiserror", "thiserror",
"time", "time 0.3.36",
"tracing-subscriber", "tracing-subscriber",
] ]
@ -7705,7 +7967,7 @@ dependencies = [
"radix_trie", "radix_trie",
"rand 0.8.5", "rand 0.8.5",
"thiserror", "thiserror",
"time", "time 0.3.36",
"tokio", "tokio",
"trust-dns-proto 0.20.4", "trust-dns-proto 0.20.4",
] ]
@ -7806,6 +8068,15 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "typemap-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867"
dependencies = [
"unsafe-any-ors",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -7898,6 +8169,21 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "unsafe-any-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad"
dependencies = [
"destructure_traitobject",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "unsigned-varint" name = "unsigned-varint"
version = "0.7.1" version = "0.7.1"
@ -8015,6 +8301,12 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -8552,12 +8844,14 @@ dependencies = [
"duration-str", "duration-str",
"error-chain", "error-chain",
"ethereum-types 0.14.1", "ethereum-types 0.14.1",
"ethers",
"exit-future", "exit-future",
"file_location_cache", "file_location_cache",
"futures", "futures",
"itertools 0.10.5", "itertools 0.10.5",
"libp2p", "libp2p",
"log_entry_sync", "log_entry_sync",
"metrics",
"miner", "miner",
"network", "network",
"pruner", "pruner",
@ -8618,7 +8912,7 @@ dependencies = [
"hmac 0.12.1", "hmac 0.12.1",
"pbkdf2 0.11.0", "pbkdf2 0.11.0",
"sha1", "sha1",
"time", "time 0.3.36",
"zstd", "zstd",
] ]

View File

@ -27,6 +27,9 @@ members = [
] ]
resolver = "2" resolver = "2"
[workspace.dependencies]
metrics = { git = "https://github.com/Conflux-Chain/conflux-rust.git", rev = "992ebc5483d937c8f6b883e266f8ed2a67a7fa9a" }
[patch.crates-io] [patch.crates-io]
discv5 = { path = "version-meld/discv5" } discv5 = { path = "version-meld/discv5" }
eth2_ssz = { path = "version-meld/eth2_ssz" } eth2_ssz = { path = "version-meld/eth2_ssz" }

View File

@ -5,3 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.19.2", features = ["sync", "time"] } tokio = { version = "1.19.2", features = ["sync", "time"] }
metrics = { workspace = true }

View File

@ -1,7 +1,9 @@
use crate::error::Error; use crate::error::Error;
use crate::metrics::unbounded_channel;
use metrics::{Counter, CounterUsize};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::oneshot;
use tokio::sync::{mpsc, oneshot};
use tokio::time::timeout; use tokio::time::timeout;
const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(3); const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(3);
@ -19,20 +21,30 @@ pub struct Channel<N, Req, Res> {
} }
impl<N, Req, Res> Channel<N, Req, Res> { impl<N, Req, Res> Channel<N, Req, Res> {
pub fn unbounded() -> (Sender<N, Req, Res>, Receiver<N, Req, Res>) { pub fn unbounded(name: &str) -> (Sender<N, Req, Res>, Receiver<N, Req, Res>) {
let (sender, receiver) = mpsc::unbounded_channel(); let metrics_group = format!("common_channel_{}", name);
(Sender { chan: sender }, Receiver { chan: receiver }) let (sender, receiver) = unbounded_channel(metrics_group.as_str());
let metrics_timeout = CounterUsize::register_with_group(metrics_group.as_str(), "timeout");
(
Sender {
chan: sender,
metrics_timeout,
},
receiver,
)
} }
} }
pub struct Sender<N, Req, Res> { pub struct Sender<N, Req, Res> {
chan: mpsc::UnboundedSender<Message<N, Req, Res>>, chan: crate::metrics::Sender<Message<N, Req, Res>>,
metrics_timeout: Arc<dyn Counter<usize>>,
} }
impl<N, Req, Res> Clone for Sender<N, Req, Res> { impl<N, Req, Res> Clone for Sender<N, Req, Res> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Sender { Sender {
chan: self.chan.clone(), chan: self.chan.clone(),
metrics_timeout: self.metrics_timeout.clone(),
} }
} }
} }
@ -53,24 +65,15 @@ impl<N, Req, Res> Sender<N, Req, Res> {
timeout(DEFAULT_REQUEST_TIMEOUT, receiver) timeout(DEFAULT_REQUEST_TIMEOUT, receiver)
.await .await
.map_err(|_| Error::TimeoutError)? .map_err(|_| {
self.metrics_timeout.inc(1);
Error::TimeoutError
})?
.map_err(|e| Error::RecvError(e)) .map_err(|e| Error::RecvError(e))
} }
} }
pub struct Receiver<N, Req, Res> { pub type Receiver<N, Req, Res> = crate::metrics::Receiver<Message<N, Req, Res>>;
chan: mpsc::UnboundedReceiver<Message<N, Req, Res>>,
}
impl<N, Req, Res> Receiver<N, Req, Res> {
pub async fn recv(&mut self) -> Option<Message<N, Req, Res>> {
self.chan.recv().await
}
pub fn try_recv(&mut self) -> Result<Message<N, Req, Res>, TryRecvError> {
self.chan.try_recv()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -91,7 +94,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn request_response() { async fn request_response() {
let (tx, mut rx) = Channel::<Notification, Request, Response>::unbounded(); let (tx, mut rx) = Channel::<Notification, Request, Response>::unbounded("test");
let task1 = async move { let task1 = async move {
match rx.recv().await.expect("not dropped") { match rx.recv().await.expect("not dropped") {

View File

@ -1,5 +1,6 @@
mod channel; mod channel;
pub mod error; pub mod error;
pub mod metrics;
pub mod test_util; pub mod test_util;
pub use crate::channel::{Channel, Message, Receiver, ResponseSender, Sender}; pub use crate::channel::{Channel, Message, Receiver, ResponseSender, Sender};

View File

@ -0,0 +1,112 @@
use std::{fmt::Debug, sync::Arc, time::Instant};
use metrics::{register_meter_with_group, Counter, CounterUsize, Histogram, Meter, Sample};
use tokio::sync::mpsc::{
error::{SendError, TryRecvError},
unbounded_channel as new_unbounded_channel, UnboundedReceiver, UnboundedSender,
};
pub fn unbounded_channel<T>(metric_name: &str) -> (Sender<T>, Receiver<T>) {
let (sender, receiver) = new_unbounded_channel();
let metrics_queued = CounterUsize::register_with_group(metric_name, "size");
(
Sender::new(sender, metric_name, metrics_queued.clone()),
Receiver::new(receiver, metric_name, metrics_queued),
)
}
pub struct Sender<T> {
sender: UnboundedSender<(Instant, T)>,
metrics_send_qps: Arc<dyn Meter>,
metrics_queued: Arc<dyn Counter<usize>>,
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
metrics_send_qps: self.metrics_send_qps.clone(),
metrics_queued: self.metrics_queued.clone(),
}
}
}
impl<T> Debug for Sender<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.sender)
}
}
impl<T> Sender<T> {
pub(crate) fn new(
sender: UnboundedSender<(Instant, T)>,
metrics_group: &str,
metrics_queued: Arc<dyn Counter<usize>>,
) -> Self {
Self {
sender,
metrics_send_qps: register_meter_with_group(metrics_group, "send"),
metrics_queued,
}
}
pub fn send(&self, value: T) -> Result<(), SendError<T>> {
match self.sender.send((Instant::now(), value)) {
Ok(()) => {
self.metrics_send_qps.mark(1);
self.metrics_queued.inc(1);
Ok(())
}
Err(e) => Err(SendError(e.0 .1)),
}
}
}
pub struct Receiver<T> {
receiver: UnboundedReceiver<(Instant, T)>,
metrics_recv_qps: Arc<dyn Meter>,
metrics_queued: Arc<dyn Counter<usize>>,
metrics_queue_latency: Arc<dyn Histogram>,
}
impl<T> Debug for Receiver<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.receiver)
}
}
impl<T> Receiver<T> {
pub(crate) fn new(
receiver: UnboundedReceiver<(Instant, T)>,
metrics_group: &str,
metrics_queued: Arc<dyn Counter<usize>>,
) -> Self {
Self {
receiver,
metrics_recv_qps: register_meter_with_group(metrics_group, "recv"),
metrics_queued,
metrics_queue_latency: Sample::ExpDecay(0.015).register_with_group(
metrics_group,
"latency",
1024,
),
}
}
fn on_recv(&self, value: (Instant, T)) -> T {
self.metrics_recv_qps.mark(1);
self.metrics_queued.dec(1);
self.metrics_queue_latency.update_since(value.0);
value.1
}
pub async fn recv(&mut self) -> Option<T> {
let value = self.receiver.recv().await?;
Some(self.on_recv(value))
}
pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
let value = self.receiver.try_recv()?;
Ok(self.on_recv(value))
}
}

View File

@ -37,6 +37,8 @@ serde = { version = "1.0.137", features = ["derive"] }
duration-str = "0.5.1" duration-str = "0.5.1"
config = "0.13.1" config = "0.13.1"
public-ip = "0.2" public-ip = "0.2"
ethers = "2.0.14"
metrics = { workspace = true }
[dependencies.libp2p] [dependencies.libp2p]
version = "0.45.1" version = "0.45.1"

View File

@ -22,3 +22,5 @@ contract-interface = { path = "../../common/contract-interface" }
futures-core = "0.3.28" futures-core = "0.3.28"
futures-util = "0.3.28" futures-util = "0.3.28"
thiserror = "1.0.44" thiserror = "1.0.44"
lazy_static = "1.4.0"
metrics = { workspace = true }

View File

@ -0,0 +1,7 @@
use std::sync::Arc;
use metrics::{register_timer, Timer};
lazy_static::lazy_static! {
pub static ref STORE_PUT_TX: Arc<dyn Timer> = register_timer("log_entry_sync_store_put_tx");
}

View File

@ -11,7 +11,7 @@ use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::{Duration, Instant};
use storage::log_store::{tx_store::BlockHashAndSubmissionIndex, Store}; use storage::log_store::{tx_store::BlockHashAndSubmissionIndex, Store};
use task_executor::{ShutdownReason, TaskExecutor}; use task_executor::{ShutdownReason, TaskExecutor};
use tokio::sync::broadcast; use tokio::sync::broadcast;
@ -358,7 +358,11 @@ impl LogSyncManager {
} }
async fn put_tx_inner(&mut self, tx: Transaction) -> bool { async fn put_tx_inner(&mut self, tx: Transaction) -> bool {
if let Err(e) = self.store.put_tx(tx.clone()) { let start_time = Instant::now();
let result = self.store.put_tx(tx.clone());
metrics::STORE_PUT_TX.update_since(start_time);
if let Err(e) = result {
error!("put_tx error: e={:?}", e); error!("put_tx error: e={:?}", e);
false false
} else { } else {
@ -458,3 +462,4 @@ pub(crate) mod config;
mod data_cache; mod data_cache;
mod log_entry_fetcher; mod log_entry_fetcher;
mod log_query; mod log_query;
mod metrics;

View File

@ -1,11 +1,16 @@
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use ethers::core::k256::SecretKey; use ethers::core::k256::SecretKey;
use ethers::middleware::SignerMiddleware; use ethers::middleware::SignerMiddleware;
use ethers::providers::Http; use ethers::providers::Http;
use ethers::providers::HttpRateLimitRetryPolicy;
use ethers::providers::Middleware; use ethers::providers::Middleware;
use ethers::providers::Provider; use ethers::providers::Provider;
use ethers::providers::RetryClient;
use ethers::providers::RetryClientBuilder;
use ethers::signers::LocalWallet; use ethers::signers::LocalWallet;
use ethers::signers::Signer; use ethers::signers::Signer;
use storage::config::ShardConfig; use storage::config::ShardConfig;
@ -21,9 +26,12 @@ pub struct MinerConfig {
pub(crate) iter_batch: usize, pub(crate) iter_batch: usize,
pub(crate) shard_config: ShardConfig, pub(crate) shard_config: ShardConfig,
pub(crate) context_query_interval: Duration, pub(crate) context_query_interval: Duration,
pub(crate) rate_limit_retries: u32,
pub(crate) timeout_retries: u32,
pub(crate) initial_backoff: u64,
} }
pub type MineServiceMiddleware = SignerMiddleware<Provider<Http>, LocalWallet>; pub type MineServiceMiddleware = SignerMiddleware<Arc<Provider<RetryClient<Http>>>, LocalWallet>;
impl MinerConfig { impl MinerConfig {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -38,6 +46,9 @@ impl MinerConfig {
iter_batch: usize, iter_batch: usize,
context_query_seconds: u64, context_query_seconds: u64,
shard_config: ShardConfig, shard_config: ShardConfig,
rate_limit_retries: u32,
timeout_retries: u32,
initial_backoff: u64,
) -> Option<MinerConfig> { ) -> Option<MinerConfig> {
miner_key.map(|miner_key| MinerConfig { miner_key.map(|miner_key| MinerConfig {
miner_id, miner_id,
@ -50,12 +61,24 @@ impl MinerConfig {
iter_batch, iter_batch,
shard_config, shard_config,
context_query_interval: Duration::from_secs(context_query_seconds), context_query_interval: Duration::from_secs(context_query_seconds),
rate_limit_retries,
timeout_retries,
initial_backoff,
}) })
} }
pub(crate) async fn make_provider(&self) -> Result<MineServiceMiddleware, String> { pub(crate) async fn make_provider(&self) -> Result<MineServiceMiddleware, String> {
let provider = Provider::<Http>::try_from(&self.rpc_endpoint_url) let provider = Arc::new(Provider::new(
.map_err(|e| format!("Can not parse blockchain endpoint: {:?}", e))?; RetryClientBuilder::default()
.rate_limit_retries(self.rate_limit_retries)
.timeout_retries(self.timeout_retries)
.initial_backoff(Duration::from_millis(self.initial_backoff))
.build(
Http::from_str(&self.rpc_endpoint_url)
.map_err(|e| format!("Cannot parse blockchain endpoint: {:?}", e))?,
Box::new(HttpRateLimitRetryPolicy),
),
));
let chain_id = provider let chain_id = provider
.get_chainid() .get_chainid()
.await .await
@ -64,6 +87,7 @@ impl MinerConfig {
.map_err(|e| format!("Cannot parse private key: {:?}", e))?; .map_err(|e| format!("Cannot parse private key: {:?}", e))?;
let signer = LocalWallet::from(secret_key).with_chain_id(chain_id.as_u64()); let signer = LocalWallet::from(secret_key).with_chain_id(chain_id.as_u64());
let middleware = SignerMiddleware::new(provider, signer); let middleware = SignerMiddleware::new(provider, signer);
Ok(middleware) Ok(middleware)
} }
} }

View File

@ -11,6 +11,7 @@ use libp2p::gossipsub::{
use libp2p::Multiaddr; use libp2p::Multiaddr;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use shared_types::NetworkIdentity;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
@ -122,6 +123,9 @@ pub struct Config {
/// Whether metrics are enabled. /// Whether metrics are enabled.
pub metrics_enabled: bool, pub metrics_enabled: bool,
/// The id of the storage network.
pub network_id: NetworkIdentity,
} }
impl Default for Config { impl Default for Config {
@ -199,6 +203,7 @@ impl Default for Config {
shutdown_after_sync: false, shutdown_after_sync: false,
topics: Vec::new(), topics: Vec::new(),
metrics_enabled: false, metrics_enabled: false,
network_id: Default::default(),
} }
} }
} }

View File

@ -25,6 +25,7 @@ pub mod types;
pub use config::gossip_max_size; pub use config::gossip_max_size;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::time::Instant;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use shared_types::TxID; use shared_types::TxID;
@ -97,8 +98,8 @@ pub use service::{load_private_key, Context, Libp2pEvent, Service, NETWORK_KEY_F
/// Application level requests sent to the network. /// Application level requests sent to the network.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum RequestId { pub enum RequestId {
Router, Router(Instant),
Sync(SyncId), Sync(Instant, SyncId),
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View File

@ -391,7 +391,9 @@ mod tests {
use std::io::Write; use std::io::Write;
fn status_message() -> StatusMessage { fn status_message() -> StatusMessage {
StatusMessage { data: 1 } StatusMessage {
data: Default::default(),
}
} }
fn ping_message() -> Ping { fn ping_message() -> Ping {
@ -560,7 +562,10 @@ mod tests {
assert_eq!(stream_identifier.len(), 10); assert_eq!(stream_identifier.len(), 10);
// Status message is 84 bytes uncompressed. `max_compressed_len` is 32 + 84 + 84/6 = 130. // Status message is 84 bytes uncompressed. `max_compressed_len` is 32 + 84 + 84/6 = 130.
let status_message_bytes = StatusMessage { data: 1 }.as_ssz_bytes(); let status_message_bytes = StatusMessage {
data: Default::default(),
}
.as_ssz_bytes();
let mut uvi_codec: Uvi<usize> = Uvi::default(); let mut uvi_codec: Uvi<usize> = Uvi::default();
let mut dst = BytesMut::with_capacity(1024); let mut dst = BytesMut::with_capacity(1024);

View File

@ -9,7 +9,7 @@ use ssz_types::{
use std::ops::Deref; use std::ops::Deref;
use strum::IntoStaticStr; use strum::IntoStaticStr;
pub type Hash256 = ethereum_types::H256; pub type Hash256 = ethereum_types::H256;
use shared_types::{ChunkArrayWithProof, TxID}; use shared_types::{ChunkArrayWithProof, NetworkIdentity, TxID};
pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, FixedVector}; pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, FixedVector};
@ -71,7 +71,7 @@ impl ToString for ErrorType {
/// The STATUS request/response handshake message. /// The STATUS request/response handshake message.
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)]
pub struct StatusMessage { pub struct StatusMessage {
pub data: u64, pub data: NetworkIdentity,
} }
/// The PING request/response message. /// The PING request/response message.

View File

@ -84,6 +84,7 @@ impl<AppReqId: ReqId> Service<AppReqId> {
.iter() .iter()
.map(|x| PeerId::from(x.clone())) .map(|x| PeerId::from(x.clone()))
.collect(), .collect(),
config.network_id.clone(),
)); ));
// try and construct UPnP port mappings if required. // try and construct UPnP port mappings if required.

View File

@ -4,6 +4,7 @@ use crate::Client;
use crate::EnrExt; use crate::EnrExt;
use crate::{Enr, GossipTopic, Multiaddr, PeerId}; use crate::{Enr, GossipTopic, Multiaddr, PeerId};
use parking_lot::RwLock; use parking_lot::RwLock;
use shared_types::NetworkIdentity;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::atomic::{AtomicU16, Ordering};
@ -22,10 +23,19 @@ pub struct NetworkGlobals {
pub peers: RwLock<PeerDB>, pub peers: RwLock<PeerDB>,
/// The current gossipsub topic subscriptions. /// The current gossipsub topic subscriptions.
pub gossipsub_subscriptions: RwLock<HashSet<GossipTopic>>, pub gossipsub_subscriptions: RwLock<HashSet<GossipTopic>>,
/// The id of the storage network.
pub network_id: RwLock<NetworkIdentity>,
} }
impl NetworkGlobals { impl NetworkGlobals {
pub fn new(enr: Enr, tcp_port: u16, udp_port: u16, trusted_peers: Vec<PeerId>) -> Self { pub fn new(
enr: Enr,
tcp_port: u16,
udp_port: u16,
trusted_peers: Vec<PeerId>,
network_id: NetworkIdentity,
) -> Self {
NetworkGlobals { NetworkGlobals {
local_enr: RwLock::new(enr.clone()), local_enr: RwLock::new(enr.clone()),
peer_id: RwLock::new(enr.peer_id()), peer_id: RwLock::new(enr.peer_id()),
@ -34,6 +44,7 @@ impl NetworkGlobals {
listen_port_udp: AtomicU16::new(udp_port), listen_port_udp: AtomicU16::new(udp_port),
peers: RwLock::new(PeerDB::new(trusted_peers)), peers: RwLock::new(PeerDB::new(trusted_peers)),
gossipsub_subscriptions: RwLock::new(HashSet::new()), gossipsub_subscriptions: RwLock::new(HashSet::new()),
network_id: RwLock::new(network_id),
} }
} }
@ -63,6 +74,10 @@ impl NetworkGlobals {
self.listen_port_udp.load(Ordering::Relaxed) self.listen_port_udp.load(Ordering::Relaxed)
} }
pub fn network_id(&self) -> NetworkIdentity {
self.network_id.read().clone()
}
/// Returns the number of libp2p connected peers. /// Returns the number of libp2p connected peers.
pub fn connected_peers(&self) -> usize { pub fn connected_peers(&self) -> usize {
self.peers.read().connected_peer_ids().count() self.peers.read().connected_peer_ids().count()
@ -95,6 +110,6 @@ impl NetworkGlobals {
let enr_key: discv5::enr::CombinedKey = let enr_key: discv5::enr::CombinedKey =
discv5::enr::CombinedKey::from_libp2p(&keypair).unwrap(); discv5::enr::CombinedKey::from_libp2p(&keypair).unwrap();
let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
NetworkGlobals::new(enr, 9000, 9000, vec![]) NetworkGlobals::new(enr, 9000, 9000, vec![], Default::default())
} }
} }

View File

@ -23,10 +23,14 @@ fn test_status_rpc() {
let (mut sender, mut receiver) = common::build_node_pair(Arc::downgrade(&rt)).await; let (mut sender, mut receiver) = common::build_node_pair(Arc::downgrade(&rt)).await;
// Dummy STATUS RPC message // Dummy STATUS RPC message
let rpc_request = Request::Status(StatusMessage { data: 2 }); let rpc_request = Request::Status(StatusMessage {
data: Default::default(),
});
// Dummy STATUS RPC message // Dummy STATUS RPC message
let rpc_response = Response::Status(StatusMessage { data: 3 }); let rpc_response = Response::Status(StatusMessage {
data: Default::default(),
});
// build the sender future // build the sender future
let sender_future = async { let sender_future = async {

View File

@ -1,11 +1,13 @@
use anyhow::{anyhow, bail, Result}; use anyhow::{bail, Result};
use contract_interface::ChunkLinearReward; use contract_interface::ChunkLinearReward;
use ethereum_types::Address; use ethereum_types::Address;
use ethers::prelude::{Http, Provider}; use ethers::prelude::{Http, Provider};
use ethers::providers::{HttpRateLimitRetryPolicy, RetryClient, RetryClientBuilder};
use miner::MinerMessage; use miner::MinerMessage;
use rand::Rng; use rand::Rng;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use storage::config::{ShardConfig, SHARD_CONFIG_KEY}; use storage::config::{ShardConfig, SHARD_CONFIG_KEY};
@ -34,17 +36,16 @@ pub struct PrunerConfig {
pub rpc_endpoint_url: String, pub rpc_endpoint_url: String,
pub reward_address: Address, pub reward_address: Address,
pub rate_limit_retries: u32,
pub timeout_retries: u32,
pub initial_backoff: u64,
} }
impl PrunerConfig { impl PrunerConfig {
fn start_prune_size(&self) -> u64 { fn start_prune_size(&self) -> u64 {
(self.max_num_sectors as f32 * PRUNE_THRESHOLD) as u64 (self.max_num_sectors as f32 * PRUNE_THRESHOLD) as u64
} }
fn make_provider(&self) -> Result<Provider<Http>> {
Provider::<Http>::try_from(&self.rpc_endpoint_url)
.map_err(|e| anyhow!("Can not parse blockchain endpoint: {:?}", e))
}
} }
pub struct Pruner { pub struct Pruner {
@ -57,7 +58,7 @@ pub struct Pruner {
sender: mpsc::UnboundedSender<PrunerMessage>, sender: mpsc::UnboundedSender<PrunerMessage>,
miner_sender: Option<broadcast::Sender<MinerMessage>>, miner_sender: Option<broadcast::Sender<MinerMessage>>,
reward_contract: ChunkLinearReward<Provider<Http>>, reward_contract: ChunkLinearReward<Arc<Provider<RetryClient<Http>>>>,
} }
impl Pruner { impl Pruner {
@ -73,8 +74,18 @@ impl Pruner {
let (first_rewardable_chunk, first_tx_seq) = get_first_rewardable_chunk(store.as_ref()) let (first_rewardable_chunk, first_tx_seq) = get_first_rewardable_chunk(store.as_ref())
.await? .await?
.unwrap_or((0, 0)); .unwrap_or((0, 0));
let reward_contract =
ChunkLinearReward::new(config.reward_address, Arc::new(config.make_provider()?)); let provider = Arc::new(Provider::new(
RetryClientBuilder::default()
.rate_limit_retries(config.rate_limit_retries)
.timeout_retries(config.timeout_retries)
.initial_backoff(Duration::from_millis(config.initial_backoff))
.build(
Http::from_str(&config.rpc_endpoint_url)?,
Box::new(HttpRateLimitRetryPolicy),
),
));
let reward_contract = ChunkLinearReward::new(config.reward_address, Arc::new(provider));
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
let pruner = Pruner { let pruner = Pruner {
config, config,

View File

@ -24,6 +24,7 @@ rand = "0.8.5"
serde = { version = "1.0.137", features = ["derive"] } serde = { version = "1.0.137", features = ["derive"] }
duration-str = "0.5.1" duration-str = "0.5.1"
public-ip = "0.2" public-ip = "0.2"
metrics = { workspace = true }
[dev-dependencies] [dev-dependencies]
channel = { path = "../../common/channel" } channel = { path = "../../common/channel" }

View File

@ -2,6 +2,7 @@
extern crate tracing; extern crate tracing;
mod libp2p_event_handler; mod libp2p_event_handler;
mod metrics;
mod peer_manager; mod peer_manager;
mod service; mod service;
@ -23,6 +24,7 @@ pub struct Config {
pub max_idle_outgoing_peers: usize, pub max_idle_outgoing_peers: usize,
pub libp2p_nodes: Vec<Multiaddr>, pub libp2p_nodes: Vec<Multiaddr>,
pub private_ip_enabled: bool, pub private_ip_enabled: bool,
pub check_announced_ip: bool,
} }
impl Default for Config { impl Default for Config {
@ -34,6 +36,7 @@ impl Default for Config {
max_idle_outgoing_peers: 20, max_idle_outgoing_peers: 20,
libp2p_nodes: vec![], libp2p_nodes: vec![],
private_ip_enabled: false, private_ip_enabled: false,
check_announced_ip: false,
} }
} }
} }

View File

@ -1,11 +1,11 @@
use std::net::IpAddr; use std::net::IpAddr;
use std::time::Instant;
use std::{ops::Neg, sync::Arc}; use std::{ops::Neg, sync::Arc};
use chunk_pool::ChunkPoolMessage; use chunk_pool::ChunkPoolMessage;
use file_location_cache::FileLocationCache; use file_location_cache::FileLocationCache;
use network::multiaddr::Protocol; use network::multiaddr::Protocol;
use network::types::{AnnounceShardConfig, SignedAnnounceShardConfig}; use network::types::{AnnounceShardConfig, SignedAnnounceShardConfig};
use network::Multiaddr;
use network::{ use network::{
rpc::StatusMessage, rpc::StatusMessage,
types::{ types::{
@ -15,29 +15,37 @@ use network::{
Keypair, MessageAcceptance, MessageId, NetworkGlobals, NetworkMessage, PeerId, PeerRequestId, Keypair, MessageAcceptance, MessageId, NetworkGlobals, NetworkMessage, PeerId, PeerRequestId,
PublicKey, PubsubMessage, Request, RequestId, Response, PublicKey, PubsubMessage, Request, RequestId, Response,
}; };
use shared_types::{bytes_to_chunks, timestamp_now, TxID}; use network::{Multiaddr, PeerAction, ReportSource};
use shared_types::{bytes_to_chunks, timestamp_now, NetworkIdentity, TxID};
use storage::config::ShardConfig; use storage::config::ShardConfig;
use storage_async::Store; use storage_async::Store;
use sync::{SyncMessage, SyncSender}; use sync::{SyncMessage, SyncSender};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::{mpsc, RwLock}; use tokio::sync::{mpsc, RwLock};
use crate::metrics;
use crate::peer_manager::PeerManager; use crate::peer_manager::PeerManager;
use crate::Config; use crate::Config;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref FIND_FILE_TIMEOUT: chrono::Duration = chrono::Duration::minutes(2); pub static ref FIND_FILE_TIMEOUT: chrono::Duration = chrono::Duration::minutes(5);
pub static ref ANNOUNCE_FILE_TIMEOUT: chrono::Duration = chrono::Duration::minutes(2); pub static ref ANNOUNCE_FILE_TIMEOUT: chrono::Duration = chrono::Duration::minutes(5);
pub static ref ANNOUNCE_SHARD_CONFIG_TIMEOUT: chrono::Duration = chrono::Duration::minutes(2); pub static ref ANNOUNCE_SHARD_CONFIG_TIMEOUT: chrono::Duration = chrono::Duration::minutes(5);
pub static ref TOLERABLE_DRIFT: chrono::Duration = chrono::Duration::seconds(5); pub static ref TOLERABLE_DRIFT: chrono::Duration = chrono::Duration::seconds(10);
} }
#[allow(deprecated)] fn duration_since(timestamp: u32, metric: Arc<dyn ::metrics::Histogram>) -> chrono::Duration {
fn duration_since(timestamp: u32) -> chrono::Duration {
let timestamp = i64::from(timestamp); let timestamp = i64::from(timestamp);
let timestamp = chrono::NaiveDateTime::from_timestamp_opt(timestamp, 0).expect("should fit"); let timestamp = chrono::DateTime::from_timestamp(timestamp, 0).expect("should fit");
let now = chrono::Utc::now().naive_utc(); let now = chrono::Utc::now();
now.signed_duration_since(timestamp) let duration = now.signed_duration_since(timestamp);
let num_secs = duration.num_seconds();
if num_secs > 0 {
metric.update(num_secs as u64);
}
duration
} }
fn peer_id_to_public_key(peer_id: &PeerId) -> Result<PublicKey, String> { fn peer_id_to_public_key(peer_id: &PeerId) -> Result<PublicKey, String> {
@ -139,14 +147,18 @@ impl Libp2pEventHandler {
} }
pub fn send_status(&self, peer_id: PeerId) { pub fn send_status(&self, peer_id: PeerId) {
let status_message = StatusMessage { data: 123 }; // dummy status message let status_message = StatusMessage {
data: self.network_globals.network_id(),
};
debug!(%peer_id, ?status_message, "Sending Status request"); debug!(%peer_id, ?status_message, "Sending Status request");
self.send_to_network(NetworkMessage::SendRequest { self.send_to_network(NetworkMessage::SendRequest {
peer_id, peer_id,
request_id: RequestId::Router, request_id: RequestId::Router(Instant::now()),
request: Request::Status(status_message), request: Request::Status(status_message),
}); });
metrics::LIBP2P_SEND_STATUS.mark(1);
} }
pub async fn on_peer_connected(&self, peer_id: PeerId, outgoing: bool) { pub async fn on_peer_connected(&self, peer_id: PeerId, outgoing: bool) {
@ -155,12 +167,16 @@ impl Libp2pEventHandler {
if outgoing { if outgoing {
self.send_status(peer_id); self.send_status(peer_id);
self.send_to_sync(SyncMessage::PeerConnected { peer_id }); self.send_to_sync(SyncMessage::PeerConnected { peer_id });
metrics::LIBP2P_HANDLE_PEER_CONNECTED_OUTGOING.mark(1);
} else {
metrics::LIBP2P_HANDLE_PEER_CONNECTED_INCOMING.mark(1);
} }
} }
pub async fn on_peer_disconnected(&self, peer_id: PeerId) { pub async fn on_peer_disconnected(&self, peer_id: PeerId) {
self.peers.write().await.remove(&peer_id); self.peers.write().await.remove(&peer_id);
self.send_to_sync(SyncMessage::PeerDisconnected { peer_id }); self.send_to_sync(SyncMessage::PeerDisconnected { peer_id });
metrics::LIBP2P_HANDLE_PEER_DISCONNECTED.mark(1);
} }
pub async fn on_rpc_request( pub async fn on_rpc_request(
@ -174,6 +190,7 @@ impl Libp2pEventHandler {
match request { match request {
Request::Status(status) => { Request::Status(status) => {
self.on_status_request(peer_id, request_id, status); self.on_status_request(peer_id, request_id, status);
metrics::LIBP2P_HANDLE_REQUEST_STATUS.mark(1);
} }
Request::GetChunks(request) => { Request::GetChunks(request) => {
self.send_to_sync(SyncMessage::RequestChunks { self.send_to_sync(SyncMessage::RequestChunks {
@ -181,6 +198,7 @@ impl Libp2pEventHandler {
request_id, request_id,
request, request,
}); });
metrics::LIBP2P_HANDLE_REQUEST_GET_CHUNKS.mark(1);
} }
Request::DataByHash(_) => { Request::DataByHash(_) => {
// ignore // ignore
@ -191,7 +209,10 @@ impl Libp2pEventHandler {
fn on_status_request(&self, peer_id: PeerId, request_id: PeerRequestId, status: StatusMessage) { fn on_status_request(&self, peer_id: PeerId, request_id: PeerRequestId, status: StatusMessage) {
debug!(%peer_id, ?status, "Received Status request"); debug!(%peer_id, ?status, "Received Status request");
let status_message = StatusMessage { data: 456 }; // dummy status message let network_id = self.network_globals.network_id();
let status_message = StatusMessage {
data: network_id.clone(),
};
debug!(%peer_id, ?status_message, "Sending Status response"); debug!(%peer_id, ?status_message, "Sending Status response");
self.send_to_network(NetworkMessage::SendResponse { self.send_to_network(NetworkMessage::SendResponse {
@ -199,6 +220,12 @@ impl Libp2pEventHandler {
id: request_id, id: request_id,
response: Response::Status(status_message), response: Response::Status(status_message),
}); });
self.on_status_message(peer_id, status, network_id);
}
fn on_status_response(&self, peer_id: PeerId, status: StatusMessage) {
let network_id = self.network_globals.network_id();
self.on_status_message(peer_id, status, network_id);
} }
pub async fn on_rpc_response( pub async fn on_rpc_response(
@ -212,10 +239,22 @@ impl Libp2pEventHandler {
match response { match response {
Response::Status(status_message) => { Response::Status(status_message) => {
debug!(%peer_id, ?status_message, "Received Status response"); debug!(%peer_id, ?status_message, "Received Status response");
match request_id {
RequestId::Router(since) => {
metrics::LIBP2P_HANDLE_RESPONSE_STATUS.mark(1);
metrics::LIBP2P_HANDLE_RESPONSE_STATUS_LATENCY.update_since(since);
}
_ => unreachable!("All status response belong to router"),
}
self.on_status_response(peer_id, status_message);
} }
Response::Chunks(response) => { Response::Chunks(response) => {
let request_id = match request_id { let request_id = match request_id {
RequestId::Sync(sync_id) => sync_id, RequestId::Sync(since, sync_id) => {
metrics::LIBP2P_HANDLE_RESPONSE_GET_CHUNKS.mark(1);
metrics::LIBP2P_HANDLE_RESPONSE_GET_CHUNKS_LATENCY.update_since(since);
sync_id
}
_ => unreachable!("All Chunks responses belong to sync"), _ => unreachable!("All Chunks responses belong to sync"),
}; };
@ -235,12 +274,16 @@ impl Libp2pEventHandler {
self.peers.write().await.update(&peer_id); self.peers.write().await.update(&peer_id);
// Check if the failed RPC belongs to sync // Check if the failed RPC belongs to sync
if let RequestId::Sync(request_id) = request_id { if let RequestId::Sync(since, request_id) = request_id {
self.send_to_sync(SyncMessage::RpcError { self.send_to_sync(SyncMessage::RpcError {
peer_id, peer_id,
request_id, request_id,
}); });
metrics::LIBP2P_HANDLE_RESPONSE_ERROR_LATENCY.update_since(since);
} }
metrics::LIBP2P_HANDLE_RESPONSE_ERROR.mark(1);
} }
pub async fn on_pubsub_message( pub async fn on_pubsub_message(
@ -254,11 +297,24 @@ impl Libp2pEventHandler {
match message { match message {
PubsubMessage::ExampleMessage(_) => MessageAcceptance::Ignore, PubsubMessage::ExampleMessage(_) => MessageAcceptance::Ignore,
PubsubMessage::FindFile(msg) => self.on_find_file(msg).await, PubsubMessage::FindFile(msg) => {
PubsubMessage::FindChunks(msg) => self.on_find_chunks(msg).await, metrics::LIBP2P_HANDLE_PUBSUB_FIND_FILE.mark(1);
PubsubMessage::AnnounceFile(msg) => self.on_announce_file(propagation_source, msg), self.on_find_file(msg).await
PubsubMessage::AnnounceChunks(msg) => self.on_announce_chunks(propagation_source, msg), }
PubsubMessage::FindChunks(msg) => {
metrics::LIBP2P_HANDLE_PUBSUB_FIND_CHUNKS.mark(1);
self.on_find_chunks(msg).await
}
PubsubMessage::AnnounceFile(msg) => {
metrics::LIBP2P_HANDLE_PUBSUB_ANNOUNCE_FILE.mark(1);
self.on_announce_file(propagation_source, msg)
}
PubsubMessage::AnnounceChunks(msg) => {
metrics::LIBP2P_HANDLE_PUBSUB_ANNOUNCE_CHUNKS.mark(1);
self.on_announce_chunks(propagation_source, msg)
}
PubsubMessage::AnnounceShardConfig(msg) => { PubsubMessage::AnnounceShardConfig(msg) => {
metrics::LIBP2P_HANDLE_PUBSUB_ANNOUNCE_SHARD.mark(1);
self.on_announce_shard_config(propagation_source, msg) self.on_announce_shard_config(propagation_source, msg)
} }
} }
@ -389,9 +445,12 @@ impl Libp2pEventHandler {
let FindFile { tx_id, timestamp } = msg; let FindFile { tx_id, timestamp } = msg;
// verify timestamp // verify timestamp
let d = duration_since(timestamp); let d = duration_since(
timestamp,
metrics::LIBP2P_HANDLE_PUBSUB_LATENCY_FIND_FILE.clone(),
);
if d < TOLERABLE_DRIFT.neg() || d > *FIND_FILE_TIMEOUT { if d < TOLERABLE_DRIFT.neg() || d > *FIND_FILE_TIMEOUT {
debug!(%timestamp, "Invalid timestamp, ignoring FindFile message"); debug!(%timestamp, ?d, "Invalid timestamp, ignoring FindFile message");
return MessageAcceptance::Ignore; return MessageAcceptance::Ignore;
} }
@ -399,7 +458,7 @@ impl Libp2pEventHandler {
if matches!(self.store.check_tx_completed(tx_id.seq).await, Ok(true)) { if matches!(self.store.check_tx_completed(tx_id.seq).await, Ok(true)) {
if let Ok(Some(tx)) = self.store.get_tx_by_seq_number(tx_id.seq).await { if let Ok(Some(tx)) = self.store.get_tx_by_seq_number(tx_id.seq).await {
if tx.id() == tx_id { if tx.id() == tx_id {
debug!(?tx_id, "Found file locally, responding to FindFile query"); trace!(?tx_id, "Found file locally, responding to FindFile query");
return match self.construct_announce_file_message(tx_id).await { return match self.construct_announce_file_message(tx_id).await {
Some(msg) => { Some(msg) => {
@ -415,7 +474,7 @@ impl Libp2pEventHandler {
// try from cache // try from cache
if let Some(mut msg) = self.file_location_cache.get_one(tx_id) { if let Some(mut msg) = self.file_location_cache.get_one(tx_id) {
debug!(?tx_id, "Found file in cache, responding to FindFile query"); trace!(?tx_id, "Found file in cache, responding to FindFile query");
msg.resend_timestamp = timestamp_now(); msg.resend_timestamp = timestamp_now();
self.publish(PubsubMessage::AnnounceFile(msg)); self.publish(PubsubMessage::AnnounceFile(msg));
@ -467,9 +526,12 @@ impl Libp2pEventHandler {
} }
// verify timestamp // verify timestamp
let d = duration_since(msg.timestamp); let d = duration_since(
msg.timestamp,
metrics::LIBP2P_HANDLE_PUBSUB_LATENCY_FIND_CHUNKS.clone(),
);
if d < TOLERABLE_DRIFT.neg() || d > *FIND_FILE_TIMEOUT { if d < TOLERABLE_DRIFT.neg() || d > *FIND_FILE_TIMEOUT {
debug!(%msg.timestamp, "Invalid timestamp, ignoring FindFile message"); debug!(%msg.timestamp, ?d, "Invalid timestamp, ignoring FindChunks message");
return MessageAcceptance::Ignore; return MessageAcceptance::Ignore;
} }
@ -503,7 +565,7 @@ impl Libp2pEventHandler {
_ => return MessageAcceptance::Accept, _ => return MessageAcceptance::Accept,
}; };
debug!(?msg, "Found chunks to respond FindChunks message"); trace!(?msg, "Found chunks to respond FindChunks message");
match self match self
.construct_announce_chunks_message(msg.tx_id, msg.index_start, msg.index_end) .construct_announce_chunks_message(msg.tx_id, msg.index_start, msg.index_end)
@ -535,10 +597,14 @@ impl Libp2pEventHandler {
None => return false, None => return false,
}; };
metrics::LIBP2P_VERIFY_ANNOUNCED_IP.mark(1);
let seen_ips: Vec<IpAddr> = match self.network_globals.peers.read().peer_info(peer_id) { let seen_ips: Vec<IpAddr> = match self.network_globals.peers.read().peer_info(peer_id) {
Some(v) => v.seen_ip_addresses().collect(), Some(v) => v.seen_ip_addresses().collect(),
None => { None => {
debug!(%announced_ip, "Failed to verify announced IP address, no peer info found"); // ignore file announcement from un-seen peers
trace!(%announced_ip, "Failed to verify announced IP address, no peer info found");
metrics::LIBP2P_VERIFY_ANNOUNCED_IP_UNSEEN.mark(1);
return false; return false;
} }
}; };
@ -546,7 +612,9 @@ impl Libp2pEventHandler {
if seen_ips.iter().any(|x| *x == announced_ip) { if seen_ips.iter().any(|x| *x == announced_ip) {
true true
} else { } else {
debug!(%announced_ip, ?seen_ips, "Failed to verify announced IP address, mismatch with seen ips"); // ignore file announcement if announced IP and seen IP mismatch
trace!(%announced_ip, ?seen_ips, "Failed to verify announced IP address, mismatch with seen ips");
metrics::LIBP2P_VERIFY_ANNOUNCED_IP_MISMATCH.mark(1);
false false
} }
} }
@ -568,14 +636,20 @@ impl Libp2pEventHandler {
} }
// verify announced ip address if required // verify announced ip address if required
if !self.config.private_ip_enabled && !self.verify_announced_address(&msg.peer_id, &addr) { if !self.config.private_ip_enabled
&& self.config.check_announced_ip
&& !self.verify_announced_address(&msg.peer_id, &addr)
{
return MessageAcceptance::Reject; return MessageAcceptance::Reject;
} }
// propagate gossip to peers // propagate gossip to peers
let d = duration_since(msg.resend_timestamp); let d = duration_since(
msg.resend_timestamp,
metrics::LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_FILE.clone(),
);
if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_FILE_TIMEOUT { if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_FILE_TIMEOUT {
debug!(%msg.resend_timestamp, "Invalid resend timestamp, ignoring AnnounceFile message"); debug!(%msg.resend_timestamp, ?d, "Invalid resend timestamp, ignoring AnnounceFile message");
return MessageAcceptance::Ignore; return MessageAcceptance::Ignore;
} }
@ -609,14 +683,20 @@ impl Libp2pEventHandler {
} }
// verify announced ip address if required // verify announced ip address if required
if !self.config.private_ip_enabled && !self.verify_announced_address(&msg.peer_id, &addr) { if !self.config.private_ip_enabled
&& self.config.check_announced_ip
&& !self.verify_announced_address(&msg.peer_id, &addr)
{
return MessageAcceptance::Reject; return MessageAcceptance::Reject;
} }
// propagate gossip to peers // propagate gossip to peers
let d = duration_since(msg.resend_timestamp); let d = duration_since(
msg.resend_timestamp,
metrics::LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_SHARD.clone(),
);
if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_SHARD_CONFIG_TIMEOUT { if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_SHARD_CONFIG_TIMEOUT {
debug!(%msg.resend_timestamp, "Invalid resend timestamp, ignoring AnnounceShardConfig message"); debug!(%msg.resend_timestamp, ?d, "Invalid resend timestamp, ignoring AnnounceShardConfig message");
return MessageAcceptance::Ignore; return MessageAcceptance::Ignore;
} }
@ -655,14 +735,20 @@ impl Libp2pEventHandler {
} }
// verify announced ip address if required // verify announced ip address if required
if !self.config.private_ip_enabled && !self.verify_announced_address(&msg.peer_id, &addr) { if !self.config.private_ip_enabled
&& self.config.check_announced_ip
&& !self.verify_announced_address(&msg.peer_id, &addr)
{
return MessageAcceptance::Reject; return MessageAcceptance::Reject;
} }
// propagate gossip to peers // propagate gossip to peers
let d = duration_since(msg.resend_timestamp); let d = duration_since(
msg.resend_timestamp,
metrics::LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_CHUNKS.clone(),
);
if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_FILE_TIMEOUT { if d < TOLERABLE_DRIFT.neg() || d > *ANNOUNCE_FILE_TIMEOUT {
debug!(%msg.resend_timestamp, "Invalid resend timestamp, ignoring AnnounceChunks message"); debug!(%msg.resend_timestamp, ?d, "Invalid resend timestamp, ignoring AnnounceChunks message");
return MessageAcceptance::Ignore; return MessageAcceptance::Ignore;
} }
@ -671,6 +757,23 @@ impl Libp2pEventHandler {
MessageAcceptance::Accept MessageAcceptance::Accept
} }
fn on_status_message(
&self,
peer_id: PeerId,
status: StatusMessage,
network_id: NetworkIdentity,
) {
if status.data != network_id {
warn!(%peer_id, ?network_id, ?status.data, "Report peer with incompatible network id");
self.send_to_network(NetworkMessage::ReportPeer {
peer_id,
action: PeerAction::Fatal,
source: ReportSource::Gossipsub,
msg: "Incompatible network id in StatusMessage",
})
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -723,7 +826,7 @@ mod tests {
let runtime = TestRuntime::default(); let runtime = TestRuntime::default();
let (network_globals, keypair) = Context::new_network_globals(); let (network_globals, keypair) = Context::new_network_globals();
let (network_send, network_recv) = mpsc::unbounded_channel(); let (network_send, network_recv) = mpsc::unbounded_channel();
let (sync_send, sync_recv) = channel::Channel::unbounded(); let (sync_send, sync_recv) = channel::Channel::unbounded("test");
let (chunk_pool_send, _chunk_pool_recv) = mpsc::unbounded_channel(); let (chunk_pool_send, _chunk_pool_recv) = mpsc::unbounded_channel();
let store = LogManager::memorydb(LogConfig::default()).unwrap(); let store = LogManager::memorydb(LogConfig::default()).unwrap();
Self { Self {
@ -762,7 +865,8 @@ mod tests {
let keypair = Keypair::generate_secp256k1(); let keypair = Keypair::generate_secp256k1();
let enr_key = CombinedKey::from_libp2p(&keypair).unwrap(); let enr_key = CombinedKey::from_libp2p(&keypair).unwrap();
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = NetworkGlobals::new(enr, 30000, 30000, vec![]); let network_globals =
NetworkGlobals::new(enr, 30000, 30000, vec![], Default::default());
let listen_addr: Multiaddr = "/ip4/127.0.0.1/tcp/30000".parse().unwrap(); let listen_addr: Multiaddr = "/ip4/127.0.0.1/tcp/30000".parse().unwrap();
network_globals.listen_multiaddrs.write().push(listen_addr); network_globals.listen_multiaddrs.write().push(listen_addr);
@ -779,7 +883,7 @@ mod tests {
}) => { }) => {
assert_eq!(peer_id, expected_peer_id); assert_eq!(peer_id, expected_peer_id);
assert!(matches!(request, Request::Status(..))); assert!(matches!(request, Request::Status(..)));
assert!(matches!(request_id, RequestId::Router)) assert!(matches!(request_id, RequestId::Router(..)))
} }
Ok(_) => panic!("Unexpected network message type received"), Ok(_) => panic!("Unexpected network message type received"),
Err(e) => panic!("No network message received: {:?}", e), Err(e) => panic!("No network message received: {:?}", e),
@ -876,7 +980,9 @@ mod tests {
let alice = PeerId::random(); let alice = PeerId::random();
let req_id = (ConnectionId::new(4), SubstreamId(12)); let req_id = (ConnectionId::new(4), SubstreamId(12));
let request = Request::Status(StatusMessage { data: 412 }); let request = Request::Status(StatusMessage {
data: Default::default(),
});
handler.on_rpc_request(alice, req_id, request).await; handler.on_rpc_request(alice, req_id, request).await;
match ctx.network_recv.try_recv() { match ctx.network_recv.try_recv() {
@ -943,7 +1049,7 @@ mod tests {
handler handler
.on_rpc_response( .on_rpc_response(
alice, alice,
RequestId::Sync(SyncId::SerialSync { tx_id: id }), RequestId::Sync(Instant::now(), SyncId::SerialSync { tx_id: id }),
Response::Chunks(data.clone()), Response::Chunks(data.clone()),
) )
.await; .await;
@ -971,7 +1077,10 @@ mod tests {
let alice = PeerId::random(); let alice = PeerId::random();
let id = TxID::random_hash(555); let id = TxID::random_hash(555);
handler handler
.on_rpc_error(alice, RequestId::Sync(SyncId::SerialSync { tx_id: id })) .on_rpc_error(
alice,
RequestId::Sync(Instant::now(), SyncId::SerialSync { tx_id: id }),
)
.await; .await;
match ctx.sync_recv.try_recv() { match ctx.sync_recv.try_recv() {

View File

@ -0,0 +1,54 @@
use std::sync::Arc;
use metrics::{register_meter, register_meter_with_group, Histogram, Meter, Sample};
lazy_static::lazy_static! {
// service
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE: Arc<dyn Meter> = register_meter("router_service_route_network_message");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_SEND_REQUEST: Arc<dyn Meter> = register_meter("router_service_route_network_message_send_request");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_SEND_RESPONSE: Arc<dyn Meter> = register_meter("router_service_route_network_message_send_response");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_SEND_ERROR_RESPONSE: Arc<dyn Meter> = register_meter("router_service_route_network_message_send_error_response");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_PUBLISH: Arc<dyn Meter> = register_meter("router_service_route_network_message_publish");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_REPORT_PEER: Arc<dyn Meter> = register_meter("router_service_route_network_message_report_peer");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_GOODBYE_PEER: Arc<dyn Meter> = register_meter("router_service_route_network_message_goodbye_peer");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER: Arc<dyn Meter> = register_meter_with_group("router_service_route_network_message_dail_peer", "all");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_ALREADY: Arc<dyn Meter> = register_meter_with_group("router_service_route_network_message_dail_peer", "already");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_NEW_OK: Arc<dyn Meter> = register_meter_with_group("router_service_route_network_message_dail_peer", "ok");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_NEW_FAIL: Arc<dyn Meter> = register_meter_with_group("router_service_route_network_message_dail_peer", "fail");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_ANNOUNCE_LOCAL_FILE: Arc<dyn Meter> = register_meter("router_service_route_network_message_announce_local_file");
pub static ref SERVICE_ROUTE_NETWORK_MESSAGE_UPNP: Arc<dyn Meter> = register_meter("router_service_route_network_message_upnp");
pub static ref SERVICE_EXPIRED_PEERS: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_service_expired_peers", 1024);
pub static ref SERVICE_EXPIRED_PEERS_DISCONNECT_OK: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_service_expired_peers_disconnect_ok", 1024);
pub static ref SERVICE_EXPIRED_PEERS_DISCONNECT_FAIL: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_service_expired_peers_disconnect_fail", 1024);
// libp2p_event_handler
pub static ref LIBP2P_SEND_STATUS: Arc<dyn Meter> = register_meter("router_libp2p_send_status");
pub static ref LIBP2P_HANDLE_PEER_CONNECTED_OUTGOING: Arc<dyn Meter> = register_meter_with_group("router_libp2p_handle_peer_connected", "outgoing");
pub static ref LIBP2P_HANDLE_PEER_CONNECTED_INCOMING: Arc<dyn Meter> = register_meter_with_group("router_libp2p_handle_peer_connected", "incoming");
pub static ref LIBP2P_HANDLE_PEER_DISCONNECTED: Arc<dyn Meter> = register_meter("router_libp2p_handle_peer_disconnected");
pub static ref LIBP2P_HANDLE_REQUEST_STATUS: Arc<dyn Meter> = register_meter("router_libp2p_handle_request_status");
pub static ref LIBP2P_HANDLE_REQUEST_GET_CHUNKS: Arc<dyn Meter> = register_meter("router_libp2p_handle_request_get_chunks");
pub static ref LIBP2P_HANDLE_RESPONSE_STATUS: Arc<dyn Meter> = register_meter_with_group("router_libp2p_handle_response_status", "qps");
pub static ref LIBP2P_HANDLE_RESPONSE_STATUS_LATENCY: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register_with_group("router_libp2p_handle_response_status", "latency", 1024);
pub static ref LIBP2P_HANDLE_RESPONSE_GET_CHUNKS: Arc<dyn Meter> = register_meter_with_group("router_libp2p_handle_response_get_chunks", "qps");
pub static ref LIBP2P_HANDLE_RESPONSE_GET_CHUNKS_LATENCY: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register_with_group("router_libp2p_handle_response_get_chunks", "latency", 1024);
pub static ref LIBP2P_HANDLE_RESPONSE_ERROR: Arc<dyn Meter> = register_meter_with_group("router_libp2p_handle_response_error", "qps");
pub static ref LIBP2P_HANDLE_RESPONSE_ERROR_LATENCY: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register_with_group("router_libp2p_handle_response_error", "latency", 1024);
pub static ref LIBP2P_HANDLE_PUBSUB_FIND_FILE: Arc<dyn Meter> = register_meter("router_libp2p_handle_pubsub_find_file");
pub static ref LIBP2P_HANDLE_PUBSUB_FIND_CHUNKS: Arc<dyn Meter> = register_meter("router_libp2p_handle_pubsub_find_chunks");
pub static ref LIBP2P_HANDLE_PUBSUB_ANNOUNCE_FILE: Arc<dyn Meter> = register_meter("router_libp2p_handle_pubsub_announce_file");
pub static ref LIBP2P_HANDLE_PUBSUB_ANNOUNCE_CHUNKS: Arc<dyn Meter> = register_meter("router_libp2p_handle_pubsub_announce_chunks");
pub static ref LIBP2P_HANDLE_PUBSUB_ANNOUNCE_SHARD: Arc<dyn Meter> = register_meter("router_libp2p_handle_pubsub_announce_shard");
pub static ref LIBP2P_HANDLE_PUBSUB_LATENCY_FIND_FILE: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_libp2p_handle_pubsub_latency_find_file", 1024);
pub static ref LIBP2P_HANDLE_PUBSUB_LATENCY_FIND_CHUNKS: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_libp2p_handle_pubsub_latency_find_chunks", 1024);
pub static ref LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_FILE: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_libp2p_handle_pubsub_latency_announce_file", 1024);
pub static ref LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_CHUNKS: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_libp2p_handle_pubsub_latency_announce_chunks", 1024);
pub static ref LIBP2P_HANDLE_PUBSUB_LATENCY_ANNOUNCE_SHARD: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("router_libp2p_handle_pubsub_latency_announce_shard", 1024);
pub static ref LIBP2P_VERIFY_ANNOUNCED_IP: Arc<dyn Meter> = register_meter("router_libp2p_verify_announced_ip");
pub static ref LIBP2P_VERIFY_ANNOUNCED_IP_UNSEEN: Arc<dyn Meter> = register_meter("router_libp2p_verify_announced_ip_unseen");
pub static ref LIBP2P_VERIFY_ANNOUNCED_IP_MISMATCH: Arc<dyn Meter> = register_meter("router_libp2p_verify_announced_ip_mismatch");
}

View File

@ -1,3 +1,4 @@
use crate::metrics;
use crate::Config; use crate::Config;
use crate::{libp2p_event_handler::Libp2pEventHandler, peer_manager::PeerManager}; use crate::{libp2p_event_handler::Libp2pEventHandler, peer_manager::PeerManager};
use chunk_pool::ChunkPoolMessage; use chunk_pool::ChunkPoolMessage;
@ -224,6 +225,8 @@ impl RouterService {
) { ) {
trace!(?msg, "Received new message"); trace!(?msg, "Received new message");
metrics::SERVICE_ROUTE_NETWORK_MESSAGE.mark(1);
match msg { match msg {
NetworkMessage::SendRequest { NetworkMessage::SendRequest {
peer_id, peer_id,
@ -231,6 +234,7 @@ impl RouterService {
request_id, request_id,
} => { } => {
self.libp2p.send_request(peer_id, request_id, request); self.libp2p.send_request(peer_id, request_id, request);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_SEND_REQUEST.mark(1);
} }
NetworkMessage::SendResponse { NetworkMessage::SendResponse {
peer_id, peer_id,
@ -238,6 +242,7 @@ impl RouterService {
id, id,
} => { } => {
self.libp2p.send_response(peer_id, id, response); self.libp2p.send_response(peer_id, id, response);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_SEND_RESPONSE.mark(1);
} }
NetworkMessage::SendErrorResponse { NetworkMessage::SendErrorResponse {
peer_id, peer_id,
@ -246,6 +251,7 @@ impl RouterService {
reason, reason,
} => { } => {
self.libp2p.respond_with_error(peer_id, id, error, reason); self.libp2p.respond_with_error(peer_id, id, error, reason);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_SEND_ERROR_RESPONSE.mark(1);
} }
NetworkMessage::Publish { messages } => { NetworkMessage::Publish { messages } => {
if self.libp2p.swarm.connected_peers().next().is_none() { if self.libp2p.swarm.connected_peers().next().is_none() {
@ -257,7 +263,7 @@ impl RouterService {
break; break;
} }
Err(err) => { Err(err) => {
debug!(address = %multiaddr, error = ?err, "Could not connect to peer") debug!(address = %multiaddr, error = ?err, "Could not connect to peer");
} }
}; };
} }
@ -275,29 +281,44 @@ impl RouterService {
"Sending pubsub messages", "Sending pubsub messages",
); );
self.libp2p.swarm.behaviour_mut().publish(messages); self.libp2p.swarm.behaviour_mut().publish(messages);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_PUBLISH.mark(1);
} }
NetworkMessage::ReportPeer { NetworkMessage::ReportPeer {
peer_id, peer_id,
action, action,
source, source,
msg, msg,
} => self.libp2p.report_peer(&peer_id, action, source, msg), } => {
self.libp2p.report_peer(&peer_id, action, source, msg);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_REPORT_PEER.mark(1);
}
NetworkMessage::GoodbyePeer { NetworkMessage::GoodbyePeer {
peer_id, peer_id,
reason, reason,
source, source,
} => self.libp2p.goodbye_peer(&peer_id, reason, source), } => {
self.libp2p.goodbye_peer(&peer_id, reason, source);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_GOODBYE_PEER.mark(1);
}
NetworkMessage::DialPeer { address, peer_id } => { NetworkMessage::DialPeer { address, peer_id } => {
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER.mark(1);
if self.libp2p.swarm.is_connected(&peer_id) { if self.libp2p.swarm.is_connected(&peer_id) {
self.libp2p_event_handler self.libp2p_event_handler
.send_to_sync(SyncMessage::PeerConnected { peer_id }); .send_to_sync(SyncMessage::PeerConnected { peer_id });
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_ALREADY.mark(1);
} else { } else {
match Swarm::dial(&mut self.libp2p.swarm, address.clone()) { match Swarm::dial(&mut self.libp2p.swarm, address.clone()) {
Ok(()) => debug!(%address, "Dialing libp2p peer"), Ok(()) => {
debug!(%address, "Dialing libp2p peer");
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_NEW_OK.mark(1);
}
Err(err) => { Err(err) => {
info!(%address, error = ?err, "Failed to dial peer"); info!(%address, error = ?err, "Failed to dial peer");
self.libp2p_event_handler self.libp2p_event_handler
.send_to_sync(SyncMessage::DailFailed { peer_id, err }); .send_to_sync(SyncMessage::DailFailed { peer_id, err });
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_DAIL_PEER_NEW_FAIL.mark(1);
} }
}; };
} }
@ -309,12 +330,14 @@ impl RouterService {
.await .await
{ {
self.libp2p_event_handler.publish(msg); self.libp2p_event_handler.publish(msg);
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_ANNOUNCE_LOCAL_FILE.mark(1);
} }
} }
NetworkMessage::UPnPMappingEstablished { NetworkMessage::UPnPMappingEstablished {
tcp_socket, tcp_socket,
udp_socket, udp_socket,
} => { } => {
metrics::SERVICE_ROUTE_NETWORK_MESSAGE_UPNP.mark(1);
self.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port())); self.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port()));
// If there is an external TCP port update, modify our local ENR. // If there is an external TCP port update, modify our local ENR.
if let Some(tcp_socket) = tcp_socket { if let Some(tcp_socket) = tcp_socket {
@ -362,17 +385,31 @@ impl RouterService {
async fn on_heartbeat(&mut self) { async fn on_heartbeat(&mut self) {
let expired_peers = self.peers.write().await.expired_peers(); let expired_peers = self.peers.write().await.expired_peers();
trace!("heartbeat, expired peers = {:?}", expired_peers.len()); let num_expired_peers = expired_peers.len() as u64;
metrics::SERVICE_EXPIRED_PEERS.update(num_expired_peers);
if num_expired_peers > 0 {
debug!(%num_expired_peers, "Heartbeat, remove expired peers")
}
let mut num_succeeded = 0;
let mut num_failed = 0;
for peer_id in expired_peers { for peer_id in expired_peers {
// async operation, once peer disconnected, swarm event `PeerDisconnected` // async operation, once peer disconnected, swarm event `PeerDisconnected`
// will be polled to handle in advance. // will be polled to handle in advance.
match self.libp2p.swarm.disconnect_peer_id(peer_id) { match self.libp2p.swarm.disconnect_peer_id(peer_id) {
Ok(_) => debug!(%peer_id, "Peer expired and disconnect it"), Ok(_) => {
Err(_) => error!(%peer_id, "Peer expired but failed to disconnect"), debug!(%peer_id, "Peer expired and disconnect it");
num_succeeded += 1;
}
Err(_) => {
debug!(%peer_id, "Peer expired but failed to disconnect");
num_failed += 1;
} }
} }
} }
metrics::SERVICE_EXPIRED_PEERS_DISCONNECT_OK.update(num_succeeded);
metrics::SERVICE_EXPIRED_PEERS_DISCONNECT_FAIL.update(num_failed);
}
} }
impl Drop for RouterService { impl Drop for RouterService {

View File

@ -26,3 +26,4 @@ storage-async = { path = "../storage-async" }
merkle_light = { path = "../../common/merkle_light" } merkle_light = { path = "../../common/merkle_light" }
merkle_tree = { path = "../../common/merkle_tree"} merkle_tree = { path = "../../common/merkle_tree"}
futures-channel = "^0.3" futures-channel = "^0.3"
metrics = { workspace = true }

View File

@ -1,7 +1,7 @@
use crate::types::{LocationInfo, NetworkInfo, PeerInfo}; use crate::types::{LocationInfo, NetworkInfo, PeerInfo};
use jsonrpsee::core::RpcResult; use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc; use jsonrpsee::proc_macros::rpc;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use sync::{FileSyncInfo, SyncServiceState}; use sync::{FileSyncInfo, SyncServiceState};
#[rpc(server, client, namespace = "admin")] #[rpc(server, client, namespace = "admin")]
@ -48,4 +48,10 @@ pub trait Rpc {
tx_seq: u64, tx_seq: u64,
all_shards: bool, all_shards: bool,
) -> RpcResult<Option<Vec<LocationInfo>>>; ) -> RpcResult<Option<Vec<LocationInfo>>>;
#[method(name = "getMetrics")]
async fn get_metrics(
&self,
maybe_prefix: Option<String>,
) -> RpcResult<BTreeMap<String, String>>;
} }

View File

@ -4,8 +4,9 @@ use crate::{error, Context};
use futures::prelude::*; use futures::prelude::*;
use jsonrpsee::core::async_trait; use jsonrpsee::core::async_trait;
use jsonrpsee::core::RpcResult; use jsonrpsee::core::RpcResult;
use metrics::{DEFAULT_GROUPING_REGISTRY, DEFAULT_REGISTRY};
use network::{multiaddr::Protocol, Multiaddr}; use network::{multiaddr::Protocol, Multiaddr};
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use std::net::IpAddr; use std::net::IpAddr;
use storage::config::all_shards_available; use storage::config::all_shards_available;
use sync::{FileSyncInfo, SyncRequest, SyncResponse, SyncServiceState}; use sync::{FileSyncInfo, SyncRequest, SyncResponse, SyncServiceState};
@ -246,4 +247,40 @@ impl RpcServer for RpcServerImpl {
Ok(None) Ok(None)
} }
} }
async fn get_metrics(
&self,
maybe_prefix: Option<String>,
) -> RpcResult<BTreeMap<String, String>> {
let mut result = BTreeMap::new();
for (name, metric) in DEFAULT_REGISTRY.read().get_all() {
match &maybe_prefix {
Some(prefix) if !name.starts_with(prefix) => {}
_ => {
result.insert(
name.clone(),
format!("{} {}", metric.get_type(), metric.get_value()),
);
}
}
}
for (group_name, metrics) in DEFAULT_GROUPING_REGISTRY.read().get_all() {
for (metric_name, metric) in metrics.iter() {
let name = format!("{}.{}", group_name, metric_name);
match &maybe_prefix {
Some(prefix) if !name.starts_with(prefix) => {}
_ => {
result.insert(
name,
format!("{} {}", metric.get_type(), metric.get_value()),
);
}
}
}
}
Ok(result)
}
} }

View File

@ -4,7 +4,7 @@ use anyhow::{anyhow, bail, Error};
use append_merkle::{ use append_merkle::{
AppendMerkleTree, Proof as RawProof, RangeProof as RawRangeProof, Sha3Algorithm, AppendMerkleTree, Proof as RawProof, RangeProof as RawRangeProof, Sha3Algorithm,
}; };
use ethereum_types::{H256, U256}; use ethereum_types::{Address, H256, U256};
use merkle_light::merkle::MerkleTree; use merkle_light::merkle::MerkleTree;
use merkle_light::proof::Proof as RawFileProof; use merkle_light::proof::Proof as RawFileProof;
use merkle_light::{hash::Algorithm, merkle::next_pow2}; use merkle_light::{hash::Algorithm, merkle::next_pow2};
@ -366,3 +366,14 @@ impl TryFrom<FileProof> for FlowProof {
} }
} }
} }
#[derive(
DeriveEncode, DeriveDecode, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize,
)]
pub struct NetworkIdentity {
/// The chain id of the blockchain network.
pub chain_id: u64,
/// The address of the deployed Flow contract on the blockchain.
pub flow_address: Address,
}

View File

@ -3,6 +3,7 @@ use clap::{arg, command, Command};
pub fn cli_app<'a>() -> Command<'a> { pub fn cli_app<'a>() -> Command<'a> {
command!() command!()
.arg(arg!(-c --config <FILE> "Sets a custom config file")) .arg(arg!(-c --config <FILE> "Sets a custom config file"))
.arg(arg!(--"log-config-file" [FILE] "Sets log configuration file (Default: log_config)"))
.arg(arg!(--"miner-key" [KEY] "Sets miner private key (Default: None)")) .arg(arg!(--"miner-key" [KEY] "Sets miner private key (Default: None)"))
.arg( .arg(
arg!(--"blockchain-rpc-endpoint" [URL] "Sets blockchain RPC endpoint (Default: http://127.0.0.1:8545)") arg!(--"blockchain-rpc-endpoint" [URL] "Sets blockchain RPC endpoint (Default: http://127.0.0.1:8545)")

View File

@ -2,11 +2,13 @@
use crate::ZgsConfig; use crate::ZgsConfig;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use ethers::prelude::{Http, Middleware, Provider};
use log_entry_sync::{CacheConfig, ContractAddress, LogSyncConfig}; use log_entry_sync::{CacheConfig, ContractAddress, LogSyncConfig};
use miner::MinerConfig; use miner::MinerConfig;
use network::NetworkConfig; use network::NetworkConfig;
use pruner::PrunerConfig; use pruner::PrunerConfig;
use rpc::RPCConfig; use rpc::RPCConfig;
use shared_types::NetworkIdentity;
use std::net::IpAddr; use std::net::IpAddr;
use std::time::Duration; use std::time::Duration;
use storage::config::ShardConfig; use storage::config::ShardConfig;
@ -25,6 +27,21 @@ impl ZgsConfig {
network_config.libp2p_port = self.network_libp2p_port; network_config.libp2p_port = self.network_libp2p_port;
network_config.disable_discovery = self.network_disable_discovery; network_config.disable_discovery = self.network_disable_discovery;
network_config.discovery_port = self.network_discovery_port; network_config.discovery_port = self.network_discovery_port;
let flow_address = self
.log_contract_address
.parse::<ContractAddress>()
.map_err(|e| format!("Unable to parse log_contract_address: {:?}", e))?;
let provider = Provider::<Http>::try_from(&self.blockchain_rpc_endpoint)
.map_err(|e| format!("Can not parse blockchain endpoint: {:?}", e))?;
let chain_id = provider
.get_chainid()
.await
.map_err(|e| format!("Unable to get chain id: {:?}", e))?
.as_u64();
network_config.network_id = NetworkIdentity {
chain_id,
flow_address,
};
if !self.network_disable_discovery { if !self.network_disable_discovery {
network_config.enr_tcp_port = Some(self.network_enr_tcp_port); network_config.enr_tcp_port = Some(self.network_enr_tcp_port);
@ -183,6 +200,9 @@ impl ZgsConfig {
iter_batch, iter_batch,
context_query_seconds, context_query_seconds,
shard_config, shard_config,
self.rate_limit_retries,
self.timeout_retries,
self.initial_backoff,
)) ))
} }
@ -218,6 +238,9 @@ impl ZgsConfig {
batch_wait_time: Duration::from_millis(self.prune_batch_wait_time_ms), batch_wait_time: Duration::from_millis(self.prune_batch_wait_time_ms),
rpc_endpoint_url: self.blockchain_rpc_endpoint.clone(), rpc_endpoint_url: self.blockchain_rpc_endpoint.clone(),
reward_address, reward_address,
rate_limit_retries: self.rate_limit_retries,
timeout_retries: self.timeout_retries,
initial_backoff: self.initial_backoff,
})) }))
} else { } else {
Ok(None) Ok(None)

View File

@ -98,6 +98,9 @@ pub struct ZgsConfig {
// file location cache config, configured by [file_location_cache] section by `config` crate. // file location cache config, configured by [file_location_cache] section by `config` crate.
pub file_location_cache: file_location_cache::Config, pub file_location_cache: file_location_cache::Config,
// metrics config, configured by [metrics] section by `config` crate.
pub metrics: metrics::MetricsConfiguration,
} }
impl Deref for ZgsConfig { impl Deref for ZgsConfig {

View File

@ -60,6 +60,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// CLI, config, and logs // CLI, config, and logs
let matches = cli::cli_app().get_matches(); let matches = cli::cli_app().get_matches();
let config = ZgsConfig::parse(&matches)?; let config = ZgsConfig::parse(&matches)?;
metrics::initialize(config.metrics.clone());
log::configure( log::configure(
&config.log_config_file, &config.log_config_file,
&config.log_directory, &config.log_directory,

View File

@ -609,9 +609,9 @@ impl LogManager {
.get_tx_by_seq_number(last_tx_seq)? .get_tx_by_seq_number(last_tx_seq)?
.expect("tx missing"); .expect("tx missing");
let mut current_len = initial_data.leaves(); let mut current_len = initial_data.leaves();
let expected_len = (last_tx.start_entry_index + last_tx.num_entries() as u64) let expected_len =
/ PORA_CHUNK_SIZE as u64; sector_to_segment(last_tx.start_entry_index + last_tx.num_entries() as u64);
match expected_len.cmp(&(current_len as u64)) { match expected_len.cmp(&(current_len)) {
Ordering::Less => { Ordering::Less => {
bail!( bail!(
"Unexpected DB: merkle tree larger than the known data size,\ "Unexpected DB: merkle tree larger than the known data size,\
@ -634,10 +634,9 @@ impl LogManager {
let previous_tx = tx_store let previous_tx = tx_store
.get_tx_by_seq_number(last_tx_seq - 1)? .get_tx_by_seq_number(last_tx_seq - 1)?
.expect("tx missing"); .expect("tx missing");
let expected_len = ((previous_tx.start_entry_index let expected_len = sector_to_segment(
+ previous_tx.num_entries() as u64) previous_tx.start_entry_index + previous_tx.num_entries() as u64,
/ PORA_CHUNK_SIZE as u64) );
as usize;
if current_len > expected_len { if current_len > expected_len {
while let Some((subtree_depth, _)) = initial_data.subtree_list.pop() while let Some((subtree_depth, _)) = initial_data.subtree_list.pop()
{ {
@ -737,13 +736,13 @@ impl LogManager {
maybe_tx_seq: Option<u64>, maybe_tx_seq: Option<u64>,
) -> Result<FlowProof> { ) -> Result<FlowProof> {
let merkle = self.merkle.read_recursive(); let merkle = self.merkle.read_recursive();
let chunk_index = flow_index / PORA_CHUNK_SIZE as u64; let seg_index = sector_to_segment(flow_index);
let top_proof = match maybe_tx_seq { let top_proof = match maybe_tx_seq {
None => merkle.pora_chunks_merkle.gen_proof(chunk_index as usize)?, None => merkle.pora_chunks_merkle.gen_proof(seg_index)?,
Some(tx_seq) => merkle Some(tx_seq) => merkle
.pora_chunks_merkle .pora_chunks_merkle
.at_version(tx_seq)? .at_version(tx_seq)?
.gen_proof(chunk_index as usize)?, .gen_proof(seg_index)?,
}; };
// TODO(zz): Maybe we can decide that all proofs are at the PoRA chunk level, so // TODO(zz): Maybe we can decide that all proofs are at the PoRA chunk level, so
@ -753,11 +752,11 @@ impl LogManager {
// and `flow_index` must be within a complete PoRA chunk. For possible future usages, // and `flow_index` must be within a complete PoRA chunk. For possible future usages,
// we'll need to find the flow length at the given root and load a partial chunk // we'll need to find the flow length at the given root and load a partial chunk
// if `flow_index` is in the last chunk. // if `flow_index` is in the last chunk.
let sub_proof = if chunk_index as usize != merkle.pora_chunks_merkle.leaves() - 1 let sub_proof = if seg_index != merkle.pora_chunks_merkle.leaves() - 1
|| merkle.last_chunk_merkle.leaves() == 0 || merkle.last_chunk_merkle.leaves() == 0
{ {
self.flow_store self.flow_store
.gen_proof_in_batch(chunk_index as usize, flow_index as usize % PORA_CHUNK_SIZE)? .gen_proof_in_batch(seg_index, flow_index as usize % PORA_CHUNK_SIZE)?
} else { } else {
match maybe_tx_seq { match maybe_tx_seq {
None => merkle None => merkle
@ -1236,3 +1235,11 @@ pub fn tx_subtree_root_list_padded(data: &[u8]) -> Vec<(usize, DataRoot)> {
root_list root_list
} }
pub fn sector_to_segment(sector_index: u64) -> usize {
(sector_index / PORA_CHUNK_SIZE as u64) as usize
}
pub fn segment_to_sector(segment_index: usize) -> usize {
segment_index * PORA_CHUNK_SIZE
}

View File

@ -20,6 +20,8 @@ tracing = "0.1.35"
eth2_ssz = "0.4.0" eth2_ssz = "0.4.0"
serde = { version = "1.0.137", features = ["derive"] } serde = { version = "1.0.137", features = ["derive"] }
duration-str = "0.5.1" duration-str = "0.5.1"
lazy_static = "1.4.0"
metrics = { workspace = true }
[dev-dependencies] [dev-dependencies]
merkle_light = { path = "../../common/merkle_light" } merkle_light = { path = "../../common/merkle_light" }

View File

@ -1,7 +1,7 @@
use crate::{controllers::SyncState, Config, SyncRequest, SyncResponse, SyncSender}; use crate::{controllers::SyncState, SyncRequest, SyncResponse, SyncSender};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashSet, fmt::Debug, sync::Arc}; use std::{collections::HashSet, fmt::Debug, sync::Arc, time::Duration};
use storage_async::Store; use storage_async::Store;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -15,18 +15,23 @@ pub enum SyncResult {
/// Supports to sync files concurrently. /// Supports to sync files concurrently.
#[derive(Clone)] #[derive(Clone)]
pub struct Batcher { pub struct Batcher {
pub(crate) config: Config,
capacity: usize, capacity: usize,
find_peer_timeout: Duration,
tasks: Arc<RwLock<HashSet<u64>>>, // files to sync tasks: Arc<RwLock<HashSet<u64>>>, // files to sync
store: Store, store: Store,
sync_send: SyncSender, sync_send: SyncSender,
} }
impl Batcher { impl Batcher {
pub fn new(config: Config, capacity: usize, store: Store, sync_send: SyncSender) -> Self { pub fn new(
capacity: usize,
find_peer_timeout: Duration,
store: Store,
sync_send: SyncSender,
) -> Self {
Self { Self {
config,
capacity, capacity,
find_peer_timeout,
tasks: Default::default(), tasks: Default::default(),
store, store,
sync_send, sync_send,
@ -128,7 +133,7 @@ impl Batcher {
// finding peers timeout // finding peers timeout
Some(SyncState::FindingPeers { origin, .. }) Some(SyncState::FindingPeers { origin, .. })
if origin.elapsed() > self.config.find_peer_timeout => if origin.elapsed() > self.find_peer_timeout =>
{ {
debug!(%tx_seq, "Terminate file sync due to finding peers timeout"); debug!(%tx_seq, "Terminate file sync due to finding peers timeout");
self.terminate_file_sync(tx_seq, false).await; self.terminate_file_sync(tx_seq, false).await;
@ -137,7 +142,7 @@ impl Batcher {
// connecting peers timeout // connecting peers timeout
Some(SyncState::ConnectingPeers { origin, .. }) Some(SyncState::ConnectingPeers { origin, .. })
if origin.elapsed() > self.config.find_peer_timeout => if origin.elapsed() > self.find_peer_timeout =>
{ {
debug!(%tx_seq, "Terminate file sync due to connecting peers timeout"); debug!(%tx_seq, "Terminate file sync due to connecting peers timeout");
self.terminate_file_sync(tx_seq, false).await; self.terminate_file_sync(tx_seq, false).await;

View File

@ -1,5 +1,8 @@
use super::{batcher::Batcher, sync_store::SyncStore}; use super::{batcher::Batcher, sync_store::SyncStore};
use crate::{auto_sync::batcher::SyncResult, Config, SyncSender}; use crate::{
auto_sync::{batcher::SyncResult, metrics},
Config, SyncSender,
};
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::{ use std::sync::{
@ -19,6 +22,7 @@ pub struct RandomBatcherState {
#[derive(Clone)] #[derive(Clone)]
pub struct RandomBatcher { pub struct RandomBatcher {
config: Config,
batcher: Batcher, batcher: Batcher,
sync_store: Arc<SyncStore>, sync_store: Arc<SyncStore>,
} }
@ -31,7 +35,13 @@ impl RandomBatcher {
sync_store: Arc<SyncStore>, sync_store: Arc<SyncStore>,
) -> Self { ) -> Self {
Self { Self {
batcher: Batcher::new(config, config.max_random_workers, store, sync_send), config,
batcher: Batcher::new(
config.max_random_workers,
config.random_find_peer_timeout,
store,
sync_send,
),
sync_store, sync_store,
} }
} }
@ -53,10 +63,16 @@ impl RandomBatcher {
// disable file sync until catched up // disable file sync until catched up
if !catched_up.load(Ordering::Relaxed) { if !catched_up.load(Ordering::Relaxed) {
trace!("Cannot sync file in catch-up phase"); trace!("Cannot sync file in catch-up phase");
sleep(self.batcher.config.auto_sync_idle_interval).await; sleep(self.config.auto_sync_idle_interval).await;
continue; continue;
} }
if let Ok(state) = self.get_state().await {
metrics::RANDOM_STATE_TXS_SYNCING.update(state.tasks.len() as u64);
metrics::RANDOM_STATE_TXS_READY.update(state.ready_txs as u64);
metrics::RANDOM_STATE_TXS_PENDING.update(state.pending_txs as u64);
}
match self.sync_once().await { match self.sync_once().await {
Ok(true) => {} Ok(true) => {}
Ok(false) => { Ok(false) => {
@ -64,11 +80,11 @@ impl RandomBatcher {
"File sync still in progress or idle, state = {:?}", "File sync still in progress or idle, state = {:?}",
self.get_state().await self.get_state().await
); );
sleep(self.batcher.config.auto_sync_idle_interval).await; sleep(self.config.auto_sync_idle_interval).await;
} }
Err(err) => { Err(err) => {
warn!(%err, "Failed to sync file once, state = {:?}", self.get_state().await); warn!(%err, "Failed to sync file once, state = {:?}", self.get_state().await);
sleep(self.batcher.config.auto_sync_error_interval).await; sleep(self.config.auto_sync_error_interval).await;
} }
} }
} }
@ -86,6 +102,11 @@ impl RandomBatcher {
}; };
debug!(%tx_seq, ?sync_result, "Completed to sync file, state = {:?}", self.get_state().await); debug!(%tx_seq, ?sync_result, "Completed to sync file, state = {:?}", self.get_state().await);
match sync_result {
SyncResult::Completed => metrics::RANDOM_SYNC_RESULT_COMPLETED.mark(1),
SyncResult::Failed => metrics::RANDOM_SYNC_RESULT_FAILED.inc(1),
SyncResult::Timeout => metrics::RANDOM_SYNC_RESULT_TIMEOUT.inc(1),
}
match sync_result { match sync_result {
SyncResult::Completed => self.sync_store.remove_tx(tx_seq).await?, SyncResult::Completed => self.sync_store.remove_tx(tx_seq).await?,

View File

@ -2,7 +2,7 @@ use super::{
batcher::{Batcher, SyncResult}, batcher::{Batcher, SyncResult},
sync_store::SyncStore, sync_store::SyncStore,
}; };
use crate::{Config, SyncSender}; use crate::{auto_sync::metrics, Config, SyncSender};
use anyhow::Result; use anyhow::Result;
use log_entry_sync::LogSyncEvent; use log_entry_sync::LogSyncEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -23,6 +23,7 @@ use tokio::{
/// Supports to sync files in sequence concurrently. /// Supports to sync files in sequence concurrently.
#[derive(Clone)] #[derive(Clone)]
pub struct SerialBatcher { pub struct SerialBatcher {
config: Config,
batcher: Batcher, batcher: Batcher,
/// Next tx seq to sync. /// Next tx seq to sync.
@ -80,13 +81,17 @@ impl SerialBatcher {
sync_send: SyncSender, sync_send: SyncSender,
sync_store: Arc<SyncStore>, sync_store: Arc<SyncStore>,
) -> Result<Self> { ) -> Result<Self> {
let capacity = config.max_sequential_workers;
// continue file sync from break point in db // continue file sync from break point in db
let (next_tx_seq, max_tx_seq) = sync_store.get_tx_seq_range().await?; let (next_tx_seq, max_tx_seq) = sync_store.get_tx_seq_range().await?;
Ok(Self { Ok(Self {
batcher: Batcher::new(config, capacity, store, sync_send), config,
batcher: Batcher::new(
config.max_sequential_workers,
config.sequential_find_peer_timeout,
store,
sync_send,
),
next_tx_seq: Arc::new(AtomicU64::new(next_tx_seq.unwrap_or(0))), next_tx_seq: Arc::new(AtomicU64::new(next_tx_seq.unwrap_or(0))),
max_tx_seq: Arc::new(AtomicU64::new(max_tx_seq.unwrap_or(u64::MAX))), max_tx_seq: Arc::new(AtomicU64::new(max_tx_seq.unwrap_or(u64::MAX))),
pending_completed_txs: Default::default(), pending_completed_txs: Default::default(),
@ -136,10 +141,19 @@ impl SerialBatcher {
// disable file sync until catched up // disable file sync until catched up
if !catched_up.load(Ordering::Relaxed) { if !catched_up.load(Ordering::Relaxed) {
trace!("Cannot sync file in catch-up phase"); trace!("Cannot sync file in catch-up phase");
sleep(self.batcher.config.auto_sync_idle_interval).await; sleep(self.config.auto_sync_idle_interval).await;
continue; continue;
} }
// update metrics
let state = self.get_state().await;
metrics::SEQUENTIAL_STATE_TXS_SYNCING.update(state.tasks.len() as u64);
if state.max != u64::MAX {
metrics::SEQUENTIAL_STATE_GAP_NEXT_MAX.update((state.max - state.next) as usize);
}
metrics::SEQUENTIAL_STATE_TXS_PENDING.update(state.pendings.len() as u64);
metrics::SEQUENTIAL_STATE_GAP_NEXT_DB.update((state.next - state.next_in_db) as usize);
// sync files // sync files
match self.sync_once().await { match self.sync_once().await {
Ok(true) => {} Ok(true) => {}
@ -148,11 +162,11 @@ impl SerialBatcher {
"File sync still in progress or idle, state = {:?}", "File sync still in progress or idle, state = {:?}",
self.get_state().await self.get_state().await
); );
sleep(self.batcher.config.auto_sync_idle_interval).await; sleep(self.config.auto_sync_idle_interval).await;
} }
Err(err) => { Err(err) => {
warn!(%err, "Failed to sync file once, state = {:?}", self.get_state().await); warn!(%err, "Failed to sync file once, state = {:?}", self.get_state().await);
sleep(self.batcher.config.auto_sync_error_interval).await; sleep(self.config.auto_sync_error_interval).await;
} }
} }
} }
@ -247,6 +261,12 @@ impl SerialBatcher {
}; };
info!(%tx_seq, ?sync_result, "Completed to sync file, state = {:?}", self.get_state().await); info!(%tx_seq, ?sync_result, "Completed to sync file, state = {:?}", self.get_state().await);
match sync_result {
SyncResult::Completed => metrics::SEQUENTIAL_SYNC_RESULT_COMPLETED.mark(1),
SyncResult::Failed => metrics::SEQUENTIAL_SYNC_RESULT_FAILED.inc(1),
SyncResult::Timeout => metrics::SEQUENTIAL_SYNC_RESULT_TIMEOUT.inc(1),
}
self.pending_completed_txs self.pending_completed_txs
.write() .write()
.await .await

View File

@ -21,6 +21,7 @@ pub struct AutoSyncManager {
pub serial: SerialBatcher, pub serial: SerialBatcher,
pub random: RandomBatcher, pub random: RandomBatcher,
pub file_announcement_send: UnboundedSender<u64>, pub file_announcement_send: UnboundedSender<u64>,
pub catched_up: Arc<AtomicBool>,
} }
impl AutoSyncManager { impl AutoSyncManager {
@ -52,11 +53,12 @@ impl AutoSyncManager {
executor.spawn(random.clone().start(catched_up.clone()), "auto_sync_random"); executor.spawn(random.clone().start(catched_up.clone()), "auto_sync_random");
// handle on catched up notification // handle on catched up notification
let catched_up_cloned = catched_up.clone();
executor.spawn( executor.spawn(
async move { async move {
if catch_up_end_recv.await.is_ok() { if catch_up_end_recv.await.is_ok() {
info!("log entry catched up"); info!("log entry catched up");
catched_up.store(true, Ordering::Relaxed); catched_up_cloned.store(true, Ordering::Relaxed);
} }
}, },
"auto_sync_wait_for_catchup", "auto_sync_wait_for_catchup",
@ -66,6 +68,7 @@ impl AutoSyncManager {
serial, serial,
random, random,
file_announcement_send: send, file_announcement_send: send,
catched_up,
}) })
} }
} }

View File

@ -0,0 +1,24 @@
use std::sync::Arc;
use metrics::{register_meter, Counter, CounterUsize, Gauge, GaugeUsize, Histogram, Meter, Sample};
lazy_static::lazy_static! {
// sequential auto sync
pub static ref SEQUENTIAL_STATE_TXS_SYNCING: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_auto_sequential_state_txs_syncing", 1024);
pub static ref SEQUENTIAL_STATE_GAP_NEXT_MAX: Arc<dyn Gauge<usize>> = GaugeUsize::register("sync_auto_sequential_state_gap_next_max");
pub static ref SEQUENTIAL_STATE_TXS_PENDING: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_auto_sequential_state_txs_pending", 1024);
pub static ref SEQUENTIAL_STATE_GAP_NEXT_DB: Arc<dyn Gauge<usize>> = GaugeUsize::register("sync_auto_sequential_state_gap_next_db");
pub static ref SEQUENTIAL_SYNC_RESULT_COMPLETED: Arc<dyn Meter> = register_meter("sync_auto_sequential_sync_result_completed");
pub static ref SEQUENTIAL_SYNC_RESULT_FAILED: Arc<dyn Counter<usize>> = CounterUsize::register("sync_auto_sequential_sync_result_failed");
pub static ref SEQUENTIAL_SYNC_RESULT_TIMEOUT: Arc<dyn Counter<usize>> = CounterUsize::register("sync_auto_sequential_sync_result_timeout");
// random auto sync
pub static ref RANDOM_STATE_TXS_SYNCING: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_auto_random_state_txs_syncing", 1024);
pub static ref RANDOM_STATE_TXS_READY: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_auto_random_state_txs_ready", 1024);
pub static ref RANDOM_STATE_TXS_PENDING: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_auto_random_state_txs_pending", 1024);
pub static ref RANDOM_SYNC_RESULT_COMPLETED: Arc<dyn Meter> = register_meter("sync_auto_random_sync_result_completed");
pub static ref RANDOM_SYNC_RESULT_FAILED: Arc<dyn Counter<usize>> = CounterUsize::register("sync_auto_random_sync_result_failed");
pub static ref RANDOM_SYNC_RESULT_TIMEOUT: Arc<dyn Counter<usize>> = CounterUsize::register("sync_auto_random_sync_result_timeout");
}

View File

@ -2,5 +2,6 @@ mod batcher;
pub mod batcher_random; pub mod batcher_random;
pub mod batcher_serial; pub mod batcher_serial;
pub mod manager; pub mod manager;
mod metrics;
pub mod sync_store; pub mod sync_store;
mod tx_store; mod tx_store;

View File

@ -0,0 +1,11 @@
use std::sync::Arc;
use metrics::{register_timer, Counter, CounterUsize, Histogram, Sample, Timer};
lazy_static::lazy_static! {
pub static ref SERIAL_SYNC_FILE_COMPLETED: Arc<dyn Timer> = register_timer("sync_controllers_serial_sync_file_completed");
pub static ref SERIAL_SYNC_CHUNKS_COMPLETED: Arc<dyn Timer> = register_timer("sync_controllers_serial_sync_chunks_completed");
pub static ref SERIAL_SYNC_SEGMENT_LATENCY: Arc<dyn Histogram> = Sample::ExpDecay(0.015).register("sync_controllers_serial_sync_segment_latency", 1024);
pub static ref SERIAL_SYNC_SEGMENT_TIMEOUT: Arc<dyn Counter<usize>> = CounterUsize::register("sync_controllers_serial_sync_segment_timeout");
pub static ref SERIAL_SYNC_UNEXPECTED_ERRORS: Arc<dyn Counter<usize>> = CounterUsize::register("sync_controllers_serial_sync_unexpected_errors");
}

View File

@ -1,3 +1,4 @@
mod metrics;
mod peers; mod peers;
mod serial; mod serial;
@ -17,10 +18,12 @@ pub struct FileSyncGoal {
pub index_start: u64, pub index_start: u64,
/// Chunk index to sync to (exclusive). /// Chunk index to sync to (exclusive).
pub index_end: u64, pub index_end: u64,
/// `true` if we are syncing all the needed data of this file.
pub all_chunks: bool,
} }
impl FileSyncGoal { impl FileSyncGoal {
pub fn new(num_chunks: u64, index_start: u64, index_end: u64) -> Self { pub fn new(num_chunks: u64, index_start: u64, index_end: u64, all_chunks: bool) -> Self {
assert!( assert!(
index_start < index_end && index_end <= num_chunks, index_start < index_end && index_end <= num_chunks,
"invalid index_end" "invalid index_end"
@ -29,15 +32,16 @@ impl FileSyncGoal {
num_chunks, num_chunks,
index_start, index_start,
index_end, index_end,
all_chunks,
} }
} }
pub fn new_file(num_chunks: u64) -> Self { pub fn new_file(num_chunks: u64) -> Self {
Self::new(num_chunks, 0, num_chunks) Self::new(num_chunks, 0, num_chunks, true)
} }
pub fn is_all_chunks(&self) -> bool { pub fn is_all_chunks(&self) -> bool {
self.index_start == 0 && self.index_end == self.num_chunks self.all_chunks
} }
} }

View File

@ -1,6 +1,6 @@
use crate::context::SyncNetworkContext; use crate::context::SyncNetworkContext;
use crate::controllers::peers::{PeerState, SyncPeers}; use crate::controllers::peers::{PeerState, SyncPeers};
use crate::controllers::{FileSyncGoal, FileSyncInfo}; use crate::controllers::{metrics, FileSyncGoal, FileSyncInfo};
use crate::{Config, InstantWrapper}; use crate::{Config, InstantWrapper};
use file_location_cache::FileLocationCache; use file_location_cache::FileLocationCache;
use libp2p::swarm::DialError; use libp2p::swarm::DialError;
@ -12,7 +12,7 @@ use network::{
use rand::Rng; use rand::Rng;
use shared_types::{timestamp_now, ChunkArrayWithProof, TxID, CHUNK_SIZE}; use shared_types::{timestamp_now, ChunkArrayWithProof, TxID, CHUNK_SIZE};
use std::{sync::Arc, time::Instant}; use std::{sync::Arc, time::Instant};
use storage::log_store::log_manager::PORA_CHUNK_SIZE; use storage::log_store::log_manager::{sector_to_segment, segment_to_sector, PORA_CHUNK_SIZE};
use storage_async::Store; use storage_async::Store;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -139,7 +139,7 @@ impl SerialSyncController {
if let Some((start, end)) = maybe_range { if let Some((start, end)) = maybe_range {
// Sync new chunks regardless of previously downloaded file or chunks. // Sync new chunks regardless of previously downloaded file or chunks.
// It's up to client to avoid duplicated chunks sync. // It's up to client to avoid duplicated chunks sync.
self.goal = FileSyncGoal::new(self.goal.num_chunks, start, end); self.goal = FileSyncGoal::new(self.goal.num_chunks, start, end, false);
self.next_chunk = start; self.next_chunk = start;
} else if self.goal.is_all_chunks() { } else if self.goal.is_all_chunks() {
// retry the failed file sync at break point // retry the failed file sync at break point
@ -258,7 +258,8 @@ impl SerialSyncController {
// request next chunk array // request next chunk array
let from_chunk = self.next_chunk; let from_chunk = self.next_chunk;
let to_chunk = std::cmp::min(from_chunk + PORA_CHUNK_SIZE as u64, self.goal.index_end); let to_chunk = std::cmp::min(from_chunk + PORA_CHUNK_SIZE as u64, self.goal.index_end);
let request_id = network::RequestId::Sync(RequestId::SerialSync { tx_id: self.tx_id }); let request_id =
network::RequestId::Sync(Instant::now(), RequestId::SerialSync { tx_id: self.tx_id });
// TODO: It's possible that we read it while `nex_tx_seq - 1` is still being committed. // TODO: It's possible that we read it while `nex_tx_seq - 1` is still being committed.
// We can wait for its commitment, but this will slow down this state machine. // We can wait for its commitment, but this will slow down this state machine.
// Or we can use `next_tx_seq - 2`, but for a restarted node without receiving new // Or we can use `next_tx_seq - 2`, but for a restarted node without receiving new
@ -311,15 +312,15 @@ impl SerialSyncController {
.peers .peers
.add_new_peer_with_config(peer_id, addr.clone(), shard_config) .add_new_peer_with_config(peer_id, addr.clone(), shard_config)
{ {
info!(%self.tx_seq, %peer_id, %addr, "Found new peer"); debug!(%self.tx_seq, %peer_id, %addr, "Found new peer");
true true
} else { } else {
// e.g. multiple `AnnounceFile` messages propagated // e.g. multiple `AnnounceFile` messages propagated
debug!(%self.tx_seq, %peer_id, %addr, "Found an existing peer"); trace!(%self.tx_seq, %peer_id, %addr, "Found an existing peer");
false false
} }
} else { } else {
debug!(%self.tx_seq, %peer_id, %addr, "Found peer without shard config"); info!(%self.tx_seq, %peer_id, %addr, "Found peer without shard config");
false false
} }
} }
@ -406,7 +407,6 @@ impl SerialSyncController {
} }
pub async fn on_response(&mut self, from_peer_id: PeerId, response: ChunkArrayWithProof) { pub async fn on_response(&mut self, from_peer_id: PeerId, response: ChunkArrayWithProof) {
debug!(%self.tx_seq, %from_peer_id, "Received RPC response");
if self.handle_on_response_mismatch(from_peer_id) { if self.handle_on_response_mismatch(from_peer_id) {
return; return;
} }
@ -429,6 +429,7 @@ impl SerialSyncController {
let data_len = response.chunks.data.len(); let data_len = response.chunks.data.len();
if data_len == 0 || data_len % CHUNK_SIZE > 0 { if data_len == 0 || data_len % CHUNK_SIZE > 0 {
warn!(%from_peer_id, %self.tx_seq, %data_len, "Invalid chunk response data length"); warn!(%from_peer_id, %self.tx_seq, %data_len, "Invalid chunk response data length");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.ban_peer(from_peer_id, "Invalid chunk response data length"); self.ban_peer(from_peer_id, "Invalid chunk response data length");
self.state = SyncState::Idle; self.state = SyncState::Idle;
return; return;
@ -466,6 +467,7 @@ impl SerialSyncController {
} }
Err(err) => { Err(err) => {
warn!(%err, %self.tx_seq, "Failed to validate chunks response"); warn!(%err, %self.tx_seq, "Failed to validate chunks response");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.ban_peer(from_peer_id, "Chunk array validation failed"); self.ban_peer(from_peer_id, "Chunk array validation failed");
self.state = SyncState::Idle; self.state = SyncState::Idle;
return; return;
@ -474,11 +476,13 @@ impl SerialSyncController {
self.failures = 0; self.failures = 0;
metrics::SERIAL_SYNC_SEGMENT_LATENCY.update_since(since.0);
let shard_config = self.store.get_store().flow().get_shard_config(); let shard_config = self.store.get_store().flow().get_shard_config();
let next_chunk = shard_config.next_segment_index( let next_chunk = segment_to_sector(shard_config.next_segment_index(
(from_chunk / PORA_CHUNK_SIZE as u64) as usize, sector_to_segment(from_chunk),
(self.tx_start_chunk_in_flow / PORA_CHUNK_SIZE as u64) as usize, sector_to_segment(self.tx_start_chunk_in_flow),
) * PORA_CHUNK_SIZE; ));
// store in db // store in db
match self match self
.store .store
@ -488,6 +492,7 @@ impl SerialSyncController {
Ok(true) => self.next_chunk = next_chunk as u64, Ok(true) => self.next_chunk = next_chunk as u64,
Ok(false) => { Ok(false) => {
warn!(%self.tx_seq, ?self.tx_id, "Transaction reverted while storing chunks"); warn!(%self.tx_seq, ?self.tx_id, "Transaction reverted while storing chunks");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.state = SyncState::Failed { self.state = SyncState::Failed {
reason: FailureReason::TxReverted(self.tx_id), reason: FailureReason::TxReverted(self.tx_id),
}; };
@ -495,6 +500,7 @@ impl SerialSyncController {
} }
Err(err) => { Err(err) => {
error!(%err, %self.tx_seq, "Unexpected DB error while storing chunks"); error!(%err, %self.tx_seq, "Unexpected DB error while storing chunks");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.state = SyncState::Failed { self.state = SyncState::Failed {
reason: FailureReason::DBError(err.to_string()), reason: FailureReason::DBError(err.to_string()),
}; };
@ -511,6 +517,7 @@ impl SerialSyncController {
// completed to download chunks // completed to download chunks
if !self.goal.is_all_chunks() { if !self.goal.is_all_chunks() {
self.state = SyncState::Completed; self.state = SyncState::Completed;
metrics::SERIAL_SYNC_CHUNKS_COMPLETED.update_since(self.since.0);
return; return;
} }
@ -523,15 +530,18 @@ impl SerialSyncController {
Ok(true) => { Ok(true) => {
info!(%self.tx_seq, "Succeeded to finalize file"); info!(%self.tx_seq, "Succeeded to finalize file");
self.state = SyncState::Completed; self.state = SyncState::Completed;
metrics::SERIAL_SYNC_FILE_COMPLETED.update_since(self.since.0);
} }
Ok(false) => { Ok(false) => {
warn!(?self.tx_id, %self.tx_seq, "Transaction reverted during finalize_tx"); warn!(?self.tx_id, %self.tx_seq, "Transaction reverted during finalize_tx");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.state = SyncState::Failed { self.state = SyncState::Failed {
reason: FailureReason::TxReverted(self.tx_id), reason: FailureReason::TxReverted(self.tx_id),
}; };
} }
Err(err) => { Err(err) => {
error!(%err, %self.tx_seq, "Unexpected error during finalize_tx"); error!(%err, %self.tx_seq, "Unexpected error during finalize_tx");
metrics::SERIAL_SYNC_UNEXPECTED_ERRORS.inc(1);
self.state = SyncState::Failed { self.state = SyncState::Failed {
reason: FailureReason::DBError(err.to_string()), reason: FailureReason::DBError(err.to_string()),
}; };
@ -566,12 +576,11 @@ impl SerialSyncController {
/// Randomly select a `Connected` peer to sync chunks. /// Randomly select a `Connected` peer to sync chunks.
fn select_peer_for_request(&self, request: &GetChunksRequest) -> Option<PeerId> { fn select_peer_for_request(&self, request: &GetChunksRequest) -> Option<PeerId> {
let segment_index = let segment_index = sector_to_segment(request.index_start + self.tx_start_chunk_in_flow);
(request.index_start + self.tx_start_chunk_in_flow) / PORA_CHUNK_SIZE as u64;
let mut peers = self.peers.filter_peers(vec![PeerState::Connected]); let mut peers = self.peers.filter_peers(vec![PeerState::Connected]);
peers.retain(|peer_id| match self.peers.shard_config(peer_id) { peers.retain(|peer_id| match self.peers.shard_config(peer_id) {
Some(v) => v.in_range(segment_index), Some(v) => v.in_range(segment_index as u64),
None => false, None => false,
}); });
@ -675,6 +684,7 @@ impl SerialSyncController {
debug!(%self.tx_seq, "No peer to continue downloading and try to find other peers to download"); debug!(%self.tx_seq, "No peer to continue downloading and try to find other peers to download");
self.state = SyncState::Idle; self.state = SyncState::Idle;
} else if since.elapsed() >= self.config.peer_chunks_download_timeout { } else if since.elapsed() >= self.config.peer_chunks_download_timeout {
metrics::SERIAL_SYNC_SEGMENT_TIMEOUT.inc(1);
self.handle_response_failure(peer_id, "RPC timeout"); self.handle_response_failure(peer_id, "RPC timeout");
} else { } else {
completed = true; completed = true;
@ -874,7 +884,7 @@ mod tests {
); );
match request_id { match request_id {
network::RequestId::Sync(sync_id) => match sync_id { network::RequestId::Sync(_, sync_id) => match sync_id {
network::SyncId::SerialSync { tx_id } => { network::SyncId::SerialSync { tx_id } => {
assert_eq!(tx_id, controller.tx_id); assert_eq!(tx_id, controller.tx_id);
} }

View File

@ -52,7 +52,9 @@ pub struct Config {
pub max_sequential_workers: usize, pub max_sequential_workers: usize,
pub max_random_workers: usize, pub max_random_workers: usize,
#[serde(deserialize_with = "deserialize_duration")] #[serde(deserialize_with = "deserialize_duration")]
pub find_peer_timeout: Duration, pub sequential_find_peer_timeout: Duration,
#[serde(deserialize_with = "deserialize_duration")]
pub random_find_peer_timeout: Duration,
} }
impl Default for Config { impl Default for Config {
@ -61,26 +63,27 @@ impl Default for Config {
// sync service config // sync service config
heartbeat_interval: Duration::from_secs(5), heartbeat_interval: Duration::from_secs(5),
auto_sync_enabled: false, auto_sync_enabled: false,
max_sync_files: 16, max_sync_files: 32,
sync_file_by_rpc_enabled: true, sync_file_by_rpc_enabled: true,
sync_file_on_announcement_enabled: false, sync_file_on_announcement_enabled: false,
// serial sync config // serial sync config
max_chunks_to_request: 2 * 1024, max_chunks_to_request: 2 * 1024,
max_request_failures: 5, max_request_failures: 5,
peer_connect_timeout: Duration::from_secs(5), peer_connect_timeout: Duration::from_secs(15),
peer_disconnect_timeout: Duration::from_secs(5), peer_disconnect_timeout: Duration::from_secs(15),
peer_find_timeout: Duration::from_secs(5), peer_find_timeout: Duration::from_secs(30),
peer_chunks_download_timeout: Duration::from_secs(5), peer_chunks_download_timeout: Duration::from_secs(15),
peer_wait_outgoing_connection_timeout: Duration::from_secs(10), peer_wait_outgoing_connection_timeout: Duration::from_secs(10),
peer_next_chunks_request_wait_timeout: Duration::from_secs(3), peer_next_chunks_request_wait_timeout: Duration::from_secs(3),
// auto sync config // auto sync config
auto_sync_idle_interval: Duration::from_secs(3), auto_sync_idle_interval: Duration::from_secs(3),
auto_sync_error_interval: Duration::from_secs(10), auto_sync_error_interval: Duration::from_secs(10),
max_sequential_workers: 8, max_sequential_workers: 24,
max_random_workers: 4, max_random_workers: 8,
find_peer_timeout: Duration::from_secs(10), sequential_find_peer_timeout: Duration::from_secs(60),
random_find_peer_timeout: Duration::from_secs(500),
} }
} }
} }
@ -110,6 +113,7 @@ impl InstantWrapper {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SyncServiceState { pub struct SyncServiceState {
pub num_syncing: usize, pub num_syncing: usize,
pub catched_up: Option<bool>,
pub auto_sync_serial: Option<SerialBatcherState>, pub auto_sync_serial: Option<SerialBatcherState>,
pub auto_sync_random: Option<RandomBatcherState>, pub auto_sync_random: Option<RandomBatcherState>,
} }

View File

@ -4,7 +4,7 @@ use crate::controllers::{
FailureReason, FileSyncGoal, FileSyncInfo, SerialSyncController, SyncState, FailureReason, FileSyncGoal, FileSyncInfo, SerialSyncController, SyncState,
}; };
use crate::{Config, SyncServiceState}; use crate::{Config, SyncServiceState};
use anyhow::{bail, Result}; use anyhow::{anyhow, bail, Result};
use file_location_cache::FileLocationCache; use file_location_cache::FileLocationCache;
use libp2p::swarm::DialError; use libp2p::swarm::DialError;
use log_entry_sync::LogSyncEvent; use log_entry_sync::LogSyncEvent;
@ -14,13 +14,16 @@ use network::{
rpc::GetChunksRequest, rpc::RPCResponseErrorCode, Multiaddr, NetworkMessage, PeerId, rpc::GetChunksRequest, rpc::RPCResponseErrorCode, Multiaddr, NetworkMessage, PeerId,
PeerRequestId, SyncId as RequestId, PeerRequestId, SyncId as RequestId,
}; };
use shared_types::{bytes_to_chunks, timestamp_now, ChunkArrayWithProof, TxID}; use shared_types::{bytes_to_chunks, timestamp_now, ChunkArrayWithProof, Transaction, TxID};
use std::sync::atomic::Ordering;
use std::{ use std::{
cmp,
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap},
sync::Arc, sync::Arc,
}; };
use storage::config::ShardConfig; use storage::config::ShardConfig;
use storage::error::Result as StorageResult; use storage::error::Result as StorageResult;
use storage::log_store::log_manager::{sector_to_segment, segment_to_sector, PORA_CHUNK_SIZE};
use storage::log_store::Store as LogStore; use storage::log_store::Store as LogStore;
use storage_async::Store; use storage_async::Store;
use tokio::sync::{broadcast, mpsc, oneshot}; use tokio::sync::{broadcast, mpsc, oneshot};
@ -156,7 +159,7 @@ impl SyncService {
event_recv: broadcast::Receiver<LogSyncEvent>, event_recv: broadcast::Receiver<LogSyncEvent>,
catch_up_end_recv: oneshot::Receiver<()>, catch_up_end_recv: oneshot::Receiver<()>,
) -> Result<SyncSender> { ) -> Result<SyncSender> {
let (sync_send, sync_recv) = channel::Channel::unbounded(); let (sync_send, sync_recv) = channel::Channel::unbounded("sync");
let store = Store::new(store, executor.clone()); let store = Store::new(store, executor.clone());
// init auto sync // init auto sync
@ -275,11 +278,13 @@ impl SyncService {
let state = match &self.auto_sync_manager { let state = match &self.auto_sync_manager {
Some(manager) => SyncServiceState { Some(manager) => SyncServiceState {
num_syncing: self.controllers.len(), num_syncing: self.controllers.len(),
catched_up: Some(manager.catched_up.load(Ordering::Relaxed)),
auto_sync_serial: Some(manager.serial.get_state().await), auto_sync_serial: Some(manager.serial.get_state().await),
auto_sync_random: manager.random.get_state().await.ok(), auto_sync_random: manager.random.get_state().await.ok(),
}, },
None => SyncServiceState { None => SyncServiceState {
num_syncing: self.controllers.len(), num_syncing: self.controllers.len(),
catched_up: None,
auto_sync_serial: None, auto_sync_serial: None,
auto_sync_random: None, auto_sync_random: None,
}, },
@ -630,9 +635,19 @@ impl SyncService {
bail!("File already exists"); bail!("File already exists");
} }
let (index_start, index_end) = match maybe_range { let (index_start, index_end, all_chunks) = match maybe_range {
Some((start, end)) => (start, end), Some((start, end)) => (start, end, false),
None => (0, num_chunks), None => {
let start = match Self::tx_sync_start_index(&self.store, &tx).await? {
Some(s) => s,
None => {
debug!(%tx.seq, "No more data needed");
self.store.finalize_tx_with_hash(tx.seq, tx.hash()).await?;
return Ok(());
}
};
(start, num_chunks, true)
}
}; };
if index_start >= index_end || index_end > num_chunks { if index_start >= index_end || index_end > num_chunks {
@ -643,7 +658,7 @@ impl SyncService {
self.config, self.config,
tx.id(), tx.id(),
tx.start_entry_index(), tx.start_entry_index(),
FileSyncGoal::new(num_chunks, index_start, index_end), FileSyncGoal::new(num_chunks, index_start, index_end, all_chunks),
self.ctx.clone(), self.ctx.clone(),
self.store.clone(), self.store.clone(),
self.file_location_cache.clone(), self.file_location_cache.clone(),
@ -749,7 +764,7 @@ impl SyncService {
to_terminate.push(*tx_seq); to_terminate.push(*tx_seq);
} }
} }
} else { } else if self.controllers.contains_key(&min_tx_seq) {
to_terminate.push(min_tx_seq); to_terminate.push(min_tx_seq);
} }
@ -757,9 +772,12 @@ impl SyncService {
self.controllers.remove(tx_seq); self.controllers.remove(tx_seq);
} }
let num_terminated = to_terminate.len();
if num_terminated > 0 {
debug!(?to_terminate, "File sync terminated"); debug!(?to_terminate, "File sync terminated");
}
to_terminate.len() num_terminated
} }
fn on_heartbeat(&mut self) { fn on_heartbeat(&mut self) {
@ -787,6 +805,35 @@ impl SyncService {
self.controllers.remove(&tx_seq); self.controllers.remove(&tx_seq);
} }
} }
async fn tx_sync_start_index(store: &Store, tx: &Transaction) -> Result<Option<u64>> {
let shard_config = store.get_store().flow().get_shard_config();
let start_segment = sector_to_segment(tx.start_entry_index());
let end =
bytes_to_chunks(usize::try_from(tx.size).map_err(|e| anyhow!("tx size e={}", e))?);
let mut start = if shard_config.in_range(start_segment as u64) {
0
} else {
segment_to_sector(shard_config.next_segment_index(0, start_segment))
};
while start < end {
if store
.get_chunks_by_tx_and_index_range(
tx.seq,
start,
cmp::min(start + PORA_CHUNK_SIZE, end),
)
.await?
.is_none()
{
return Ok(Some(start as u64));
}
start = segment_to_sector(
shard_config.next_segment_index(sector_to_segment(start as u64), start_segment),
);
}
Ok(None)
}
} }
#[cfg(test)] #[cfg(test)]
@ -906,7 +953,7 @@ mod tests {
create_file_location_cache(init_peer_id, vec![txs[0].id()]); create_file_location_cache(init_peer_id, vec![txs[0].id()]);
let (network_send, mut network_recv) = mpsc::unbounded_channel::<NetworkMessage>(); let (network_send, mut network_recv) = mpsc::unbounded_channel::<NetworkMessage>();
let (_, sync_recv) = channel::Channel::unbounded(); let (_, sync_recv) = channel::Channel::unbounded("test");
let mut sync = SyncService { let mut sync = SyncService {
config: Config::default(), config: Config::default(),
@ -935,7 +982,7 @@ mod tests {
create_file_location_cache(init_peer_id, vec![txs[0].id()]); create_file_location_cache(init_peer_id, vec![txs[0].id()]);
let (network_send, mut network_recv) = mpsc::unbounded_channel::<NetworkMessage>(); let (network_send, mut network_recv) = mpsc::unbounded_channel::<NetworkMessage>();
let (_, sync_recv) = channel::Channel::unbounded(); let (_, sync_recv) = channel::Channel::unbounded("test");
let mut sync = SyncService { let mut sync = SyncService {
config: Config::default(), config: Config::default(),
@ -1703,7 +1750,7 @@ mod tests {
}; };
let sync_id = match request_id { let sync_id = match request_id {
network::RequestId::Sync(sync_id) => sync_id, network::RequestId::Sync(_, sync_id) => sync_id,
_ => unreachable!("All Chunks responses belong to sync"), _ => unreachable!("All Chunks responses belong to sync"),
}; };

View File

@ -0,0 +1,264 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
#######################################################################
### Network Config Options ###
#######################################################################
# Data directory where node's keyfile is stored.
# network_dir = "network"
# IP address to listen on.
# network_listen_address = "0.0.0.0"
# The address to broadcast to peers about which address we are listening on. Generally,
# configure public IP address for UDP discovery. If not specified, program will try to
# detect public IP address automatically.
# network_enr_address = ""
# The tcp port to broadcast to peers in order to reach back for libp2p services.
# network_enr_tcp_port = 1234
# The udp port to broadcast to peers in order to reach back for discovery.
# network_enr_udp_port = 1234
# The TCP port that libp2p listens on.
# network_libp2p_port = 1234
# UDP port that discovery listens on.
# network_discovery_port = 1234
# Target number of connected peers.
# network_target_peers = 50
# List of nodes to bootstrap UDP discovery. Note, `network_enr_address` should be
# configured as well to enable UDP discovery.
network_boot_nodes = ["/ip4/35.95.5.134/udp/1234/p2p/16Uiu2HAmFGrDV8wKToa1dd8uh6bz8bSY28n33iRP3pvfeBU6ysCw","/ip4/35.84.189.77/udp/1234/p2p/16Uiu2HAmF7t5iuRoWLMvQVfHbbJr5TFgHo2oU1CDxJm56eLdxRAY"]
# List of libp2p nodes to initially connect to.
# network_libp2p_nodes = []
# Indicates if the user has set the network to be in private mode. Currently this
# prevents sending client identifying information over identify.
# network_private = false
# Disables the discovery protocol from starting.
# network_disable_discovery = false
#######################################################################
### UDP Discovery Config Options ###
#######################################################################
# The request timeout for each UDP request.
# discv5_request_timeout_secs = 5
# The timeout after which a `QueryPeer` in an ongoing query is marked unresponsive.
# Unresponsive peers don't count towards the parallelism limits for a query.
# Hence, we may potentially end up making more requests to good peers.
# discv5_query_peer_timeout_secs = 2
# The number of retries for each UDP request.
# discv5_request_retries = 1
# The number of peers to request in parallel in a single query.
# discv5_query_parallelism = 5
# Reports all discovered ENR's when traversing the DHT to the event stream.
# discv5_report_discovered_peers = false
# Disables the incoming packet filter.
# discv5_disable_packet_filter = false
# Disable to limit the number of IP addresses from the same
# /24 subnet in the kbuckets table. This is to mitigate eclipse attacks.
# discv5_disable_ip_limit = false
#######################################################################
### Log Sync Config Options ###
#######################################################################
# RPC endpoint to sync event logs on EVM compatible blockchain.
# blockchain_rpc_endpoint = "http://127.0.0.1:8545"
# Flow contract address to sync event logs.
log_contract_address = "0x0460aA47b41a66694c0a73f667a1b795A5ED3556"
# Block number to sync event logs from blockchain. Generally, this is
# the block number when flow contract deployed.
log_sync_start_block_number = 595059
# Number of blocks to confirm a transaction.
confirmation_block_count = 6
# Maximum number of event logs to poll at a time.
# log_page_size = 999
# Maximum data size to cache in memory (by default, 100MB).
# max_cache_data_size = 104857600
# TTL to cache data in memory.
# cache_tx_seq_ttl = 500
# The number of retries after a RPC request times out.
# rate_limit_retries = 100
# The nubmer of retries for rate limited responses.
# timeout_retries = 100
# The duration to wait before retry, in ms.
# initial_backoff = 500
# The duration between each paginated getLogs RPC call, in ms.
# This is set to avoid triggering the throttling mechanism in the RPC server.
# recover_query_delay = 50
# The counter assumed the finalized block behind the latest block.
# default_finalized_block_count = 100
# Remove finalized block trigger interval.
# remove_finalized_block_interval_minutes = 30
# Watch_loop (eth_getLogs) trigger interval.
# watch_loop_wait_time_ms = 500
#######################################################################
### RPC Config Options ###
#######################################################################
# Whether to provide RPC service.
# rpc_enabled = true
# HTTP server address to bind for public RPC.
# rpc_listen_address = "0.0.0.0:5678"
# HTTP server address to bind for admin and debug RPC.
# rpc_listen_address_admin = "127.0.0.1:5679"
# Maximum data size of RPC request body (by default, 100MB).
# max_request_body_size = 104857600
# Number of chunks for a single segment.
# rpc_chunks_per_segment = 1024
# Maximum file size that allowed to cache in memory (by default, 10MB).
# rpc_max_cache_file_size = 10485760
#######################################################################
### Chunk Pool Config Options ###
#######################################################################
# Maximum number of threads to upload segments of a single file simultaneously.
# chunk_pool_write_window_size = 4
# Maximum data size of cached segment in pool (by default, 4MB).
# chunk_pool_max_cached_chunks_all = 4194304
# Maximum number of threads to upload segments for all files simultaneously.
# chunk_pool_max_writings = 16
# Expiration time to cache uploaded segments in memory.
# chunk_pool_expiration_time_secs = 300
#######################################################################
### DB Config Options ###
#######################################################################
# Directory to store data.
# db_dir = "db"
#######################################################################
### Misc Config Options ###
#######################################################################
# Log configuration file.
# log_config_file = "log_config"
# Log directory.
# log_directory = "log"
#######################################################################
### Mine Config Options ###
#######################################################################
# Mine contract address for incentive.
mine_contract_address = "0x1785c8683b3c527618eFfF78d876d9dCB4b70285"
# Miner key is used to sign blockchain transaction for incentive.
# The value should be a hex string of length 64 without 0x prefix.
#
# Note, the corresponding address should have enough tokens to pay
# transaction gas fee.
# miner_key = ""
#######################################################################
### Sharding Config Options ###
#######################################################################
# The max number of chunk entries to store in db.
# Each entry is 256B, so the db size is roughly limited to
# `256 * db_max_num_sectors` Bytes.
# If this limit is reached, the node will update its `shard_position`
# and store only half data.
#
db_max_num_sectors = 1000000000
# The format is <shard_id>/<shard_number>, where the shard number is 2^n.
# This only applies if there is no stored shard config in db.
# shard_position = "0/2"
reward_contract_address = "0x0496D0817BD8519e0de4894Dc379D35c35275609"
# The time interval to check if we should half `shard_position` to prune data.
#
# prune_check_time_s = 60
# The number of chunk entries to delete in a batch when we prune data.
#
# prune_batch_size = 1024
# The time interval to wait between each prune batch deletion to avoid
# IO resource exhaustion.
#
# prune_batch_wait_time_ms = 1000
#######################################################################
### File Sync Config Options ###
#######################################################################
[sync]
# Enable file sync among peers automatically. When enabled, each node will store
# all files, and sufficient disk space is required.
auto_sync_enabled = true
# Maximum number of files in sync from other peers simultaneously.
# max_sync_files = 32
# Enable to start a file sync via RPC (e.g. `admin_startSyncFile`).
# sync_file_by_rpc_enabled = true
# Enable to start a file sync automatically when a file announcement P2P message received.
# sync_file_on_announcement_enabled = false
# Maximum threads to sync files in sequence.
# max_sequential_workers = 24
# Maximum threads to sync files randomly.
# max_random_workers = 8
#######################################################################
### File Location Cache Options ###
#######################################################################
# [file_location_cache]
# File location cache is a cache that maintains storage positions of files.
# Storage location information is represented by the IP address of the storage node and the timestamp indicating when the node declared that it stores the corresponding file.
# It has both a global capacity limit and a limit on the capacity for location information of each individual file.
# When the cache is full, the storage position information with oldest timestamp will be replaced.
# Global cache capacity.
# max_entries_total = 4096
# Location information capacity for each file.
# max_entries_per_file = 4
# Validity period of location information.
# If the timestamp in the storage location information exceeds this duration from the current time, it will be removed from the cache.
# entry_expiration_time_secs = 3600

View File

@ -33,7 +33,7 @@
# List of nodes to bootstrap UDP discovery. Note, `network_enr_address` should be # List of nodes to bootstrap UDP discovery. Note, `network_enr_address` should be
# configured as well to enable UDP discovery. # configured as well to enable UDP discovery.
network_boot_nodes = ["/ip4/54.219.26.22/udp/1234/p2p/16Uiu2HAmTVDGNhkHD98zDnJxQWu3i1FL1aFYeh9wiQTNu4pDCgps","/ip4/52.52.127.117/udp/1234/p2p/16Uiu2HAkzRjxK2gorngB1Xq84qDrT4hSVznYDHj6BkbaE4SGx9oS","/ip4/18.167.69.68/udp/1234/p2p/16Uiu2HAm2k6ua2mGgvZ8rTMV8GhpW71aVzkQWy7D37TTDuLCpgmX"] network_boot_nodes = ["/ip4/54.219.26.22/udp/1234/p2p/16Uiu2HAmTVDGNhkHD98zDnJxQWu3i1FL1aFYeh9wiQTNu4pDCgps","/ip4/52.52.127.117/udp/1234/p2p/16Uiu2HAkzRjxK2gorngB1Xq84qDrT4hSVznYDHj6BkbaE4SGx9oS","/ip4/18.162.65.205/udp/1234/p2p/16Uiu2HAm2k6ua2mGgvZ8rTMV8GhpW71aVzkQWy7D37TTDuLCpgmX"]
# List of libp2p nodes to initially connect to. # List of libp2p nodes to initially connect to.
# network_libp2p_nodes = [] # network_libp2p_nodes = []
@ -80,14 +80,14 @@ network_boot_nodes = ["/ip4/54.219.26.22/udp/1234/p2p/16Uiu2HAmTVDGNhkHD98zDnJxQ
# blockchain_rpc_endpoint = "http://127.0.0.1:8545" # blockchain_rpc_endpoint = "http://127.0.0.1:8545"
# Flow contract address to sync event logs. # Flow contract address to sync event logs.
log_contract_address = "0x8873cc79c5b3b5666535C825205C9a128B1D75F1" log_contract_address = "0xbD2C3F0E65eDF5582141C35969d66e34629cC768"
# Block number to sync event logs from blockchain. Generally, this is # Block number to sync event logs from blockchain. Generally, this is
# the block number when flow contract deployed. # the block number when flow contract deployed.
log_sync_start_block_number = 802 log_sync_start_block_number = 595059
# Number of blocks to confirm a transaction. # Number of blocks to confirm a transaction.
# confirmation_block_count = 12 confirmation_block_count = 6
# Maximum number of event logs to poll at a time. # Maximum number of event logs to poll at a time.
# log_page_size = 999 # log_page_size = 999
@ -179,11 +179,8 @@ log_sync_start_block_number = 802
### Mine Config Options ### ### Mine Config Options ###
####################################################################### #######################################################################
# Mine contract address for PoRA. # Mine contract address for incentive.
mine_contract_address = "0x6176AA095C47A7F79deE2ea473B77ebf50035421" mine_contract_address = "0x6815F41019255e00D6F34aAB8397a6Af5b6D806f"
# Reward contract address for incentive.
reward_contract_address = "0x4a62e08198b8B2a791532280bEA976EE3b024d79"
# Miner key is used to sign blockchain transaction for incentive. # Miner key is used to sign blockchain transaction for incentive.
# The value should be a hex string of length 64 without 0x prefix. # The value should be a hex string of length 64 without 0x prefix.
@ -213,12 +210,13 @@ reward_contract_address = "0x4a62e08198b8B2a791532280bEA976EE3b024d79"
# If this limit is reached, the node will update its `shard_position` # If this limit is reached, the node will update its `shard_position`
# and store only half data. # and store only half data.
# #
# db_max_num_sectors = 1000000000 db_max_num_sectors = 1000000000
# The format is <shard_id>/<shard_number>, where the shard number is 2^n. # The format is <shard_id>/<shard_number>, where the shard number is 2^n.
# This only applies if there is no stored shard config in db. # This only applies if there is no stored shard config in db.
# shard_position = "0/2" # shard_position = "0/2"
reward_contract_address = "0x51998C4d486F406a788B766d93510980ae1f9360"
# The time interval to check if we should half `shard_position` to prune data. # The time interval to check if we should half `shard_position` to prune data.
# #
# prune_check_time_s = 60 # prune_check_time_s = 60
@ -242,11 +240,7 @@ reward_contract_address = "0x4a62e08198b8B2a791532280bEA976EE3b024d79"
auto_sync_enabled = true auto_sync_enabled = true
# Maximum number of files in sync from other peers simultaneously. # Maximum number of files in sync from other peers simultaneously.
# max_sync_files = 16 # max_sync_files = 32
# Timeout to terminate a file sync when automatically sync from other peers.
# If timeout, terminated file sync will be triggered later.
# find_peer_timeout = "10s"
# Enable to start a file sync via RPC (e.g. `admin_startSyncFile`). # Enable to start a file sync via RPC (e.g. `admin_startSyncFile`).
# sync_file_by_rpc_enabled = true # sync_file_by_rpc_enabled = true
@ -255,7 +249,28 @@ auto_sync_enabled = true
# sync_file_on_announcement_enabled = false # sync_file_on_announcement_enabled = false
# Maximum threads to sync files in sequence. # Maximum threads to sync files in sequence.
# max_sequential_workers = 8 # max_sequential_workers = 24
# Maximum threads to sync files randomly. # Maximum threads to sync files randomly.
# max_random_workers = 4 # max_random_workers = 8
#######################################################################
### File Location Cache Options ###
#######################################################################
# [file_location_cache]
# File location cache is a cache that maintains storage positions of files.
# Storage location information is represented by the IP address of the storage node and the timestamp indicating when the node declared that it stores the corresponding file.
# It has both a global capacity limit and a limit on the capacity for location information of each individual file.
# When the cache is full, the storage position information with oldest timestamp will be replaced.
# Global cache capacity.
# max_entries_total = 4096
# Location information capacity for each file.
# max_entries_per_file = 4
# Validity period of location information.
# If the timestamp in the storage location information exceeds this duration from the current time, it will be removed from the cache.
# entry_expiration_time_secs = 3600

View File

@ -242,11 +242,7 @@
# auto_sync_enabled = false # auto_sync_enabled = false
# Maximum number of files in sync from other peers simultaneously. # Maximum number of files in sync from other peers simultaneously.
# max_sync_files = 16 # max_sync_files = 32
# Timeout to terminate a file sync when automatically sync from other peers.
# If timeout, terminated file sync will be triggered later.
# find_peer_timeout = "10s"
# Enable to start a file sync via RPC (e.g. `admin_startSyncFile`). # Enable to start a file sync via RPC (e.g. `admin_startSyncFile`).
# sync_file_by_rpc_enabled = true # sync_file_by_rpc_enabled = true
@ -255,10 +251,10 @@
# sync_file_on_announcement_enabled = false # sync_file_on_announcement_enabled = false
# Maximum threads to sync files in sequence. # Maximum threads to sync files in sequence.
# max_sequential_workers = 8 # max_sequential_workers = 24
# Maximum threads to sync files randomly. # Maximum threads to sync files randomly.
# max_random_workers = 4 # max_random_workers = 8
####################################################################### #######################################################################
### File Location Cache Options ### ### File Location Cache Options ###

View File

@ -1 +1 @@
debug,hyper=info,h2=info,rpc=info,discv5=info,router=info,jsonrpsee_http_server=info info

1
run/log_config_debug Normal file
View File

@ -0,0 +1 @@
debug,hyper=info,h2=info,rpc=info,discv5=info,jsonrpsee_http_server=info

View File

@ -68,7 +68,7 @@ class ZgsNode(TestNode):
os.mkdir(self.data_dir) os.mkdir(self.data_dir)
log_config_path = os.path.join(self.data_dir, self.config["log_config_file"]) log_config_path = os.path.join(self.data_dir, self.config["log_config_file"])
with open(log_config_path, "w") as f: with open(log_config_path, "w") as f:
f.write("debug,hyper=info,h2=info") f.write("trace,hyper=info,h2=info")
initialize_toml_config(self.config_file, self.config) initialize_toml_config(self.config_file, self.config)

View File

@ -1,6 +1,6 @@
use super::*; use super::*;
use core::num::NonZeroUsize; use core::num::NonZeroUsize;
use ethereum_types::{H256, U128, U256}; use ethereum_types::{H160, H256, U128, U256};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::sync::Arc; use std::sync::Arc;
@ -277,6 +277,27 @@ impl Decode for H256 {
} }
} }
impl Decode for H160 {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
20
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let len = bytes.len();
let expected = <Self as Decode>::ssz_fixed_len();
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
Ok(H160::from_slice(bytes))
}
}
}
impl Decode for U256 { impl Decode for U256 {
fn is_ssz_fixed_len() -> bool { fn is_ssz_fixed_len() -> bool {
true true

View File

@ -1,6 +1,6 @@
use super::*; use super::*;
use core::num::NonZeroUsize; use core::num::NonZeroUsize;
use ethereum_types::{H256, U128, U256}; use ethereum_types::{H160, H256, U128, U256};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::sync::Arc; use std::sync::Arc;
@ -323,6 +323,24 @@ impl Encode for H256 {
} }
} }
impl Encode for H160 {
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
20
}
fn ssz_bytes_len(&self) -> usize {
20
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(self.as_bytes());
}
}
impl Encode for U256 { impl Encode for U256 {
fn is_ssz_fixed_len() -> bool { fn is_ssz_fixed_len() -> bool {
true true