Merge branch 'master' into master-sf

This commit is contained in:
pawanjay176
2020-04-28 18:19:33 +05:30
164 changed files with 5444 additions and 2104 deletions

View File

@@ -74,3 +74,10 @@ jobs:
- uses: actions/checkout@v1
- name: Typecheck benchmark code without running it
run: make check-benches
clippy:
runs-on: ubuntu-latest
needs: cargo-fmt
steps:
- uses: actions/checkout@v1
- name: Lint code for quality and style with Clippy
run: make lint

423
Cargo.lock generated
View File

@@ -6,6 +6,7 @@ version = "0.0.1"
dependencies = [
"bls 0.2.0",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap_utils 0.1.0",
"deposit_contract 0.2.0",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"environment 0.2.0",
@@ -20,9 +21,10 @@ dependencies = [
"slog-async 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slog-term 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"types 0.2.0",
"validator_client 0.2.0",
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -80,7 +82,7 @@ dependencies = [
[[package]]
name = "amcl"
version = "0.2.0"
source = "git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10#38c6c33925b24c9319a1febfb621ff9bbf6d49f7"
source = "git+https://github.com/sigp/milagro_bls?tag=v1.0.1#2ccdd4b517c1ab3debe10277deed9d1b1cbbe9ce"
dependencies = [
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -139,7 +141,7 @@ name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -225,6 +227,7 @@ dependencies = [
"proto_array_fork_choice 0.2.0",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe_arith 0.1.0",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -367,7 +370,7 @@ dependencies = [
"eth2_ssz 0.1.2",
"eth2_ssz_types 0.2.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)",
"milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?tag=v1.0.1)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -498,6 +501,18 @@ dependencies = [
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap_utils"
version = "0.1.0"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"eth2_ssz 0.1.2",
"eth2_testnet_config 0.2.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"types 0.2.0",
]
[[package]]
name = "clear_on_drop"
version = "0.2.3"
@@ -895,6 +910,16 @@ dependencies = [
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "derive_more"
version = "0.99.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
@@ -988,13 +1013,13 @@ dependencies = [
[[package]]
name = "enr"
version = "0.1.0-alpha.3"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"base64 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bs58 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1085,7 +1110,7 @@ dependencies = [
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tree_hash 0.1.1",
"types 0.2.0",
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1097,7 +1122,7 @@ dependencies = [
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
"types 0.2.0",
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1115,7 +1140,7 @@ dependencies = [
"hashmap_delay 0.2.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"lighthouse_metrics 0.2.0",
"lru 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1134,6 +1159,7 @@ dependencies = [
"types 0.2.0",
"unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"version 0.2.0",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1165,7 +1191,7 @@ dependencies = [
"eth2_hashing 0.1.1",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)",
"milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?tag=v1.0.1)",
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1230,6 +1256,20 @@ dependencies = [
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethabi"
version = "9.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethabi"
version = "11.0.0"
@@ -1451,61 +1491,6 @@ dependencies = [
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-executor"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-io"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-sink"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-task"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-util"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.3.55"
@@ -1640,7 +1625,7 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.1.12"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1967,6 +1952,18 @@ dependencies = [
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-core"
version = "14.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "keccak"
version = "0.1.0"
@@ -2001,10 +1998,12 @@ name = "lcli"
version = "0.2.0"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap_utils 0.1.0",
"deposit_contract 0.2.0",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"environment 0.2.0",
"eth1_test_rig 0.2.0",
"eth2-libp2p 0.2.0",
"eth2_ssz 0.1.2",
"eth2_testnet_config 0.2.0",
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2059,34 +2058,34 @@ dependencies = [
[[package]]
name = "libp2p"
version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core-derive 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-deflate 0.5.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-discv5 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-dns 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-floodsub 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-gossipsub 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-identify 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-kad 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-mdns 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-mplex 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-noise 0.11.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-ping 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-plaintext 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-secio 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-tcp 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-uds 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-wasm-ext 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-websocket 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-yamux 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-core-derive 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-deflate 0.5.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-discv5 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-dns 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-floodsub 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-gossipsub 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-identify 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-kad 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-mdns 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-mplex 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-noise 0.11.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-ping 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-plaintext 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-secio 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-tcp 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-uds 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-wasm-ext 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-websocket 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-yamux 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2098,7 +2097,7 @@ dependencies = [
[[package]]
name = "libp2p-core"
version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"asn1_der 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bs58 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2110,15 +2109,15 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"multistream-select 0.6.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"multistream-select 0.6.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2133,7 +2132,7 @@ dependencies = [
[[package]]
name = "libp2p-core-derive"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2142,34 +2141,34 @@ dependencies = [
[[package]]
name = "libp2p-deflate"
version = "0.5.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libp2p-discv5"
version = "0.1.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"enr 0.1.0-alpha.3 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"enr 0.1.0-alpha.3 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2184,10 +2183,10 @@ dependencies = [
[[package]]
name = "libp2p-dns"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2195,15 +2194,15 @@ dependencies = [
[[package]]
name = "libp2p-floodsub"
version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bs58 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2213,7 +2212,7 @@ dependencies = [
[[package]]
name = "libp2p-gossipsub"
version = "0.1.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2221,8 +2220,8 @@ dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lru 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2238,14 +2237,14 @@ dependencies = [
[[package]]
name = "libp2p-identify"
version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2257,18 +2256,18 @@ dependencies = [
[[package]]
name = "libp2p-kad"
version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2284,16 +2283,16 @@ dependencies = [
[[package]]
name = "libp2p-mdns"
version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"data-encoding 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2306,12 +2305,12 @@ dependencies = [
[[package]]
name = "libp2p-mplex"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2322,13 +2321,13 @@ dependencies = [
[[package]]
name = "libp2p-noise"
version = "0.11.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2342,14 +2341,14 @@ dependencies = [
[[package]]
name = "libp2p-ping"
version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2359,14 +2358,14 @@ dependencies = [
[[package]]
name = "libp2p-plaintext"
version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2374,7 +2373,7 @@ dependencies = [
[[package]]
name = "libp2p-secio"
version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2383,13 +2382,13 @@ dependencies = [
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2403,10 +2402,10 @@ dependencies = [
[[package]]
name = "libp2p-swarm"
version = "0.3.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2416,13 +2415,13 @@ dependencies = [
[[package]]
name = "libp2p-tcp"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnet 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2432,10 +2431,10 @@ dependencies = [
[[package]]
name = "libp2p-uds"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2443,11 +2442,11 @@ dependencies = [
[[package]]
name = "libp2p-wasm-ext"
version = "0.6.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2457,13 +2456,13 @@ dependencies = [
[[package]]
name = "libp2p-websocket"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"soketto 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2475,10 +2474,10 @@ dependencies = [
[[package]]
name = "libp2p-yamux"
version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"yamux 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2517,6 +2516,7 @@ dependencies = [
"account_manager 0.0.1",
"beacon_node 0.2.0",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap_utils 0.1.0",
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"environment 0.2.0",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2642,14 +2642,15 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe_arith 0.1.0",
]
[[package]]
name = "milagro_bls"
version = "1.0.1"
source = "git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10#38c6c33925b24c9319a1febfb621ff9bbf6d49f7"
source = "git+https://github.com/sigp/milagro_bls?tag=v1.0.1#2ccdd4b517c1ab3debe10277deed9d1b1cbbe9ce"
dependencies = [
"amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)",
"amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?tag=v1.0.1)",
"hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2759,7 +2760,7 @@ dependencies = [
[[package]]
name = "multistream-select"
version = "0.6.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2896,7 +2897,7 @@ name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2976,14 +2977,14 @@ dependencies = [
[[package]]
name = "parity-multiaddr"
version = "0.6.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"bs58 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"data-encoding 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)",
"parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)",
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2993,7 +2994,7 @@ dependencies = [
[[package]]
name = "parity-multihash"
version = "0.2.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3774,7 +3775,7 @@ dependencies = [
[[package]]
name = "rw-stream-sink"
version = "0.1.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97#4e3003d5283040fee10da1299252dd060a838d97"
source = "git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add#71cf486b4d992862f5a05f9f4ef5e5c1631f4add"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3786,6 +3787,10 @@ name = "ryu"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "safe_arith"
version = "0.1.0"
[[package]]
name = "safemem"
version = "0.3.3"
@@ -4237,6 +4242,7 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"merkle_proof 0.2.0",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe_arith 0.1.0",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -4903,6 +4909,7 @@ dependencies = [
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe_arith 0.1.0",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -5062,6 +5069,7 @@ dependencies = [
"tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tree_hash 0.1.1",
"types 0.2.0",
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -5276,6 +5284,34 @@ dependencies = [
"websocket 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "web3"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 14.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"websocket 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "webpki"
version = "0.21.2"
@@ -5463,7 +5499,7 @@ dependencies = [
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3"
"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
"checksum amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "<none>"
"checksum amcl 0.2.0 (git+https://github.com/sigp/milagro_bls?tag=v1.0.1)" = "<none>"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arc-swap 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62"
"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
@@ -5541,6 +5577,7 @@ dependencies = [
"checksum db-key 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b72465f46d518f6015d9cf07f7f3013a95dd6b9c2747c3d65ae0cce43929d14f"
"checksum derivative 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135"
"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe"
"checksum derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
@@ -5549,12 +5586,13 @@ dependencies = [
"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
"checksum enr 0.1.0-alpha.3 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum enr 0.1.0-alpha.3 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd"
"checksum ethabi 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97652a7d1f2504d6c885c87e242a06ccef5bd3054093d3fb742d8fb64806231a"
"checksum ethabi 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebdeeea85a6d217b9fcc862906d7e283c047e04114165c433756baf5dce00a6c"
"checksum ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "965126c64662832991f5a748893577630b558e47fa94e7f35aefcd20d737cef7"
"checksum ethbloom 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3932e82d64d347a045208924002930dc105a138995ccdc1479d0f05f0359f17c"
"checksum ethbloom 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cfe1c169414b709cf28aa30c74060bdb830a03a8ba473314d079ac79d80a5f"
"checksum ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62d1bc682337e2c5ec98930853674dd2b4bd5d0d246933a9e98e5280f7c76c5f"
@@ -5594,7 +5632,7 @@ dependencies = [
"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353"
"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead"
"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
"checksum hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
"checksum hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
"checksum hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3"
@@ -5627,6 +5665,7 @@ dependencies = [
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
"checksum jsonrpc-core 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b83fdc5e0218128d0d270f2f2e7a5ea716f3240c8518a58bc89e6716ba8581"
"checksum jsonrpc-core 14.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25525f6002338fb4debb5167a89a0b47f727a5a48418417545ad3429758b7fec"
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
@@ -5636,28 +5675,28 @@ dependencies = [
"checksum leveldb-sys 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71f46429bb70612c3e939aaeed27ffd31a24a773d21728a1a426e4089d6778d2"
"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
"checksum libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd"
"checksum libp2p 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-core-derive 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-deflate 0.5.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-discv5 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-dns 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-floodsub 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-gossipsub 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-identify 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-kad 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-mdns 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-mplex 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-noise 0.11.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-ping 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-plaintext 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-secio 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-tcp 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-uds 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-wasm-ext 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-websocket 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p-yamux 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum libp2p 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-core 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-core-derive 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-deflate 0.5.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-discv5 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-dns 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-floodsub 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-gossipsub 0.1.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-identify 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-kad 0.13.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-mdns 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-mplex 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-noise 0.11.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-ping 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-plaintext 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-secio 0.13.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-swarm 0.3.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-tcp 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-uds 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-wasm-ext 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-websocket 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libp2p-yamux 0.13.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962"
"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
@@ -5672,7 +5711,7 @@ dependencies = [
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
"checksum milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?branch=eth2.0-v0.10)" = "<none>"
"checksum milagro_bls 1.0.1 (git+https://github.com/sigp/milagro_bls?tag=v1.0.1)" = "<none>"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
"checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
@@ -5682,8 +5721,7 @@ dependencies = [
"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
"checksum multistream-select 0.6.1 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum multistream-select 0.6.1 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
@@ -5700,8 +5738,8 @@ dependencies = [
"checksum openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)" = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
"checksum owning_ref 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
"checksum parity-codec 3.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b9df1283109f542d8852cd6b30e9341acc2137481eb6157d2e62af68b0afec9"
"checksum parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum parity-multiaddr 0.6.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum parity-multihash 0.2.0 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum parity-scale-codec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "329c8f7f4244ddb5c37c103641027a76c530e65e8e4b8240b29f81ea40508b17"
"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f"
"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
@@ -5773,8 +5811,8 @@ dependencies = [
"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
"checksum rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=4e3003d5283040fee10da1299252dd060a838d97)" = "<none>"
"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
"checksum rw-stream-sink 0.1.2 (git+https://github.com/SigP/rust-libp2p?rev=71cf486b4d992862f5a05f9f4ef5e5c1631f4add)" = "<none>"
"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
"checksum schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19"
@@ -5910,6 +5948,7 @@ dependencies = [
"checksum wasm-bindgen-test-macro 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cf2f86cd78a2aa7b1fb4bb6ed854eccb7f9263089c79542dca1576a1518a8467"
"checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac"
"checksum web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
"checksum web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0631c83208cf420eeb2ed9b6cb2d5fc853aa76a43619ccec2a3d52d741f1261"
"checksum web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "076f34ed252d74a8521e3b013254b1a39f94a98f23aae7cfc85cda6e7b395664"
"checksum webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4"

View File

@@ -5,6 +5,7 @@ members = [
"eth2/state_processing",
"eth2/types",
"eth2/utils/bls",
"eth2/utils/clap_utils",
"eth2/utils/compare_fields",
"eth2/utils/compare_fields_derive",
"eth2/utils/deposit_contract",
@@ -17,6 +18,7 @@ members = [
"eth2/utils/lighthouse_metrics",
"eth2/utils/merkle_proof",
"eth2/utils/int_to_bytes",
"eth2/utils/safe_arith",
"eth2/utils/serde_hex",
"eth2/utils/slot_clock",
"eth2/utils/rest_types",

View File

@@ -2,11 +2,14 @@
EF_TESTS = "tests/ef_tests"
# Builds the entire workspace in release (optimized).
# Builds the Lighthouse binary in release (optimized).
#
# Binaries will most likely be found in `./target/release`
install:
cargo install --path lighthouse --force --locked
# Builds the lcli binary in release (optimized).
install-lcli:
cargo install --path lcli --force --locked
# Runs the full workspace tests in **release**, without downloading any additional
@@ -42,6 +45,11 @@ test: test-release
# Runs the entire test suite, downloading test vectors if required.
test-full: cargo-fmt test-release test-debug test-ef
# Lints the code for bad style and potentially unsafe arithmetic using Clippy.
# Clippy lints are opt-in per-crate for now, which is why we allow all by default.
lint:
cargo clippy --all -- -A clippy::all
# Runs the makefile in the `ef_tests` repo.
#
# May download and extract an archive of test vectors from the ethereum

View File

@@ -50,9 +50,9 @@ Current development overview:
- ~~**April 2019**: Inital single-client testnets.~~
- ~~**September 2019**: Inter-operability with other Ethereum 2.0 clients.~~
- ~~**Q1 2020**: `lighthouse-0.1.0` release: All major phase 0 features implemented.~~
- **Q1 2020**: Public, multi-client testnet with user-facing functionality.
- **Q2 2020**: Public, multi-client testnet with user-facing functionality.
- **Q2 2020**: Third-party security review.
- **Q3 2020**: Production Beacon Chain testnet (tentative).
- **Q4 2020**: Production Beacon Chain testnet (tentative).
## Documentation

View File

@@ -24,5 +24,7 @@ hex = "0.3"
validator_client = { path = "../validator_client" }
rayon = "1.2.0"
eth2_testnet_config = { path = "../eth2/utils/eth2_testnet_config" }
web3 = "0.8.0"
web3 = "0.10.0"
futures = "0.1.25"
clap_utils = { path = "../eth2/utils/clap_utils" }
tokio = "0.1.22"

View File

@@ -1,3 +1,4 @@
use crate::deposits;
use clap::{App, Arg, SubCommand};
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
@@ -6,7 +7,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.about("Utilities for generating and managing Ethereum 2.0 accounts.")
.subcommand(
SubCommand::with_name("validator")
.about("Generate or manage Etheruem 2.0 validators.")
.about("Generate or manage Ethereum 2.0 validators.")
.subcommand(deposits::cli_app())
.subcommand(
SubCommand::with_name("new")
.about("Create a new Ethereum 2.0 validator.")
@@ -52,14 +54,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true)
.help("The password file to unlock the eth1 account (see --index)"),
)
.arg(
Arg::with_name("testnet-dir")
.long("testnet-dir")
.value_name("DIRECTORY")
.takes_value(true)
.help("The directory from which to read the deposit contract /
address. Defaults to the current Lighthouse testnet."),
)
.subcommand(
SubCommand::with_name("insecure")
.about("Produce insecure, ephemeral validators. DO NOT USE TO STORE VALUE.")

View File

@@ -0,0 +1,255 @@
use clap::{App, Arg, ArgMatches};
use clap_utils;
use environment::Environment;
use futures::{
future::{self, loop_fn, Loop},
Future,
};
use slog::{info, Logger};
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, Instant};
use tokio::timer::Delay;
use types::EthSpec;
use validator_client::validator_directory::ValidatorDirectoryBuilder;
use web3::{
transports::Ipc,
types::{Address, SyncInfo, SyncState},
Transport, Web3,
};
const SYNCING_STATE_RETRY_DELAY: Duration = Duration::from_secs(2);
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new("deposited")
.about("Creates new Lighthouse validator keys and directories. Each newly-created validator
will have a deposit transaction formed and submitted to the deposit contract via
--eth1-ipc. This application will only write each validator keys to disk if the deposit
transaction returns successfully from the eth1 node. The process exits immediately if any
Eth1 tx fails. Does not wait for Eth1 confirmation blocks, so there is no guarantee that a
deposit will be accepted in the Eth1 chain. Before key generation starts, this application
will wait until the eth1 indicates that it is not syncing via the eth_syncing endpoint")
.arg(
Arg::with_name("validator-dir")
.long("validator-dir")
.value_name("VALIDATOR_DIRECTORY")
.help("The path where the validator directories will be created. Defaults to ~/.lighthouse/validators")
.takes_value(true),
)
.arg(
Arg::with_name("eth1-ipc")
.long("eth1-ipc")
.value_name("ETH1_IPC_PATH")
.help("Path to an Eth1 JSON-RPC IPC endpoint")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("from-address")
.long("from-address")
.value_name("FROM_ETH1_ADDRESS")
.help("The address that will submit the eth1 deposit. Must be unlocked on the node
at --eth1-ipc.")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("deposit-gwei")
.long("deposit-gwei")
.value_name("DEPOSIT_GWEI")
.help("The GWEI value of the deposit amount. Defaults to the minimum amount
required for an active validator (MAX_EFFECTIVE_BALANCE.")
.takes_value(true),
)
.arg(
Arg::with_name("count")
.long("count")
.value_name("DEPOSIT_COUNT")
.help("The number of deposits to create, regardless of how many already exist")
.conflicts_with("limit")
.takes_value(true),
)
.arg(
Arg::with_name("at-most")
.long("at-most")
.value_name("VALIDATOR_COUNT")
.help("Observe the number of validators in --validator-dir, only creating enough to
ensure reach the given count. Never deletes an existing validator.")
.conflicts_with("count")
.takes_value(true),
)
}
pub fn cli_run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) -> Result<(), String> {
let spec = env.core_context().eth2_config.spec;
let log = env.core_context().log;
let validator_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
"validator_dir",
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
let from_address: Address = clap_utils::parse_required(matches, "from-address")?;
let deposit_gwei = clap_utils::parse_optional(matches, "deposit-gwei")?
.unwrap_or_else(|| spec.max_effective_balance);
let count: Option<usize> = clap_utils::parse_optional(matches, "count")?;
let at_most: Option<usize> = clap_utils::parse_optional(matches, "at-most")?;
let starting_validator_count = existing_validator_count(&validator_dir)?;
let n = match (count, at_most) {
(Some(_), Some(_)) => Err("Cannot supply --count and --at-most".to_string()),
(None, None) => Err("Must supply either --count or --at-most".to_string()),
(Some(count), None) => Ok(count),
(None, Some(at_most)) => Ok(at_most.saturating_sub(starting_validator_count)),
}?;
if n == 0 {
info!(
log,
"No need to produce and validators, exiting";
"--count" => count,
"--at-most" => at_most,
"existing_validators" => starting_validator_count,
);
return Ok(());
}
let deposit_contract = env
.testnet
.as_ref()
.ok_or_else(|| "Unable to run account manager without a testnet dir".to_string())?
.deposit_contract_address()
.map_err(|e| format!("Unable to parse deposit contract address: {}", e))?;
if deposit_contract == Address::zero() {
return Err("Refusing to deposit to the zero address. Check testnet configuration.".into());
}
let (_event_loop_handle, transport) =
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
let web3 = Web3::new(transport);
env.runtime()
.block_on(poll_until_synced(web3.clone(), log.clone()))?;
for i in 0..n {
let tx_hash_log = log.clone();
env.runtime()
.block_on(
ValidatorDirectoryBuilder::default()
.spec(spec.clone())
.custom_deposit_amount(deposit_gwei)
.thread_random_keypairs()
.submit_eth1_deposit(web3.clone(), from_address, deposit_contract)
.map(move |(builder, tx_hash)| {
info!(
tx_hash_log,
"Validator deposited";
"eth1_tx_hash" => format!("{:?}", tx_hash),
"index" => format!("{}/{}", i + 1, n),
);
builder
}),
)?
.create_directory(validator_dir.clone())?
.write_keypair_files()?
.write_eth1_data_file()?
.build()?;
}
let ending_validator_count = existing_validator_count(&validator_dir)?;
let delta = ending_validator_count.saturating_sub(starting_validator_count);
info!(
log,
"Success";
"validators_created_and_deposited" => delta,
);
Ok(())
}
/// Returns the number of validators that exist in the given `validator_dir`.
///
/// This function just assumes any file is a validator directory, making it likely to return a
/// higher number than accurate but never a lower one.
fn existing_validator_count(validator_dir: &PathBuf) -> Result<usize, String> {
fs::read_dir(&validator_dir)
.map(|iter| iter.count())
.map_err(|e| format!("Unable to read {:?}: {}", validator_dir, e))
}
/// Run a poll on the `eth_syncing` endpoint, blocking until the node is synced.
fn poll_until_synced<T>(web3: Web3<T>, log: Logger) -> impl Future<Item = (), Error = String> + Send
where
T: Transport + Send + 'static,
<T as Transport>::Out: Send,
{
loop_fn((web3.clone(), log.clone()), move |(web3, log)| {
web3.clone()
.eth()
.syncing()
.map_err(|e| format!("Unable to read syncing state from eth1 node: {:?}", e))
.and_then::<_, Box<dyn Future<Item = _, Error = _> + Send>>(move |sync_state| {
match sync_state {
SyncState::Syncing(SyncInfo {
current_block,
highest_block,
..
}) => {
info!(
log,
"Waiting for eth1 node to sync";
"est_highest_block" => format!("{}", highest_block),
"current_block" => format!("{}", current_block),
);
Box::new(
Delay::new(Instant::now() + SYNCING_STATE_RETRY_DELAY)
.map_err(|e| format!("Failed to trigger delay: {:?}", e))
.and_then(|_| future::ok(Loop::Continue((web3, log)))),
)
}
SyncState::NotSyncing => Box::new(
web3.clone()
.eth()
.block_number()
.map_err(|e| {
format!("Unable to read block number from eth1 node: {:?}", e)
})
.and_then::<_, Box<dyn Future<Item = _, Error = _> + Send>>(
|block_number| {
if block_number > 0.into() {
info!(
log,
"Eth1 node is synced";
"head_block" => format!("{}", block_number),
);
Box::new(future::ok(Loop::Break((web3, log))))
} else {
Box::new(
Delay::new(Instant::now() + SYNCING_STATE_RETRY_DELAY)
.map_err(|e| {
format!("Failed to trigger delay: {:?}", e)
})
.and_then(|_| {
info!(
log,
"Waiting for eth1 node to sync";
"current_block" => 0,
);
future::ok(Loop::Continue((web3, log)))
}),
)
}
},
),
),
}
})
})
.map(|_| ())
}

View File

@@ -1,4 +1,5 @@
mod cli;
mod deposits;
use clap::ArgMatches;
use deposit_contract::DEPOSIT_GAS;
@@ -6,7 +7,7 @@ use environment::{Environment, RuntimeContext};
use eth2_testnet_config::Eth2TestnetConfig;
use futures::{future, Future, IntoFuture, Stream};
use rayon::prelude::*;
use slog::{crit, error, info, Logger};
use slog::{error, info, Logger};
use std::fs;
use std::fs::File;
use std::io::Read;
@@ -21,20 +22,8 @@ use web3::{
pub use cli::cli_app;
/// Run the account manager, logging an error if the operation did not succeed.
pub fn run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) {
let log = env.core_context().log.clone();
match run_account_manager(matches, env) {
Ok(()) => (),
Err(e) => crit!(log, "Account manager failed"; "error" => e),
}
}
/// Run the account manager, returning an error if the operation did not succeed.
fn run_account_manager<T: EthSpec>(
matches: &ArgMatches,
mut env: Environment<T>,
) -> Result<(), String> {
pub fn run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) -> Result<(), String> {
let context = env.core_context();
let log = context.log.clone();
@@ -60,6 +49,7 @@ fn run_account_manager<T: EthSpec>(
match matches.subcommand() {
("validator", Some(matches)) => match matches.subcommand() {
("deposited", Some(matches)) => deposits::cli_run(matches, env)?,
("new", Some(matches)) => run_new_validator_subcommand(matches, datadir, env)?,
_ => {
return Err("Invalid 'validator new' command. See --help.".to_string());

View File

@@ -11,6 +11,9 @@ path = "src/lib.rs"
[dev-dependencies]
node_test_rig = { path = "../tests/node_test_rig" }
[features]
write_ssz_files = ["beacon_chain/write_ssz_files"] # Writes debugging .ssz files to /tmp during block processing.
[dependencies]
eth2_config = { path = "../eth2/utils/eth2_config" }
beacon_chain = { path = "beacon_chain" }

View File

@@ -5,7 +5,6 @@ authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com
edition = "2018"
[features]
write_ssz_files = [] # Writes debugging .ssz files to /tmp during block processing.
[dependencies]
@@ -42,6 +41,7 @@ rand = "0.7.2"
proto_array_fork_choice = { path = "../../eth2/proto_array_fork_choice" }
lru = "0.4.3"
tempfile = "3.1.0"
safe_arith = { path = "../../eth2/utils/safe_arith" }
[dev-dependencies]
lazy_static = "1.4.0"

View File

@@ -8,6 +8,7 @@ use crate::events::{EventHandler, EventKind};
use crate::fork_choice::{Error as ForkChoiceError, ForkChoice};
use crate::head_tracker::HeadTracker;
use crate::metrics;
use crate::migrate::Migrate;
use crate::naive_aggregation_pool::{Error as NaiveAggregationError, NaiveAggregationPool};
use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::shuffling_cache::ShufflingCache;
@@ -28,19 +29,23 @@ use state_processing::{
};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io::prelude::*;
use std::sync::Arc;
use std::time::{Duration, Instant};
use store::iter::{
BlockRootsIterator, ReverseBlockRootIterator, ReverseStateRootIterator, StateRootsIterator,
BlockRootsIterator, ParentRootBlockIterator, ReverseBlockRootIterator,
ReverseStateRootIterator, StateRootsIterator,
};
use store::{Error as DBError, Migrate, Store};
use store::{Error as DBError, Store};
use types::*;
// Text included in blocks.
// Must be 32-bytes or panic.
//
// |-------must be this long------|
pub const GRAFFITI: &str = "sigp/lighthouse-0.1.1-prerelease";
pub const GRAFFITI: &str = "sigp/lighthouse-0.2.0-prerelease";
/// The time-out before failure during an operation to take a read/write RwLock on the canonical
/// head.
@@ -159,7 +164,7 @@ pub struct HeadInfo {
pub trait BeaconChainTypes: Send + Sync + 'static {
type Store: store::Store<Self::EthSpec>;
type StoreMigrator: store::Migrate<Self::Store, Self::EthSpec>;
type StoreMigrator: Migrate<Self::Store, Self::EthSpec>;
type SlotClock: slot_clock::SlotClock;
type Eth1Chain: Eth1ChainBackend<Self::EthSpec, Self::Store>;
type EthSpec: types::EthSpec;
@@ -199,7 +204,7 @@ pub struct BeaconChain<T: BeaconChainTypes> {
/// A handler for events generated by the beacon chain.
pub event_handler: T::EventHandler,
/// Used to track the heads of the beacon chain.
pub(crate) head_tracker: HeadTracker,
pub(crate) head_tracker: Arc<HeadTracker>,
/// A cache dedicated to block processing.
pub(crate) snapshot_cache: TimeoutRwLock<SnapshotCache<T::EthSpec>>,
/// Caches the shuffling for a given epoch and state root.
@@ -497,6 +502,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.head_tracker.heads()
}
pub fn knows_head(&self, block_hash: &SignedBeaconBlockHash) -> bool {
self.head_tracker.contains_head((*block_hash).into())
}
/// Returns the `BeaconState` at the given slot.
///
/// Returns `None` when the state is not found in the database or there is an error skipping
@@ -1115,11 +1124,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
})?;
let signature_set = indexed_attestation_signature_set_from_pubkeys(
|validator_index| {
pubkey_cache
.get(validator_index)
.map(|pk| Cow::Borrowed(pk.as_point()))
},
|validator_index| pubkey_cache.get(validator_index).map(Cow::Borrowed),
&attestation.signature,
&indexed_attestation,
&fork,
@@ -1230,6 +1235,76 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}
/// Check that the shuffling at `block_root` is equal to one of the shufflings of `state`.
///
/// The `target_epoch` argument determines which shuffling to check compatibility with, it
/// should be equal to the current or previous epoch of `state`, or else `false` will be
/// returned.
///
/// The compatibility check is designed to be fast: we check that the block that
/// determined the RANDAO mix for the `target_epoch` matches the ancestor of the block
/// identified by `block_root` (at that slot).
pub fn shuffling_is_compatible(
&self,
block_root: &Hash256,
target_epoch: Epoch,
state: &BeaconState<T::EthSpec>,
) -> bool {
let slots_per_epoch = T::EthSpec::slots_per_epoch();
let shuffling_lookahead = 1 + self.spec.min_seed_lookahead.as_u64();
// Shuffling can't have changed if we're in the first few epochs
if state.current_epoch() < shuffling_lookahead {
return true;
}
// Otherwise the shuffling is determined by the block at the end of the target epoch
// minus the shuffling lookahead (usually 2). We call this the "pivot".
let pivot_slot =
if target_epoch == state.previous_epoch() || target_epoch == state.current_epoch() {
(target_epoch - shuffling_lookahead).end_slot(slots_per_epoch)
} else {
return false;
};
let state_pivot_block_root = match state.get_block_root(pivot_slot) {
Ok(root) => *root,
Err(e) => {
warn!(
&self.log,
"Missing pivot block root for attestation";
"slot" => pivot_slot,
"error" => format!("{:?}", e),
);
return false;
}
};
// Use fork choice's view of the block DAG to quickly evaluate whether the attestation's
// pivot block is the same as the current state's pivot block. If it is, then the
// attestation's shuffling is the same as the current state's.
// To account for skipped slots, find the first block at *or before* the pivot slot.
let fork_choice_lock = self.fork_choice.core_proto_array();
let pivot_block_root = fork_choice_lock
.iter_block_roots(block_root)
.find(|(_, slot)| *slot <= pivot_slot)
.map(|(block_root, _)| block_root);
drop(fork_choice_lock);
match pivot_block_root {
Some(root) => root == state_pivot_block_root,
None => {
debug!(
&self.log,
"Discarding attestation because of missing ancestor";
"pivot_slot" => pivot_slot.as_u64(),
"block_root" => format!("{:?}", block_root),
);
false
}
}
}
/// Accept some exit and queue it for inclusion in an appropriate block.
pub fn process_voluntary_exit(
&self,
@@ -1365,6 +1440,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Err(BlockError::BlockIsAlreadyKnown) => continue,
// If the block is the genesis block, simply ignore this block.
Err(BlockError::GenesisBlock) => continue,
// If the block is is for a finalized slot, simply ignore this block.
//
// The block is either:
//
// 1. In the canonical finalized chain.
// 2. In some non-canonical chain at a slot that has been finalized already.
//
// In the case of (1), there's no need to re-import and later blocks in this
// segement might be useful.
//
// In the case of (2), skipping the block is valid since we should never import it.
// However, we will potentially get a `ParentUnknown` on a later block. The sync
// protocol will need to ensure this is handled gracefully.
Err(BlockError::WouldRevertFinalizedSlot { .. }) => continue,
// If there was an error whilst determining if the block was invalid, return that
// error.
Err(BlockError::BeaconChainError(e)) => {
@@ -1445,7 +1534,34 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&self,
block: SignedBeaconBlock<T::EthSpec>,
) -> Result<GossipVerifiedBlock<T>, BlockError> {
GossipVerifiedBlock::new(block, self)
let slot = block.message.slot;
let graffiti_string = String::from_utf8(block.message.body.graffiti[..].to_vec())
.unwrap_or_else(|_| format!("{:?}", &block.message.body.graffiti[..]));
match GossipVerifiedBlock::new(block, self) {
Ok(verified) => {
debug!(
self.log,
"Successfully processed gossip block";
"graffiti" => graffiti_string,
"slot" => slot,
"root" => format!("{:?}", verified.block_root()),
);
Ok(verified)
}
Err(e) => {
debug!(
self.log,
"Rejected gossip block";
"error" => format!("{:?}", e),
"graffiti" => graffiti_string,
"slot" => slot,
);
Err(e)
}
}
}
/// Returns `Ok(block_root)` if the given `unverified_block` was successfully verified and
@@ -1722,6 +1838,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.deposits_for_block_inclusion(&state, &eth1_data, &self.spec)?
.into();
// Map from attestation head block root to shuffling compatibility.
// Used to memoize the `attestation_shuffling_is_compatible` function.
let mut shuffling_filter_cache = HashMap::new();
let attestation_filter = |att: &&Attestation<T::EthSpec>| -> bool {
*shuffling_filter_cache
.entry((att.data.beacon_block_root, att.data.target.epoch))
.or_insert_with(|| {
self.shuffling_is_compatible(
&att.data.beacon_block_root,
att.data.target.epoch,
&state,
)
})
};
let mut block = SignedBeaconBlock {
message: BeaconBlock {
slot: state.slot,
@@ -1736,7 +1867,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
attester_slashings: attester_slashings.into(),
attestations: self
.op_pool
.get_attestations(&state, &self.spec)
.get_attestations(&state, attestation_filter, &self.spec)
.map_err(BlockProductionError::OpPoolError)?
.into(),
deposits,
@@ -1794,6 +1925,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let beacon_block_root = self.fork_choice.find_head(&self)?;
let current_head = self.head_info()?;
let old_finalized_root = current_head.finalized_checkpoint.root;
if beacon_block_root == current_head.block_root {
return Ok(());
@@ -1921,7 +2053,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
});
if new_finalized_epoch != old_finalized_epoch {
self.after_finalization(old_finalized_epoch, finalized_root)?;
self.after_finalization(
old_finalized_epoch,
finalized_root,
old_finalized_root.into(),
)?;
}
let _ = self.event_handler.register(EventKind::BeaconHeadChanged {
@@ -1950,6 +2086,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&self,
old_finalized_epoch: Epoch,
finalized_block_root: Hash256,
old_finalized_root: SignedBeaconBlockHash,
) -> Result<(), Error> {
let finalized_block = self
.store
@@ -1989,10 +2126,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// TODO: configurable max finality distance
let max_finality_distance = 0;
self.store_migrator.freeze_to_state(
self.store_migrator.process_finalization(
finalized_block.state_root,
finalized_state,
max_finality_distance,
Arc::clone(&self.head_tracker),
old_finalized_root,
finalized_block_root.into(),
);
let _ = self.event_handler.register(EventKind::BeaconFinalization {
@@ -2076,6 +2216,100 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.slot_clock
.duration_to_slot(epoch.start_slot(T::EthSpec::slots_per_epoch()))
}
pub fn dump_as_dot<W: Write>(&self, output: &mut W) {
let canonical_head_hash = self
.canonical_head
.try_read_for(HEAD_LOCK_TIMEOUT)
.ok_or_else(|| Error::CanonicalHeadLockTimeout)
.unwrap()
.beacon_block_root;
let mut visited: HashSet<Hash256> = HashSet::new();
let mut finalized_blocks: HashSet<Hash256> = HashSet::new();
let genesis_block_hash = Hash256::zero();
write!(output, "digraph beacon {{\n").unwrap();
write!(output, "\t_{:?}[label=\"genesis\"];\n", genesis_block_hash).unwrap();
// Canonical head needs to be processed first as otherwise finalized blocks aren't detected
// properly.
let heads = {
let mut heads = self.heads();
let canonical_head_index = heads
.iter()
.position(|(block_hash, _)| *block_hash == canonical_head_hash)
.unwrap();
let (canonical_head_hash, canonical_head_slot) =
heads.swap_remove(canonical_head_index);
heads.insert(0, (canonical_head_hash, canonical_head_slot));
heads
};
for (head_hash, _head_slot) in heads {
for (block_hash, signed_beacon_block) in
ParentRootBlockIterator::new(&*self.store, head_hash)
{
if visited.contains(&block_hash) {
break;
}
visited.insert(block_hash);
if signed_beacon_block.slot() % T::EthSpec::slots_per_epoch() == 0 {
let block = self.get_block(&block_hash).unwrap().unwrap();
let state = self
.get_state(&block.state_root(), Some(block.slot()))
.unwrap()
.unwrap();
finalized_blocks.insert(state.finalized_checkpoint.root);
}
if block_hash == canonical_head_hash {
write!(
output,
"\t_{:?}[label=\"{} ({})\" shape=box3d];\n",
block_hash,
block_hash,
signed_beacon_block.slot()
)
.unwrap();
} else if finalized_blocks.contains(&block_hash) {
write!(
output,
"\t_{:?}[label=\"{} ({})\" shape=Msquare];\n",
block_hash,
block_hash,
signed_beacon_block.slot()
)
.unwrap();
} else {
write!(
output,
"\t_{:?}[label=\"{} ({})\" shape=box];\n",
block_hash,
block_hash,
signed_beacon_block.slot()
)
.unwrap();
}
write!(
output,
"\t_{:?} -> _{:?};\n",
block_hash,
signed_beacon_block.parent_root()
)
.unwrap();
}
}
write!(output, "}}\n").unwrap();
}
// Used for debugging
#[allow(dead_code)]
pub fn dump_dot_file(&self, file_name: &str) {
let mut file = std::fs::File::create(file_name).unwrap();
self.dump_as_dot(&mut file);
}
}
impl<T: BeaconChainTypes> Drop for BeaconChain<T> {

View File

@@ -49,19 +49,22 @@ use crate::{
metrics, BeaconChain, BeaconChainError, BeaconChainTypes, BeaconSnapshot,
};
use parking_lot::RwLockReadGuard;
use slog::{error, Logger};
use slot_clock::SlotClock;
use ssz::Encode;
use state_processing::{
block_signature_verifier::{
BlockSignatureVerifier, Error as BlockSignatureVerifierError, G1Point,
},
block_signature_verifier::{BlockSignatureVerifier, Error as BlockSignatureVerifierError},
per_block_processing, per_slot_processing, BlockProcessingError, BlockSignatureStrategy,
SlotProcessingError,
};
use std::borrow::Cow;
use std::fs;
use std::io::Write;
use store::{Error as DBError, StateBatch};
use tree_hash::TreeHash;
use types::{
BeaconBlock, BeaconState, BeaconStateError, ChainSpec, CloneConfig, EthSpec, Hash256,
RelativeEpoch, SignedBeaconBlock, Slot,
PublicKey, RelativeEpoch, SignedBeaconBlock, Slot,
};
mod block_processing_outcome;
@@ -71,6 +74,12 @@ pub use block_processing_outcome::BlockProcessingOutcome;
/// Maximum block slot number. Block with slots bigger than this constant will NOT be processed.
const MAXIMUM_BLOCK_SLOT_NUMBER: u64 = 4_294_967_296; // 2^32
/// If true, everytime a block is processed the pre-state, post-state and block are written to SSZ
/// files in the temp directory.
///
/// Only useful for testing.
const WRITE_BLOCK_PROCESSING_SSZ: bool = cfg!(feature = "write_ssz_files");
/// Returned when a block was not verified. A block is not verified for two reasons:
///
/// - The block is malformed/invalid (indicated by all results other than `BeaconChainError`.
@@ -304,6 +313,10 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
Err(BlockError::ProposalSignatureInvalid)
}
}
pub fn block_root(&self) -> Hash256 {
self.block_root
}
}
impl<T: BeaconChainTypes> IntoFullyVerifiedBlock<T> for GossipVerifiedBlock<T> {
@@ -517,6 +530,13 @@ impl<T: BeaconChainTypes> FullyVerifiedBlock<T> {
* invalid.
*/
write_state(
&format!("state_pre_block_{}", block_root),
&state,
&chain.log,
);
write_block(&block, block_root, &chain.log);
let core_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_CORE);
if let Err(err) = per_block_processing(
@@ -547,6 +567,12 @@ impl<T: BeaconChainTypes> FullyVerifiedBlock<T> {
metrics::stop_timer(state_root_timer);
write_state(
&format!("state_post_block_{}", block_root),
&state,
&chain.log,
);
/*
* Check to ensure the state root on the block matches the one we have calculated.
*/
@@ -785,7 +811,7 @@ fn get_signature_verifier<'a, E: EthSpec>(
state: &'a BeaconState<E>,
validator_pubkey_cache: &'a ValidatorPubkeyCache,
spec: &'a ChainSpec,
) -> BlockSignatureVerifier<'a, E, impl Fn(usize) -> Option<Cow<'a, G1Point>> + Clone> {
) -> BlockSignatureVerifier<'a, E, impl Fn(usize) -> Option<Cow<'a, PublicKey>> + Clone> {
BlockSignatureVerifier::new(
state,
move |validator_index| {
@@ -794,7 +820,7 @@ fn get_signature_verifier<'a, E: EthSpec>(
if validator_index < state.validators.len() {
validator_pubkey_cache
.get(validator_index)
.map(|pk| Cow::Borrowed(pk.as_point()))
.map(|pk| Cow::Borrowed(pk))
} else {
None
}
@@ -802,3 +828,46 @@ fn get_signature_verifier<'a, E: EthSpec>(
spec,
)
}
fn write_state<T: EthSpec>(prefix: &str, state: &BeaconState<T>, log: &Logger) {
if WRITE_BLOCK_PROCESSING_SSZ {
let root = state.tree_hash_root();
let filename = format!("{}_slot_{}_root_{}.ssz", prefix, state.slot, root);
let mut path = std::env::temp_dir().join("lighthouse");
let _ = fs::create_dir_all(path.clone());
path = path.join(filename);
match fs::File::create(path.clone()) {
Ok(mut file) => {
let _ = file.write_all(&state.as_ssz_bytes());
}
Err(e) => error!(
log,
"Failed to log state";
"path" => format!("{:?}", path),
"error" => format!("{:?}", e)
),
}
}
}
fn write_block<T: EthSpec>(block: &SignedBeaconBlock<T>, root: Hash256, log: &Logger) {
if WRITE_BLOCK_PROCESSING_SSZ {
let filename = format!("block_slot_{}_root{}.ssz", block.message.slot, root);
let mut path = std::env::temp_dir().join("lighthouse");
let _ = fs::create_dir_all(path.clone());
path = path.join(filename);
match fs::File::create(path.clone()) {
Ok(mut file) => {
let _ = file.write_all(&block.as_ssz_bytes());
}
Err(e) => error!(
log,
"Failed to log block";
"path" => format!("{:?}", path),
"error" => format!("{:?}", e)
),
}
}
}

View File

@@ -4,7 +4,7 @@ use types::{Hash256, Slot};
/// This is a legacy object that is being kept around to reduce merge conflicts.
///
/// As soon as this is merged into master, it should be removed as soon as possible.
/// TODO: As soon as this is merged into master, it should be removed as soon as possible.
#[derive(Debug, PartialEq)]
pub enum BlockProcessingOutcome {
/// Block was valid and imported into the block graph.

View File

@@ -5,6 +5,7 @@ use crate::eth1_chain::{CachingEth1Backend, SszEth1};
use crate::events::NullEventHandler;
use crate::fork_choice::SszForkChoice;
use crate::head_tracker::HeadTracker;
use crate::migrate::Migrate;
use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::shuffling_cache::ShufflingCache;
use crate::snapshot_cache::{SnapshotCache, DEFAULT_SNAPSHOT_CACHE_SIZE};
@@ -47,7 +48,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
for Witness<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TSlotClock: SlotClock + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -97,7 +98,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TSlotClock: SlotClock + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -229,7 +230,7 @@ where
.get::<PersistedBeaconChain>(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY))
.map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))?
.ok_or_else(|| {
"No persisted beacon chain found in store. Try deleting the .lighthouse/beacon dir."
"No persisted beacon chain found in store. Try purging the beacon chain database."
.to_string()
})?;
@@ -442,7 +443,7 @@ where
event_handler: self
.event_handler
.ok_or_else(|| "Cannot build without an event handler".to_string())?,
head_tracker: self.head_tracker.unwrap_or_default(),
head_tracker: Arc::new(self.head_tracker.unwrap_or_default()),
snapshot_cache: TimeoutRwLock::new(SnapshotCache::new(
DEFAULT_SNAPSHOT_CACHE_SIZE,
canonical_head,
@@ -475,7 +476,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TSlotClock: SlotClock + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -545,7 +546,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TSlotClock: SlotClock + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,
@@ -583,7 +584,7 @@ impl<TStore, TStoreMigrator, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,
@@ -622,7 +623,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec> + 'static,
TStoreMigrator: Migrate<TStore, TEthSpec> + 'static,
TSlotClock: SlotClock + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -654,12 +655,12 @@ fn genesis_block<T: EthSpec>(
#[cfg(test)]
mod test {
use super::*;
use crate::migrate::{MemoryStore, NullMigrator};
use eth2_hashing::hash;
use genesis::{generate_deterministic_keypairs, interop_genesis_state};
use sloggers::{null::NullLoggerBuilder, Build};
use ssz::Encode;
use std::time::Duration;
use store::{migrate::NullMigrator, MemoryStore};
use tempfile::tempdir;
use types::{EthSpec, MinimalEthSpec, Slot};

View File

@@ -43,6 +43,14 @@ pub enum Error {
///
/// The eth1 caches are stale, or a junk value was voted into the chain.
UnknownPreviousEth1BlockHash,
/// An arithmetic error occurred.
ArithError(safe_arith::ArithError),
}
impl From<safe_arith::ArithError> for Error {
fn from(e: safe_arith::ArithError) -> Self {
Self::ArithError(e)
}
}
#[derive(Encode, Decode, Clone)]
@@ -367,7 +375,7 @@ impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S
_spec: &ChainSpec,
) -> Result<Vec<Deposit>, Error> {
let deposit_index = state.eth1_deposit_index;
let deposit_count = if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data_vote) {
let deposit_count = if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data_vote)? {
new_eth1_data.deposit_count
} else {
state.eth1_data.deposit_count

View File

@@ -25,11 +25,22 @@ impl HeadTracker {
/// the upstream user.
pub fn register_block<E: EthSpec>(&self, block_root: Hash256, block: &BeaconBlock<E>) {
let mut map = self.0.write();
map.remove(&block.parent_root);
map.insert(block_root, block.slot);
}
/// Removes abandoned head.
pub fn remove_head(&self, block_root: Hash256) {
let mut map = self.0.write();
debug_assert!(map.contains_key(&block_root));
map.remove(&block_root);
}
/// Returns true iff `block_root` is a recognized head.
pub fn contains_head(&self, block_root: Hash256) -> bool {
self.0.read().contains_key(&block_root)
}
/// Returns the list of heads in the chain.
pub fn heads(&self) -> Vec<(Hash256, Slot)> {
self.0

View File

@@ -12,6 +12,7 @@ pub mod events;
mod fork_choice;
mod head_tracker;
mod metrics;
pub mod migrate;
mod naive_aggregation_pool;
mod persisted_beacon_chain;
mod shuffling_cache;

View File

@@ -0,0 +1,349 @@
use crate::errors::BeaconChainError;
use crate::head_tracker::HeadTracker;
use parking_lot::Mutex;
use slog::{debug, warn, Logger};
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
use std::mem;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use store::iter::{ParentRootBlockIterator, RootsIterator};
use store::{hot_cold_store::HotColdDBError, Error, SimpleDiskStore, Store};
pub use store::{DiskStore, MemoryStore};
use types::*;
use types::{BeaconState, EthSpec, Hash256, Slot};
/// Trait for migration processes that update the database upon finalization.
pub trait Migrate<S: Store<E>, E: EthSpec>: Send + Sync + 'static {
fn new(db: Arc<S>, log: Logger) -> Self;
fn process_finalization(
&self,
_state_root: Hash256,
_new_finalized_state: BeaconState<E>,
_max_finality_distance: u64,
_head_tracker: Arc<HeadTracker>,
_old_finalized_block_hash: SignedBeaconBlockHash,
_new_finalized_block_hash: SignedBeaconBlockHash,
) {
}
/// Traverses live heads and prunes blocks and states of chains that we know can't be built
/// upon because finalization would prohibit it. This is a optimisation intended to save disk
/// space.
///
/// Assumptions:
/// * It is called after every finalization.
fn prune_abandoned_forks(
store: Arc<S>,
head_tracker: Arc<HeadTracker>,
old_finalized_block_hash: SignedBeaconBlockHash,
new_finalized_block_hash: SignedBeaconBlockHash,
new_finalized_slot: Slot,
) -> Result<(), BeaconChainError> {
let old_finalized_slot = store
.get_block(&old_finalized_block_hash.into())?
.ok_or_else(|| BeaconChainError::MissingBeaconBlock(old_finalized_block_hash.into()))?
.slot();
// Collect hashes from new_finalized_block back to old_finalized_block (inclusive)
let mut found_block = false; // hack for `take_until`
let newly_finalized_blocks: HashMap<SignedBeaconBlockHash, Slot> = HashMap::from_iter(
ParentRootBlockIterator::new(&*store, new_finalized_block_hash.into())
.take_while(|(block_hash, _)| {
if found_block {
false
} else {
found_block |= *block_hash == old_finalized_block_hash.into();
true
}
})
.map(|(block_hash, block)| (block_hash.into(), block.slot())),
);
// We don't know which blocks are shared among abandoned chains, so we buffer and delete
// everything in one fell swoop.
let mut abandoned_blocks: HashSet<SignedBeaconBlockHash> = HashSet::new();
let mut abandoned_states: HashSet<(Slot, BeaconStateHash)> = HashSet::new();
let mut abandoned_heads: HashSet<Hash256> = HashSet::new();
for (head_hash, head_slot) in head_tracker.heads() {
let mut potentially_abandoned_head: Option<Hash256> = Some(head_hash);
let mut potentially_abandoned_blocks: Vec<(
Slot,
Option<SignedBeaconBlockHash>,
Option<BeaconStateHash>,
)> = Vec::new();
let head_state_hash = store
.get_block(&head_hash)?
.ok_or_else(|| BeaconStateError::MissingBeaconBlock(head_hash.into()))?
.state_root();
let iterator = std::iter::once((head_hash, head_state_hash, head_slot))
.chain(RootsIterator::from_block(Arc::clone(&store), head_hash)?);
for (block_hash, state_hash, slot) in iterator {
if slot < old_finalized_slot {
// We must assume here any candidate chains include old_finalized_block_hash,
// i.e. there aren't any forks starting at a block that is a strict ancestor of
// old_finalized_block_hash.
break;
}
match newly_finalized_blocks.get(&block_hash.into()).copied() {
// Block is not finalized, mark it and its state for deletion
None => {
potentially_abandoned_blocks.push((
slot,
Some(block_hash.into()),
Some(state_hash.into()),
));
}
Some(finalized_slot) => {
// Block root is finalized, and we have reached the slot it was finalized
// at: we've hit a shared part of the chain.
if finalized_slot == slot {
// The first finalized block of a candidate chain lies after (in terms
// of slots order) the newly finalized block. It's not a candidate for
// prunning.
if finalized_slot == new_finalized_slot {
potentially_abandoned_blocks.clear();
potentially_abandoned_head.take();
}
break;
}
// Block root is finalized, but we're at a skip slot: delete the state only.
else {
potentially_abandoned_blocks.push((
slot,
None,
Some(state_hash.into()),
));
}
}
}
}
abandoned_heads.extend(potentially_abandoned_head.into_iter());
if !potentially_abandoned_blocks.is_empty() {
abandoned_blocks.extend(
potentially_abandoned_blocks
.iter()
.filter_map(|(_, maybe_block_hash, _)| *maybe_block_hash),
);
abandoned_states.extend(potentially_abandoned_blocks.iter().filter_map(
|(slot, _, maybe_state_hash)| match maybe_state_hash {
None => None,
Some(state_hash) => Some((*slot, *state_hash)),
},
));
}
}
// XXX Should be performed atomically, see
// https://github.com/sigp/lighthouse/issues/692
for block_hash in abandoned_blocks.into_iter() {
store.delete_block(&block_hash.into())?;
}
for (slot, state_hash) in abandoned_states.into_iter() {
store.delete_state(&state_hash.into(), slot)?;
}
for head_hash in abandoned_heads.into_iter() {
head_tracker.remove_head(head_hash);
}
Ok(())
}
}
/// Migrator that does nothing, for stores that don't need migration.
pub struct NullMigrator;
impl<E: EthSpec> Migrate<SimpleDiskStore<E>, E> for NullMigrator {
fn new(_: Arc<SimpleDiskStore<E>>, _: Logger) -> Self {
NullMigrator
}
}
impl<E: EthSpec> Migrate<MemoryStore<E>, E> for NullMigrator {
fn new(_: Arc<MemoryStore<E>>, _: Logger) -> Self {
NullMigrator
}
}
/// Migrator that immediately calls the store's migration function, blocking the current execution.
///
/// Mostly useful for tests.
pub struct BlockingMigrator<S> {
db: Arc<S>,
}
impl<E: EthSpec, S: Store<E>> Migrate<S, E> for BlockingMigrator<S> {
fn new(db: Arc<S>, _: Logger) -> Self {
BlockingMigrator { db }
}
fn process_finalization(
&self,
state_root: Hash256,
new_finalized_state: BeaconState<E>,
_max_finality_distance: u64,
head_tracker: Arc<HeadTracker>,
old_finalized_block_hash: SignedBeaconBlockHash,
new_finalized_block_hash: SignedBeaconBlockHash,
) {
if let Err(e) = S::process_finalization(self.db.clone(), state_root, &new_finalized_state) {
// This migrator is only used for testing, so we just log to stderr without a logger.
eprintln!("Migration error: {:?}", e);
}
if let Err(e) = Self::prune_abandoned_forks(
self.db.clone(),
head_tracker,
old_finalized_block_hash,
new_finalized_block_hash,
new_finalized_state.slot,
) {
eprintln!("Pruning error: {:?}", e);
}
}
}
type MpscSender<E> = mpsc::Sender<(
Hash256,
BeaconState<E>,
Arc<HeadTracker>,
SignedBeaconBlockHash,
SignedBeaconBlockHash,
Slot,
)>;
/// Migrator that runs a background thread to migrate state from the hot to the cold database.
pub struct BackgroundMigrator<E: EthSpec> {
db: Arc<DiskStore<E>>,
tx_thread: Mutex<(MpscSender<E>, thread::JoinHandle<()>)>,
log: Logger,
}
impl<E: EthSpec> Migrate<DiskStore<E>, E> for BackgroundMigrator<E> {
fn new(db: Arc<DiskStore<E>>, log: Logger) -> Self {
let tx_thread = Mutex::new(Self::spawn_thread(db.clone(), log.clone()));
Self { db, tx_thread, log }
}
/// Perform the freezing operation on the database,
fn process_finalization(
&self,
finalized_state_root: Hash256,
new_finalized_state: BeaconState<E>,
max_finality_distance: u64,
head_tracker: Arc<HeadTracker>,
old_finalized_block_hash: SignedBeaconBlockHash,
new_finalized_block_hash: SignedBeaconBlockHash,
) {
if !self.needs_migration(new_finalized_state.slot, max_finality_distance) {
return;
}
let (ref mut tx, ref mut thread) = *self.tx_thread.lock();
let new_finalized_slot = new_finalized_state.slot;
if let Err(tx_err) = tx.send((
finalized_state_root,
new_finalized_state,
head_tracker,
old_finalized_block_hash,
new_finalized_block_hash,
new_finalized_slot,
)) {
let (new_tx, new_thread) = Self::spawn_thread(self.db.clone(), self.log.clone());
drop(mem::replace(tx, new_tx));
let old_thread = mem::replace(thread, new_thread);
// Join the old thread, which will probably have panicked, or may have
// halted normally just now as a result of us dropping the old `mpsc::Sender`.
if let Err(thread_err) = old_thread.join() {
warn!(
self.log,
"Migration thread died, so it was restarted";
"reason" => format!("{:?}", thread_err)
);
}
// Retry at most once, we could recurse but that would risk overflowing the stack.
let _ = tx.send(tx_err.0);
}
}
}
impl<E: EthSpec> BackgroundMigrator<E> {
/// Return true if a migration needs to be performed, given a new `finalized_slot`.
fn needs_migration(&self, finalized_slot: Slot, max_finality_distance: u64) -> bool {
let finality_distance = finalized_slot - self.db.get_split_slot();
finality_distance > max_finality_distance
}
/// Spawn a new child thread to run the migration process.
///
/// Return a channel handle for sending new finalized states to the thread.
fn spawn_thread(
db: Arc<DiskStore<E>>,
log: Logger,
) -> (
mpsc::Sender<(
Hash256,
BeaconState<E>,
Arc<HeadTracker>,
SignedBeaconBlockHash,
SignedBeaconBlockHash,
Slot,
)>,
thread::JoinHandle<()>,
) {
let (tx, rx) = mpsc::channel();
let thread = thread::spawn(move || {
while let Ok((
state_root,
state,
head_tracker,
old_finalized_block_hash,
new_finalized_block_hash,
new_finalized_slot,
)) = rx.recv()
{
match DiskStore::process_finalization(db.clone(), state_root, &state) {
Ok(()) => {}
Err(Error::HotColdDBError(HotColdDBError::FreezeSlotUnaligned(slot))) => {
debug!(
log,
"Database migration postponed, unaligned finalized block";
"slot" => slot.as_u64()
);
}
Err(e) => {
warn!(
log,
"Database migration failed";
"error" => format!("{:?}", e)
);
}
};
match Self::prune_abandoned_forks(
db.clone(),
head_tracker,
old_finalized_block_hash,
new_finalized_block_hash,
new_finalized_slot,
) {
Ok(()) => {}
Err(e) => warn!(log, "Block pruning failed: {:?}", e),
}
}
});
(tx, thread)
}
}

View File

@@ -1,6 +1,7 @@
pub use crate::beacon_chain::{
BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY,
};
use crate::migrate::{BlockingMigrator, Migrate, NullMigrator};
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::{
builder::{BeaconChainBuilder, Witness},
@@ -14,16 +15,16 @@ use sloggers::{null::NullLoggerBuilder, Build};
use slot_clock::TestingSlotClock;
use state_processing::per_slot_processing;
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use store::{
migrate::{BlockingMigrator, NullMigrator},
DiskStore, MemoryStore, Migrate, Store,
};
use store::{DiskStore, MemoryStore, Store};
use tempfile::{tempdir, TempDir};
use tree_hash::TreeHash;
use types::{
AggregateSignature, Attestation, BeaconState, ChainSpec, Domain, EthSpec, Hash256, Keypair,
SecretKey, Signature, SignedBeaconBlock, SignedRoot, Slot,
AggregateSignature, Attestation, BeaconState, BeaconStateHash, ChainSpec, Domain, EthSpec,
Hash256, Keypair, SecretKey, Signature, SignedBeaconBlock, SignedBeaconBlockHash, SignedRoot,
Slot,
};
pub use types::test_utils::generate_deterministic_keypairs;
@@ -135,7 +136,10 @@ impl<E: EthSpec> BeaconChainHarness<DiskHarnessType<E>> {
.logger(log.clone())
.custom_spec(spec.clone())
.store(store.clone())
.store_migrator(<BlockingMigrator<_> as Migrate<_, E>>::new(store))
.store_migrator(<BlockingMigrator<_> as Migrate<_, E>>::new(
store,
log.clone(),
))
.data_dir(data_dir.path().to_path_buf())
.genesis_state(
interop_genesis_state::<E>(&keypairs, HARNESS_GENESIS_TIME, &spec)
@@ -175,7 +179,10 @@ impl<E: EthSpec> BeaconChainHarness<DiskHarnessType<E>> {
.logger(log.clone())
.custom_spec(spec)
.store(store.clone())
.store_migrator(<BlockingMigrator<_> as Migrate<_, E>>::new(store))
.store_migrator(<BlockingMigrator<_> as Migrate<_, E>>::new(
store,
log.clone(),
))
.data_dir(data_dir.path().to_path_buf())
.resume_from_db()
.expect("should resume beacon chain from db")
@@ -272,6 +279,123 @@ where
head_block_root.expect("did not produce any blocks")
}
/// Returns current canonical head slot
pub fn get_chain_slot(&self) -> Slot {
self.chain.slot().unwrap()
}
/// Returns current canonical head state
pub fn get_head_state(&self) -> BeaconState<E> {
self.chain.head().unwrap().beacon_state
}
/// Adds a single block (synchronously) onto either the canonical chain (block_strategy ==
/// OnCanonicalHead) or a fork (block_strategy == ForkCanonicalChainAt).
pub fn add_block(
&self,
state: &BeaconState<E>,
block_strategy: BlockStrategy,
slot: Slot,
validators: &[usize],
) -> (SignedBeaconBlockHash, BeaconState<E>) {
while self.chain.slot().expect("should have a slot") < slot {
self.advance_slot();
}
let (block, new_state) = self.build_block(state.clone(), slot, block_strategy);
let block_root = self
.chain
.process_block(block)
.expect("should not error during block processing");
self.chain.fork_choice().expect("should find head");
let attestation_strategy = AttestationStrategy::SomeValidators(validators.to_vec());
self.add_free_attestations(&attestation_strategy, &new_state, block_root, slot);
(block_root.into(), new_state)
}
/// `add_block()` repeated `num_blocks` times.
pub fn add_blocks(
&self,
mut state: BeaconState<E>,
mut slot: Slot,
num_blocks: usize,
attesting_validators: &[usize],
block_strategy: BlockStrategy,
) -> (
HashMap<Slot, SignedBeaconBlockHash>,
HashMap<Slot, BeaconStateHash>,
Slot,
SignedBeaconBlockHash,
BeaconState<E>,
) {
let mut blocks: HashMap<Slot, SignedBeaconBlockHash> = HashMap::with_capacity(num_blocks);
let mut states: HashMap<Slot, BeaconStateHash> = HashMap::with_capacity(num_blocks);
for _ in 0..num_blocks {
let (new_root_hash, new_state) =
self.add_block(&state, block_strategy, slot, attesting_validators);
blocks.insert(slot, new_root_hash);
states.insert(slot, new_state.tree_hash_root().into());
state = new_state;
slot += 1;
}
let head_hash = blocks[&(slot - 1)];
(blocks, states, slot, head_hash, state)
}
/// A wrapper on `add_blocks()` to avoid passing enums explicitly.
pub fn add_canonical_chain_blocks(
&self,
state: BeaconState<E>,
slot: Slot,
num_blocks: usize,
attesting_validators: &[usize],
) -> (
HashMap<Slot, SignedBeaconBlockHash>,
HashMap<Slot, BeaconStateHash>,
Slot,
SignedBeaconBlockHash,
BeaconState<E>,
) {
let block_strategy = BlockStrategy::OnCanonicalHead;
self.add_blocks(
state,
slot,
num_blocks,
attesting_validators,
block_strategy,
)
}
/// A wrapper on `add_blocks()` to avoid passing enums explicitly.
pub fn add_stray_blocks(
&self,
state: BeaconState<E>,
slot: Slot,
num_blocks: usize,
attesting_validators: &[usize],
) -> (
HashMap<Slot, SignedBeaconBlockHash>,
HashMap<Slot, BeaconStateHash>,
Slot,
SignedBeaconBlockHash,
BeaconState<E>,
) {
let block_strategy = BlockStrategy::ForkCanonicalChainAt {
previous_slot: slot,
first_slot: slot + 2,
};
self.add_blocks(
state,
slot + 2,
num_blocks,
attesting_validators,
block_strategy,
)
}
/// Returns a newly created block, signed by the proposer for the given slot.
fn build_block(
&self,
@@ -347,7 +471,9 @@ where
.process_attestation(attestation, AttestationType::Aggregated)
.expect("should not error during attestation processing")
{
AttestationProcessingOutcome::Processed => (),
// PastEpoch can occur if we fork over several epochs
AttestationProcessingOutcome::Processed
| AttestationProcessingOutcome::PastEpoch { .. } => (),
other => panic!("did not successfully process attestation: {:?}", other),
}
});

View File

@@ -40,8 +40,8 @@ fn produces_attestations() {
let state = &harness.chain.head().expect("should get head").beacon_state;
assert_eq!(state.slot, num_blocks_produced, "head should have updated");
assert!(
state.finalized_checkpoint.epoch > 0,
assert_ne!(
state.finalized_checkpoint.epoch, 0,
"head should have updated"
);

View File

@@ -6,9 +6,12 @@ extern crate lazy_static;
use beacon_chain::test_utils::{
AttestationStrategy, BeaconChainHarness, BlockStrategy, DiskHarnessType,
};
use beacon_chain::{AttestationProcessingOutcome, AttestationType};
use beacon_chain::BeaconSnapshot;
use beacon_chain::{AttestationProcessingOutcome, AttestationType, StateSkipConfig};
use rand::Rng;
use sloggers::{null::NullLoggerBuilder, Build};
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use store::{
iter::{BlockRootsIterator, StateRootsIterator},
@@ -20,11 +23,12 @@ use types::test_utils::{SeedableRng, XorShiftRng};
use types::*;
// Should ideally be divisible by 3.
pub const VALIDATOR_COUNT: usize = 24;
pub const LOW_VALIDATOR_COUNT: usize = 24;
pub const HIGH_VALIDATOR_COUNT: usize = 64;
lazy_static! {
/// A cached set of keys.
static ref KEYPAIRS: Vec<Keypair> = types::test_utils::generate_deterministic_keypairs(VALIDATOR_COUNT);
static ref KEYPAIRS: Vec<Keypair> = types::test_utils::generate_deterministic_keypairs(HIGH_VALIDATOR_COUNT);
}
type E = MinimalEthSpec;
@@ -57,7 +61,7 @@ fn full_participation_no_skips() {
let num_blocks_produced = E::slots_per_epoch() * 5;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
harness.extend_chain(
num_blocks_produced as usize,
@@ -77,7 +81,7 @@ fn randomised_skips() {
let mut num_blocks_produced = 0;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
let rng = &mut XorShiftRng::from_seed([42; 16]);
let mut head_slot = 0;
@@ -113,14 +117,16 @@ fn randomised_skips() {
fn long_skip() {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
// Number of blocks to create in the first run, intentionally not falling on an epoch
// boundary in order to check that the DB hot -> cold migration is capable of reaching
// back across the skip distance, and correctly migrating those extra non-finalized states.
let initial_blocks = E::slots_per_epoch() * 5 + E::slots_per_epoch() / 2;
let skip_slots = E::slots_per_historical_root() as u64 * 8;
let final_blocks = E::slots_per_epoch() * 4;
// Create the minimum ~2.5 epochs of extra blocks required to re-finalize the chain.
// Having this set lower ensures that we start justifying and finalizing quickly after a skip.
let final_blocks = 2 * E::slots_per_epoch() + E::slots_per_epoch() / 2;
harness.extend_chain(
initial_blocks as usize,
@@ -223,7 +229,7 @@ fn split_slot_restore() {
let split_slot = {
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
let num_blocks = 4 * E::slots_per_epoch();
@@ -251,10 +257,10 @@ fn epoch_boundary_state_attestation_processing() {
let num_blocks_produced = E::slots_per_epoch() * 5;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
let late_validators = vec![0, 1];
let timely_validators = (2..VALIDATOR_COUNT).collect::<Vec<_>>();
let timely_validators = (2..LOW_VALIDATOR_COUNT).collect::<Vec<_>>();
let mut late_attestations = vec![];
@@ -333,7 +339,7 @@ fn epoch_boundary_state_attestation_processing() {
fn delete_blocks_and_states() {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), VALIDATOR_COUNT);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
let unforked_blocks = 4 * E::slots_per_epoch();
@@ -345,13 +351,11 @@ fn delete_blocks_and_states() {
);
// Create a fork post-finalization.
let two_thirds = (VALIDATOR_COUNT / 3) * 2;
let two_thirds = (LOW_VALIDATOR_COUNT / 3) * 2;
let honest_validators: Vec<usize> = (0..two_thirds).collect();
let faulty_validators: Vec<usize> = (two_thirds..VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (two_thirds..LOW_VALIDATOR_COUNT).collect();
// NOTE: should remove this -1 and/or write a similar test once #845 is resolved
// https://github.com/sigp/lighthouse/issues/845
let fork_blocks = 2 * E::slots_per_epoch() - 1;
let fork_blocks = 2 * E::slots_per_epoch();
let (honest_head, faulty_head) = harness.generate_two_forks_by_skipping_a_block(
&honest_validators,
@@ -425,6 +429,825 @@ fn delete_blocks_and_states() {
check_chain_dump(&harness, unforked_blocks + fork_blocks + 1);
}
// Check that we never produce invalid blocks when there is deep forking that changes the shuffling.
// See https://github.com/sigp/lighthouse/issues/845
fn multi_epoch_fork_valid_blocks_test(
initial_blocks: usize,
num_fork1_blocks: usize,
num_fork2_blocks: usize,
num_fork1_validators: usize,
) -> (TempDir, TestHarness, Hash256, Hash256) {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
// Create the initial portion of the chain
if initial_blocks > 0 {
harness.extend_chain(
initial_blocks,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
}
assert!(num_fork1_validators <= LOW_VALIDATOR_COUNT);
let fork1_validators: Vec<usize> = (0..num_fork1_validators).collect();
let fork2_validators: Vec<usize> = (num_fork1_validators..LOW_VALIDATOR_COUNT).collect();
let (head1, head2) = harness.generate_two_forks_by_skipping_a_block(
&fork1_validators,
&fork2_validators,
num_fork1_blocks,
num_fork2_blocks,
);
(db_path, harness, head1, head2)
}
// This is the minimal test of block production with different shufflings.
#[test]
fn block_production_different_shuffling_early() {
let slots_per_epoch = E::slots_per_epoch() as usize;
multi_epoch_fork_valid_blocks_test(
slots_per_epoch - 2,
slots_per_epoch + 3,
slots_per_epoch + 3,
LOW_VALIDATOR_COUNT / 2,
);
}
#[test]
fn block_production_different_shuffling_long() {
let slots_per_epoch = E::slots_per_epoch() as usize;
multi_epoch_fork_valid_blocks_test(
2 * slots_per_epoch - 2,
3 * slots_per_epoch,
3 * slots_per_epoch,
LOW_VALIDATOR_COUNT / 2,
);
}
// Check that the op pool safely includes multiple attestations per block when necessary.
// This checks the correctness of the shuffling compatibility memoization.
#[test]
fn multiple_attestations_per_block() {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store, HIGH_VALIDATOR_COUNT);
let chain = &harness.chain;
harness.extend_chain(
MainnetEthSpec::slots_per_epoch() as usize * 3,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
let head = chain.head().unwrap();
let committees_per_slot = head
.beacon_state
.get_committee_count_at_slot(head.beacon_state.slot)
.unwrap();
assert!(committees_per_slot > 1);
for snapshot in chain.chain_dump().unwrap() {
assert_eq!(
snapshot.beacon_block.message.body.attestations.len() as u64,
if snapshot.beacon_block.slot() <= 1 {
0
} else {
committees_per_slot
}
);
}
}
#[test]
fn shuffling_compatible_linear_chain() {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
// Skip the block at the end of the first epoch.
let head_block_root = harness.extend_chain(
4 * E::slots_per_epoch() as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
check_shuffling_compatible(
&harness,
&get_state_for_block(&harness, head_block_root),
head_block_root,
true,
true,
None,
None,
);
}
#[test]
fn shuffling_compatible_missing_pivot_block() {
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
// Skip the block at the end of the first epoch.
harness.extend_chain(
E::slots_per_epoch() as usize - 2,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
harness.advance_slot();
harness.advance_slot();
let head_block_root = harness.extend_chain(
2 * E::slots_per_epoch() as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
check_shuffling_compatible(
&harness,
&get_state_for_block(&harness, head_block_root),
head_block_root,
true,
true,
Some(E::slots_per_epoch() - 2),
Some(E::slots_per_epoch() - 2),
);
}
#[test]
fn shuffling_compatible_simple_fork() {
let slots_per_epoch = E::slots_per_epoch() as usize;
let (db_path, harness, head1, head2) = multi_epoch_fork_valid_blocks_test(
2 * slots_per_epoch,
3 * slots_per_epoch,
3 * slots_per_epoch,
LOW_VALIDATOR_COUNT / 2,
);
let head1_state = get_state_for_block(&harness, head1);
let head2_state = get_state_for_block(&harness, head2);
check_shuffling_compatible(&harness, &head1_state, head1, true, true, None, None);
check_shuffling_compatible(&harness, &head1_state, head2, false, false, None, None);
check_shuffling_compatible(&harness, &head2_state, head1, false, false, None, None);
check_shuffling_compatible(&harness, &head2_state, head2, true, true, None, None);
drop(db_path);
}
#[test]
fn shuffling_compatible_short_fork() {
let slots_per_epoch = E::slots_per_epoch() as usize;
let (db_path, harness, head1, head2) = multi_epoch_fork_valid_blocks_test(
2 * slots_per_epoch - 2,
slots_per_epoch + 2,
slots_per_epoch + 2,
LOW_VALIDATOR_COUNT / 2,
);
let head1_state = get_state_for_block(&harness, head1);
let head2_state = get_state_for_block(&harness, head2);
check_shuffling_compatible(&harness, &head1_state, head1, true, true, None, None);
check_shuffling_compatible(&harness, &head1_state, head2, false, true, None, None);
// NOTE: don't check this case, as block 14 from the first chain appears valid on the second
// chain due to it matching the second chain's block 15.
// check_shuffling_compatible(&harness, &head2_state, head1, false, true, None, None);
check_shuffling_compatible(
&harness,
&head2_state,
head2,
true,
true,
// Required because of the skipped slot.
Some(2 * E::slots_per_epoch() - 2),
None,
);
drop(db_path);
}
fn get_state_for_block(harness: &TestHarness, block_root: Hash256) -> BeaconState<E> {
let head_block = harness.chain.get_block(&block_root).unwrap().unwrap();
harness
.chain
.get_state(&head_block.state_root(), Some(head_block.slot()))
.unwrap()
.unwrap()
}
/// Check the invariants that apply to `shuffling_is_compatible`.
fn check_shuffling_compatible(
harness: &TestHarness,
head_state: &BeaconState<E>,
head_block_root: Hash256,
current_epoch_valid: bool,
previous_epoch_valid: bool,
current_epoch_cutoff_slot: Option<u64>,
previous_epoch_cutoff_slot: Option<u64>,
) {
let shuffling_lookahead = harness.chain.spec.min_seed_lookahead.as_u64() + 1;
let current_pivot_slot =
(head_state.current_epoch() - shuffling_lookahead).end_slot(E::slots_per_epoch());
let previous_pivot_slot =
(head_state.previous_epoch() - shuffling_lookahead).end_slot(E::slots_per_epoch());
for (block_root, slot) in harness
.chain
.rev_iter_block_roots_from(head_block_root)
.unwrap()
{
// Shuffling is compatible targeting the current epoch,
// iff slot is greater than or equal to the current epoch pivot block
assert_eq!(
harness.chain.shuffling_is_compatible(
&block_root,
head_state.current_epoch(),
&head_state
),
current_epoch_valid
&& slot >= current_epoch_cutoff_slot.unwrap_or(current_pivot_slot.as_u64())
);
// Similarly for the previous epoch
assert_eq!(
harness.chain.shuffling_is_compatible(
&block_root,
head_state.previous_epoch(),
&head_state
),
previous_epoch_valid
&& slot >= previous_epoch_cutoff_slot.unwrap_or(previous_pivot_slot.as_u64())
);
// Targeting the next epoch should always return false
assert_eq!(
harness.chain.shuffling_is_compatible(
&block_root,
head_state.current_epoch() + 1,
&head_state
),
false
);
// Targeting two epochs before the current epoch should also always return false
if head_state.current_epoch() >= 2 {
assert_eq!(
harness.chain.shuffling_is_compatible(
&block_root,
head_state.current_epoch() - 2,
&head_state
),
false
);
}
}
}
// Ensure blocks from abandoned forks are pruned from the Hot DB
#[test]
fn prunes_abandoned_fork_between_two_finalized_checkpoints() {
const VALIDATOR_COUNT: usize = 24;
const VALIDATOR_SUPERMAJORITY: usize = (VALIDATOR_COUNT / 3) * 2;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(Arc::clone(&store), VALIDATOR_COUNT);
const HONEST_VALIDATOR_COUNT: usize = VALIDATOR_SUPERMAJORITY;
let honest_validators: Vec<usize> = (0..HONEST_VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (HONEST_VALIDATOR_COUNT..VALIDATOR_COUNT).collect();
let slots_per_epoch: usize = MinimalEthSpec::slots_per_epoch() as usize;
let slot = harness.get_chain_slot();
let state = harness.get_head_state();
let (canonical_blocks_pre_finalization, _, slot, _, state) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch, &honest_validators);
let (stray_blocks, stray_states, _, stray_head, _) = harness.add_stray_blocks(
harness.get_head_state(),
slot,
slots_per_epoch - 1,
&faulty_validators,
);
// Precondition: Ensure all stray_blocks blocks are still known
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness
.chain
.get_state(&state_hash.into(), Some(slot))
.unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
// Precondition: Only genesis is finalized
let chain_dump = harness.chain.chain_dump().unwrap();
assert_eq!(
get_finalized_epoch_boundary_blocks(&chain_dump),
vec![Hash256::zero().into()].into_iter().collect(),
);
assert!(harness.chain.knows_head(&stray_head));
// Trigger finalization
let (canonical_blocks_post_finalization, _, _, _, _) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch * 5, &honest_validators);
// Postcondition: New blocks got finalized
let chain_dump = harness.chain.chain_dump().unwrap();
let finalized_blocks = get_finalized_epoch_boundary_blocks(&chain_dump);
assert_eq!(
finalized_blocks,
vec![
Hash256::zero().into(),
canonical_blocks_pre_finalization[&Slot::new(slots_per_epoch as u64)],
canonical_blocks_post_finalization[&Slot::new((slots_per_epoch * 2) as u64)],
]
.into_iter()
.collect()
);
// Postcondition: Ensure all stray_blocks blocks have been pruned
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_none(),
"abandoned block {} should have been pruned",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_none(),
"stray state {} at slot {} should have been deleted",
state_hash,
slot
);
}
assert!(!harness.chain.knows_head(&stray_head));
}
#[test]
fn pruning_does_not_touch_abandoned_block_shared_with_canonical_chain() {
const VALIDATOR_COUNT: usize = 24;
const VALIDATOR_SUPERMAJORITY: usize = (VALIDATOR_COUNT / 3) * 2;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(Arc::clone(&store), VALIDATOR_COUNT);
const HONEST_VALIDATOR_COUNT: usize = VALIDATOR_SUPERMAJORITY;
let honest_validators: Vec<usize> = (0..HONEST_VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (HONEST_VALIDATOR_COUNT..VALIDATOR_COUNT).collect();
let all_validators: Vec<usize> = (0..VALIDATOR_COUNT).collect();
let slots_per_epoch: usize = MinimalEthSpec::slots_per_epoch() as usize;
// Fill up 0th epoch
let slot = harness.get_chain_slot();
let state = harness.get_head_state();
let (canonical_blocks_zeroth_epoch, _, slot, _, state) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch, &honest_validators);
// Fill up 1st epoch
let (_, _, canonical_slot, shared_head, canonical_state) =
harness.add_canonical_chain_blocks(state, slot, 1, &all_validators);
let (stray_blocks, stray_states, _, stray_head, _) = harness.add_stray_blocks(
canonical_state.clone(),
canonical_slot,
1,
&faulty_validators,
);
// Preconditions
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
let chain_dump = harness.chain.chain_dump().unwrap();
assert_eq!(
get_finalized_epoch_boundary_blocks(&chain_dump),
vec![Hash256::zero().into()].into_iter().collect(),
);
assert!(get_blocks(&chain_dump).contains(&shared_head));
// Trigger finalization
let (canonical_blocks, _, _, _, _) = harness.add_canonical_chain_blocks(
canonical_state,
canonical_slot,
slots_per_epoch * 5,
&honest_validators,
);
// Postconditions
let chain_dump = harness.chain.chain_dump().unwrap();
let finalized_blocks = get_finalized_epoch_boundary_blocks(&chain_dump);
assert_eq!(
finalized_blocks,
vec![
Hash256::zero().into(),
canonical_blocks_zeroth_epoch[&Slot::new(slots_per_epoch as u64)],
canonical_blocks[&Slot::new((slots_per_epoch * 2) as u64)],
]
.into_iter()
.collect()
);
for &block_hash in stray_blocks.values() {
assert!(
harness
.chain
.get_block(&block_hash.into())
.unwrap()
.is_none(),
"stray block {} should have been pruned",
block_hash,
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_none(),
"stray state {} at slot {} should have been deleted",
state_hash,
slot
);
}
assert!(!harness.chain.knows_head(&stray_head));
assert!(get_blocks(&chain_dump).contains(&shared_head));
}
#[test]
fn pruning_does_not_touch_blocks_prior_to_finalization() {
const VALIDATOR_COUNT: usize = 24;
const VALIDATOR_SUPERMAJORITY: usize = (VALIDATOR_COUNT / 3) * 2;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(Arc::clone(&store), VALIDATOR_COUNT);
const HONEST_VALIDATOR_COUNT: usize = VALIDATOR_SUPERMAJORITY;
let honest_validators: Vec<usize> = (0..HONEST_VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (HONEST_VALIDATOR_COUNT..VALIDATOR_COUNT).collect();
let slots_per_epoch: usize = MinimalEthSpec::slots_per_epoch() as usize;
// Fill up 0th epoch with canonical chain blocks
let slot = harness.get_chain_slot();
let state = harness.get_head_state();
let (canonical_blocks_zeroth_epoch, _, slot, _, state) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch, &honest_validators);
// Fill up 1st epoch. Contains a fork.
let (stray_blocks, stray_states, _, stray_head, _) =
harness.add_stray_blocks(state.clone(), slot, slots_per_epoch - 1, &faulty_validators);
// Preconditions
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
let chain_dump = harness.chain.chain_dump().unwrap();
assert_eq!(
get_finalized_epoch_boundary_blocks(&chain_dump),
vec![Hash256::zero().into()].into_iter().collect(),
);
// Trigger finalization
let (_, _, _, _, _) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch * 4, &honest_validators);
// Postconditions
let chain_dump = harness.chain.chain_dump().unwrap();
let finalized_blocks = get_finalized_epoch_boundary_blocks(&chain_dump);
assert_eq!(
finalized_blocks,
vec![
Hash256::zero().into(),
canonical_blocks_zeroth_epoch[&Slot::new(slots_per_epoch as u64)],
]
.into_iter()
.collect()
);
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
assert!(harness.chain.knows_head(&stray_head));
}
#[test]
fn prunes_fork_running_past_finalized_checkpoint() {
const VALIDATOR_COUNT: usize = 24;
const VALIDATOR_SUPERMAJORITY: usize = (VALIDATOR_COUNT / 3) * 2;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(Arc::clone(&store), VALIDATOR_COUNT);
const HONEST_VALIDATOR_COUNT: usize = VALIDATOR_SUPERMAJORITY;
let honest_validators: Vec<usize> = (0..HONEST_VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (HONEST_VALIDATOR_COUNT..VALIDATOR_COUNT).collect();
let slots_per_epoch: usize = MinimalEthSpec::slots_per_epoch() as usize;
// Fill up 0th epoch with canonical chain blocks
let slot = harness.get_chain_slot();
let state = harness.get_head_state();
let (canonical_blocks_zeroth_epoch, _, slot, _, state) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch, &honest_validators);
// Fill up 1st epoch. Contains a fork.
let (stray_blocks_first_epoch, stray_states_first_epoch, stray_slot, _, stray_state) =
harness.add_stray_blocks(state.clone(), slot, slots_per_epoch, &faulty_validators);
let (canonical_blocks_first_epoch, _, canonical_slot, _, canonical_state) =
harness.add_canonical_chain_blocks(state, slot, slots_per_epoch, &honest_validators);
// Fill up 2nd epoch. Extends both the canonical chain and the fork.
let (stray_blocks_second_epoch, stray_states_second_epoch, _, stray_head, _) = harness
.add_stray_blocks(
stray_state,
stray_slot,
slots_per_epoch - 1,
&faulty_validators,
);
// Precondition: Ensure all stray_blocks blocks are still known
let stray_blocks: HashMap<Slot, SignedBeaconBlockHash> = stray_blocks_first_epoch
.into_iter()
.chain(stray_blocks_second_epoch.into_iter())
.collect();
let stray_states: HashMap<Slot, BeaconStateHash> = stray_states_first_epoch
.into_iter()
.chain(stray_states_second_epoch.into_iter())
.collect();
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
// Precondition: Only genesis is finalized
let chain_dump = harness.chain.chain_dump().unwrap();
assert_eq!(
get_finalized_epoch_boundary_blocks(&chain_dump),
vec![Hash256::zero().into()].into_iter().collect(),
);
assert!(harness.chain.knows_head(&stray_head));
// Trigger finalization
let (canonical_blocks_second_epoch, _, _, _, _) = harness.add_canonical_chain_blocks(
canonical_state,
canonical_slot,
slots_per_epoch * 4,
&honest_validators,
);
// Postconditions
let canonical_blocks: HashMap<Slot, SignedBeaconBlockHash> = canonical_blocks_zeroth_epoch
.into_iter()
.chain(canonical_blocks_first_epoch.into_iter())
.chain(canonical_blocks_second_epoch.into_iter())
.collect();
// Postcondition: New blocks got finalized
let chain_dump = harness.chain.chain_dump().unwrap();
let finalized_blocks = get_finalized_epoch_boundary_blocks(&chain_dump);
assert_eq!(
finalized_blocks,
vec![
Hash256::zero().into(),
canonical_blocks[&Slot::new(slots_per_epoch as u64)],
canonical_blocks[&Slot::new((slots_per_epoch * 2) as u64)],
]
.into_iter()
.collect()
);
// Postcondition: Ensure all stray_blocks blocks have been pruned
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_none(),
"abandoned block {} should have been pruned",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_none(),
"stray state {} at slot {} should have been deleted",
state_hash,
slot
);
}
assert!(!harness.chain.knows_head(&stray_head));
}
// This is to check if state outside of normal block processing are pruned correctly.
#[test]
fn prunes_skipped_slots_states() {
const VALIDATOR_COUNT: usize = 24;
const VALIDATOR_SUPERMAJORITY: usize = (VALIDATOR_COUNT / 3) * 2;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(Arc::clone(&store), VALIDATOR_COUNT);
const HONEST_VALIDATOR_COUNT: usize = VALIDATOR_SUPERMAJORITY;
let honest_validators: Vec<usize> = (0..HONEST_VALIDATOR_COUNT).collect();
let faulty_validators: Vec<usize> = (HONEST_VALIDATOR_COUNT..VALIDATOR_COUNT).collect();
let slots_per_epoch: usize = MinimalEthSpec::slots_per_epoch() as usize;
// Arrange skipped slots so as to cross the epoch boundary. That way, we excercise the code
// responsible for storing state outside of normal block processing.
let canonical_slot = harness.get_chain_slot();
let canonical_state = harness.get_head_state();
let (canonical_blocks_zeroth_epoch, _, canonical_slot, _, canonical_state) = harness
.add_canonical_chain_blocks(
canonical_state,
canonical_slot,
slots_per_epoch - 1,
&honest_validators,
);
let (stray_blocks, stray_states, stray_slot, _, _) = harness.add_stray_blocks(
canonical_state.clone(),
canonical_slot,
slots_per_epoch,
&faulty_validators,
);
// Preconditions
for &block_hash in stray_blocks.values() {
let block = harness.chain.get_block(&block_hash.into()).unwrap();
assert!(
block.is_some(),
"stray block {} should be still present",
block_hash
);
}
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_some(),
"stray state {} at slot {} should be still present",
state_hash,
slot
);
}
let chain_dump = harness.chain.chain_dump().unwrap();
assert_eq!(
get_finalized_epoch_boundary_blocks(&chain_dump),
vec![Hash256::zero().into()].into_iter().collect(),
);
// Make sure slots were skipped
let stray_state = harness
.chain
.state_at_slot(stray_slot, StateSkipConfig::WithoutStateRoots)
.unwrap();
let block_root = stray_state.get_block_root(canonical_slot - 1);
assert_eq!(stray_state.get_block_root(canonical_slot), block_root);
assert_eq!(stray_state.get_block_root(canonical_slot + 1), block_root);
let skipped_slots = vec![canonical_slot, canonical_slot + 1];
for &slot in &skipped_slots {
assert_eq!(stray_state.get_block_root(slot), block_root);
let state_hash = stray_state.get_state_root(slot).unwrap();
assert!(
harness
.chain
.get_state(&state_hash, Some(slot))
.unwrap()
.is_some(),
"skipped slots state should be still present"
);
}
// Trigger finalization
let (canonical_blocks_post_finalization, _, _, _, _) = harness.add_canonical_chain_blocks(
canonical_state,
canonical_slot,
slots_per_epoch * 5,
&honest_validators,
);
// Postconditions
let chain_dump = harness.chain.chain_dump().unwrap();
let finalized_blocks = get_finalized_epoch_boundary_blocks(&chain_dump);
let canonical_blocks: HashMap<Slot, SignedBeaconBlockHash> = canonical_blocks_zeroth_epoch
.into_iter()
.chain(canonical_blocks_post_finalization.into_iter())
.collect();
assert_eq!(
finalized_blocks,
vec![
Hash256::zero().into(),
canonical_blocks[&Slot::new(slots_per_epoch as u64)],
]
.into_iter()
.collect()
);
for (&slot, &state_hash) in &stray_states {
let state = harness.chain.get_state(&state_hash.into(), None).unwrap();
assert!(
state.is_none(),
"stray state {} at slot {} should have been deleted",
state_hash,
slot
);
}
for &slot in &skipped_slots {
assert_eq!(stray_state.get_block_root(slot), block_root);
let state_hash = stray_state.get_state_root(slot).unwrap();
assert!(
harness
.chain
.get_state(&state_hash, Some(slot))
.unwrap()
.is_none(),
"skipped slot states should have been pruned"
);
}
}
/// Check that the head state's slot matches `expected_slot`.
fn check_slot(harness: &TestHarness, expected_slot: u64) {
let state = &harness.chain.head().expect("should get head").beacon_state;
@@ -548,3 +1371,19 @@ fn check_iterators(harness: &TestHarness) {
Some(Slot::new(0))
);
}
fn get_finalized_epoch_boundary_blocks(
dump: &[BeaconSnapshot<MinimalEthSpec>],
) -> HashSet<SignedBeaconBlockHash> {
dump.iter()
.cloned()
.map(|checkpoint| checkpoint.beacon_state.finalized_checkpoint.root.into())
.collect()
}
fn get_blocks(dump: &[BeaconSnapshot<MinimalEthSpec>]) -> HashSet<SignedBeaconBlockHash> {
dump.iter()
.cloned()
.map(|checkpoint| checkpoint.beacon_block_root.into())
.collect()
}

View File

@@ -4,11 +4,9 @@ use crate::Client;
use beacon_chain::{
builder::{BeaconChainBuilder, Witness},
eth1_chain::{CachingEth1Backend, Eth1Chain},
slot_clock::{SlotClock, SystemTimeSlotClock},
store::{
migrate::{BackgroundMigrator, Migrate, NullMigrator},
DiskStore, MemoryStore, SimpleDiskStore, Store, StoreConfig,
},
slot_clock::{SlotClock, SystemTimeSlotClock},
store::{DiskStore, MemoryStore, SimpleDiskStore, Store, StoreConfig},
BeaconChain, BeaconChainTypes, Eth1ChainBackend, EventHandler,
};
use environment::RuntimeContext;
@@ -68,7 +66,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec>,
TStoreMigrator: Migrate<TStore, TEthSpec>,
TSlotClock: SlotClock + Clone + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -403,7 +401,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec>,
TStoreMigrator: Migrate<TStore, TEthSpec>,
TSlotClock: SlotClock + Clone + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -450,7 +448,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec>,
TStoreMigrator: Migrate<TStore, TEthSpec>,
TSlotClock: SlotClock + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
@@ -498,7 +496,7 @@ impl<TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TSlotClock: SlotClock + 'static,
TStoreMigrator: store::Migrate<DiskStore<TEthSpec>, TEthSpec> + 'static,
TStoreMigrator: Migrate<DiskStore<TEthSpec>, TEthSpec> + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, DiskStore<TEthSpec>> + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,
@@ -540,7 +538,7 @@ impl<TStoreMigrator, TSlotClock, TEth1Backend, TEthSpec, TEventHandler>
>
where
TSlotClock: SlotClock + 'static,
TStoreMigrator: store::Migrate<SimpleDiskStore<TEthSpec>, TEthSpec> + 'static,
TStoreMigrator: Migrate<SimpleDiskStore<TEthSpec>, TEthSpec> + 'static,
TEth1Backend: Eth1ChainBackend<TEthSpec, SimpleDiskStore<TEthSpec>> + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,
@@ -600,10 +598,15 @@ where
TEventHandler: EventHandler<TEthSpec> + 'static,
{
pub fn background_migrator(mut self) -> Result<Self, String> {
let context = self
.runtime_context
.as_ref()
.ok_or_else(|| "disk_store requires a log".to_string())?
.service_context("freezer_db".into());
let store = self.store.clone().ok_or_else(|| {
"background_migrator requires the store to be initialized".to_string()
})?;
self.store_migrator = Some(BackgroundMigrator::new(store));
self.store_migrator = Some(BackgroundMigrator::new(store, context.log.clone()));
Ok(self)
}
}
@@ -621,7 +624,7 @@ impl<TStore, TStoreMigrator, TSlotClock, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec>,
TStoreMigrator: Migrate<TStore, TEthSpec>,
TSlotClock: SlotClock + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,
@@ -727,7 +730,7 @@ impl<TStore, TStoreMigrator, TEth1Backend, TEthSpec, TEventHandler>
>
where
TStore: Store<TEthSpec> + 'static,
TStoreMigrator: store::Migrate<TStore, TEthSpec>,
TStoreMigrator: Migrate<TStore, TEthSpec>,
TEth1Backend: Eth1ChainBackend<TEthSpec, TStore> + 'static,
TEthSpec: EthSpec + 'static,
TEventHandler: EventHandler<TEthSpec> + 'static,

View File

@@ -48,18 +48,18 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
let speedo = Mutex::new(Speedo::default());
// Note: `interval_at` panics when interval_duration is 0
// TODO: `Return type of closure passed to `for_each` is restricted to `Future<Output = ()>`
// Hence, shifting the .then() error logs into the `for_each` closure.
// Can be solved with `TryStreamExt::try_for_each` if `Interval` implemented `TryStream`.
// Check if this can be refactored better.
let interval_future = interval_at(start_instant, interval_duration).for_each(|_| {
let connected_peer_count = network.connected_peers();
let interval_future = Interval::new(start_instant, interval_duration)
.map_err(
move |e| error!(log_1, "Slot notifier timer failed"; "error" => format!("{:?}", e)),
)
.for_each(move |_| {
let log = log_2.clone();
let head_info = match beacon_chain.head_info() {
Ok(head) => head,
Err(e) => {
error!(
let connected_peer_count = network.connected_peers();
let sync_state = network.sync_state();
let head_info = beacon_chain.head_info()
.map_err(|e| error!(
log,
"Notifier failed to notify, Failed to get beacon chain head info";
"error" => format!("{:?}", e)
@@ -69,10 +69,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
};
let head_slot = head_info.slot;
let head_epoch = head_slot.epoch(T::EthSpec::slots_per_epoch());
let current_slot = match beacon_chain.slot() {
Ok(slot) => slot,
Err(e) => {
let current_slot = beacon_chain.slot().map_err(|e| {
error!(
log,
"Notify failed to notify, Unable to read current slot";
@@ -120,6 +117,26 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
slot_distance_pretty(head_distance, slot_duration)
);
info!(
log,
"Syncing";
"peers" => peer_count_pretty(connected_peer_count),
"finalized_root" => format!("{}", finalized_root),
"finalized_epoch" => finalized_epoch,
"head_block" => format!("{}", head_root),
"head_slot" => head_slot,
"current_slot" => current_slot,
"sync_state" =>format!("{}", sync_state)
);
// Log if we are syncing
if sync_state.is_syncing() {
let distance = format!(
"{} slots ({})",
head_distance.as_u64(),
slot_distance_pretty(head_distance, slot_duration)
);
info!(
log,
"Syncing";
@@ -128,15 +145,21 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
"speed" => sync_speed_pretty(speedo.slots_per_second()),
"est_time" => estimated_time_pretty(speedo.estimated_time_till_slot(current_slot)),
);
return futures::future::ready(());
};
macro_rules! not_quite_synced_log {
($message: expr) => {
} else {
if sync_state.is_synced() {
info!(
log,
$message;
log_2,
"Synced";
"peers" => peer_count_pretty(connected_peer_count),
"finalized_root" => format!("{}", finalized_root),
"finalized_epoch" => finalized_epoch,
"epoch" => current_epoch,
"slot" => current_slot,
);
} else {
info!(
log_2,
"Searching for peers";
"peers" => peer_count_pretty(connected_peer_count),
"finalized_root" => format!("{}", finalized_root),
"finalized_epoch" => finalized_epoch,
@@ -145,25 +168,19 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
);
}
}
if head_epoch + 1 == current_epoch {
not_quite_synced_log!("Synced to previous epoch")
} else if head_slot != current_slot {
not_quite_synced_log!("Synced to current epoch")
} else {
info!(
log,
"Synced";
"peers" => peer_count_pretty(connected_peer_count),
"finalized_root" => format!("{}", finalized_root),
"finalized_epoch" => finalized_epoch,
"epoch" => current_epoch,
"slot" => current_slot,
Ok(())
})
.then(move |result| {
match result {
Ok(()) => Ok(()),
Err(e) => {
error!(
log_3,
"Notifier failed to notify";
"error" => format!("{:?}", e)
);
};
futures::future::ready(())
});
Ok(())
} } });
let (exit_signal, exit) = tokio::sync::oneshot::channel();

View File

@@ -8,7 +8,7 @@ edition = "2018"
hex = "0.3"
# rust-libp2p is presently being sourced from a Sigma Prime fork of the
# `libp2p/rust-libp2p` repository.
libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "4e3003d5283040fee10da1299252dd060a838d97" }
libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "71cf486b4d992862f5a05f9f4ef5e5c1631f4add" }
types = { path = "../../eth2/types" }
hashmap_delay = { path = "../../eth2/utils/hashmap_delay" }
eth2_ssz_types = { path = "../../eth2/utils/ssz_types" }
@@ -33,6 +33,7 @@ parking_lot = "0.9.0"
sha2 = "0.8.0"
base64 = "0.11.0"
snap = "1"
void = "1.0.2"
[dev-dependencies]
slog-stdlog = "4.0.0"

View File

@@ -49,6 +49,7 @@ pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> {
/// A cache of recently seen gossip messages. This is used to filter out any possible
/// duplicates that may still be seen over gossipsub.
#[behaviour(ignore)]
// TODO: Remove this
seen_gossip_messages: LruCache<MessageId, ()>,
/// A collections of variables accessible outside the network service.
#[behaviour(ignore)]
@@ -297,14 +298,17 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
}
/// Sends a PING/PONG request/response to a peer.
fn send_ping(&mut self, id: RequestId, peer_id: PeerId) {
let pong_response = RPCEvent::Response(
id,
RPCErrorResponse::Success(RPCResponse::Pong(crate::rpc::methods::Ping {
fn send_ping(&mut self, id: RequestId, peer_id: PeerId, is_request: bool) {
let ping = crate::rpc::methods::Ping {
data: self.meta_data.seq_number,
})),
);
self.send_rpc(peer_id, pong_response);
};
let event = if is_request {
RPCEvent::Request(id, RPCRequest::Ping(ping))
} else {
RPCEvent::Response(id, RPCErrorResponse::Success(RPCResponse::Pong(ping)))
};
self.send_rpc(peer_id, event);
}
/// Sends a METADATA request to a peer.
@@ -349,7 +353,14 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec>
}
}
} else {
warn!(self.log, "A duplicate gossipsub message was received"; "message" => format!("{:?}", gs_msg));
match PubsubMessage::<TSpec>::decode(&gs_msg.topics, &gs_msg.data) {
Err(e) => {
debug!(self.log, "Could not decode gossipsub message"; "error" => format!("{}", e))
}
Ok(msg) => {
crit!(self.log, "A duplicate gossipsub message was received"; "message_source" => format!("{}", gs_msg.source), "propagated_peer" => format!("{}",propagation_source), "message" => format!("{}", msg));
}
}
}
}
GossipsubEvent::Subscribed { peer_id, topic } => {
@@ -417,7 +428,8 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec>
RPCEvent::Request(id, RPCRequest::Ping(ping)) => {
// inform the peer manager and send the response
self.peer_manager.ping_request(&peer_id, ping.data);
self.send_ping(id, peer_id);
// send a ping response
self.send_ping(id, peer_id, false);
}
RPCEvent::Request(id, RPCRequest::MetaData(_)) => {
// send the requested meta-data
@@ -466,16 +478,16 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
));
}
PeerManagerEvent::Ping(peer_id) => {
// send a ping to this peer
self.send_ping(RequestId::from(0usize), peer_id);
// send a ping request to this peer
self.send_ping(RequestId::from(0usize), peer_id, true);
}
PeerManagerEvent::MetaData(peer_id) => {
self.send_meta_data_request(peer_id);
}
PeerManagerEvent::DisconnectPeer(_peer_id) => {
PeerManagerEvent::_DisconnectPeer(_peer_id) => {
//TODO: Implement
}
PeerManagerEvent::BanPeer(_peer_id) => {
PeerManagerEvent::_BanPeer(_peer_id) => {
//TODO: Implement
}
},

View File

@@ -101,7 +101,7 @@ impl Default for Config {
// parameter.
let gs_config = GossipsubConfigBuilder::new()
.max_transmit_size(GOSSIP_MAX_SIZE)
.heartbeat_interval(Duration::from_secs(20)) // TODO: Reduce for mainnet
.heartbeat_interval(Duration::from_secs(1))
.manual_propagation() // require validation before propagation
.no_source_id()
.message_id_fn(gossip_message_id)
@@ -114,7 +114,8 @@ impl Default for Config {
.enr_update(true) // update IP based on PONG responses
.enr_peer_update_min(2) // prevents NAT's should be raised for mainnet
.query_parallelism(5)
.query_timeout(Duration::from_secs(2))
.query_timeout(Duration::from_secs(60))
.query_peer_timeout(Duration::from_secs(2))
.ip_limit(false) // limits /24 IP's in buckets. Enable for mainnet
.ping_interval(Duration::from_secs(300))
.build();

View File

@@ -1,10 +1,11 @@
//! Helper functions and an extension trait for Ethereum 2 ENRs.
pub use libp2p::{core::identity::Keypair, discv5::enr::CombinedKey};
use super::ENR_FILENAME;
use crate::types::{Enr, EnrBitfield};
use crate::NetworkConfig;
use libp2p::core::identity::Keypair;
use libp2p::discv5::enr::{CombinedKey, EnrBuilder};
use libp2p::discv5::enr::EnrBuilder;
use slog::{debug, warn};
use ssz::{Decode, Encode};
use ssz_types::BitVector;

View File

@@ -2,21 +2,21 @@
pub(crate) mod enr;
// Allow external use of the lighthouse ENR builder
pub use enr::build_enr;
pub use enr::{build_enr, CombinedKey, Keypair};
use crate::metrics;
use crate::{error, Enr, NetworkConfig, NetworkGlobals};
use enr::{Eth2Enr, BITFIELD_ENR_KEY, ETH2_ENR_KEY};
use futures::prelude::*;
use libp2p::core::{identity::Keypair, ConnectedPoint, Multiaddr, PeerId};
use libp2p::core::{ConnectedPoint, Multiaddr, PeerId};
use libp2p::discv5::enr::NodeId;
use libp2p::discv5::{Discv5, Discv5Event};
use libp2p::multiaddr::Protocol;
use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters, ProtocolsHandler};
use slog::{crit, debug, info, trace, warn};
use slog::{crit, debug, info, warn};
use ssz::{Decode, Encode};
use ssz_types::BitVector;
use std::collections::HashSet;
use std::collections::{HashSet, VecDeque};
use std::net::SocketAddr;
use std::path::Path;
use std::sync::Arc;
@@ -30,13 +30,16 @@ const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120;
/// Initial delay between peer searches.
const INITIAL_SEARCH_DELAY: u64 = 5;
/// Local ENR storage filename.
const ENR_FILENAME: &str = "enr.dat";
pub const ENR_FILENAME: &str = "enr.dat";
/// Number of peers we'd like to have connected to a given long-lived subnet.
const TARGET_SUBNET_PEERS: u64 = 3;
/// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5
/// libp2p protocol.
pub struct Discovery<TSubstream, TSpec: EthSpec> {
/// Events to be processed by the behaviour.
events: VecDeque<NetworkBehaviourAction<void::Void, Discv5Event>>,
/// The currently banned peers.
banned_peers: HashSet<PeerId>,
@@ -105,7 +108,7 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
"peer_id" => format!("{}", bootnode_enr.peer_id()),
"ip" => format!("{:?}", bootnode_enr.ip()),
"udp" => format!("{:?}", bootnode_enr.udp()),
"tcp" => format!("{:?}", bootnode_enr.udp())
"tcp" => format!("{:?}", bootnode_enr.tcp())
);
let _ = discovery.add_enr(bootnode_enr).map_err(|e| {
warn!(
@@ -117,6 +120,7 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
}
Ok(Self {
events: VecDeque::with_capacity(16),
banned_peers: HashSet::new(),
max_peers: config.max_peers,
peer_discovery_delay: Delay::new(Instant::now()),
@@ -409,16 +413,18 @@ where
match self.discovery.poll(params) {
Async::Ready(NetworkBehaviourAction::GenerateEvent(event)) => {
match event {
Discv5Event::Discovered(enr) => {
Discv5Event::Discovered(_enr) => {
// peers that get discovered during a query but are not contactable or
// don't match a predicate can end up here. For debugging purposes we
// log these to see if we are unnecessarily dropping discovered peers
/*
if enr.eth2() == self.local_enr().eth2() {
trace!(self.log, "Peer found in process of query"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
} else {
// this is temporary warning for debugging the DHT
warn!(self.log, "Found peer during discovery not on correct fork"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
}
*/
}
Discv5Event::SocketUpdated(socket) => {
info!(self.log, "Address updated"; "ip" => format!("{}",socket.ip()), "udp_port" => format!("{}", socket.port()));
@@ -448,19 +454,19 @@ where
for peer_id in closer_peers {
// if we need more peers, attempt a connection
if self.network_globals.connected_peers() < self.max_peers
&& self
if self.network_globals.connected_or_dialing_peers()
< self.max_peers
&& !self
.network_globals
.peers
.read()
.peer_info(&peer_id)
.is_none()
.is_connected_or_dialing(&peer_id)
&& !self.banned_peers.contains(&peer_id)
{
debug!(self.log, "Peer discovered"; "peer_id"=> format!("{:?}", peer_id));
return Async::Ready(NetworkBehaviourAction::DialPeer {
peer_id,
});
debug!(self.log, "Connecting to discovered peer"; "peer_id"=> format!("{:?}", peer_id));
self.network_globals.peers.write().dialing_peer(&peer_id);
self.events
.push_back(NetworkBehaviourAction::DialPeer { peer_id });
}
}
}
@@ -472,6 +478,12 @@ where
Async::NotReady => break,
}
}
// process any queued events
if let Some(event) = self.events.pop_front() {
return Async::Ready(event);
}
Async::NotReady
}
}

View File

@@ -20,6 +20,6 @@ pub use config::Config as NetworkConfig;
pub use libp2p::gossipsub::{MessageId, Topic, TopicHash};
pub use libp2p::{multiaddr, Multiaddr};
pub use libp2p::{PeerId, Swarm};
pub use peer_manager::{PeerDB, PeerInfo, PeerSyncStatus};
pub use peer_manager::{PeerDB, PeerInfo, PeerSyncStatus, SyncInfo};
pub use rpc::RPCEvent;
pub use service::Service;
pub use service::{Service, NETWORK_KEY_FILENAME};

View File

@@ -3,9 +3,10 @@
//! Currently using identify to fingerprint.
use libp2p::identify::IdentifyInfo;
use serde::Serialize;
#[derive(Debug)]
/// Various client and protocol information related to a node.
#[derive(Clone, Debug, Serialize)]
pub struct Client {
/// The client's name (Ex: lighthouse, prism, nimbus, etc)
pub kind: ClientKind,
@@ -19,7 +20,7 @@ pub struct Client {
pub agent_string: Option<String>,
}
#[derive(Debug)]
#[derive(Clone, Debug, Serialize)]
pub enum ClientKind {
/// A lighthouse node (the best kind).
Lighthouse,
@@ -125,6 +126,11 @@ fn client_from_agent_version(agent_version: &str) -> (ClientKind, String, String
}
(kind, version, os_version)
}
Some("github.com") => {
let kind = ClientKind::Prysm;
let unknown = String::from("unknown");
(kind, unknown.clone(), unknown)
}
_ => {
let unknown = String::from("unknown");
(ClientKind::Unknown, unknown.clone(), unknown)

View File

@@ -16,12 +16,14 @@ use types::EthSpec;
mod client;
mod peer_info;
mod peer_sync_status;
mod peerdb;
pub use peer_info::{PeerInfo, PeerSyncStatus};
pub use peer_info::PeerInfo;
pub use peer_sync_status::{PeerSyncStatus, SyncInfo};
/// The minimum reputation before a peer is disconnected.
// Most likely this needs tweaking
const MINIMUM_REPUTATION_BEFORE_BAN: Rep = 20;
const _MINIMUM_REPUTATION_BEFORE_BAN: Rep = 20;
/// The time in seconds between re-status's peers.
const STATUS_INTERVAL: u64 = 300;
/// The time in seconds between PING events. We do not send a ping if the other peer as PING'd us within
@@ -48,13 +50,13 @@ pub struct PeerManager<TSpec: EthSpec> {
/// Each variant has an associated reputation change.
pub enum PeerAction {
/// The peer timed out on an RPC request/response.
TimedOut = -10,
_TimedOut = -10,
/// The peer sent and invalid request/response or encoding.
InvalidMessage = -20,
_InvalidMessage = -20,
/// The peer sent something objectively malicious.
Malicious = -50,
_Malicious = -50,
/// Received an expected message.
ValidMessage = 20,
_ValidMessage = 20,
/// Peer disconnected.
Disconnected = -30,
}
@@ -68,9 +70,9 @@ pub enum PeerManagerEvent {
/// Request METADATA from a peer.
MetaData(PeerId),
/// The peer should be disconnected.
DisconnectPeer(PeerId),
_DisconnectPeer(PeerId),
/// The peer should be disconnected and banned.
BanPeer(PeerId),
_BanPeer(PeerId),
}
impl<TSpec: EthSpec> PeerManager<TSpec> {
@@ -89,10 +91,12 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// A ping request has been received.
// NOTE: The behaviour responds with a PONG automatically
// TODO: Update last seen
pub fn ping_request(&mut self, peer_id: &PeerId, seq: u64) {
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a ping
// reset the to-ping timer for this peer
debug!(self.log, "Received a ping request"; "peer_id" => format!("{}", peer_id), "seq_no" => seq);
self.ping_peers.insert(peer_id.clone());
// if the sequence number is unknown send update the meta data of the peer.
@@ -114,6 +118,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
}
/// A PONG has been returned from a peer.
// TODO: Update last seen
pub fn pong_response(&mut self, peer_id: &PeerId, seq: u64) {
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a pong
@@ -137,11 +142,13 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
}
/// Received a metadata response from a peer.
// TODO: Update last seen
pub fn meta_data_response(&mut self, peer_id: &PeerId, meta_data: MetaData<TSpec>) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
if let Some(known_meta_data) = &peer_info.meta_data {
if known_meta_data.seq_number < meta_data.seq_number {
debug!(self.log, "Updating peer's metadata"; "peer_id" => format!("{}", peer_id), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
peer_info.meta_data = Some(meta_data);
} else {
warn!(self.log, "Received old metadata"; "peer_id" => format!("{}", peer_id), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
}
@@ -162,23 +169,27 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// Checks the reputation of a peer and if it is too low, bans it and
/// sends the corresponding event. Informs if it got banned
fn gets_banned(&mut self, peer_id: &PeerId) -> bool {
fn _gets_banned(&mut self, peer_id: &PeerId) -> bool {
// if the peer was already banned don't inform again
let mut peerdb = self.network_globals.peers.write();
if peerdb.reputation(peer_id) < MINIMUM_REPUTATION_BEFORE_BAN
&& !peerdb.connection_status(peer_id).is_banned()
if let Some(connection_status) = peerdb.connection_status(peer_id) {
if peerdb.reputation(peer_id) < _MINIMUM_REPUTATION_BEFORE_BAN
&& !connection_status.is_banned()
{
peerdb.ban(peer_id);
self.events.push(PeerManagerEvent::BanPeer(peer_id.clone()));
self.events
.push(PeerManagerEvent::_BanPeer(peer_id.clone()));
return true;
}
}
false
}
/// Requests that a peer get disconnected.
pub fn disconnect_peer(&mut self, peer_id: &PeerId) {
pub fn _disconnect_peer(&mut self, peer_id: &PeerId) {
self.events
.push(PeerManagerEvent::DisconnectPeer(peer_id.clone()));
.push(PeerManagerEvent::_DisconnectPeer(peer_id.clone()));
}
/// Updates the state of the peer as disconnected.
@@ -213,7 +224,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
}
/// Provides a given peer's reputation if it exists.
pub fn get_peer_rep(&self, peer_id: &PeerId) -> Rep {
pub fn _get_peer_rep(&self, peer_id: &PeerId) -> Rep {
self.network_globals.peers.read().reputation(peer_id)
}
@@ -235,7 +246,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// Reports a peer for some action.
///
/// If the peer doesn't exist, log a warning and insert defaults.
pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction) {
pub fn _report_peer(&mut self, peer_id: &PeerId, action: PeerAction) {
self.update_reputations();
self.network_globals
.peers
@@ -269,7 +280,8 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
{
let mut peerdb = self.network_globals.peers.write();
if peerdb.connection_status(peer_id).is_banned() {
if peerdb.connection_status(peer_id).map(|c| c.is_banned()) == Some(true) {
// don't connect if the peer is banned
return false;
}
@@ -293,6 +305,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
true
}
/// Notifies the peer manager that this peer is being dialed.
pub fn _dialing_peer(&mut self, peer_id: &PeerId) {
self.network_globals.peers.write().dialing_peer(peer_id);
}
}
impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
@@ -304,12 +321,18 @@ impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
while let Async::Ready(Some(peer_id)) = self.ping_peers.poll().map_err(|e| {
error!(self.log, "Failed to check for peers to ping"; "error" => format!("{}",e));
})? {
debug!(self.log, "Pinging peer"; "peer_id" => format!("{}", peer_id));
// add the ping timer back
self.ping_peers.insert(peer_id.clone());
self.events.push(PeerManagerEvent::Ping(peer_id));
}
while let Async::Ready(Some(peer_id)) = self.ping_peers.poll().map_err(|e| {
while let Async::Ready(Some(peer_id)) = self.status_peers.poll().map_err(|e| {
error!(self.log, "Failed to check for peers to status"; "error" => format!("{}",e));
})? {
debug!(self.log, "Sending Status to peer"; "peer_id" => format!("{}", peer_id));
// add the status timer back
self.status_peers.insert(peer_id.clone());
self.events.push(PeerManagerEvent::Status(peer_id));
}

View File

@@ -1,13 +1,19 @@
use super::client::Client;
use super::peerdb::{Rep, DEFAULT_REPUTATION};
use super::PeerSyncStatus;
use crate::rpc::MetaData;
use crate::Multiaddr;
use serde::{
ser::{SerializeStructVariant, Serializer},
Serialize,
};
use std::time::Instant;
use types::{EthSpec, Slot, SubnetId};
use types::{EthSpec, SubnetId};
use PeerConnectionStatus::*;
/// Information about a given connected peer.
#[derive(Debug)]
#[derive(Clone, Debug, Serialize)]
#[serde(bound = "T: EthSpec")]
pub struct PeerInfo<T: EthSpec> {
/// The connection status of the peer
_status: PeerStatus,
@@ -54,12 +60,13 @@ impl<T: EthSpec> PeerInfo<T> {
}
}
#[derive(Debug)]
#[derive(Clone, Debug, Serialize)]
/// The current health status of the peer.
pub enum PeerStatus {
/// The peer is healthy
/// The peer is healthy.
Healthy,
/// The peer is clogged. It has not been responding to requests on time
Clogged,
/// The peer is clogged. It has not been responding to requests on time.
_Clogged,
}
impl Default for PeerStatus {
@@ -68,48 +75,65 @@ impl Default for PeerStatus {
}
}
/// Connection Status of the peer
/// Connection Status of the peer.
#[derive(Debug, Clone)]
pub enum PeerConnectionStatus {
/// The peer is connected.
Connected {
/// number of ingoing connections
/// number of ingoing connections.
n_in: u8,
/// number of outgoing connections
/// number of outgoing connections.
n_out: u8,
},
/// The peer has disconnected.
Disconnected {
/// last time the peer was connected or discovered
/// last time the peer was connected or discovered.
since: Instant,
},
/// The peer has been banned and is disconnected.
Banned {
/// moment when the peer was banned
/// moment when the peer was banned.
since: Instant,
},
Unknown {
/// time since we know of this peer
/// We are currently dialing this peer.
Dialing {
/// time since we last communicated with the peer.
since: Instant,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum PeerSyncStatus {
/// At the current state as our node or ahead of us.
Synced {
/// The last known head slot from the peer's handshake.
status_head_slot: Slot,
},
/// Is behind our current head and not useful for block downloads.
Behind {
/// The last known head slot from the peer's handshake.
status_head_slot: Slot,
},
/// Not currently known as a STATUS handshake has not occurred.
Unknown,
/// Serialization for http requests.
impl Serialize for PeerConnectionStatus {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Connected { n_in, n_out } => {
let mut s = serializer.serialize_struct_variant("", 0, "Connected", 2)?;
s.serialize_field("in", n_in)?;
s.serialize_field("out", n_out)?;
s.end()
}
Disconnected { since } => {
let mut s = serializer.serialize_struct_variant("", 1, "Disconnected", 1)?;
s.serialize_field("since", &since.elapsed().as_secs())?;
s.end()
}
Banned { since } => {
let mut s = serializer.serialize_struct_variant("", 2, "Banned", 1)?;
s.serialize_field("since", &since.elapsed().as_secs())?;
s.end()
}
Dialing { since } => {
let mut s = serializer.serialize_struct_variant("", 3, "Dialing", 1)?;
s.serialize_field("since", &since.elapsed().as_secs())?;
s.end()
}
}
}
}
impl Default for PeerConnectionStatus {
fn default() -> Self {
PeerConnectionStatus::Unknown {
PeerConnectionStatus::Dialing {
since: Instant::now(),
}
}
@@ -124,6 +148,14 @@ impl PeerConnectionStatus {
}
}
/// Checks if the status is connected
pub fn is_dialing(&self) -> bool {
match self {
PeerConnectionStatus::Dialing { .. } => true,
_ => false,
}
}
/// Checks if the status is banned
pub fn is_banned(&self) -> bool {
match self {
@@ -145,7 +177,7 @@ impl PeerConnectionStatus {
pub fn connect_ingoing(&mut self) {
match self {
Connected { n_in, .. } => *n_in += 1,
Disconnected { .. } | Banned { .. } | Unknown { .. } => {
Disconnected { .. } | Banned { .. } | Dialing { .. } => {
*self = Connected { n_in: 1, n_out: 0 }
}
}
@@ -156,7 +188,7 @@ impl PeerConnectionStatus {
pub fn connect_outgoing(&mut self) {
match self {
Connected { n_out, .. } => *n_out += 1,
Disconnected { .. } | Banned { .. } | Unknown { .. } => {
Disconnected { .. } | Banned { .. } | Dialing { .. } => {
*self = Connected { n_in: 0, n_out: 1 }
}
}

View File

@@ -0,0 +1,104 @@
//! Handles individual sync status for peers.
use serde::Serialize;
use types::{Epoch, Hash256, Slot};
#[derive(Clone, Debug, Serialize)]
/// The current sync status of the peer.
pub enum PeerSyncStatus {
/// At the current state as our node or ahead of us.
Synced { info: SyncInfo },
/// The peer has greater knowledge about the canonical chain than we do.
Advanced { info: SyncInfo },
/// Is behind our current head and not useful for block downloads.
Behind { info: SyncInfo },
/// Not currently known as a STATUS handshake has not occurred.
Unknown,
}
/// This is stored inside the PeerSyncStatus and is very similar to `PeerSyncInfo` in the
/// `Network` crate.
#[derive(Clone, Debug, Serialize)]
pub struct SyncInfo {
pub status_head_slot: Slot,
pub status_head_root: Hash256,
pub status_finalized_epoch: Epoch,
pub status_finalized_root: Hash256,
}
impl PeerSyncStatus {
/// Returns true if the peer has advanced knowledge of the chain.
pub fn is_advanced(&self) -> bool {
match self {
PeerSyncStatus::Advanced { .. } => true,
_ => false,
}
}
/// Returns true if the peer is up to date with the current chain.
pub fn is_synced(&self) -> bool {
match self {
PeerSyncStatus::Synced { .. } => true,
_ => false,
}
}
/// Returns true if the peer is behind the current chain.
pub fn is_behind(&self) -> bool {
match self {
PeerSyncStatus::Behind { .. } => true,
_ => false,
}
}
/// Updates the sync state given a fully synced peer.
/// Returns true if the state has changed.
pub fn update_synced(&mut self, info: SyncInfo) -> bool {
let new_state = PeerSyncStatus::Synced { info };
match self {
PeerSyncStatus::Synced { .. } | PeerSyncStatus::Unknown => {
*self = new_state;
false // state was not updated
}
_ => {
*self = new_state;
true
}
}
}
/// Updates the sync state given a peer that is further ahead in the chain than us.
/// Returns true if the state has changed.
pub fn update_advanced(&mut self, info: SyncInfo) -> bool {
let new_state = PeerSyncStatus::Advanced { info };
match self {
PeerSyncStatus::Advanced { .. } | PeerSyncStatus::Unknown => {
*self = new_state;
false // state was not updated
}
_ => {
*self = new_state;
true
}
}
}
/// Updates the sync state given a peer that is behind us in the chain.
/// Returns true if the state has changed.
pub fn update_behind(&mut self, info: SyncInfo) -> bool {
let new_state = PeerSyncStatus::Behind { info };
match self {
PeerSyncStatus::Behind { .. } | PeerSyncStatus::Unknown => {
*self = new_state;
false // state was not updated
}
_ => {
*self = new_state;
true
}
}
}
}

View File

@@ -1,8 +1,10 @@
use super::peer_info::{PeerConnectionStatus, PeerInfo, PeerSyncStatus};
use super::peer_info::{PeerConnectionStatus, PeerInfo};
use super::peer_sync_status::PeerSyncStatus;
use crate::rpc::methods::MetaData;
use crate::PeerId;
use slog::{crit, warn};
use std::collections::HashMap;
use std::time::Instant;
use types::{EthSpec, SubnetId};
/// A peer's reputation.
@@ -41,8 +43,13 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
.map_or(DEFAULT_REPUTATION, |info| info.reputation)
}
/// Returns an iterator over all peers in the db.
pub fn peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> {
self.peers.iter()
}
/// Gives the ids of all known peers.
pub fn peers(&self) -> impl Iterator<Item = &PeerId> {
pub fn peer_ids(&self) -> impl Iterator<Item = &PeerId> {
self.peers.keys()
}
@@ -66,10 +73,27 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
}
/// Gives the ids of all known connected peers.
pub fn connected_peers(&self) -> impl Iterator<Item = &PeerId> {
pub fn connected_peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> {
self.peers
.iter()
.filter(|(_, info)| info.connection_status.is_connected())
}
/// Gives the ids of all known connected peers.
pub fn connected_peer_ids(&self) -> impl Iterator<Item = &PeerId> {
self.peers
.iter()
.filter(|(_, info)| info.connection_status.is_connected())
.map(|(peer_id, _)| peer_id)
}
/// Connected or dialing peers
pub fn connected_or_dialing_peers(&self) -> impl Iterator<Item = &PeerId> {
self.peers
.iter()
.filter(|(_, info)| {
info.connection_status.is_connected() || info.connection_status.is_dialing()
})
.map(|(peer_id, _)| peer_id)
}
@@ -78,7 +102,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
self.peers
.iter()
.filter(|(_, info)| {
if let PeerSyncStatus::Synced { .. } = info.sync_status {
if info.sync_status.is_synced() || info.sync_status.is_advanced() {
return info.connection_status.is_connected();
}
false
@@ -141,16 +165,47 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
}
/// Returns the peer's connection status. Returns unknown if the peer is not in the DB.
pub fn connection_status(&self, peer_id: &PeerId) -> PeerConnectionStatus {
pub fn connection_status(&self, peer_id: &PeerId) -> Option<PeerConnectionStatus> {
self.peer_info(peer_id)
.map_or(PeerConnectionStatus::default(), |info| {
info.connection_status.clone()
})
.map(|info| info.connection_status.clone())
}
/// Returns if the peer is already connected.
pub fn is_connected(&self, peer_id: &PeerId) -> bool {
if let Some(PeerConnectionStatus::Connected { .. }) = self.connection_status(peer_id) {
true
} else {
false
}
}
/// If we are connected or currently dialing the peer returns true.
pub fn is_connected_or_dialing(&self, peer_id: &PeerId) -> bool {
match self.connection_status(peer_id) {
Some(PeerConnectionStatus::Connected { .. })
| Some(PeerConnectionStatus::Dialing { .. }) => true,
_ => false,
}
}
/* Setters */
/// Sets a peer as connected with an ingoing connection
/// A peer is being dialed.
pub fn dialing_peer(&mut self, peer_id: &PeerId) {
let info = self
.peers
.entry(peer_id.clone())
.or_insert_with(|| Default::default());
if info.connection_status.is_disconnected() {
self.n_dc -= 1;
}
info.connection_status = PeerConnectionStatus::Dialing {
since: Instant::now(),
};
}
/// Sets a peer as connected with an ingoing connection.
pub fn connect_ingoing(&mut self, peer_id: &PeerId) {
let info = self
.peers
@@ -163,7 +218,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
info.connection_status.connect_ingoing();
}
/// Sets a peer as connected with an outgoing connection
/// Sets a peer as connected with an outgoing connection.
pub fn connect_outgoing(&mut self, peer_id: &PeerId) {
let info = self
.peers
@@ -176,7 +231,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
info.connection_status.connect_outgoing();
}
/// Sets the peer as disconnected
/// Sets the peer as disconnected.
pub fn disconnect(&mut self, peer_id: &PeerId) {
let log_ref = &self.log;
let info = self.peers.entry(peer_id.clone()).or_insert_with(|| {
@@ -364,7 +419,7 @@ mod tests {
}
assert_eq!(pdb.n_dc, 0);
for p in pdb.connected_peers().cloned().collect::<Vec<_>>() {
for p in pdb.connected_peer_ids().cloned().collect::<Vec<_>>() {
pdb.disconnect(&p);
}

View File

@@ -1,10 +1,7 @@
use crate::rpc::methods::*;
use crate::rpc::{
codec::base::OutboundCodec,
protocol::{
ProtocolId, RPCError, RPC_BLOCKS_BY_RANGE, RPC_BLOCKS_BY_ROOT, RPC_GOODBYE, RPC_META_DATA,
RPC_PING, RPC_STATUS,
},
protocol::{Encoding, Protocol, ProtocolId, RPCError, Version},
};
use crate::rpc::{ErrorMessage, RPCErrorResponse, RPCRequest, RPCResponse};
use libp2p::bytes::{BufMut, Bytes, BytesMut};
@@ -28,7 +25,7 @@ impl<T: EthSpec> SSZInboundCodec<T> {
uvi_codec.set_max_len(max_packet_size);
// this encoding only applies to ssz.
debug_assert!(protocol.encoding.as_str() == "ssz");
debug_assert_eq!(protocol.encoding, Encoding::SSZ);
SSZInboundCodec {
inner: uvi_codec,
@@ -81,39 +78,34 @@ impl<TSpec: EthSpec> Decoder for SSZInboundCodec<TSpec> {
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match self.inner.decode(src).map_err(RPCError::from) {
Ok(Some(packet)) => match self.protocol.message_name.as_str() {
RPC_STATUS => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Status(StatusMessage::from_ssz_bytes(
Ok(Some(packet)) => match self.protocol.message_name {
Protocol::Status => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Status(StatusMessage::from_ssz_bytes(
&packet,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_GOODBYE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Goodbye(GoodbyeReason::from_ssz_bytes(
Protocol::Goodbye => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Goodbye(GoodbyeReason::from_ssz_bytes(
&packet,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_RANGE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::BlocksByRange(
Protocol::BlocksByRange => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::BlocksByRange(
BlocksByRangeRequest::from_ssz_bytes(&packet)?,
))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_ROOT => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::BlocksByRoot(BlocksByRootRequest {
Protocol::BlocksByRoot => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::BlocksByRoot(BlocksByRootRequest {
block_roots: Vec::from_ssz_bytes(&packet)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Ping(Ping {
Protocol::Ping => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Ping(Ping {
data: u64::from_ssz_bytes(&packet)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => {
Protocol::MetaData => match self.protocol.version {
Version::V1 => {
if packet.len() > 0 {
Err(RPCError::Custom(
"Get metadata request should be empty".into(),
@@ -122,9 +114,7 @@ impl<TSpec: EthSpec> Decoder for SSZInboundCodec<TSpec> {
Ok(Some(RPCRequest::MetaData(PhantomData)))
}
}
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
},
Ok(None) => Ok(None),
Err(e) => Err(e),
@@ -146,7 +136,7 @@ impl<TSpec: EthSpec> SSZOutboundCodec<TSpec> {
uvi_codec.set_max_len(max_packet_size);
// this encoding only applies to ssz.
debug_assert!(protocol.encoding.as_str() == "ssz");
debug_assert_eq!(protocol.encoding, Encoding::SSZ);
SSZOutboundCodec {
inner: uvi_codec,
@@ -191,39 +181,35 @@ impl<TSpec: EthSpec> Decoder for SSZOutboundCodec<TSpec> {
// the object is empty. We return the empty object if this is the case
// clear the buffer and return an empty object
src.clear();
match self.protocol.message_name.as_str() {
RPC_STATUS => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
match self.protocol.message_name {
Protocol::Status => match self.protocol.version {
Version::V1 => Err(RPCError::Custom(
"Status stream terminated unexpectedly".into(),
)), // cannot have an empty HELLO message. The stream has terminated unexpectedly
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_GOODBYE => Err(RPCError::InvalidProtocol("GOODBYE doesn't have a response")),
RPC_BLOCKS_BY_RANGE => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
Protocol::Goodbye => {
Err(RPCError::InvalidProtocol("GOODBYE doesn't have a response"))
}
Protocol::BlocksByRange => match self.protocol.version {
Version::V1 => Err(RPCError::Custom(
"Status stream terminated unexpectedly, empty block".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_ROOT => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
Protocol::BlocksByRoot => match self.protocol.version {
Version::V1 => Err(RPCError::Custom(
"Status stream terminated unexpectedly, empty block".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
Protocol::Ping => match self.protocol.version {
Version::V1 => Err(RPCError::Custom(
"PING stream terminated unexpectedly".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
Protocol::MetaData => match self.protocol.version {
Version::V1 => Err(RPCError::Custom(
"Metadata stream terminated unexpectedly".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
} else {
match self.inner.decode(src).map_err(RPCError::from) {
@@ -231,41 +217,35 @@ impl<TSpec: EthSpec> Decoder for SSZOutboundCodec<TSpec> {
// take the bytes from the buffer
let raw_bytes = packet.take();
match self.protocol.message_name.as_str() {
RPC_STATUS => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::Status(StatusMessage::from_ssz_bytes(
&raw_bytes,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
match self.protocol.message_name {
Protocol::Status => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::Status(
StatusMessage::from_ssz_bytes(&raw_bytes)?,
))),
},
RPC_GOODBYE => {
Protocol::Goodbye => {
Err(RPCError::InvalidProtocol("GOODBYE doesn't have a response"))
}
RPC_BLOCKS_BY_RANGE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::BlocksByRange(Box::new(
Protocol::BlocksByRange => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::BlocksByRange(Box::new(
SignedBeaconBlock::from_ssz_bytes(&raw_bytes)?,
)))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_ROOT => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
Protocol::BlocksByRoot => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
SignedBeaconBlock::from_ssz_bytes(&raw_bytes)?,
)))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::Pong(Ping {
Protocol::Ping => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::Pong(Ping {
data: u64::from_ssz_bytes(&raw_bytes)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::MetaData(MetaData::from_ssz_bytes(
&raw_bytes,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
Protocol::MetaData => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::MetaData(
MetaData::from_ssz_bytes(&raw_bytes)?,
))),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
}
Ok(None) => Ok(None), // waiting for more bytes

View File

@@ -1,10 +1,7 @@
use crate::rpc::methods::*;
use crate::rpc::{
codec::base::OutboundCodec,
protocol::{
ProtocolId, RPCError, RPC_BLOCKS_BY_RANGE, RPC_BLOCKS_BY_ROOT, RPC_GOODBYE, RPC_META_DATA,
RPC_PING, RPC_STATUS,
},
protocol::{Encoding, Protocol, ProtocolId, RPCError, Version},
};
use crate::rpc::{ErrorMessage, RPCErrorResponse, RPCRequest, RPCResponse};
use libp2p::bytes::BytesMut;
@@ -34,7 +31,7 @@ impl<T: EthSpec> SSZSnappyInboundCodec<T> {
pub fn new(protocol: ProtocolId, max_packet_size: usize) -> Self {
let uvi_codec = Uvi::default();
// this encoding only applies to ssz_snappy.
debug_assert!(protocol.encoding.as_str() == "ssz_snappy");
debug_assert_eq!(protocol.encoding, Encoding::SSZSnappy);
SSZSnappyInboundCodec {
inner: uvi_codec,
@@ -122,39 +119,34 @@ impl<TSpec: EthSpec> Decoder for SSZSnappyInboundCodec<TSpec> {
let n = reader.get_ref().position();
self.len = None;
src.split_to(n as usize);
match self.protocol.message_name.as_str() {
RPC_STATUS => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Status(StatusMessage::from_ssz_bytes(
match self.protocol.message_name {
Protocol::Status => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Status(StatusMessage::from_ssz_bytes(
&decoded_buffer,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_GOODBYE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Goodbye(GoodbyeReason::from_ssz_bytes(
&decoded_buffer,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
Protocol::Goodbye => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Goodbye(
GoodbyeReason::from_ssz_bytes(&decoded_buffer)?,
))),
},
RPC_BLOCKS_BY_RANGE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::BlocksByRange(
Protocol::BlocksByRange => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::BlocksByRange(
BlocksByRangeRequest::from_ssz_bytes(&decoded_buffer)?,
))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_ROOT => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::BlocksByRoot(BlocksByRootRequest {
Protocol::BlocksByRoot => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::BlocksByRoot(BlocksByRootRequest {
block_roots: Vec::from_ssz_bytes(&decoded_buffer)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Ping(Ping::from_ssz_bytes(
Protocol::Ping => match self.protocol.version {
Version::V1 => Ok(Some(RPCRequest::Ping(Ping::from_ssz_bytes(
&decoded_buffer,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => {
Protocol::MetaData => match self.protocol.version {
Version::V1 => {
if decoded_buffer.len() > 0 {
Err(RPCError::Custom(
"Get metadata request should be empty".into(),
@@ -163,9 +155,7 @@ impl<TSpec: EthSpec> Decoder for SSZSnappyInboundCodec<TSpec> {
Ok(Some(RPCRequest::MetaData(PhantomData)))
}
}
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
}
Err(e) => match e.kind() {
@@ -194,7 +184,7 @@ impl<TSpec: EthSpec> SSZSnappyOutboundCodec<TSpec> {
pub fn new(protocol: ProtocolId, max_packet_size: usize) -> Self {
let uvi_codec = Uvi::default();
// this encoding only applies to ssz_snappy.
debug_assert!(protocol.encoding.as_str() == "ssz_snappy");
debug_assert_eq!(protocol.encoding, Encoding::SSZSnappy);
SSZSnappyOutboundCodec {
inner: uvi_codec,
@@ -279,41 +269,35 @@ impl<TSpec: EthSpec> Decoder for SSZSnappyOutboundCodec<TSpec> {
let n = reader.get_ref().position();
self.len = None;
src.split_to(n as usize);
match self.protocol.message_name.as_str() {
RPC_STATUS => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::Status(StatusMessage::from_ssz_bytes(
&decoded_buffer,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
match self.protocol.message_name {
Protocol::Status => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::Status(
StatusMessage::from_ssz_bytes(&decoded_buffer)?,
))),
},
RPC_GOODBYE => {
Protocol::Goodbye => {
Err(RPCError::InvalidProtocol("GOODBYE doesn't have a response"))
}
RPC_BLOCKS_BY_RANGE => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::BlocksByRange(Box::new(
Protocol::BlocksByRange => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::BlocksByRange(Box::new(
SignedBeaconBlock::from_ssz_bytes(&decoded_buffer)?,
)))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_BLOCKS_BY_ROOT => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
Protocol::BlocksByRoot => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
SignedBeaconBlock::from_ssz_bytes(&decoded_buffer)?,
)))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::Pong(Ping {
Protocol::Ping => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::Pong(Ping {
data: u64::from_ssz_bytes(&decoded_buffer)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::MetaData(MetaData::from_ssz_bytes(
Protocol::MetaData => match self.protocol.version {
Version::V1 => Ok(Some(RPCResponse::MetaData(MetaData::from_ssz_bytes(
&decoded_buffer,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
}
Err(e) => match e.kind() {

View File

@@ -1,6 +1,7 @@
//! Available RPC methods types and ids.
use crate::types::EnrBitfield;
use serde::Serialize;
use ssz_derive::{Decode, Encode};
use types::{Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot};
@@ -37,7 +38,8 @@ pub struct Ping {
}
/// The METADATA response structure.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
#[derive(Encode, Decode, Clone, Debug, PartialEq, Serialize)]
#[serde(bound = "T: EthSpec")]
pub struct MetaData<T: EthSpec> {
/// A sequential counter indicating when data gets modified.
pub seq_number: u64,

View File

@@ -34,18 +34,68 @@ const TTFB_TIMEOUT: u64 = 5;
const REQUEST_TIMEOUT: u64 = 15;
/// Protocol names to be used.
#[derive(Debug, Clone)]
pub enum Protocol {
/// The Status protocol name.
pub const RPC_STATUS: &str = "status";
Status,
/// The Goodbye protocol name.
pub const RPC_GOODBYE: &str = "goodbye";
Goodbye,
/// The `BlocksByRange` protocol name.
pub const RPC_BLOCKS_BY_RANGE: &str = "beacon_blocks_by_range";
BlocksByRange,
/// The `BlocksByRoot` protocol name.
pub const RPC_BLOCKS_BY_ROOT: &str = "beacon_blocks_by_root";
BlocksByRoot,
/// The `Ping` protocol name.
pub const RPC_PING: &str = "ping";
Ping,
/// The `MetaData` protocol name.
pub const RPC_META_DATA: &str = "metadata";
MetaData,
}
/// RPC Versions
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Version {
/// Version 1 of RPC
V1,
}
/// RPC Encondings supported.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Encoding {
SSZ,
SSZSnappy,
}
impl std::fmt::Display for Protocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let repr = match self {
Protocol::Status => "status",
Protocol::Goodbye => "goodbye",
Protocol::BlocksByRange => "beacon_blocks_by_range",
Protocol::BlocksByRoot => "beacon_blocks_by_root",
Protocol::Ping => "ping",
Protocol::MetaData => "metadata",
};
f.write_str(repr)
}
}
impl std::fmt::Display for Encoding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let repr = match self {
Encoding::SSZ => "ssz",
Encoding::SSZSnappy => "ssz_snappy",
};
f.write_str(repr)
}
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let repr = match self {
Version::V1 => "1",
};
f.write_str(repr)
}
}
#[derive(Debug, Clone)]
pub struct RPCProtocol<TSpec: EthSpec> {
@@ -59,18 +109,18 @@ impl<TSpec: EthSpec> UpgradeInfo for RPCProtocol<TSpec> {
/// The list of supported RPC protocols for Lighthouse.
fn protocol_info(&self) -> Self::InfoIter {
vec![
ProtocolId::new(RPC_STATUS, "1", "ssz_snappy"),
ProtocolId::new(RPC_STATUS, "1", "ssz"),
ProtocolId::new(RPC_GOODBYE, "1", "ssz_snappy"),
ProtocolId::new(RPC_GOODBYE, "1", "ssz"),
ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz_snappy"),
ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz"),
ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz_snappy"),
ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz"),
ProtocolId::new(RPC_PING, "1", "ssz_snappy"),
ProtocolId::new(RPC_PING, "1", "ssz"),
ProtocolId::new(RPC_META_DATA, "1", "ssz_snappy"),
ProtocolId::new(RPC_META_DATA, "1", "ssz"),
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZ),
ProtocolId::new(Protocol::Goodbye, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Goodbye, Version::V1, Encoding::SSZ),
ProtocolId::new(Protocol::BlocksByRange, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::BlocksByRange, Version::V1, Encoding::SSZ),
ProtocolId::new(Protocol::BlocksByRoot, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::BlocksByRoot, Version::V1, Encoding::SSZ),
ProtocolId::new(Protocol::Ping, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Ping, Version::V1, Encoding::SSZ),
ProtocolId::new(Protocol::MetaData, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::MetaData, Version::V1, Encoding::SSZ),
]
}
}
@@ -79,13 +129,13 @@ impl<TSpec: EthSpec> UpgradeInfo for RPCProtocol<TSpec> {
#[derive(Clone, Debug)]
pub struct ProtocolId {
/// The rpc message type/name.
pub message_name: String,
pub message_name: Protocol,
/// The version of the RPC.
pub version: String,
pub version: Version,
/// The encoding of the RPC.
pub encoding: String,
pub encoding: Encoding,
/// The protocol id that is formed from the above fields.
protocol_id: String,
@@ -93,16 +143,16 @@ pub struct ProtocolId {
/// An RPC protocol ID.
impl ProtocolId {
pub fn new(message_name: &str, version: &str, encoding: &str) -> Self {
pub fn new(message_name: Protocol, version: Version, encoding: Encoding) -> Self {
let protocol_id = format!(
"{}/{}/{}/{}",
PROTOCOL_PREFIX, message_name, version, encoding
);
ProtocolId {
message_name: message_name.into(),
version: version.into(),
encoding: encoding.into(),
message_name,
version: version,
encoding,
protocol_id,
}
}
@@ -154,13 +204,13 @@ where
protocol: ProtocolId,
) -> Self::Future {
let protocol_name = protocol.message_name.clone();
let codec = match protocol.encoding.as_str() {
"ssz_snappy" => {
let codec = match protocol.encoding {
Encoding::SSZSnappy => {
let ssz_snappy_codec =
BaseInboundCodec::new(SSZSnappyInboundCodec::new(protocol, MAX_RPC_SIZE));
InboundCodec::SSZSnappy(ssz_snappy_codec)
}
"ssz" | _ => {
Encoding::SSZ => {
let ssz_codec = BaseInboundCodec::new(SSZInboundCodec::new(protocol, MAX_RPC_SIZE));
InboundCodec::SSZ(ssz_codec)
}
@@ -171,13 +221,13 @@ where
let socket = Framed::new(timed_socket, codec);
// MetaData requests should be empty, return the stream
if protocol_name == RPC_META_DATA {
futures::future::Either::A(futures::future::ok((
match protocol_name {
Protocol::MetaData => futures::future::Either::A(futures::future::ok((
RPCRequest::MetaData(PhantomData),
socket,
)))
} else {
futures::future::Either::B(
))),
_ => futures::future::Either::B(
socket
.into_future()
.timeout(Duration::from_secs(REQUEST_TIMEOUT))
@@ -190,7 +240,7 @@ where
)),
}
} as FnAndThen<TSocket, TSpec>),
)
),
}
}
}
@@ -226,28 +276,28 @@ impl<TSpec: EthSpec> RPCRequest<TSpec> {
match self {
// add more protocols when versions/encodings are supported
RPCRequest::Status(_) => vec![
ProtocolId::new(RPC_STATUS, "1", "ssz_snappy"),
ProtocolId::new(RPC_STATUS, "1", "ssz"),
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Status, Version::V1, Encoding::SSZ),
],
RPCRequest::Goodbye(_) => vec![
ProtocolId::new(RPC_GOODBYE, "1", "ssz_snappy"),
ProtocolId::new(RPC_GOODBYE, "1", "ssz"),
ProtocolId::new(Protocol::Goodbye, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Goodbye, Version::V1, Encoding::SSZ),
],
RPCRequest::BlocksByRange(_) => vec![
ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz_snappy"),
ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz"),
ProtocolId::new(Protocol::BlocksByRange, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::BlocksByRange, Version::V1, Encoding::SSZ),
],
RPCRequest::BlocksByRoot(_) => vec![
ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz_snappy"),
ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz"),
ProtocolId::new(Protocol::BlocksByRoot, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::BlocksByRoot, Version::V1, Encoding::SSZ),
],
RPCRequest::Ping(_) => vec![
ProtocolId::new(RPC_PING, "1", "ssz_snappy"),
ProtocolId::new(RPC_PING, "1", "ssz"),
ProtocolId::new(Protocol::Ping, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::Ping, Version::V1, Encoding::SSZ),
],
RPCRequest::MetaData(_) => vec![
ProtocolId::new(RPC_META_DATA, "1", "ssz_snappy"),
ProtocolId::new(RPC_META_DATA, "1", "ssz"),
ProtocolId::new(Protocol::MetaData, Version::V1, Encoding::SSZSnappy),
ProtocolId::new(Protocol::MetaData, Version::V1, Encoding::SSZ),
],
}
}
@@ -316,13 +366,13 @@ where
socket: upgrade::Negotiated<TSocket>,
protocol: Self::Info,
) -> Self::Future {
let codec = match protocol.encoding.as_str() {
"ssz_snappy" => {
let codec = match protocol.encoding {
Encoding::SSZSnappy => {
let ssz_snappy_codec =
BaseOutboundCodec::new(SSZSnappyOutboundCodec::new(protocol, MAX_RPC_SIZE));
OutboundCodec::SSZSnappy(ssz_snappy_codec)
}
"ssz" | _ => {
Encoding::SSZ => {
let ssz_codec =
BaseOutboundCodec::new(SSZOutboundCodec::new(protocol, MAX_RPC_SIZE));
OutboundCodec::SSZ(ssz_codec)

View File

@@ -27,7 +27,7 @@ use types::{EnrForkId, EthSpec};
type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
type Libp2pBehaviour<TSpec> = Behaviour<Substream<StreamMuxerBox>, TSpec>;
const NETWORK_KEY_FILENAME: &str = "key";
pub const NETWORK_KEY_FILENAME: &str = "key";
/// The time in milliseconds to wait before banning a peer. This allows for any Goodbye messages to be
/// flushed and protocols to be negotiated.
const BAN_PEER_WAIT_TIMEOUT: u64 = 200;
@@ -138,6 +138,11 @@ impl<TSpec: EthSpec> Service<TSpec> {
if let Protocol::Udp(_) = components[1] {
continue;
}
// inform the peer manager that we are currently dialing this peer
network_globals
.peers
.write()
.dialing_peer(&bootnode_enr.peer_id());
dial_addr(multiaddr);
}
}

View File

@@ -80,7 +80,12 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
/// Returns the number of libp2p connected peers.
pub fn connected_peers(&self) -> usize {
self.peers.read().connected_peers().count()
self.peers.read().connected_peer_ids().count()
}
/// Returns the number of libp2p peers that are either connected or being dialed.
pub fn connected_or_dialing_peers(&self) -> usize {
self.peers.read().connected_or_dialing_peers().count()
}
/// Returns in the node is syncing.

View File

@@ -172,3 +172,30 @@ impl<T: EthSpec> PubsubMessage<T> {
}
}
}
impl<T: EthSpec> std::fmt::Display for PubsubMessage<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PubsubMessage::BeaconBlock(block) => write!(
f,
"Beacon Block: slot: {}, proposer_index: {}",
block.message.slot, block.message.proposer_index
),
PubsubMessage::AggregateAndProofAttestation(att) => write!(
f,
"Aggregate and Proof: slot: {}, index: {}, aggregator_index: {}",
att.message.aggregate.data.slot,
att.message.aggregate.data.index,
att.message.aggregator_index,
),
PubsubMessage::Attestation(data) => write!(
f,
"Attestation: subnet_id: {}, attestation_slot: {}, attestation_index: {}",
*data.0, data.1.data.slot, data.1.data.index,
),
PubsubMessage::VoluntaryExit(_data) => write!(f, "Voluntary Exit"),
PubsubMessage::ProposerSlashing(_data) => write!(f, "Proposer Slashing"),
PubsubMessage::AttesterSlashing(_data) => write!(f, "Attester Slashing"),
}
}
}

View File

@@ -4,6 +4,7 @@ use eth2_libp2p::Multiaddr;
use eth2_libp2p::NetworkConfig;
use eth2_libp2p::Service as LibP2PService;
use slog::{debug, error, o, Drain};
use std::net::{TcpListener, UdpSocket};
use std::time::Duration;
use types::{EnrForkId, MinimalEthSpec};
@@ -22,6 +23,38 @@ pub fn build_log(level: slog::Level, enabled: bool) -> slog::Logger {
}
}
// A bit of hack to find an unused port.
///
/// Does not guarantee that the given port is unused after the function exists, just that it was
/// unused before the function started (i.e., it does not reserve a port).
pub fn unused_port(transport: &str) -> Result<u16, String> {
let local_addr = match transport {
"tcp" => {
let listener = TcpListener::bind("127.0.0.1:0").map_err(|e| {
format!("Failed to create TCP listener to find unused port: {:?}", e)
})?;
listener.local_addr().map_err(|e| {
format!(
"Failed to read TCP listener local_addr to find unused port: {:?}",
e
)
})?
}
"udp" => {
let socket = UdpSocket::bind("127.0.0.1:0")
.map_err(|e| format!("Failed to create UDP socket to find unused port: {:?}", e))?;
socket.local_addr().map_err(|e| {
format!(
"Failed to read UDP socket local_addr to find unused port: {:?}",
e
)
})?
}
_ => return Err("Invalid transport to find unused port".into()),
};
Ok(local_addr.port())
}
pub fn build_config(
port: u16,
mut boot_nodes: Vec<Enr>,
@@ -45,11 +78,11 @@ pub fn build_config(
}
pub fn build_libp2p_instance(
port: u16,
boot_nodes: Vec<Enr>,
secret_key: Option<String>,
log: slog::Logger,
) -> LibP2PService<E> {
let port = unused_port("tcp").unwrap();
let config = build_config(port, boot_nodes, secret_key);
// launch libp2p service
LibP2PService::new(&config, EnrForkId::default(), log.clone())
@@ -66,14 +99,9 @@ pub fn get_enr(node: &LibP2PService<E>) -> Enr {
// Returns `n` libp2p peers in fully connected topology.
#[allow(dead_code)]
pub fn build_full_mesh(
log: slog::Logger,
n: usize,
start_port: Option<u16>,
) -> Vec<LibP2PService<E>> {
let base_port = start_port.unwrap_or(10000);
let mut nodes: Vec<LibP2PService<E>> = (base_port..base_port + n as u16)
.map(|p| build_libp2p_instance(p, vec![], None, log.clone()))
pub fn build_full_mesh(log: slog::Logger, n: usize) -> Vec<LibP2PService<E>> {
let mut nodes: Vec<LibP2PService<E>> = (0..n)
.map(|_| build_libp2p_instance(vec![], None, log.clone()))
.collect();
let multiaddrs: Vec<Multiaddr> = nodes
.iter()
@@ -96,15 +124,12 @@ pub fn build_full_mesh(
// Constructs a pair of nodes with seperate loggers. The sender dials the receiver.
// This returns a (sender, receiver) pair.
#[allow(dead_code)]
pub fn build_node_pair(
log: &slog::Logger,
start_port: u16,
) -> (LibP2PService<E>, LibP2PService<E>) {
pub fn build_node_pair(log: &slog::Logger) -> (LibP2PService<E>, LibP2PService<E>) {
let sender_log = log.new(o!("who" => "sender"));
let receiver_log = log.new(o!("who" => "receiver"));
let mut sender = build_libp2p_instance(start_port, vec![], None, sender_log);
let receiver = build_libp2p_instance(start_port + 1, vec![], None, receiver_log);
let mut sender = build_libp2p_instance(vec![], None, sender_log);
let receiver = build_libp2p_instance(vec![], None, receiver_log);
let receiver_multiaddr = receiver.swarm.discovery().local_enr().clone().multiaddr()[1].clone();
match libp2p::Swarm::dial_addr(&mut sender.swarm, receiver_multiaddr) {
@@ -116,10 +141,9 @@ pub fn build_node_pair(
// Returns `n` peers in a linear topology
#[allow(dead_code)]
pub fn build_linear(log: slog::Logger, n: usize, start_port: Option<u16>) -> Vec<LibP2PService<E>> {
let base_port = start_port.unwrap_or(9000);
let mut nodes: Vec<LibP2PService<E>> = (base_port..base_port + n as u16)
.map(|p| build_libp2p_instance(p, vec![], None, log.clone()))
pub fn build_linear(log: slog::Logger, n: usize) -> Vec<LibP2PService<E>> {
let mut nodes: Vec<LibP2PService<E>> = (0..n)
.map(|_| build_libp2p_instance(vec![], None, log.clone()))
.collect();
let multiaddrs: Vec<Multiaddr> = nodes
.iter()

View File

@@ -25,7 +25,7 @@ fn test_gossipsub_forward() {
let log = common::build_log(Level::Info, false);
let num_nodes = 20;
let mut nodes = common::build_linear(log.clone(), num_nodes, Some(19000));
let mut nodes = common::build_linear(log.clone(), num_nodes);
let mut received_count = 0;
let spec = E::default_spec();
let empty_block = BeaconBlock::empty(&spec);
@@ -98,7 +98,7 @@ fn test_gossipsub_full_mesh_publish() {
// as nodes may get pruned out of the mesh before the gossipsub message
// is published to them.
let num_nodes = 12;
let mut nodes = common::build_full_mesh(log, num_nodes, Some(11320));
let mut nodes = common::build_full_mesh(log, num_nodes);
let mut publishing_node = nodes.pop().unwrap();
let spec = E::default_spec();
let empty_block = BeaconBlock::empty(&spec);

View File

@@ -125,12 +125,14 @@ fn test_secio_noise_fallback() {
let log = common::build_log(log_level, enable_logging);
let noisy_config = common::build_config(56010, vec![], None);
let port = common::unused_port("tcp").unwrap();
let noisy_config = common::build_config(port, vec![], None);
let mut noisy_node = Service::new(&noisy_config, EnrForkId::default(), log.clone())
.expect("should build a libp2p instance")
.1;
let secio_config = common::build_config(56011, vec![common::get_enr(&noisy_node)], None);
let port = common::unused_port("tcp").unwrap();
let secio_config = common::build_config(port, vec![common::get_enr(&noisy_node)], None);
// Building a custom Libp2pService from outside the crate isn't possible because of
// private fields in the Libp2pService struct. A swarm is good enough for testing

View File

@@ -25,7 +25,7 @@ fn test_status_rpc() {
let log = common::build_log(log_level, enable_logging);
// get sender/receiver
let (mut sender, mut receiver) = common::build_node_pair(&log, 10500);
let (mut sender, mut receiver) = common::build_node_pair(&log);
// Dummy STATUS RPC message
let rpc_request = RPCRequest::Status(StatusMessage {
@@ -140,7 +140,7 @@ fn test_blocks_by_range_chunked_rpc() {
let log = common::build_log(log_level, enable_logging);
// get sender/receiver
let (mut sender, mut receiver) = common::build_node_pair(&log, 10505);
let (mut sender, mut receiver) = common::build_node_pair(&log);
// BlocksByRange Request
let rpc_request = RPCRequest::BlocksByRange(BlocksByRangeRequest {
@@ -275,7 +275,7 @@ fn test_blocks_by_range_single_empty_rpc() {
let log = common::build_log(log_level, enable_logging);
// get sender/receiver
let (mut sender, mut receiver) = common::build_node_pair(&log, 10510);
let (mut sender, mut receiver) = common::build_node_pair(&log);
// BlocksByRange Request
let rpc_request = RPCRequest::BlocksByRange(BlocksByRangeRequest {
@@ -411,7 +411,7 @@ fn test_blocks_by_root_chunked_rpc() {
let spec = E::default_spec();
// get sender/receiver
let (mut sender, mut receiver) = common::build_node_pair(&log, 10515);
let (mut sender, mut receiver) = common::build_node_pair(&log);
// BlocksByRoot Request
let rpc_request = RPCRequest::BlocksByRoot(BlocksByRootRequest {
@@ -539,7 +539,7 @@ fn test_goodbye_rpc() {
let log = common::build_log(log_level, enable_logging);
// get sender/receiver
let (mut sender, mut receiver) = common::build_node_pair(&log, 10520);
let (mut sender, mut receiver) = common::build_node_pair(&log);
// Goodbye Request
let rpc_request = RPCRequest::Goodbye(GoodbyeReason::ClientShutdown);

View File

@@ -346,7 +346,8 @@ impl Eth1GenesisService {
.map_err(|e| format!("Error whilst processing deposit: {:?}", e))
})?;
process_activations(&mut local_state, spec);
process_activations(&mut local_state, spec)
.map_err(|e| format!("Error whilst processing activations: {:?}", e))?;
let is_valid = is_valid_genesis_state(&local_state, spec);
trace!(

View File

@@ -190,10 +190,24 @@ impl<T: BeaconChainTypes> AttestationService<T> {
pub fn should_process_attestation(
&mut self,
_message_id: &MessageId,
_peer_id: &PeerId,
_subnet: &SubnetId,
_attestation: &Attestation<T::EthSpec>,
peer_id: &PeerId,
subnet: &SubnetId,
attestation: &Attestation<T::EthSpec>,
) -> bool {
// verify the attestation is on the correct subnet
let expected_subnet = match attestation.subnet_id(&self.beacon_chain.spec) {
Ok(v) => v,
Err(e) => {
warn!(self.log, "Could not obtain attestation subnet_id"; "error" => format!("{:?}", e));
return false;
}
};
if expected_subnet != *subnet {
warn!(self.log, "Received an attestation on the wrong subnet"; "subnet_received" => format!("{:?}", subnet), "subnet_expected" => format!("{:?}",expected_subnet), "peer_id" => format!("{}", peer_id));
return false;
}
// TODO: Correctly handle validation aggregator checks
true
}

View File

@@ -8,7 +8,7 @@ pub mod processor;
use crate::error;
use crate::service::NetworkMessage;
use beacon_chain::{AttestationType, BeaconChain, BeaconChainTypes};
use beacon_chain::{AttestationType, BeaconChain, BeaconChainTypes, BlockError};
use eth2_libp2p::{
rpc::{RPCError, RPCErrorResponse, RPCRequest, RPCResponse, RequestId, ResponseTermination},
MessageId, NetworkGlobals, PeerId, PubsubMessage, RPCEvent,
@@ -60,7 +60,7 @@ impl<T: BeaconChainTypes> Router<T> {
executor: &tokio::runtime::TaskExecutor,
log: slog::Logger,
) -> error::Result<mpsc::UnboundedSender<RouterMessage<T::EthSpec>>> {
let message_handler_log = log.new(o!("service"=> "msg_handler"));
let message_handler_log = log.new(o!("service"=> "router"));
trace!(message_handler_log, "Service starting");
let (handler_send, handler_recv) = mpsc::unbounded_channel();
@@ -262,16 +262,20 @@ impl<T: BeaconChainTypes> Router<T> {
AttestationType::Unaggregated { should_store: true },
);
}
PubsubMessage::BeaconBlock(block) => match self.processor.should_forward_block(block) {
PubsubMessage::BeaconBlock(block) => {
match self.processor.should_forward_block(&peer_id, block) {
Ok(verified_block) => {
self.propagate_message(id, peer_id.clone());
self.processor.on_block_gossip(peer_id, verified_block);
}
Err(BlockError::ParentUnknown { .. }) => {} // performing a parent lookup
Err(e) => {
// performing a parent lookup
warn!(self.log, "Could not verify block for gossip";
"error" => format!("{:?}", e));
}
},
}
}
PubsubMessage::VoluntaryExit(_exit) => {
// TODO: Apply more sophisticated validation
self.propagate_message(id, peer_id.clone());

View File

@@ -1,5 +1,5 @@
use crate::service::NetworkMessage;
use crate::sync::SyncMessage;
use crate::sync::{PeerSyncInfo, SyncMessage};
use beacon_chain::{
AttestationProcessingOutcome, AttestationType, BeaconChain, BeaconChainTypes, BlockError,
BlockProcessingOutcome, GossipVerifiedBlock,
@@ -13,7 +13,8 @@ use std::sync::Arc;
use store::Store;
use tokio::sync::{mpsc, oneshot};
use types::{
Attestation, Epoch, EthSpec, Hash256, SignedAggregateAndProof, SignedBeaconBlock, Slot,
Attestation, ChainSpec, Epoch, EthSpec, Hash256, SignedAggregateAndProof, SignedBeaconBlock,
Slot,
};
//TODO: Rate limit requests
@@ -22,34 +23,6 @@ use types::{
/// Otherwise we queue it.
pub(crate) const FUTURE_SLOT_TOLERANCE: u64 = 1;
/// Keeps track of syncing information for known connected peers.
#[derive(Clone, Copy, Debug)]
pub struct PeerSyncInfo {
fork_digest: [u8; 4],
pub finalized_root: Hash256,
pub finalized_epoch: Epoch,
pub head_root: Hash256,
pub head_slot: Slot,
}
impl From<StatusMessage> for PeerSyncInfo {
fn from(status: StatusMessage) -> PeerSyncInfo {
PeerSyncInfo {
fork_digest: status.fork_digest,
finalized_root: status.finalized_root,
finalized_epoch: status.finalized_epoch,
head_root: status.head_root,
head_slot: status.head_slot,
}
}
}
impl PeerSyncInfo {
pub fn from_chain<T: BeaconChainTypes>(chain: &Arc<BeaconChain<T>>) -> Option<PeerSyncInfo> {
Some(Self::from(status_message(chain)?))
}
}
/// Processes validated messages from the network. It relays necessary data to the syncing thread
/// and processes blocks from the pubsub network.
pub struct Processor<T: BeaconChainTypes> {
@@ -172,7 +145,7 @@ impl<T: BeaconChainTypes> Processor<T> {
/// Process a `Status` response from a peer.
pub fn on_status_response(&mut self, peer_id: PeerId, status: StatusMessage) {
trace!(
debug!(
self.log,
"Received Status Response";
"peer" => format!("{:?}", peer_id),
@@ -489,9 +462,18 @@ impl<T: BeaconChainTypes> Processor<T> {
/// across the network.
pub fn should_forward_block(
&mut self,
peer_id: &PeerId,
block: Box<SignedBeaconBlock<T::EthSpec>>,
) -> Result<GossipVerifiedBlock<T>, BlockError> {
self.chain.verify_block_for_gossip(*block)
let result = self.chain.verify_block_for_gossip(*block.clone());
if let Err(BlockError::ParentUnknown(block_hash)) = result {
// if we don't know the parent, start a parent lookup
// TODO: Modify the return to avoid the block clone.
debug!(self.log, "Unknown block received. Starting a parent lookup"; "block_slot" => block.message.slot, "block_hash" => format!("{}", block_hash));
self.send_to_sync(SyncMessage::UnknownBlock(peer_id.clone(), block));
}
result
}
/// Process a gossip message declaring a new block.
@@ -533,7 +515,8 @@ impl<T: BeaconChainTypes> Processor<T> {
}
BlockProcessingOutcome::ParentUnknown { .. } => {
// Inform the sync manager to find parents for this block
debug!(self.log, "Block with unknown parent received";
// This should not occur. It should be checked by `should_forward_block`
error!(self.log, "Block with unknown parent attempted to be processed";
"peer_id" => format!("{:?}",peer_id));
self.send_to_sync(SyncMessage::UnknownBlock(peer_id, block));
}
@@ -644,10 +627,13 @@ pub(crate) fn status_message<T: BeaconChainTypes>(
beacon_chain: &BeaconChain<T>,
) -> Option<StatusMessage> {
let head_info = beacon_chain.head_info().ok()?;
let genesis_validators_root = beacon_chain.genesis_validators_root;
let fork_digest =
ChainSpec::compute_fork_digest(head_info.fork.current_version, genesis_validators_root);
// TODO: Update fork digest calculation
Some(StatusMessage {
fork_digest: head_info.fork.current_version,
fork_digest,
finalized_root: head_info.finalized_checkpoint.root,
finalized_epoch: head_info.finalized_checkpoint.epoch,
head_root: head_info.block_root,

View File

@@ -306,7 +306,6 @@ fn spawn_service<T: BeaconChainTypes>(
.map_err(|_| { debug!(log, "Failed to send peer disconnect to router");})?;
}
BehaviourEvent::StatusPeer(peer_id) => {
debug!(log, "Re-status peer"; "peer_id" => format!("{}", peer_id));
service.router_send
.try_send(RouterMessage::StatusPeer(peer_id))
.map_err(|_| { debug!(log, "Failed to send re-status peer to router");})?;

View File

@@ -1,9 +1,9 @@
use crate::router::processor::FUTURE_SLOT_TOLERANCE;
use crate::sync::manager::SyncMessage;
use crate::sync::range_sync::BatchId;
use crate::sync::range_sync::{BatchId, ChainId};
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError, ChainSegmentResult};
use eth2_libp2p::PeerId;
use slog::{crit, debug, error, trace, warn};
use slog::{debug, error, trace, warn};
use std::sync::{Arc, Weak};
use tokio::sync::mpsc;
use types::SignedBeaconBlock;
@@ -12,7 +12,7 @@ use types::SignedBeaconBlock;
#[derive(Clone, Debug, PartialEq)]
pub enum ProcessId {
/// Processing Id of a range syncing batch.
RangeBatchId(BatchId),
RangeBatchId(ChainId, BatchId),
/// Processing Id of the parent lookup of a block
ParentLookup(PeerId),
}
@@ -40,7 +40,7 @@ pub fn spawn_block_processor<T: BeaconChainTypes>(
std::thread::spawn(move || {
match process_id {
// this a request from the range sync
ProcessId::RangeBatchId(batch_id) => {
ProcessId::RangeBatchId(chain_id, batch_id) => {
debug!(log, "Processing batch"; "id" => *batch_id, "blocks" => downloaded_blocks.len());
let result = match process_blocks(chain, downloaded_blocks.iter(), &log) {
(_, Ok(_)) => {
@@ -59,8 +59,9 @@ pub fn spawn_block_processor<T: BeaconChainTypes>(
};
let msg = SyncMessage::BatchProcessed {
batch_id: batch_id,
downloaded_blocks: downloaded_blocks,
chain_id,
batch_id,
downloaded_blocks,
result,
};
sync_send.try_send(msg).unwrap_or_else(|_| {
@@ -102,8 +103,6 @@ pub fn spawn_block_processor<T: BeaconChainTypes>(
}
/// Helper function to process blocks batches which only consumes the chain and blocks to process.
// TODO: Verify the fork choice logic and the correct error handling from `process_chain_segment`.
// Ensure fork-choice doesn't need to be run during the failed errors.
fn process_blocks<
'a,
T: BeaconChainTypes,
@@ -125,7 +124,6 @@ fn process_blocks<
"count" => imported_blocks,
);
// Batch completed successfully with at least one block, run fork choice.
// TODO: Verify this logic
run_fork_choice(chain, log);
}
@@ -135,8 +133,10 @@ fn process_blocks<
imported_blocks,
error,
} => {
let r = handle_failed_chain_segment(chain, imported_blocks, error, log);
let r = handle_failed_chain_segment(error, log);
if imported_blocks > 0 {
run_fork_choice(chain, log);
}
(imported_blocks, r)
}
};
@@ -166,31 +166,16 @@ fn run_fork_choice<T: BeaconChainTypes>(chain: Arc<BeaconChain<T>>, log: &slog::
}
/// Helper function to handle a `BlockError` from `process_chain_segment`
fn handle_failed_chain_segment<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
imported_blocks: usize,
error: BlockError,
log: &slog::Logger,
) -> Result<(), String> {
fn handle_failed_chain_segment(error: BlockError, log: &slog::Logger) -> Result<(), String> {
match error {
BlockError::ParentUnknown(parent) => {
// blocks should be sequential and all parents should exist
warn!(
log, "Parent block is unknown";
"parent_root" => format!("{}", parent),
);
// NOTE: logic from master. TODO: check
if imported_blocks > 0 {
run_fork_choice(chain, log);
}
Err(format!("Block has an unknown parent: {}", parent))
}
BlockError::BlockIsAlreadyKnown => {
// TODO: Check handling of this
crit!(log, "Unknown handling of block error");
// This can happen for many reasons. Head sync's can download multiples and parent
// lookups can download blocks before range sync
Ok(())
}
BlockError::FutureSlot {
@@ -206,10 +191,6 @@ fn handle_failed_chain_segment<T: BeaconChainTypes>(
"block_slot" => block_slot,
"FUTURE_SLOT_TOLERANCE" => FUTURE_SLOT_TOLERANCE,
);
// NOTE: logic from master. TODO: check
if imported_blocks > 0 {
run_fork_choice(chain, log);
}
} else {
// The block is in the future, but not too far.
debug!(
@@ -226,26 +207,15 @@ fn handle_failed_chain_segment<T: BeaconChainTypes>(
))
}
BlockError::WouldRevertFinalizedSlot { .. } => {
//TODO: Check handling. Run fork choice?
debug!(
log, "Finalized or earlier block processed";
);
// block reached our finalized slot or was earlier, move to the next block
// TODO: How does this logic happen for the chain segment. We would want to
// continue processing in this case.
debug!( log, "Finalized or earlier block processed";);
Ok(())
}
BlockError::GenesisBlock => {
debug!(
log, "Genesis block was processed";
);
// TODO: Similarly here. Prefer to continue processing.
debug!(log, "Genesis block was processed");
Ok(())
}
BlockError::BeaconChainError(e) => {
// TODO: Run fork choice?
warn!(
log, "BlockProcessingFailure";
"msg" => "unexpected condition in processing block.",
@@ -255,11 +225,6 @@ fn handle_failed_chain_segment<T: BeaconChainTypes>(
Err(format!("Internal error whilst processing block: {:?}", e))
}
other => {
// TODO: Run fork choice?
// NOTE: logic from master. TODO: check
if imported_blocks > 0 {
run_fork_choice(chain, log);
}
warn!(
log, "Invalid block received";
"msg" => "peer sent invalid block",

View File

@@ -35,16 +35,15 @@
use super::block_processor::{spawn_block_processor, BatchProcessResult, ProcessId};
use super::network_context::SyncNetworkContext;
use super::range_sync::{BatchId, RangeSync};
use crate::router::processor::PeerSyncInfo;
use super::peer_sync_info::{PeerSyncInfo, PeerSyncType};
use super::range_sync::{BatchId, ChainId, RangeSync};
use crate::service::NetworkMessage;
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
use eth2_libp2p::rpc::{methods::*, RequestId};
use eth2_libp2p::types::NetworkGlobals;
use eth2_libp2p::{PeerId, PeerSyncStatus};
use eth2_libp2p::PeerId;
use fnv::FnvHashMap;
use futures::prelude::*;
use rand::seq::SliceRandom;
use slog::{crit, debug, error, info, trace, warn, Logger};
use smallvec::SmallVec;
use std::boxed::Box;
@@ -56,9 +55,9 @@ use types::{EthSpec, Hash256, SignedBeaconBlock, Slot};
/// The number of slots ahead of us that is allowed before requesting a long-range (batch) Sync
/// from a peer. If a peer is within this tolerance (forwards or backwards), it is treated as a
/// fully sync'd peer.
const SLOT_IMPORT_TOLERANCE: usize = 20;
pub const SLOT_IMPORT_TOLERANCE: usize = 20;
/// How many attempts we try to find a parent of a block before we give up trying .
const PARENT_FAIL_TOLERANCE: usize = 3;
const PARENT_FAIL_TOLERANCE: usize = 5;
/// The maximum depth we will search for a parent block. In principle we should have sync'd any
/// canonical chain to its head once the peer connects. A chain should not appear where it's depth
/// is further back than the most recent head slot.
@@ -99,6 +98,7 @@ pub enum SyncMessage<T: EthSpec> {
/// A batch has been processed by the block processor thread.
BatchProcessed {
chain_id: ChainId,
batch_id: BatchId,
downloaded_blocks: Vec<SignedBeaconBlock<T>>,
result: BatchProcessResult,
@@ -152,7 +152,7 @@ pub struct SyncManager<T: BeaconChainTypes> {
/// received or not.
///
/// The flag allows us to determine if the peer returned data or sent us nothing.
single_block_lookups: FnvHashMap<RequestId, (Hash256, bool)>,
single_block_lookups: FnvHashMap<RequestId, SingleBlockRequest>,
/// The logger for the import manager.
log: Logger,
@@ -161,6 +161,23 @@ pub struct SyncManager<T: BeaconChainTypes> {
sync_send: mpsc::UnboundedSender<SyncMessage<T::EthSpec>>,
}
/// Object representing a single block lookup request.
struct SingleBlockRequest {
/// The hash of the requested block.
pub hash: Hash256,
/// Whether a block was received from this request, or the peer returned an empty response.
pub block_returned: bool,
}
impl SingleBlockRequest {
pub fn new(hash: Hash256) -> Self {
Self {
hash,
block_returned: false,
}
}
}
/// Spawns a new `SyncManager` thread which has a weak reference to underlying beacon
/// chain. This allows the chain to be
/// dropped during the syncing process which will gracefully end the `SyncManager`.
@@ -224,7 +241,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
/// ours that we consider it fully sync'd with respect to our current chain.
fn add_peer(&mut self, peer_id: PeerId, remote: PeerSyncInfo) {
// ensure the beacon chain still exists
let local = match PeerSyncInfo::from_chain(&self.chain) {
let local_peer_info = match PeerSyncInfo::from_chain(&self.chain) {
Some(local) => local,
None => {
return error!(
@@ -235,31 +252,45 @@ impl<T: BeaconChainTypes> SyncManager<T> {
}
};
// If a peer is within SLOT_IMPORT_TOLERANCE from our head slot, ignore a batch/range sync,
// consider it a fully-sync'd peer.
if remote.head_slot.sub(local.head_slot).as_usize() < SLOT_IMPORT_TOLERANCE {
match local_peer_info.peer_sync_type(&remote) {
PeerSyncType::FullySynced => {
trace!(self.log, "Peer synced to our head found";
"peer" => format!("{:?}", peer_id),
"peer_head_slot" => remote.head_slot,
"local_head_slot" => local.head_slot,
"local_head_slot" => local_peer_info.head_slot,
);
self.synced_peer(&peer_id, remote.head_slot);
self.synced_peer(&peer_id, remote);
// notify the range sync that a peer has been added
self.range_sync.fully_synced_peer_found();
return;
}
PeerSyncType::Advanced => {
trace!(self.log, "Useful peer for sync found";
"peer" => format!("{:?}", peer_id),
"peer_head_slot" => remote.head_slot,
"local_head_slot" => local_peer_info.head_slot,
"peer_finalized_epoch" => remote.finalized_epoch,
"local_finalized_epoch" => local_peer_info.finalized_epoch,
);
// Check if the peer is significantly behind us. If within `SLOT_IMPORT_TOLERANCE`
// treat them as a fully synced peer. If not, ignore them in the sync process
if local.head_slot.sub(remote.head_slot).as_usize() < SLOT_IMPORT_TOLERANCE {
self.synced_peer(&peer_id, remote.head_slot);
// if we don't know about the peer's chain add it to the range sync, otherwise
// consider it synced (it can be the case that the peer seems ahead of us, but we
// reject its chain).
if self.chain.fork_choice.contains_block(&remote.head_root) {
self.synced_peer(&peer_id, remote);
// notify the range sync that a peer has been added
self.range_sync.fully_synced_peer_found();
} else {
self.behind_peer(&peer_id, remote.head_slot);
return;
}
// Add the peer to our RangeSync
self.range_sync.add_peer(&mut self.network, peer_id, remote);
self.range_sync
.add_peer(&mut self.network, peer_id.clone(), remote);
self.advanced_peer(&peer_id, remote);
}
}
PeerSyncType::Behind => {
self.behind_peer(&peer_id, remote);
}
}
}
/// The response to a `BlocksByRoot` request.
@@ -280,12 +311,10 @@ impl<T: BeaconChainTypes> SyncManager<T> {
// check if this is a single block lookup - i.e we were searching for a specific hash
let mut single_block_hash = None;
if let Some((block_hash, data_received)) =
self.single_block_lookups.get_mut(&request_id)
{
if let Some(block_request) = self.single_block_lookups.get_mut(&request_id) {
// update the state of the lookup indicating a block was received from the peer
*data_received = true;
single_block_hash = Some(block_hash.clone());
block_request.block_returned = true;
single_block_hash = Some(block_request.hash.clone());
}
if let Some(block_hash) = single_block_hash {
self.single_block_lookup_response(peer_id, block, block_hash);
@@ -316,12 +345,10 @@ impl<T: BeaconChainTypes> SyncManager<T> {
// this is a stream termination
// stream termination for a single block lookup, remove the key
if let Some((block_hash, data_received)) =
self.single_block_lookups.remove(&request_id)
{
if let Some(single_block_request) = self.single_block_lookups.remove(&request_id) {
// the peer didn't respond with a block that it referenced
if !data_received {
warn!(self.log, "Peer didn't respond with a block it referenced"; "referenced_block_hash" => format!("{}", block_hash), "peer_id" => format!("{}", peer_id));
if !single_block_request.block_returned {
warn!(self.log, "Peer didn't respond with a block it referenced"; "referenced_block_hash" => format!("{}", single_block_request.hash), "peer_id" => format!("{}", peer_id));
self.network.downvote_peer(peer_id);
}
return;
@@ -410,10 +437,25 @@ impl<T: BeaconChainTypes> SyncManager<T> {
/// A block has been sent to us that has an unknown parent. This begins a parent lookup search
/// to find the parent or chain of parents that match our current chain.
fn add_unknown_block(&mut self, peer_id: PeerId, block: SignedBeaconBlock<T::EthSpec>) {
// If we are not synced ignore the block
// If we are not synced or within SLOT_IMPORT_TOLERANCE of the block, ignore
if !self.network_globals.sync_state.read().is_synced() {
let head_slot = self
.chain
.head_info()
.map(|info| info.slot)
.unwrap_or_else(|_| Slot::from(0u64));
let unknown_block_slot = block.message.slot;
// if the block is far in the future, ignore it. If its within the slot tolerance of
// our current head, regardless of the syncing state, fetch it.
if (head_slot >= unknown_block_slot
&& head_slot.sub(unknown_block_slot).as_usize() > SLOT_IMPORT_TOLERANCE)
|| (head_slot < unknown_block_slot
&& unknown_block_slot.sub(head_slot).as_usize() > SLOT_IMPORT_TOLERANCE)
{
return;
}
}
// Make sure this block is not already being searched for
// NOTE: Potentially store a hashset of blocks for O(1) lookups
@@ -446,13 +488,23 @@ impl<T: BeaconChainTypes> SyncManager<T> {
return;
}
// Do not re-request a block that is already being requested
if self
.single_block_lookups
.values()
.find(|single_block_request| single_block_request.hash == block_hash)
.is_some()
{
return;
}
let request = BlocksByRootRequest {
block_roots: vec![block_hash],
};
if let Ok(request_id) = self.network.blocks_by_root_request(peer_id, request) {
self.single_block_lookups
.insert(request_id, (block_hash, false));
.insert(request_id, SingleBlockRequest::new(block_hash));
}
}
@@ -487,17 +539,14 @@ impl<T: BeaconChainTypes> SyncManager<T> {
self.update_sync_state();
}
// TODO: Group these functions into one.
/// Updates the syncing state of a peer to be synced.
fn synced_peer(&mut self, peer_id: &PeerId, status_head_slot: Slot) {
fn synced_peer(&mut self, peer_id: &PeerId, sync_info: PeerSyncInfo) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
match peer_info.sync_status {
PeerSyncStatus::Synced { .. } => {
peer_info.sync_status = PeerSyncStatus::Synced { status_head_slot }
} // just update block
PeerSyncStatus::Behind { .. } | PeerSyncStatus::Unknown => {
peer_info.sync_status = PeerSyncStatus::Synced { status_head_slot };
debug!(self.log, "Peer transitioned to synced status"; "peer_id" => format!("{}", peer_id));
}
let head_slot = sync_info.head_slot;
let finalized_epoch = sync_info.finalized_epoch;
if peer_info.sync_status.update_synced(sync_info.into()) {
debug!(self.log, "Peer transitioned sync state"; "new_state" => "synced", "peer_id" => format!("{}", peer_id), "head_slot" => head_slot, "finalized_epoch" => finalized_epoch);
}
} else {
crit!(self.log, "Status'd peer is unknown"; "peer_id" => format!("{}", peer_id));
@@ -506,21 +555,26 @@ impl<T: BeaconChainTypes> SyncManager<T> {
}
/// Updates the syncing state of a peer to be behind.
fn behind_peer(&mut self, peer_id: &PeerId, status_head_slot: Slot) {
fn advanced_peer(&mut self, peer_id: &PeerId, sync_info: PeerSyncInfo) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
match peer_info.sync_status {
PeerSyncStatus::Synced { .. } => {
debug!(self.log, "Peer transitioned to from synced state to behind"; "peer_id" => format!("{}", peer_id), "head_slot" => status_head_slot);
peer_info.sync_status = PeerSyncStatus::Behind { status_head_slot }
let head_slot = sync_info.head_slot;
let finalized_epoch = sync_info.finalized_epoch;
if peer_info.sync_status.update_advanced(sync_info.into()) {
debug!(self.log, "Peer transitioned sync state"; "new_state" => "advanced", "peer_id" => format!("{}", peer_id), "head_slot" => head_slot, "finalized_epoch" => finalized_epoch);
}
} else {
crit!(self.log, "Status'd peer is unknown"; "peer_id" => format!("{}", peer_id));
}
self.update_sync_state();
}
PeerSyncStatus::Behind { .. } => {
peer_info.sync_status = PeerSyncStatus::Behind { status_head_slot }
} // just update
PeerSyncStatus::Unknown => {
debug!(self.log, "Peer transitioned to behind sync status"; "peer_id" => format!("{}", peer_id), "head_slot" => status_head_slot);
peer_info.sync_status = PeerSyncStatus::Behind { status_head_slot }
}
/// Updates the syncing state of a peer to be behind.
fn behind_peer(&mut self, peer_id: &PeerId, sync_info: PeerSyncInfo) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
let head_slot = sync_info.head_slot;
let finalized_epoch = sync_info.finalized_epoch;
if peer_info.sync_status.update_behind(sync_info.into()) {
debug!(self.log, "Peer transitioned sync state"; "new_state" => "behind", "peer_id" => format!("{}", peer_id), "head_slot" => head_slot, "finalized_epoch" => finalized_epoch);
}
} else {
crit!(self.log, "Status'd peer is unknown"; "peer_id" => format!("{}", peer_id));
@@ -641,9 +695,16 @@ impl<T: BeaconChainTypes> SyncManager<T> {
if parent_request.failed_attempts >= PARENT_FAIL_TOLERANCE
|| parent_request.downloaded_blocks.len() >= PARENT_DEPTH_TOLERANCE
{
let error = if parent_request.failed_attempts >= PARENT_FAIL_TOLERANCE {
"too many failed attempts"
} else {
"reached maximum lookup-depth"
};
debug!(self.log, "Parent import failed";
"block" => format!("{:?}",parent_request.downloaded_blocks[0].canonical_root()),
"ancestors_found" => parent_request.downloaded_blocks.len()
"ancestors_found" => parent_request.downloaded_blocks.len(),
"reason" => error
);
return; // drop the request
}
@@ -658,20 +719,10 @@ impl<T: BeaconChainTypes> SyncManager<T> {
let request = BlocksByRootRequest {
block_roots: vec![parent_hash],
};
// select a random fully synced peer to attempt to download the parent block
let available_peers = self
.network_globals
.peers
.read()
.synced_peers()
.cloned()
.collect::<Vec<_>>();
let peer_id = if let Some(peer_id) = available_peers.choose(&mut rand::thread_rng()) {
(*peer_id).clone()
} else {
// there were no peers to choose from. We drop the lookup request
return;
};
// We continue to search for the chain of blocks from the same peer. Other peers are not
// guaranteed to have this chain of blocks.
let peer_id = parent_request.last_submitted_peer.clone();
if let Ok(request_id) = self.network.blocks_by_root_request(peer_id, request) {
// if the request was successful add the queue back into self
@@ -725,12 +776,14 @@ impl<T: BeaconChainTypes> Future for SyncManager<T> {
self.inject_error(peer_id, request_id);
}
SyncMessage::BatchProcessed {
chain_id,
batch_id,
downloaded_blocks,
result,
} => {
self.range_sync.handle_block_process_result(
&mut self.network,
chain_id,
batch_id,
downloaded_blocks,
result,

View File

@@ -4,6 +4,8 @@
mod block_processor;
pub mod manager;
mod network_context;
mod peer_sync_info;
mod range_sync;
pub use manager::SyncMessage;
pub use peer_sync_info::PeerSyncInfo;

View File

@@ -0,0 +1,113 @@
use super::manager::SLOT_IMPORT_TOLERANCE;
use crate::router::processor::status_message;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::rpc::methods::*;
use eth2_libp2p::SyncInfo;
use std::ops::Sub;
use std::sync::Arc;
use types::{Epoch, Hash256, Slot};
/// Keeps track of syncing information for known connected peers.
#[derive(Clone, Copy, Debug)]
pub struct PeerSyncInfo {
pub fork_digest: [u8; 4],
pub finalized_root: Hash256,
pub finalized_epoch: Epoch,
pub head_root: Hash256,
pub head_slot: Slot,
}
/// The type of peer relative to our current state.
pub enum PeerSyncType {
/// The peer is on our chain and is fully synced with respect to our chain.
FullySynced,
/// The peer has a greater knowledge of the chain that us that warrants a full sync.
Advanced,
/// A peer is behind in the sync and not useful to us for downloading blocks.
Behind,
}
impl From<StatusMessage> for PeerSyncInfo {
fn from(status: StatusMessage) -> PeerSyncInfo {
PeerSyncInfo {
fork_digest: status.fork_digest,
finalized_root: status.finalized_root,
finalized_epoch: status.finalized_epoch,
head_root: status.head_root,
head_slot: status.head_slot,
}
}
}
impl Into<SyncInfo> for PeerSyncInfo {
fn into(self) -> SyncInfo {
SyncInfo {
status_head_slot: self.head_slot,
status_head_root: self.head_root,
status_finalized_epoch: self.finalized_epoch,
status_finalized_root: self.finalized_root,
}
}
}
impl PeerSyncInfo {
/// Derives the peer sync information from a beacon chain.
pub fn from_chain<T: BeaconChainTypes>(chain: &Arc<BeaconChain<T>>) -> Option<PeerSyncInfo> {
Some(Self::from(status_message(chain)?))
}
/// Given another peer's `PeerSyncInfo` this will determine how useful that peer is for us in
/// regards to syncing. This returns the peer sync type that can then be handled by the
/// `SyncManager`.
pub fn peer_sync_type(&self, remote_peer_sync_info: &PeerSyncInfo) -> PeerSyncType {
// check if the peer is fully synced with our current chain
if self.is_fully_synced_peer(remote_peer_sync_info) {
PeerSyncType::FullySynced
}
// if not, check if the peer is ahead of our chain
else if self.is_advanced_peer(remote_peer_sync_info) {
PeerSyncType::Advanced
} else {
// the peer must be behind and not useful
PeerSyncType::Behind
}
}
/// Determines if another peer is fully synced with the current peer.
///
/// A fully synced peer is a peer whose finalized epoch and hash match our own and their
/// head is within SLOT_IMPORT_TOLERANCE of our own.
/// In this case we ignore any batch/range syncing.
fn is_fully_synced_peer(&self, remote: &PeerSyncInfo) -> bool {
// ensure we are on the same chain, with minor differing heads
if remote.finalized_epoch == self.finalized_epoch
&& remote.finalized_root == self.finalized_root
{
// that we are within SLOT_IMPORT_TOLERANCE of our two heads
if (self.head_slot >= remote.head_slot
&& self.head_slot.sub(remote.head_slot).as_usize() <= SLOT_IMPORT_TOLERANCE)
|| (self.head_slot < remote.head_slot)
&& remote.head_slot.sub(self.head_slot).as_usize() <= SLOT_IMPORT_TOLERANCE
{
return true;
}
}
false
}
/// Determines if a peer has more knowledge about the current chain than we do.
///
/// There are two conditions here.
/// 1) The peer could have a head slot that is greater
/// than SLOT_IMPORT_TOLERANCE of our current head.
/// 2) The peer has a greater finalized slot/epoch than our own.
fn is_advanced_peer(&self, remote: &PeerSyncInfo) -> bool {
if remote.head_slot.sub(self.head_slot).as_usize() > SLOT_IMPORT_TOLERANCE
|| self.finalized_epoch < remote.finalized_epoch
{
true
} else {
false
}
}
}

View File

@@ -1,4 +1,4 @@
use super::chain::BLOCKS_PER_BATCH;
use super::chain::EPOCHS_PER_BATCH;
use eth2_libp2p::rpc::methods::*;
use eth2_libp2p::rpc::RequestId;
use eth2_libp2p::PeerId;
@@ -76,7 +76,10 @@ impl<T: EthSpec> Batch<T> {
pub fn to_blocks_by_range_request(&self) -> BlocksByRangeRequest {
BlocksByRangeRequest {
start_slot: self.start_slot.into(),
count: std::cmp::min(BLOCKS_PER_BATCH, self.end_slot.sub(self.start_slot).into()),
count: std::cmp::min(
T::slots_per_epoch() * EPOCHS_PER_BATCH,
self.end_slot.sub(self.start_slot).into(),
),
step: 1,
}
}

View File

@@ -10,14 +10,15 @@ use slog::{crit, debug, warn};
use std::collections::HashSet;
use std::sync::Arc;
use tokio::sync::mpsc;
use types::{Hash256, SignedBeaconBlock, Slot};
use types::{Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot};
/// Blocks are downloaded in batches from peers. This constant specifies how many blocks per batch
/// is requested. There is a timeout for each batch request. If this value is too high, we will
/// downvote peers with poor bandwidth. This can be set arbitrarily high, in which case the
/// Blocks are downloaded in batches from peers. This constant specifies how many epochs worth of
/// blocks per batch are requested _at most_. A batch may request less blocks to account for
/// already requested slots. There is a timeout for each batch request. If this value is too high,
/// we will downvote peers with poor bandwidth. This can be set arbitrarily high, in which case the
/// responder will fill the response up to the max request size, assuming they have the bandwidth
/// to do so.
pub const BLOCKS_PER_BATCH: u64 = 64;
pub const EPOCHS_PER_BATCH: u64 = 2;
/// The number of times to retry a batch before the chain is considered failed and removed.
const MAX_BATCH_RETRIES: u8 = 5;
@@ -38,12 +39,18 @@ pub enum ProcessingResult {
RemoveChain,
}
/// A chain identifier
pub type ChainId = u64;
/// A chain of blocks that need to be downloaded. Peers who claim to contain the target head
/// root are grouped into the peer pool and queried for batches when downloading the
/// chain.
pub struct SyncingChain<T: BeaconChainTypes> {
/// A random id used to identify this chain.
id: ChainId,
/// The original start slot when this chain was initialised.
pub start_slot: Slot,
pub start_epoch: Epoch,
/// The target head slot.
pub target_head_slot: Slot,
@@ -74,8 +81,7 @@ pub struct SyncingChain<T: BeaconChainTypes> {
/// The current state of the chain.
pub state: ChainSyncingState,
/// A random id given to a batch process request. This is None if there is no ongoing batch
/// process.
/// The current processing batch, if any.
current_processing_batch: Option<Batch<T::EthSpec>>,
/// A send channel to the sync manager. This is given to the batch processor thread to report
@@ -99,7 +105,8 @@ pub enum ChainSyncingState {
impl<T: BeaconChainTypes> SyncingChain<T> {
pub fn new(
start_slot: Slot,
id: u64,
start_epoch: Epoch,
target_head_slot: Slot,
target_head_root: Hash256,
peer_id: PeerId,
@@ -111,7 +118,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
peer_pool.insert(peer_id);
SyncingChain {
start_slot,
id,
start_epoch,
target_head_slot,
target_head_root,
pending_batches: PendingBatches::new(),
@@ -130,8 +138,13 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
/// Returns the latest slot number that has been processed.
fn current_processed_slot(&self) -> Slot {
self.start_slot
.saturating_add(self.to_be_processed_id.saturating_sub(1u64) * BLOCKS_PER_BATCH)
self.start_epoch
.start_slot(T::EthSpec::slots_per_epoch())
.saturating_add(
self.to_be_processed_id.saturating_sub(1u64)
* T::EthSpec::slots_per_epoch()
* EPOCHS_PER_BATCH,
)
}
/// A batch of blocks has been received. This function gets run on all chains and should
@@ -242,11 +255,11 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
/// Sends a batch to the batch processor.
fn process_batch(&mut self, mut batch: Batch<T::EthSpec>) {
let downloaded_blocks = std::mem::replace(&mut batch.downloaded_blocks, Vec::new());
let batch_id = ProcessId::RangeBatchId(batch.id.clone());
let process_id = ProcessId::RangeBatchId(self.id.clone(), batch.id.clone());
self.current_processing_batch = Some(batch);
spawn_block_processor(
Arc::downgrade(&self.chain.clone()),
batch_id,
process_id,
downloaded_blocks,
self.sync_send.clone(),
self.log.clone(),
@@ -258,26 +271,36 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
pub fn on_batch_process_result(
&mut self,
network: &mut SyncNetworkContext<T::EthSpec>,
chain_id: ChainId,
batch_id: BatchId,
downloaded_blocks: &mut Option<Vec<SignedBeaconBlock<T::EthSpec>>>,
result: &BatchProcessResult,
) -> Option<ProcessingResult> {
if let Some(current_batch) = &self.current_processing_batch {
if current_batch.id != batch_id {
// batch process does not belong to this chain
if chain_id != self.id {
// the result does not belong to this chain
return None;
}
// Continue. This is our processing request
} else {
// not waiting on a processing result
match &self.current_processing_batch {
Some(current_batch) if current_batch.id != batch_id => {
debug!(self.log, "Unexpected batch result";
"chain_id" => self.id, "batch_id" => *batch_id, "expected_batch_id" => *current_batch.id);
return None;
}
None => {
debug!(self.log, "Chain was not expecting a batch result";
"chain_id" => self.id, "batch_id" => *batch_id);
return None;
}
_ => {
// chain_id and batch_id match, continue
}
}
// claim the result by consuming the option
let downloaded_blocks = downloaded_blocks.take().or_else(|| {
// if taken by another chain, we are no longer waiting on a result.
self.current_processing_batch = None;
crit!(self.log, "Processed batch taken by another chain");
crit!(self.log, "Processed batch taken by another chain"; "chain_id" => self.id);
None
})?;
@@ -289,6 +312,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// double check batches are processed in order TODO: Remove for prod
if batch.id != self.to_be_processed_id {
crit!(self.log, "Batch processed out of order";
"chain_id" => self.id,
"processed_batch_id" => *batch.id,
"expected_id" => *self.to_be_processed_id);
}
@@ -330,7 +354,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
}
BatchProcessResult::Partial => {
warn!(self.log, "Batch processing failed but at least one block was imported";
"id" => *batch.id, "peer" => format!("{}", batch.current_peer)
"chain_id" => self.id, "id" => *batch.id, "peer" => format!("{}", batch.current_peer)
);
// At least one block was successfully verified and imported, so we can be sure all
// previous batches are valid and we only need to download the current failed
@@ -343,7 +367,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// that it is likely all peers in this chain are are sending invalid batches
// repeatedly and are either malicious or faulty. We drop the chain and
// downvote all peers.
warn!(self.log, "Batch failed to download. Dropping chain and downvoting peers"; "id"=> *batch.id);
warn!(self.log, "Batch failed to download. Dropping chain and downvoting peers";
"chain_id" => self.id, "id"=> *batch.id);
for peer_id in self.peer_pool.drain() {
network.downvote_peer(peer_id);
}
@@ -355,7 +380,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
}
}
BatchProcessResult::Failed => {
warn!(self.log, "Batch processing failed"; "id" => *batch.id, "peer" => format!("{}", batch.current_peer));
warn!(self.log, "Batch processing failed";
"chain_id" => self.id,"id" => *batch.id, "peer" => format!("{}", batch.current_peer));
// The batch processing failed
// This could be because this batch is invalid, or a previous invalidated batch
// is invalid. We need to find out which and downvote the peer that has sent us
@@ -367,7 +393,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// that it is likely all peers in this chain are are sending invalid batches
// repeatedly and are either malicious or faulty. We drop the chain and
// downvote all peers.
warn!(self.log, "Batch failed to download. Dropping chain and downvoting peers"; "id"=> *batch.id);
warn!(self.log, "Batch failed to download. Dropping chain and downvoting peers";
"chain_id" => self.id, "id"=> *batch.id);
for peer_id in self.peer_pool.drain() {
network.downvote_peer(peer_id);
}
@@ -399,6 +426,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
let processed_batch = self.processed_batches.remove(0);
if *processed_batch.id >= *last_batch.id {
crit!(self.log, "A processed batch had a greater id than the current process id";
"chain_id" => self.id,
"processed_id" => *processed_batch.id,
"current_id" => *last_batch.id);
}
@@ -415,6 +443,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// now.
debug!(
self.log, "Re-processed batch validated. Downvoting original peer";
"chain_id" => self.id,
"batch_id" => *processed_batch.id,
"original_peer" => format!("{}",processed_batch.original_peer),
"new_peer" => format!("{}", processed_batch.current_peer)
@@ -494,6 +523,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
batch.current_peer = new_peer.clone();
debug!(self.log, "Re-requesting batch";
"chain_id" => self.id,
"start_slot" => batch.start_slot,
"end_slot" => batch.end_slot,
"id" => *batch.id,
@@ -514,7 +544,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
pub fn start_syncing(
&mut self,
network: &mut SyncNetworkContext<T::EthSpec>,
local_finalized_slot: Slot,
local_finalized_epoch: Epoch,
) {
// A local finalized slot is provided as other chains may have made
// progress whilst this chain was Stopped or paused. If so, update the `processed_batch_id` to
@@ -525,10 +555,17 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
// to start from this point and re-index all subsequent batches starting from one
// (effectively creating a new chain).
if local_finalized_slot > self.current_processed_slot() {
let local_finalized_slot = local_finalized_epoch.start_slot(T::EthSpec::slots_per_epoch());
let current_processed_slot = self.current_processed_slot();
if local_finalized_slot > current_processed_slot {
// Advance the chain to account for already downloaded blocks.
self.start_epoch = local_finalized_epoch;
debug!(self.log, "Updating chain's progress";
"prev_completed_slot" => self.current_processed_slot(),
"new_completed_slot" => local_finalized_slot.as_u64());
"chain_id" => self.id,
"prev_completed_slot" => current_processed_slot,
"new_completed_slot" => self.current_processed_slot());
// Re-index batches
*self.to_be_downloaded_id = 1;
*self.to_be_processed_id = 1;
@@ -554,7 +591,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
self.peer_pool.insert(peer_id.clone());
// do not request blocks if the chain is not syncing
if let ChainSyncingState::Stopped = self.state {
debug!(self.log, "Peer added to a non-syncing chain"; "peer_id" => format!("{}", peer_id));
debug!(self.log, "Peer added to a non-syncing chain";
"chain_id" => self.id, "peer_id" => format!("{}", peer_id));
return;
}
@@ -583,6 +621,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
) -> Option<ProcessingResult> {
if let Some(batch) = self.pending_batches.remove(request_id) {
warn!(self.log, "Batch failed. RPC Error";
"chain_id" => self.id,
"id" => *batch.id,
"retries" => batch.retries,
"peer" => format!("{:?}", peer_id));
@@ -606,10 +645,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
) -> ProcessingResult {
batch.retries += 1;
// TODO: Handle partially downloaded batches. Update this when building a new batch
// processor thread.
if batch.retries > MAX_BATCH_RETRIES {
if batch.retries > MAX_BATCH_RETRIES || self.peer_pool.is_empty() {
// chain is unrecoverable, remove it
ProcessingResult::RemoveChain
} else {
@@ -623,6 +659,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
batch.current_peer = new_peer.clone();
debug!(self.log, "Re-Requesting batch";
"chain_id" => self.id,
"start_slot" => batch.start_slot,
"end_slot" => batch.end_slot,
"id" => *batch.id,
@@ -647,6 +684,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
if let Some(peer_id) = self.get_next_peer() {
if let Some(batch) = self.get_next_batch(peer_id) {
debug!(self.log, "Requesting batch";
"chain_id" => self.id,
"start_slot" => batch.start_slot,
"end_slot" => batch.end_slot,
"id" => *batch.id,
@@ -679,6 +717,9 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
/// Returns the next required batch from the chain if it exists. If there are no more batches
/// required, `None` is returned.
fn get_next_batch(&mut self, peer_id: PeerId) -> Option<Batch<T::EthSpec>> {
let slots_per_epoch = T::EthSpec::slots_per_epoch();
let blocks_per_batch = slots_per_epoch * EPOCHS_PER_BATCH;
// only request batches up to the buffer size limit
if self
.completed_batches
@@ -689,16 +730,23 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
return None;
}
let batch_start_slot = self.start_epoch.start_slot(slots_per_epoch)
+ self.to_be_downloaded_id.saturating_sub(1) * blocks_per_batch;
// don't request batches beyond the target head slot
let batch_start_slot =
self.start_slot + self.to_be_downloaded_id.saturating_sub(1) * BLOCKS_PER_BATCH;
if batch_start_slot > self.target_head_slot {
return None;
}
// truncate the batch to the target head of the chain
// truncate the batch to the epoch containing the target head of the chain
let batch_end_slot = std::cmp::min(
batch_start_slot + BLOCKS_PER_BATCH,
self.target_head_slot.saturating_add(1u64),
// request either a batch containing the max number of blocks per batch
batch_start_slot + blocks_per_batch,
// or a batch of one epoch of blocks, which contains the `target_head_slot`
self.target_head_slot
.saturating_add(slots_per_epoch)
.epoch(slots_per_epoch)
.start_slot(slots_per_epoch),
);
let batch_id = self.to_be_downloaded_id;

View File

@@ -4,16 +4,16 @@
//! with this struct to to simplify the logic of the other layers of sync.
use super::chain::{ChainSyncingState, SyncingChain};
use crate::router::processor::PeerSyncInfo;
use crate::sync::manager::SyncMessage;
use crate::sync::network_context::SyncNetworkContext;
use crate::sync::PeerSyncInfo;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::{types::SyncState, NetworkGlobals, PeerId};
use slog::{debug, error, info};
use std::sync::Arc;
use tokio::sync::mpsc;
use types::EthSpec;
use types::{Hash256, Slot};
use types::{Epoch, Hash256, Slot};
/// The state of the long range/batch sync.
#[derive(Clone)]
@@ -110,10 +110,9 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
}
/// Updates the global sync state and logs any changes.
fn update_sync_state(&mut self, state: RangeSyncState) {
pub fn update_sync_state(&mut self) {
// if there is no range sync occurring, the state is either synced or not based on
// connected peers.
self.state = state;
if self.state == RangeSyncState::Idle {
// there is no range sync, let the state of peers determine the global node sync state
@@ -150,7 +149,8 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
if let RangeSyncState::Head { .. } = self.state {
if self.head_chains.is_empty() {
// Update the global network state to either synced or stalled.
self.update_sync_state(RangeSyncState::Idle);
self.state = RangeSyncState::Idle;
self.update_sync_state();
}
}
}
@@ -165,13 +165,14 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
.head_info()
.map(|info| info.slot)
.unwrap_or_else(|_| Slot::from(0u64));
// NOTE: This will modify the /node/syncing API to show current slot for all fields
// while we update peers to look for new potentially HEAD chains.
let temp_head_state = RangeSyncState::Head {
start_slot: current_slot,
head_slot: current_slot,
};
self.update_sync_state(temp_head_state);
self.state = temp_head_state;
}
}
@@ -206,7 +207,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
/// This removes any out-dated chains, swaps to any higher priority finalized chains and
/// updates the state of the collection.
pub fn update_finalized(&mut self, network: &mut SyncNetworkContext<T::EthSpec>) {
let local_slot = {
let local_epoch = {
let local = match PeerSyncInfo::from_chain(&self.beacon_chain) {
Some(local) => local,
None => {
@@ -218,9 +219,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
}
};
local
.finalized_epoch
.start_slot(T::EthSpec::slots_per_epoch())
local.finalized_epoch
};
// Remove any outdated finalized chains
@@ -241,20 +240,20 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
})
{
// A chain has more peers. Swap the syncing chain
debug!(self.log, "Switching finalized chains to sync"; "new_target_root" => format!("{}", chain.target_head_root), "new_end_slot" => chain.target_head_slot, "new_start_slot"=> chain.start_slot);
debug!(self.log, "Switching finalized chains to sync"; "new_target_root" => format!("{}", chain.target_head_root), "new_end_slot" => chain.target_head_slot, "new_start_epoch"=> local_epoch);
// update the state to a new finalized state
let state = RangeSyncState::Finalized {
start_slot: chain.start_slot,
start_slot: chain.start_epoch.start_slot(T::EthSpec::slots_per_epoch()),
head_slot: chain.target_head_slot,
head_root: chain.target_head_root,
};
self.update_sync_state(state);
self.state = state;
// Stop the current chain from syncing
self.finalized_chains[index].stop_syncing();
// Start the new chain
self.finalized_chains[new_index].start_syncing(network, local_slot);
self.finalized_chains[new_index].start_syncing(network, local_epoch);
}
} else if let Some(chain) = self
.finalized_chains
@@ -262,36 +261,36 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
.max_by_key(|chain| chain.peer_pool.len())
{
// There is no currently syncing finalization chain, starting the one with the most peers
debug!(self.log, "New finalized chain started syncing"; "new_target_root" => format!("{}", chain.target_head_root), "new_end_slot" => chain.target_head_slot, "new_start_slot"=> chain.start_slot);
chain.start_syncing(network, local_slot);
debug!(self.log, "New finalized chain started syncing"; "new_target_root" => format!("{}", chain.target_head_root), "new_end_slot" => chain.target_head_slot, "new_start_epoch"=> chain.start_epoch);
chain.start_syncing(network, local_epoch);
let state = RangeSyncState::Finalized {
start_slot: chain.start_slot,
start_slot: chain.start_epoch.start_slot(T::EthSpec::slots_per_epoch()),
head_slot: chain.target_head_slot,
head_root: chain.target_head_root,
};
self.update_sync_state(state);
self.state = state;
} else {
// There are no finalized chains, update the state.
if self.head_chains.is_empty() {
self.update_sync_state(RangeSyncState::Idle);
self.state = RangeSyncState::Idle;
} else {
// for the syncing API, we find the minimal start_slot and the maximum
// target_slot of all head chains to report back.
let (min_slot, max_slot) = self.head_chains.iter().fold(
(Slot::from(0u64), Slot::from(0u64)),
let (min_epoch, max_slot) = self.head_chains.iter().fold(
(Epoch::from(0u64), Slot::from(0u64)),
|(min, max), chain| {
(
std::cmp::min(min, chain.start_slot),
std::cmp::min(min, chain.start_epoch),
std::cmp::max(max, chain.target_head_slot),
)
},
);
let head_state = RangeSyncState::Head {
start_slot: min_slot,
start_slot: min_epoch.start_slot(T::EthSpec::slots_per_epoch()),
head_slot: max_slot,
};
self.update_sync_state(head_state);
self.state = head_state;
}
}
}
@@ -299,14 +298,16 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
/// Add a new finalized chain to the collection.
pub fn new_finalized_chain(
&mut self,
local_finalized_slot: Slot,
local_finalized_epoch: Epoch,
target_head: Hash256,
target_slot: Slot,
peer_id: PeerId,
sync_send: mpsc::UnboundedSender<SyncMessage<T::EthSpec>>,
) {
let chain_id = rand::random();
self.finalized_chains.push(SyncingChain::new(
local_finalized_slot,
chain_id,
local_finalized_epoch,
target_slot,
target_head,
peer_id,
@@ -321,7 +322,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
pub fn new_head_chain(
&mut self,
network: &mut SyncNetworkContext<T::EthSpec>,
remote_finalized_slot: Slot,
remote_finalized_epoch: Epoch,
target_head: Hash256,
target_slot: Slot,
peer_id: PeerId,
@@ -334,8 +335,10 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
});
self.head_chains.retain(|chain| !chain.peer_pool.is_empty());
let chain_id = rand::random();
let mut new_head_chain = SyncingChain::new(
remote_finalized_slot,
chain_id,
remote_finalized_epoch,
target_slot,
target_head,
peer_id,
@@ -344,7 +347,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
self.log.clone(),
);
// All head chains can sync simultaneously
new_head_chain.start_syncing(network, remote_finalized_slot);
new_head_chain.start_syncing(network, remote_finalized_epoch);
self.head_chains.push(new_head_chain);
}
@@ -429,7 +432,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
.fork_choice
.contains_block(&chain.target_head_root)
{
debug!(log_ref, "Purging out of finalized chain"; "start_slot" => chain.start_slot, "end_slot" => chain.target_head_slot);
debug!(log_ref, "Purging out of finalized chain"; "start_epoch" => chain.start_epoch, "end_slot" => chain.target_head_slot);
chain.status_peers(network);
false
} else {
@@ -442,7 +445,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
.fork_choice
.contains_block(&chain.target_head_root)
{
debug!(log_ref, "Purging out of date head chain"; "start_slot" => chain.start_slot, "end_slot" => chain.target_head_slot);
debug!(log_ref, "Purging out of date head chain"; "start_epoch" => chain.start_epoch, "end_slot" => chain.target_head_slot);
chain.status_peers(network);
false
} else {
@@ -478,7 +481,7 @@ impl<T: BeaconChainTypes> ChainCollection<T> {
chain
};
debug!(self.log, "Chain was removed"; "start_slot" => chain.start_slot.as_u64(), "end_slot" => chain.target_head_slot.as_u64());
debug!(self.log, "Chain was removed"; "start_epoch" => chain.start_epoch, "end_slot" => chain.target_head_slot);
// update the state
self.update_finalized(network);

View File

@@ -5,7 +5,9 @@ mod batch;
mod chain;
mod chain_collection;
mod range;
mod sync_type;
pub use batch::Batch;
pub use batch::BatchId;
pub use chain::ChainId;
pub use range::RangeSync;

View File

@@ -11,10 +11,10 @@
//! ## Finalized chain sync
//!
//! This occurs when a peer connects that claims to have a finalized head slot that is greater
//! than our own. In this case, we form a chain from our last finalized slot, to their claimed
//! than our own. In this case, we form a chain from our last finalized epoch, to their claimed
//! finalized slot. Any peer that also claims to have this last finalized slot is added to a pool
//! of peers from which batches of blocks may be downloaded. Blocks are downloaded until
//! the finalized slot of the chain is reached. Once reached, all peers within the pool are sent a
//! of peers from which batches of blocks may be downloaded. Blocks are downloaded until the
//! finalized slot of the chain is reached. Once reached, all peers within the pool are sent a
//! STATUS message to potentially start a head chain sync, or check if further finalized chains
//! need to be downloaded.
//!
@@ -26,11 +26,11 @@
//!
//! ## Head Chain Sync
//!
//! If a peer joins and there is no active finalized chains being synced, and it's head is
//! beyond our `SLOT_IMPORT_TOLERANCE` a chain is formed starting from this peers finalized slot
//! (this has been necessarily downloaded by our node, otherwise we would start a finalized chain
//! sync) to this peers head slot. Any other peers that match this head slot and head root, are
//! added to this chain's peer pool, which will be downloaded in parallel.
//! If a peer joins and there is no active finalized chains being synced, and it's head is beyond
//! our `SLOT_IMPORT_TOLERANCE` a chain is formed starting from this peers finalized epoch (this
//! has been necessarily downloaded by our node, otherwise we would start a finalized chain sync)
//! to this peers head slot. Any other peers that match this head slot and head root, are added to
//! this chain's peer pool, which will be downloaded in parallel.
//!
//! Unlike finalized chains, head chains can be synced in parallel.
//!
@@ -39,13 +39,14 @@
//! Each chain is downloaded in batches of blocks. The batched blocks are processed sequentially
//! and further batches are requested as current blocks are being processed.
use super::chain::ProcessingResult;
use super::chain::{ChainId, ProcessingResult};
use super::chain_collection::{ChainCollection, RangeSyncState};
use super::sync_type::RangeSyncType;
use super::BatchId;
use crate::router::processor::PeerSyncInfo;
use crate::sync::block_processor::BatchProcessResult;
use crate::sync::manager::SyncMessage;
use crate::sync::network_context::SyncNetworkContext;
use crate::sync::PeerSyncInfo;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::rpc::RequestId;
use eth2_libp2p::{NetworkGlobals, PeerId};
@@ -64,7 +65,7 @@ pub struct RangeSync<T: BeaconChainTypes> {
/// A collection of chains that need to be downloaded. This stores any head or finalized chains
/// that need to be downloaded.
chains: ChainCollection<T>,
/// Peers that join whilst a finalized chain is being download, sit in this set. Once the
/// Peers that join whilst a finalized chain is being downloaded, sit in this set. Once the
/// finalized chain(s) complete, these peer's get STATUS'ed to update their head slot before
/// the head chains are formed and downloaded.
awaiting_head_peers: HashSet<PeerId>,
@@ -112,7 +113,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
&mut self,
network: &mut SyncNetworkContext<T::EthSpec>,
peer_id: PeerId,
remote: PeerSyncInfo,
remote_info: PeerSyncInfo,
) {
// evaluate which chain to sync from
@@ -131,27 +132,26 @@ impl<T: BeaconChainTypes> RangeSync<T> {
};
// convenience variables
let remote_finalized_slot = remote
let remote_finalized_slot = remote_info
.finalized_epoch
.start_slot(T::EthSpec::slots_per_epoch());
let local_finalized_slot = local_info
.finalized_epoch
.start_slot(T::EthSpec::slots_per_epoch());
// remove peer from any chains
self.remove_peer(network, &peer_id);
// NOTE: A peer that has been re-status'd may now exist in multiple finalized chains.
// remove any out-of-date chains
self.chains.purge_outdated_chains(network);
if remote_finalized_slot > local_info.head_slot
&& !self
.beacon_chain
.fork_choice
.contains_block(&remote.finalized_root)
{
debug!(self.log, "Finalization sync peer joined"; "peer_id" => format!("{:?}", peer_id));
// determine which kind of sync to perform and set up the chains
match RangeSyncType::new(&self.beacon_chain, &local_info, &remote_info) {
RangeSyncType::Finalized => {
// Finalized chain search
debug!(self.log, "Finalization sync peer joined"; "peer_id" => format!("{:?}", peer_id));
// remove the peer from the awaiting_head_peers list if it exists
self.awaiting_head_peers.remove(&peer_id);
// Note: We keep current head chains. These can continue syncing whilst we complete
// this new finalized chain.
@@ -160,61 +160,80 @@ impl<T: BeaconChainTypes> RangeSync<T> {
// pool.
if let Some(chain) = self
.chains
.get_finalized_mut(remote.finalized_root, remote_finalized_slot)
.get_finalized_mut(remote_info.finalized_root, remote_finalized_slot)
{
debug!(self.log, "Finalized chain exists, adding peer"; "peer_id" => format!("{:?}", peer_id), "target_root" => format!("{}", chain.target_head_root), "end_slot" => chain.target_head_slot, "start_slot"=> chain.start_slot);
debug!(self.log, "Finalized chain exists, adding peer"; "peer_id" => format!("{:?}", peer_id), "target_root" => format!("{}", chain.target_head_root), "end_slot" => chain.target_head_slot, "start_epoch"=> chain.start_epoch);
// add the peer to the chain's peer pool
chain.add_peer(network, peer_id);
// check if the new peer's addition will favour a new syncing chain.
self.chains.update_finalized(network);
// update the global sync state if necessary
self.chains.update_sync_state();
} else {
// there is no finalized chain that matches this peer's last finalized target
// create a new finalized chain
debug!(self.log, "New finalized chain added to sync"; "peer_id" => format!("{:?}", peer_id), "start_slot" => local_finalized_slot.as_u64(), "end_slot" => remote_finalized_slot.as_u64(), "finalized_root" => format!("{}", remote.finalized_root));
debug!(self.log, "New finalized chain added to sync"; "peer_id" => format!("{:?}", peer_id), "start_epoch" => local_finalized_slot, "end_slot" => remote_finalized_slot, "finalized_root" => format!("{}", remote_info.finalized_root));
self.chains.new_finalized_chain(
local_finalized_slot,
remote.finalized_root,
local_info.finalized_epoch,
remote_info.finalized_root,
remote_finalized_slot,
peer_id,
self.sync_send.clone(),
);
self.chains.update_finalized(network);
// update the global sync state
self.chains.update_sync_state();
}
} else {
}
RangeSyncType::Head => {
// This peer requires a head chain sync
if self.chains.is_finalizing_sync() {
// If there are finalized chains to sync, finish these first, before syncing head
// chains. This allows us to re-sync all known peers
trace!(self.log, "Waiting for finalized sync to complete"; "peer_id" => format!("{:?}", peer_id));
// store the peer to re-status after all finalized chains complete
self.awaiting_head_peers.insert(peer_id);
return;
}
// if the peer existed in any other head chain, remove it.
self.remove_peer(network, &peer_id);
// The new peer has the same finalized (earlier filters should prevent a peer with an
// earlier finalized chain from reaching here).
debug!(self.log, "New peer added for recent head sync"; "peer_id" => format!("{:?}", peer_id));
// search if there is a matching head chain, then add the peer to the chain
if let Some(chain) = self.chains.get_head_mut(remote.head_root, remote.head_slot) {
debug!(self.log, "Adding peer to the existing head chain peer pool"; "head_root" => format!("{}",remote.head_root), "head_slot" => remote.head_slot, "peer_id" => format!("{:?}", peer_id));
if let Some(chain) = self
.chains
.get_head_mut(remote_info.head_root, remote_info.head_slot)
{
debug!(self.log, "Adding peer to the existing head chain peer pool"; "head_root" => format!("{}",remote_info.head_root), "head_slot" => remote_info.head_slot, "peer_id" => format!("{:?}", peer_id));
// add the peer to the head's pool
chain.add_peer(network, peer_id);
} else {
// There are no other head chains that match this peer's status, create a new one, and
let start_slot = std::cmp::min(local_info.head_slot, remote_finalized_slot);
debug!(self.log, "Creating a new syncing head chain"; "head_root" => format!("{}",remote.head_root), "start_slot" => start_slot, "head_slot" => remote.head_slot, "peer_id" => format!("{:?}", peer_id));
let start_epoch = std::cmp::min(local_info.head_slot, remote_finalized_slot)
.epoch(T::EthSpec::slots_per_epoch());
debug!(self.log, "Creating a new syncing head chain"; "head_root" => format!("{}",remote_info.head_root), "start_epoch" => start_epoch, "head_slot" => remote_info.head_slot, "peer_id" => format!("{:?}", peer_id));
self.chains.new_head_chain(
network,
start_slot,
remote.head_root,
remote.head_slot,
start_epoch,
remote_info.head_root,
remote_info.head_slot,
peer_id,
self.sync_send.clone(),
);
}
self.chains.update_finalized(network);
self.chains.update_sync_state();
}
}
}
@@ -252,6 +271,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
pub fn handle_block_process_result(
&mut self,
network: &mut SyncNetworkContext<T::EthSpec>,
chain_id: ChainId,
batch_id: BatchId,
downloaded_blocks: Vec<SignedBeaconBlock<T::EthSpec>>,
result: BatchProcessResult,
@@ -260,20 +280,28 @@ impl<T: BeaconChainTypes> RangeSync<T> {
let mut downloaded_blocks = Some(downloaded_blocks);
match self.chains.finalized_request(|chain| {
chain.on_batch_process_result(network, batch_id, &mut downloaded_blocks, &result)
chain.on_batch_process_result(
network,
chain_id,
batch_id,
&mut downloaded_blocks,
&result,
)
}) {
Some((index, ProcessingResult::RemoveChain)) => {
let chain = self.chains.remove_finalized_chain(index);
debug!(self.log, "Finalized chain removed"; "start_slot" => chain.start_slot.as_u64(), "end_slot" => chain.target_head_slot.as_u64());
// the chain is complete, re-status it's peers
chain.status_peers(network);
debug!(self.log, "Finalized chain removed"; "start_epoch" => chain.start_epoch, "end_slot" => chain.target_head_slot);
// update the state of the collection
self.chains.update_finalized(network);
// set the state to a head sync, to inform the manager that we are awaiting a
// the chain is complete, re-status it's peers
chain.status_peers(network);
// set the state to a head sync if there are no finalized chains, to inform the manager that we are awaiting a
// head chain.
self.chains.set_head_sync();
// Update the global variables
self.chains.update_sync_state();
// if there are no more finalized chains, re-status all known peers awaiting a head
// sync
@@ -291,6 +319,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
match self.chains.head_request(|chain| {
chain.on_batch_process_result(
network,
chain_id,
batch_id,
&mut downloaded_blocks,
&result,
@@ -298,12 +327,14 @@ impl<T: BeaconChainTypes> RangeSync<T> {
}) {
Some((index, ProcessingResult::RemoveChain)) => {
let chain = self.chains.remove_head_chain(index);
debug!(self.log, "Head chain completed"; "start_slot" => chain.start_slot.as_u64(), "end_slot" => chain.target_head_slot.as_u64());
debug!(self.log, "Head chain completed"; "start_epoch" => chain.start_epoch, "end_slot" => chain.target_head_slot);
// the chain is complete, re-status it's peers and remove it
chain.status_peers(network);
// update the state of the collection
self.chains.update_finalized(network);
// update the global state and log any change
self.chains.update_sync_state();
}
Some((_, ProcessingResult::KeepChain)) => {}
None => {
@@ -331,6 +362,8 @@ impl<T: BeaconChainTypes> RangeSync<T> {
// update the state of the collection
self.chains.update_finalized(network);
// update the global state and inform the user
self.chains.update_sync_state();
}
/// When a peer gets removed, both the head and finalized chains need to be searched to check which pool the peer is in. The chain may also have a batch or batches awaiting

View File

@@ -0,0 +1,40 @@
//! Contains logic about identifying which Sync to perform given PeerSyncInfo of ourselves and
//! of a remote.
use crate::sync::PeerSyncInfo;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use std::sync::Arc;
/// The type of Range sync that should be done relative to our current state.
pub enum RangeSyncType {
/// A finalized chain sync should be started with this peer.
Finalized,
/// A head chain sync should be started with this peer.
Head,
}
impl RangeSyncType {
/// Determines the type of sync given our local `PeerSyncInfo` and the remote's
/// `PeerSyncInfo`.
pub fn new<T: BeaconChainTypes>(
chain: &Arc<BeaconChain<T>>,
local_info: &PeerSyncInfo,
remote_info: &PeerSyncInfo,
) -> RangeSyncType {
// Check for finalized chain sync
//
// The condition is:
// - The remotes finalized epoch is greater than our current finalized epoch and we have
// not seen the finalized hash before.
if remote_info.finalized_epoch > local_info.finalized_epoch
&& !chain
.fork_choice
.contains_block(&remote_info.finalized_root)
{
RangeSyncType::Finalized
} else {
RangeSyncType::Head
}
}
}

View File

@@ -9,7 +9,7 @@ use network::NetworkMessage;
use ssz::Decode;
use store::{iter::AncestorIter, Store};
use types::{
Attestation, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch,
Attestation, BeaconState, ChainSpec, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch,
SignedAggregateAndProof, SignedBeaconBlock, Slot,
};
@@ -251,15 +251,22 @@ pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>(
pub fn publish_raw_attestations_to_network<T: BeaconChainTypes + 'static>(
mut chan: NetworkChannel<T::EthSpec>,
attestations: Vec<Attestation<T::EthSpec>>,
spec: &ChainSpec,
) -> Result<(), ApiError> {
let messages = attestations
.into_iter()
.map(|attestation| {
// create the gossip message to send to the network
let subnet_id = attestation.subnet_id();
PubsubMessage::Attestation(Box::new((subnet_id, attestation)))
let subnet_id = attestation
.subnet_id(spec)
.map_err(|e| ApiError::ServerError(format!("Unable to get subnet id: {:?}", e)))?;
Ok(PubsubMessage::Attestation(Box::new((
subnet_id,
attestation,
))))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, ApiError>>()?;
// Publish the attestations to the p2p network via gossipsub.
if let Err(e) = chan.try_send(NetworkMessage::Publish { messages }) {

View File

@@ -2,12 +2,57 @@
use crate::response_builder::ResponseBuilder;
use crate::ApiResult;
use eth2_libp2p::NetworkGlobals;
use eth2_libp2p::{NetworkGlobals, PeerInfo};
use hyper::{Body, Request};
use serde::Serialize;
use std::sync::Arc;
use types::EthSpec;
/// The syncing state of the beacon node.
pub fn syncing<T: EthSpec>(req: Request<Body>, network: Arc<NetworkGlobals<T>>) -> ApiResult {
ResponseBuilder::new(&req)?.body_no_ssz(&network.sync_state())
pub fn syncing<T: EthSpec>(
req: Request<Body>,
network_globals: Arc<NetworkGlobals<T>>,
) -> ApiResult {
ResponseBuilder::new(&req)?.body_no_ssz(&network_globals.sync_state())
}
/// Returns all known peers and corresponding information
pub fn peers<T: EthSpec>(req: Request<Body>, network_globals: Arc<NetworkGlobals<T>>) -> ApiResult {
let peers: Vec<Peer<T>> = network_globals
.peers
.read()
.peers()
.map(|(peer_id, peer_info)| Peer {
peer_id: peer_id.to_string(),
peer_info: peer_info.clone(),
})
.collect();
ResponseBuilder::new(&req)?.body_no_ssz(&peers)
}
/// Returns all known connected peers and their corresponding information
pub fn connected_peers<T: EthSpec>(
req: Request<Body>,
network_globals: Arc<NetworkGlobals<T>>,
) -> ApiResult {
let peers: Vec<Peer<T>> = network_globals
.peers
.read()
.connected_peers()
.map(|(peer_id, peer_info)| Peer {
peer_id: peer_id.to_string(),
peer_info: peer_info.clone(),
})
.collect();
ResponseBuilder::new(&req)?.body_no_ssz(&peers)
}
/// Information returned by `peers` and `connected_peers`.
#[derive(Clone, Debug, Serialize)]
#[serde(bound = "T: EthSpec")]
struct Peer<T: EthSpec> {
/// The Peer's ID
peer_id: String,
/// The PeerInfo associated with the peer.
peer_info: PeerInfo<T>,
}

View File

@@ -65,7 +65,7 @@ pub fn get_peer_list<T: BeaconChainTypes>(
let connected_peers: Vec<String> = network
.peers
.read()
.connected_peers()
.connected_peer_ids()
.map(PeerId::to_string)
.collect();
ResponseBuilder::new(&req)?.body_no_ssz(&connected_peers)

View File

@@ -41,10 +41,3 @@ pub fn syncing<T: EthSpec>(
sync_status,
})
}
pub fn lighthouse_syncing<T: EthSpec>(
req: Request<Body>,
network: Arc<NetworkGlobals<T>>,
) -> ApiResult {
ResponseBuilder::new(&req)?.body_no_ssz(&network.sync_state())
}

View File

@@ -203,7 +203,6 @@ pub fn route<T: BeaconChainTypes>(
(&Method::GET, "/advanced/operation_pool") => {
into_boxfut(advanced::get_operation_pool::<T>(req, beacon_chain))
}
(&Method::GET, "/metrics") => into_boxfut(metrics::get_prometheus::<T>(
req,
beacon_chain,
@@ -215,7 +214,12 @@ pub fn route<T: BeaconChainTypes>(
(&Method::GET, "/lighthouse/syncing") => {
into_boxfut(lighthouse::syncing::<T::EthSpec>(req, network_globals))
}
(&Method::GET, "/lighthouse/peers") => {
into_boxfut(lighthouse::peers::<T::EthSpec>(req, network_globals))
}
(&Method::GET, "/lighthouse/connected_peers") => into_boxfut(
lighthouse::connected_peers::<T::EthSpec>(req, network_globals),
),
_ => Box::new(futures::future::err(ApiError::NotFound(
"Request path and/or method not found.".to_owned(),
))),

View File

@@ -507,10 +507,11 @@ pub fn publish_attestations<T: BeaconChainTypes>(
}
}
})?;
Ok(attestations)
Ok((attestations, beacon_chain))
})
.and_then(|attestations| {
publish_raw_attestations_to_network::<T>(network_chan, attestations)
.and_then(|(attestations, beacon_chain)| {
publish_raw_attestations_to_network::<T>(network_chan, attestations, &beacon_chain.spec)
})
.and_then(|_| response_builder?.body_no_ssz(&())),
)

View File

@@ -109,7 +109,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
)
.arg(
Arg::with_name("disable-enr-auto-update")
.short("s")
.short("x")
.long("disable-enr-auto-update")
.help("Discovery automatically updates the nodes local ENR with an external IP address and port as seen by other peers on the network. \
This disables this feature, fixing the ENR's IP/PORT to those specified on boot.")
@@ -235,4 +235,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("Specifies how many states the database should cache in memory [default: 5]")
.takes_value(true)
)
/*
* Purge.
*/
.arg(
Arg::with_name("purge-db")
.long("purge-db")
.help("If present, the chain database will be deleted. Use with caution.")
)
}

View File

@@ -1,3 +1,4 @@
use beacon_chain::builder::PUBKEY_CACHE_FILENAME;
use clap::ArgMatches;
use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis};
use eth2_libp2p::{Enr, Multiaddr};
@@ -33,6 +34,32 @@ pub fn get_config<E: EthSpec>(
client_config.data_dir = get_data_dir(cli_args);
// If necessary, remove any existing database and configuration
if client_config.data_dir.exists() && cli_args.is_present("purge-db") {
// Remove the chain_db.
fs::remove_dir_all(
client_config
.get_db_path()
.ok_or("Failed to get db_path".to_string())?,
)
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
// Remove the freezer db.
fs::remove_dir_all(
client_config
.get_freezer_db_path()
.ok_or("Failed to get freezer db path".to_string())?,
)
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
// Remove the pubkey cache file if it exists
let pubkey_cache_file = client_config.data_dir.join(PUBKEY_CACHE_FILENAME);
if pubkey_cache_file.exists() {
fs::remove_file(&pubkey_cache_file)
.map_err(|e| format!("Failed to remove {:?}: {:?}", pubkey_cache_file, e))?;
}
}
// Create `datadir` and any non-existing parent directories.
fs::create_dir_all(&client_config.data_dir)
.map_err(|e| format!("Failed to create data dir: {}", e))?;
@@ -85,7 +112,6 @@ pub fn get_config<E: EthSpec>(
.map_err(|_| format!("Invalid port: {}", port_str))?;
client_config.network.libp2p_port = port;
client_config.network.discovery_port = port;
dbg!(&client_config.network.discovery_port);
}
if let Some(port_str) = cli_args.value_of("discovery-port") {
@@ -293,24 +319,7 @@ pub fn get_config<E: EthSpec>(
}
/*
* Load the eth2 testnet dir to obtain some addition config values.
*/
let eth2_testnet_config: Eth2TestnetConfig<E> =
get_eth2_testnet_config(&client_config.testnet_dir)?;
client_config.eth1.deposit_contract_address =
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
client_config.eth1.deposit_contract_deploy_block =
eth2_testnet_config.deposit_contract_deploy_block;
client_config.eth1.lowest_cached_block_number =
client_config.eth1.deposit_contract_deploy_block;
if let Some(mut boot_nodes) = eth2_testnet_config.boot_enr {
client_config.network.boot_nodes.append(&mut boot_nodes)
}
/*
* Load the eth2 testnet dir to obtain some addition config values.
* Load the eth2 testnet dir to obtain some additional config values.
*/
let eth2_testnet_config: Eth2TestnetConfig<E> =
get_eth2_testnet_config(&client_config.testnet_dir)?;

View File

@@ -10,6 +10,7 @@ pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
pub use config::{get_data_dir, get_eth2_testnet_config, get_testnet_dir};
pub use eth2_config::Eth2Config;
use beacon_chain::migrate::{BackgroundMigrator, DiskStore};
use beacon_chain::{
builder::Witness, eth1_chain::CachingEth1Backend, events::WebSocketSender,
slot_clock::SystemTimeSlotClock,
@@ -20,7 +21,6 @@ use environment::RuntimeContext;
use futures::{Future, IntoFuture};
use slog::{info, warn};
use std::ops::{Deref, DerefMut};
use store::{migrate::BackgroundMigrator, DiskStore};
use types::EthSpec;
/// A type-alias to the tighten the definition of a production-intended `Client`.

View File

@@ -204,7 +204,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
}
/// Advance the split point of the store, moving new finalized states to the freezer.
fn freeze_to_state(
fn process_finalization(
store: Arc<Self>,
frozen_head_root: Hash256,
frozen_head: &BeaconState<E>,

View File

@@ -1,4 +1,4 @@
use crate::Store;
use crate::{Error, Store};
use std::borrow::Cow;
use std::marker::PhantomData;
use std::sync::Arc;
@@ -43,12 +43,95 @@ impl<'a, U: Store<E>, E: EthSpec> AncestorIter<U, E, StateRootsIterator<'a, E, U
}
pub struct StateRootsIterator<'a, T: EthSpec, U> {
inner: RootsIterator<'a, T, U>,
}
impl<'a, T: EthSpec, U> Clone for StateRootsIterator<'a, T, U> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<'a, T: EthSpec, U: Store<T>> StateRootsIterator<'a, T, U> {
pub fn new(store: Arc<U>, beacon_state: &'a BeaconState<T>) -> Self {
Self {
inner: RootsIterator::new(store, beacon_state),
}
}
pub fn owned(store: Arc<U>, beacon_state: BeaconState<T>) -> Self {
Self {
inner: RootsIterator::owned(store, beacon_state),
}
}
}
impl<'a, T: EthSpec, U: Store<T>> Iterator for StateRootsIterator<'a, T, U> {
type Item = (Hash256, Slot);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|(_, state_root, slot)| (state_root, slot))
}
}
/// Iterates backwards through block roots. If any specified slot is unable to be retrieved, the
/// iterator returns `None` indefinitely.
///
/// Uses the `block_roots` field of `BeaconState` as the source of block roots and will
/// perform a lookup on the `Store` for a prior `BeaconState` if `block_roots` has been
/// exhausted.
///
/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`.
pub struct BlockRootsIterator<'a, T: EthSpec, U> {
inner: RootsIterator<'a, T, U>,
}
impl<'a, T: EthSpec, U> Clone for BlockRootsIterator<'a, T, U> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<'a, T: EthSpec, U: Store<T>> BlockRootsIterator<'a, T, U> {
/// Create a new iterator over all block roots in the given `beacon_state` and prior states.
pub fn new(store: Arc<U>, beacon_state: &'a BeaconState<T>) -> Self {
Self {
inner: RootsIterator::new(store, beacon_state),
}
}
/// Create a new iterator over all block roots in the given `beacon_state` and prior states.
pub fn owned(store: Arc<U>, beacon_state: BeaconState<T>) -> Self {
Self {
inner: RootsIterator::owned(store, beacon_state),
}
}
}
impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockRootsIterator<'a, T, U> {
type Item = (Hash256, Slot);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|(block_root, _, slot)| (block_root, slot))
}
}
/// Iterator over state and block roots that backtracks using the vectors from a `BeaconState`.
pub struct RootsIterator<'a, T: EthSpec, U> {
store: Arc<U>,
beacon_state: Cow<'a, BeaconState<T>>,
slot: Slot,
}
impl<'a, T: EthSpec, U> Clone for StateRootsIterator<'a, T, U> {
impl<'a, T: EthSpec, U> Clone for RootsIterator<'a, T, U> {
fn clone(&self) -> Self {
Self {
store: self.store.clone(),
@@ -58,7 +141,7 @@ impl<'a, T: EthSpec, U> Clone for StateRootsIterator<'a, T, U> {
}
}
impl<'a, T: EthSpec, U: Store<T>> StateRootsIterator<'a, T, U> {
impl<'a, T: EthSpec, U: Store<T>> RootsIterator<'a, T, U> {
pub fn new(store: Arc<U>, beacon_state: &'a BeaconState<T>) -> Self {
Self {
store,
@@ -74,10 +157,21 @@ impl<'a, T: EthSpec, U: Store<T>> StateRootsIterator<'a, T, U> {
beacon_state: Cow::Owned(beacon_state),
}
}
pub fn from_block(store: Arc<U>, block_hash: Hash256) -> Result<Self, Error> {
let block = store
.get_block(&block_hash)?
.ok_or_else(|| BeaconStateError::MissingBeaconBlock(block_hash.into()))?;
let state = store
.get_state(&block.state_root(), Some(block.slot()))?
.ok_or_else(|| BeaconStateError::MissingBeaconState(block.state_root().into()))?;
Ok(Self::owned(store, state))
}
}
impl<'a, T: EthSpec, U: Store<T>> Iterator for StateRootsIterator<'a, T, U> {
type Item = (Hash256, Slot);
impl<'a, T: EthSpec, U: Store<T>> Iterator for RootsIterator<'a, T, U> {
/// (block_root, state_root, slot)
type Item = (Hash256, Hash256, Slot);
fn next(&mut self) -> Option<Self::Item> {
if self.slot == 0 || self.slot > self.beacon_state.slot {
@@ -86,18 +180,22 @@ impl<'a, T: EthSpec, U: Store<T>> Iterator for StateRootsIterator<'a, T, U> {
self.slot -= 1;
match self.beacon_state.get_state_root(self.slot) {
Ok(root) => Some((*root, self.slot)),
Err(BeaconStateError::SlotOutOfBounds) => {
match (
self.beacon_state.get_block_root(self.slot),
self.beacon_state.get_state_root(self.slot),
) {
(Ok(block_root), Ok(state_root)) => Some((*block_root, *state_root, self.slot)),
(Err(BeaconStateError::SlotOutOfBounds), Err(BeaconStateError::SlotOutOfBounds)) => {
// Read a `BeaconState` from the store that has access to prior historical roots.
let beacon_state =
next_historical_root_backtrack_state(&*self.store, &self.beacon_state)?;
self.beacon_state = Cow::Owned(beacon_state);
let root = self.beacon_state.get_state_root(self.slot).ok()?;
let block_root = *self.beacon_state.get_block_root(self.slot).ok()?;
let state_root = *self.beacon_state.get_state_root(self.slot).ok()?;
Some((*root, self.slot))
Some((block_root, state_root, self.slot))
}
_ => None,
}
@@ -165,79 +263,7 @@ impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockIterator<'a, T, U> {
fn next(&mut self) -> Option<Self::Item> {
let (root, _slot) = self.roots.next()?;
self.roots.store.get_block(&root).ok()?
}
}
/// Iterates backwards through block roots. If any specified slot is unable to be retrieved, the
/// iterator returns `None` indefinitely.
///
/// Uses the `block_roots` field of `BeaconState` to as the source of block roots and will
/// perform a lookup on the `Store` for a prior `BeaconState` if `block_roots` has been
/// exhausted.
///
/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`.
pub struct BlockRootsIterator<'a, T: EthSpec, U> {
store: Arc<U>,
beacon_state: Cow<'a, BeaconState<T>>,
slot: Slot,
}
impl<'a, T: EthSpec, U> Clone for BlockRootsIterator<'a, T, U> {
fn clone(&self) -> Self {
Self {
store: self.store.clone(),
beacon_state: self.beacon_state.clone(),
slot: self.slot,
}
}
}
impl<'a, T: EthSpec, U: Store<T>> BlockRootsIterator<'a, T, U> {
/// Create a new iterator over all block roots in the given `beacon_state` and prior states.
pub fn new(store: Arc<U>, beacon_state: &'a BeaconState<T>) -> Self {
Self {
store,
slot: beacon_state.slot,
beacon_state: Cow::Borrowed(beacon_state),
}
}
/// Create a new iterator over all block roots in the given `beacon_state` and prior states.
pub fn owned(store: Arc<U>, beacon_state: BeaconState<T>) -> Self {
Self {
store,
slot: beacon_state.slot,
beacon_state: Cow::Owned(beacon_state),
}
}
}
impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockRootsIterator<'a, T, U> {
type Item = (Hash256, Slot);
fn next(&mut self) -> Option<Self::Item> {
if self.slot == 0 || self.slot > self.beacon_state.slot {
return None;
}
self.slot -= 1;
match self.beacon_state.get_block_root(self.slot) {
Ok(root) => Some((*root, self.slot)),
Err(BeaconStateError::SlotOutOfBounds) => {
// Read a `BeaconState` from the store that has access to prior historical roots.
let beacon_state =
next_historical_root_backtrack_state(&*self.store, &self.beacon_state)?;
self.beacon_state = Cow::Owned(beacon_state);
let root = self.beacon_state.get_block_root(self.slot).ok()?;
Some((*root, self.slot))
}
_ => None,
}
self.roots.inner.store.get_block(&root).ok()?
}
}

View File

@@ -15,7 +15,7 @@ pub mod chunked_vector;
pub mod config;
mod errors;
mod forwards_iter;
mod hot_cold_store;
pub mod hot_cold_store;
mod impls;
mod leveldb_store;
mod memory_store;
@@ -24,7 +24,6 @@ mod partial_beacon_state;
mod state_batch;
pub mod iter;
pub mod migrate;
use std::sync::Arc;
@@ -32,7 +31,6 @@ pub use self::config::StoreConfig;
pub use self::hot_cold_store::{HotColdDB as DiskStore, HotStateSummary};
pub use self::leveldb_store::LevelDB as SimpleDiskStore;
pub use self::memory_store::MemoryStore;
pub use self::migrate::Migrate;
pub use self::partial_beacon_state::PartialBeaconState;
pub use errors::Error;
pub use impls::beacon_state::StorageContainer as BeaconStateStorageContainer;
@@ -132,7 +130,7 @@ pub trait Store<E: EthSpec>: Sync + Send + Sized + 'static {
}
/// (Optionally) Move all data before the frozen slot to the freezer database.
fn freeze_to_state(
fn process_finalization(
_store: Arc<Self>,
_frozen_head_root: Hash256,
_frozen_head: &BeaconState<E>,

View File

@@ -1,153 +0,0 @@
use crate::{
hot_cold_store::HotColdDBError, DiskStore, Error, MemoryStore, SimpleDiskStore, Store,
};
use parking_lot::Mutex;
use slog::{debug, warn};
use std::mem;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use types::{BeaconState, EthSpec, Hash256, Slot};
/// Trait for migration processes that update the database upon finalization.
pub trait Migrate<S, E: EthSpec>: Send + Sync + 'static {
fn new(db: Arc<S>) -> Self;
fn freeze_to_state(
&self,
_state_root: Hash256,
_state: BeaconState<E>,
_max_finality_distance: u64,
) {
}
}
/// Migrator that does nothing, for stores that don't need migration.
pub struct NullMigrator;
impl<E: EthSpec> Migrate<SimpleDiskStore<E>, E> for NullMigrator {
fn new(_: Arc<SimpleDiskStore<E>>) -> Self {
NullMigrator
}
}
impl<E: EthSpec> Migrate<MemoryStore<E>, E> for NullMigrator {
fn new(_: Arc<MemoryStore<E>>) -> Self {
NullMigrator
}
}
/// Migrator that immediately calls the store's migration function, blocking the current execution.
///
/// Mostly useful for tests.
pub struct BlockingMigrator<S>(Arc<S>);
impl<E: EthSpec, S: Store<E>> Migrate<S, E> for BlockingMigrator<S> {
fn new(db: Arc<S>) -> Self {
BlockingMigrator(db)
}
fn freeze_to_state(
&self,
state_root: Hash256,
state: BeaconState<E>,
_max_finality_distance: u64,
) {
if let Err(e) = S::freeze_to_state(self.0.clone(), state_root, &state) {
// This migrator is only used for testing, so we just log to stderr without a logger.
eprintln!("Migration error: {:?}", e);
}
}
}
type MpscSender<E> = mpsc::Sender<(Hash256, BeaconState<E>)>;
/// Migrator that runs a background thread to migrate state from the hot to the cold database.
pub struct BackgroundMigrator<E: EthSpec> {
db: Arc<DiskStore<E>>,
tx_thread: Mutex<(MpscSender<E>, thread::JoinHandle<()>)>,
}
impl<E: EthSpec> Migrate<DiskStore<E>, E> for BackgroundMigrator<E> {
fn new(db: Arc<DiskStore<E>>) -> Self {
let tx_thread = Mutex::new(Self::spawn_thread(db.clone()));
Self { db, tx_thread }
}
/// Perform the freezing operation on the database,
fn freeze_to_state(
&self,
finalized_state_root: Hash256,
finalized_state: BeaconState<E>,
max_finality_distance: u64,
) {
if !self.needs_migration(finalized_state.slot, max_finality_distance) {
return;
}
let (ref mut tx, ref mut thread) = *self.tx_thread.lock();
if let Err(tx_err) = tx.send((finalized_state_root, finalized_state)) {
let (new_tx, new_thread) = Self::spawn_thread(self.db.clone());
drop(mem::replace(tx, new_tx));
let old_thread = mem::replace(thread, new_thread);
// Join the old thread, which will probably have panicked, or may have
// halted normally just now as a result of us dropping the old `mpsc::Sender`.
if let Err(thread_err) = old_thread.join() {
warn!(
self.db.log,
"Migration thread died, so it was restarted";
"reason" => format!("{:?}", thread_err)
);
}
// Retry at most once, we could recurse but that would risk overflowing the stack.
let _ = tx.send(tx_err.0);
}
}
}
impl<E: EthSpec> BackgroundMigrator<E> {
/// Return true if a migration needs to be performed, given a new `finalized_slot`.
fn needs_migration(&self, finalized_slot: Slot, max_finality_distance: u64) -> bool {
let finality_distance = finalized_slot - self.db.get_split_slot();
finality_distance > max_finality_distance
}
/// Spawn a new child thread to run the migration process.
///
/// Return a channel handle for sending new finalized states to the thread.
fn spawn_thread(
db: Arc<DiskStore<E>>,
) -> (
mpsc::Sender<(Hash256, BeaconState<E>)>,
thread::JoinHandle<()>,
) {
let (tx, rx) = mpsc::channel();
let thread = thread::spawn(move || {
while let Ok((state_root, state)) = rx.recv() {
match DiskStore::freeze_to_state(db.clone(), state_root, &state) {
Ok(()) => {}
Err(Error::HotColdDBError(HotColdDBError::FreezeSlotUnaligned(slot))) => {
debug!(
db.log,
"Database migration postponed, unaligned finalized block";
"slot" => slot.as_u64()
);
}
Err(e) => {
warn!(
db.log,
"Database migration failed";
"error" => format!("{:?}", e)
);
}
}
}
});
(tx, thread)
}
}

View File

@@ -7,6 +7,8 @@ The `/lighthouse` endpoints provide lighthouse-specific information about the be
HTTP Path | Description |
| --- | -- |
[`/lighthouse/syncing`](#lighthousesyncing) | Get the node's syncing status
[`/lighthouse/peers`](#lighthousepeers) | Get the peers info known by the beacon node
[`/lighthouse/connected_peers`](#lighthousepeers) | Get the connected_peers known by the beacon node
## `/lighthouse/syncing`
@@ -52,3 +54,129 @@ If the node is synced
"Synced"
}
```
## `/lighthouse/peers`
Get all known peers info from the beacon node.
### HTTP Specification
| Property | Specification |
| --- |--- |
Path | `/lighthouse/peers`
Method | GET
JSON Encoding | Object
Query Parameters | None
Typical Responses | 200
### Example Response
```json
[
{
"peer_id" : "16Uiu2HAmTEinipUS3haxqucrn7d7SmCKx5XzAVbAZCiNW54ncynG",
"peer_info" : {
"_status" : "Healthy",
"client" : {
"agent_string" : "github.com/libp2p/go-libp2p",
"kind" : "Prysm",
"os_version" : "unknown",
"protocol_version" : "ipfs/0.1.0",
"version" : "unknown"
},
"connection_status" : {
"Disconnected" : {
"since" : 3
}
},
"listening_addresses" : [
"/ip4/10.3.58.241/tcp/9001",
"/ip4/35.172.14.146/tcp/9001",
"/ip4/35.172.14.146/tcp/9001"
],
"meta_data" : {
"attnets" : "0x0000000000000000",
"seq_number" : 0
},
"reputation" : 20,
"sync_status" : {
"Synced" : {
"status_head_slot" : 18146
}
}
}
},
{
"peer_id" : "16Uiu2HAm8XZfPv3YjktCjitSRtfS7UfHfEvpiUyHrdiX6uAD55xZ",
"peer_info" : {
"_status" : "Healthy",
"client" : {
"agent_string" : null,
"kind" : "Unknown",
"os_version" : "unknown",
"protocol_version" : "unknown",
"version" : "unknown"
},
"connection_status" : {
"Disconnected" : {
"since" : 5
}
},
"listening_addresses" : [],
"meta_data" : {
"attnets" : "0x0900000000000000",
"seq_number" : 0
},
"reputation" : 20,
"sync_status" : "Unknown"
}
},
]
```
## `/lighthouse/connected_peers`
Get all known peers info from the beacon node.
### HTTP Specification
| Property | Specification |
| --- |--- |
Path | `/lighthouse/connected_peers`
Method | GET
JSON Encoding | Object
Query Parameters | None
Typical Responses | 200
### Example Response
```json
[
{
"peer_id" : "16Uiu2HAm8XZfPv3YjktCjitSRtfS7UfHfEvpiUyHrdiX6uAD55xZ",
"peer_info" : {
"_status" : "Healthy",
"client" : {
"agent_string" : null,
"kind" : "Unknown",
"os_version" : "unknown",
"protocol_version" : "unknown",
"version" : "unknown"
},
"connection_status" : {
"Connected" : {
"in" : 5,
"out" : 2
}
},
"listening_addresses" : [],
"meta_data" : {
"attnets" : "0x0900000000000000",
"seq_number" : 0
},
"reputation" : 20,
"sync_status" : "Unknown"
}
},
]
```

View File

@@ -18,6 +18,7 @@ TL;DR isn't adequate.
## TL;DR
```bash
make install-lcli
lcli new-testnet
lcli interop-genesis 128
lighthouse bn --testnet-dir ~/.lighthouse/testnet --dummy-eth1 --http --enr-match
@@ -40,7 +41,7 @@ used for starting testnets and debugging.
Install `lcli` from the root directory of this repository with:
```bash
cargo install --path lcli --force
make install-lcli
```
### 1.2 Create a testnet directory

View File

@@ -98,10 +98,14 @@ impl<T: EthSpec> OperationPool<T> {
/// Get a list of attestations for inclusion in a block.
///
/// NOTE: Assumes that all attestations in the operation_pool are valid.
/// The `validity_filter` is a closure that provides extra filtering of the attestations
/// before an approximately optimal bundle is constructed. We use it to provide access
/// to the fork choice data from the `BeaconChain` struct that doesn't logically belong
/// in the operation pool.
pub fn get_attestations(
&self,
state: &BeaconState<T>,
validity_filter: impl FnMut(&&Attestation<T>) -> bool,
spec: &ChainSpec,
) -> Result<Vec<Attestation<T>>, OpPoolError> {
// Attestations for the current fork, which may be from the current or previous epoch.
@@ -143,6 +147,7 @@ impl<T: EthSpec> OperationPool<T> {
)
.is_ok()
})
.filter(validity_filter)
.flat_map(|att| AttMaxCover::new(att, state, total_active_balance, spec));
Ok(maximum_cover(
@@ -584,7 +589,7 @@ mod release_tests {
state.slot -= 1;
assert_eq!(
op_pool
.get_attestations(state, spec)
.get_attestations(state, |_| true, spec)
.expect("should have attestations")
.len(),
0
@@ -594,7 +599,7 @@ mod release_tests {
state.slot += spec.min_attestation_inclusion_delay;
let block_attestations = op_pool
.get_attestations(state, spec)
.get_attestations(state, |_| true, spec)
.expect("Should have block attestations");
assert_eq!(block_attestations.len(), committees.len());
@@ -764,7 +769,7 @@ mod release_tests {
state.slot += spec.min_attestation_inclusion_delay;
let best_attestations = op_pool
.get_attestations(state, spec)
.get_attestations(state, |_| true, spec)
.expect("should have best attestations");
assert_eq!(best_attestations.len(), max_attestations);
@@ -839,7 +844,7 @@ mod release_tests {
state.slot += spec.min_attestation_inclusion_delay;
let best_attestations = op_pool
.get_attestations(state, spec)
.get_attestations(state, |_| true, spec)
.expect("should have valid best attestations");
assert_eq!(best_attestations.len(), max_attestations);

View File

@@ -407,4 +407,41 @@ impl ProtoArray {
&& (node.finalized_epoch == self.finalized_epoch
|| self.finalized_epoch == Epoch::new(0))
}
/// Return a reverse iterator over the nodes which comprise the chain ending at `block_root`.
pub fn iter_nodes<'a>(&'a self, block_root: &Hash256) -> Iter<'a> {
let next_node_index = self.indices.get(block_root).copied();
Iter {
next_node_index,
proto_array: self,
}
}
/// Return a reverse iterator over the block roots of the chain ending at `block_root`.
///
/// Note that unlike many other iterators, this one WILL NOT yield anything at skipped slots.
pub fn iter_block_roots<'a>(
&'a self,
block_root: &Hash256,
) -> impl Iterator<Item = (Hash256, Slot)> + 'a {
self.iter_nodes(block_root)
.map(|node| (node.root, node.slot))
}
}
/// Reverse iterator over one path through a `ProtoArray`.
pub struct Iter<'a> {
next_node_index: Option<usize>,
proto_array: &'a ProtoArray,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a ProtoNode;
fn next(&mut self) -> Option<Self::Item> {
let next_node_index = self.next_node_index?;
let node = self.proto_array.nodes.get(next_node_index)?;
self.next_node_index = node.parent;
Some(node)
}
}

View File

@@ -27,6 +27,7 @@ eth2_ssz = "0.1.2"
eth2_ssz_types = { path = "../utils/ssz_types" }
merkle_proof = { path = "../utils/merkle_proof" }
log = "0.4.8"
safe_arith = { path = "../utils/safe_arith" }
tree_hash = "0.1.0"
tree_hash_derive = "0.2"
types = { path = "../types" }

View File

@@ -1,6 +1,7 @@
use eth2_hashing::hash;
use int_to_bytes::int_to_bytes32;
use merkle_proof::{MerkleTree, MerkleTreeError};
use safe_arith::SafeArith;
use types::Hash256;
/// Emulates the eth1 deposit contract merkle tree.
@@ -46,7 +47,7 @@ impl DepositDataTree {
/// Add a deposit to the merkle tree.
pub fn push_leaf(&mut self, leaf: Hash256) -> Result<(), MerkleTreeError> {
self.tree.push_leaf(leaf, self.depth)?;
self.mix_in_length += 1;
self.mix_in_length.increment()?;
Ok(())
}
}

View File

@@ -1,4 +1,5 @@
use integer_sqrt::IntegerSquareRoot;
use safe_arith::SafeArith;
use types::*;
/// Returns the base reward for some validator.
@@ -14,10 +15,10 @@ pub fn get_base_reward<T: EthSpec>(
if total_active_balance == 0 {
Ok(0)
} else {
Ok(
state.get_effective_balance(index, spec)? * spec.base_reward_factor
/ total_active_balance.integer_sqrt()
/ spec.base_rewards_per_epoch,
)
Ok(state
.get_effective_balance(index, spec)?
.safe_mul(spec.base_reward_factor)?
.safe_div(total_active_balance.integer_sqrt())?
.safe_div(spec.base_rewards_per_epoch)?)
}
}

View File

@@ -1,4 +1,5 @@
use crate::common::initiate_validator_exit;
use safe_arith::SafeArith;
use std::cmp;
use types::{BeaconStateError as Error, *};
@@ -27,18 +28,21 @@ pub fn slash_validator<T: EthSpec>(
let validator_effective_balance = state.get_effective_balance(slashed_index, spec)?;
state.set_slashings(
epoch,
state.get_slashings(epoch)? + validator_effective_balance,
state
.get_slashings(epoch)?
.safe_add(validator_effective_balance)?,
)?;
safe_sub_assign!(
state.balances[slashed_index],
validator_effective_balance / spec.min_slashing_penalty_quotient
validator_effective_balance.safe_div(spec.min_slashing_penalty_quotient)?
);
// Apply proposer and whistleblower rewards
let proposer_index = state.get_beacon_proposer_index(state.slot, spec)?;
let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index);
let whistleblower_reward = validator_effective_balance / spec.whistleblower_reward_quotient;
let proposer_reward = whistleblower_reward / spec.proposer_reward_quotient;
let whistleblower_reward =
validator_effective_balance.safe_div(spec.whistleblower_reward_quotient)?;
let proposer_reward = whistleblower_reward.safe_div(spec.proposer_reward_quotient)?;
safe_add_assign!(state.balances[proposer_index], proposer_reward);
safe_add_assign!(

View File

@@ -1,5 +1,6 @@
use super::per_block_processing::{errors::BlockProcessingError, process_deposit};
use crate::common::DepositDataTree;
use safe_arith::SafeArith;
use tree_hash::TreeHash;
use types::DEPOSIT_TREE_DEPTH;
use types::*;
@@ -14,8 +15,9 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
deposits: Vec<Deposit>,
spec: &ChainSpec,
) -> Result<BeaconState<T>, BlockProcessingError> {
let genesis_time =
eth1_timestamp - eth1_timestamp % spec.min_genesis_delay + 2 * spec.min_genesis_delay;
let genesis_time = eth1_timestamp
.safe_sub(eth1_timestamp.safe_rem(spec.min_genesis_delay)?)?
.safe_add(2.safe_mul(spec.min_genesis_delay)?)?;
let eth1_data = Eth1Data {
// Temporary deposit root
deposit_root: Hash256::zero(),
@@ -37,7 +39,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
process_deposit(&mut state, &deposit, spec, true)?;
}
process_activations(&mut state, spec);
process_activations(&mut state, spec)?;
// Now that we have our validators, initialize the caches (including the committees)
state.build_all_caches(spec)?;
@@ -60,11 +62,14 @@ pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSp
/// Activate genesis validators, if their balance is acceptable.
///
/// Spec v0.11.1
pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) {
pub fn process_activations<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), Error> {
for (index, validator) in state.validators.iter_mut().enumerate() {
let balance = state.balances[index];
validator.effective_balance = std::cmp::min(
balance - balance % spec.effective_balance_increment,
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
spec.max_effective_balance,
);
if validator.effective_balance == spec.max_effective_balance {
@@ -72,4 +77,5 @@ pub fn process_activations<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainS
validator.activation_epoch = T::genesis_epoch();
}
}
Ok(())
}

View File

@@ -1,3 +1,5 @@
#![deny(clippy::integer_arithmetic)]
#[macro_use]
mod macros;

View File

@@ -1,6 +1,7 @@
use crate::common::{initiate_validator_exit, slash_validator};
use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid, IntoWithIndex};
use rayon::prelude::*;
use safe_arith::{ArithError, SafeArith};
use signature_sets::{block_proposal_signature_set, get_pubkey_from_state, randao_signature_set};
use std::convert::TryInto;
use tree_hash::TreeHash;
@@ -239,7 +240,7 @@ pub fn process_eth1_data<T: EthSpec>(
state: &mut BeaconState<T>,
eth1_data: &Eth1Data,
) -> Result<(), Error> {
if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data) {
if let Some(new_eth1_data) = get_new_eth1_data(state, eth1_data)? {
state.eth1_data = new_eth1_data;
}
@@ -248,14 +249,14 @@ pub fn process_eth1_data<T: EthSpec>(
Ok(())
}
/// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would
/// Returns `Ok(Some(eth1_data))` if adding the given `eth1_data` to `state.eth1_data_votes` would
/// result in a change to `state.eth1_data`.
///
/// Spec v0.11.1
pub fn get_new_eth1_data<T: EthSpec>(
state: &BeaconState<T>,
eth1_data: &Eth1Data,
) -> Option<Eth1Data> {
) -> Result<Option<Eth1Data>, ArithError> {
let num_votes = state
.eth1_data_votes
.iter()
@@ -263,10 +264,10 @@ pub fn get_new_eth1_data<T: EthSpec>(
.count();
// The +1 is to account for the `eth1_data` supplied to the function.
if 2 * (num_votes + 1) > T::SlotsPerEth1VotingPeriod::to_usize() {
Some(eth1_data.clone())
if num_votes.safe_add(1)?.safe_mul(2)? > T::SlotsPerEth1VotingPeriod::to_usize() {
Ok(Some(eth1_data.clone()))
} else {
None
Ok(None)
}
}
@@ -318,7 +319,8 @@ pub fn process_attester_slashings<T: EthSpec>(
) -> Result<(), BlockProcessingError> {
// Verify the `IndexedAttestation`s in parallel (these are the resource-consuming objects, not
// the `AttesterSlashing`s themselves).
let mut indexed_attestations: Vec<&_> = Vec::with_capacity(attester_slashings.len() * 2);
let mut indexed_attestations: Vec<&_> =
Vec::with_capacity(attester_slashings.len().safe_mul(2)?);
for attester_slashing in attester_slashings {
indexed_attestations.push(&attester_slashing.attestation_1);
indexed_attestations.push(&attester_slashing.attestation_2);
@@ -432,7 +434,12 @@ pub fn process_deposits<T: EthSpec>(
.par_iter()
.enumerate()
.try_for_each(|(i, deposit)| {
verify_deposit_merkle_proof(state, deposit, state.eth1_deposit_index + i as u64, spec)
verify_deposit_merkle_proof(
state,
deposit,
state.eth1_deposit_index.safe_add(i as u64)?,
spec,
)
.map_err(|e| e.into_with_index(i))
})?;
@@ -459,7 +466,7 @@ pub fn process_deposit<T: EthSpec>(
.map_err(|e| e.into_with_index(deposit_index))?;
}
state.eth1_deposit_index += 1;
state.eth1_deposit_index.increment()?;
// Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the
// depositing validator already exists in the registry.
@@ -495,7 +502,7 @@ pub fn process_deposit<T: EthSpec>(
exit_epoch: spec.far_future_epoch,
withdrawable_epoch: spec.far_future_epoch,
effective_balance: std::cmp::min(
amount - amount % spec.effective_balance_increment,
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
spec.max_effective_balance,
),
slashed: false,

View File

@@ -1,7 +1,9 @@
#![allow(clippy::integer_arithmetic)]
use super::signature_sets::{Error as SignatureSetError, Result as SignatureSetResult, *};
use crate::common::get_indexed_attestation;
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
use bls::{verify_signature_sets, SignatureSet};
use bls::{verify_signature_sets, PublicKey, SignatureSet};
use rayon::prelude::*;
use std::borrow::Cow;
use types::{
@@ -9,8 +11,6 @@ use types::{
SignedBeaconBlock,
};
pub use bls::G1Point;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, PartialEq)]
@@ -51,18 +51,18 @@ impl From<BlockOperationError<AttestationInvalid>> for Error {
pub struct BlockSignatureVerifier<'a, T, F>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>> + Clone,
F: Fn(usize) -> Option<Cow<'a, PublicKey>> + Clone,
{
get_pubkey: F,
state: &'a BeaconState<T>,
spec: &'a ChainSpec,
sets: Vec<SignatureSet<'a>>,
sets: Vec<SignatureSet>,
}
impl<'a, T, F> BlockSignatureVerifier<'a, T, F>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>> + Clone,
F: Fn(usize) -> Option<Cow<'a, PublicKey>> + Clone,
{
/// Create a new verifier without any included signatures. See the `include...` functions to
/// add signatures, and the `verify`
@@ -114,7 +114,7 @@ where
.sets
.into_par_iter()
.chunks(num_chunks)
.map(|chunk| verify_signature_sets(chunk.into_iter()))
.map(|chunk| verify_signature_sets(chunk))
.reduce(|| true, |current, this| current && this);
if result {

View File

@@ -1,9 +1,10 @@
use super::signature_sets::Error as SignatureSetError;
use merkle_proof::MerkleTreeError;
use safe_arith::ArithError;
use types::*;
/// The error returned from the `per_block_processing` function. Indicates that a block is either
/// invalid, or we were unable to determine it's validity (we encountered an unexpected error).
/// invalid, or we were unable to determine its validity (we encountered an unexpected error).
///
/// Any of the `...Error` variants indicate that at some point during block (and block operation)
/// verification, there was an error. There is no indication as to _where_ that error happened
@@ -48,6 +49,7 @@ pub enum BlockProcessingError {
SignatureSetError(SignatureSetError),
SszTypesError(ssz_types::Error),
MerkleTreeError(MerkleTreeError),
ArithError(ArithError),
}
impl From<BeaconStateError> for BlockProcessingError {
@@ -68,6 +70,12 @@ impl From<ssz_types::Error> for BlockProcessingError {
}
}
impl From<ArithError> for BlockProcessingError {
fn from(e: ArithError) -> Self {
BlockProcessingError::ArithError(e)
}
}
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
match e {
@@ -75,6 +83,7 @@ impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
}
}
}
@@ -101,6 +110,7 @@ macro_rules! impl_into_block_processing_error_with_index {
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
}
}
}
@@ -130,6 +140,7 @@ pub enum BlockOperationError<T> {
BeaconStateError(BeaconStateError),
SignatureSetError(SignatureSetError),
SszTypesError(ssz_types::Error),
ArithError(ArithError),
}
impl<T> BlockOperationError<T> {
@@ -155,6 +166,12 @@ impl<T> From<ssz_types::Error> for BlockOperationError<T> {
}
}
impl<T> From<ArithError> for BlockOperationError<T> {
fn from(e: ArithError) -> Self {
BlockOperationError::ArithError(e)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum HeaderInvalid {
ProposalSignatureInvalid,
@@ -267,6 +284,7 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
BlockOperationError::BeaconStateError(e) => BlockOperationError::BeaconStateError(e),
BlockOperationError::SignatureSetError(e) => BlockOperationError::SignatureSetError(e),
BlockOperationError::SszTypesError(e) => BlockOperationError::SszTypesError(e),
BlockOperationError::ArithError(e) => BlockOperationError::ArithError(e),
}
}
}

View File

@@ -2,7 +2,7 @@
//! validated individually, or alongside in others in a potentially cheaper bulk operation.
//!
//! This module exposes one function to extract each type of `SignatureSet` from a `BeaconBlock`.
use bls::{G1Point, G1Ref, SignatureSet, SignedMessage};
use bls::SignatureSet;
use ssz::DecodeError;
use std::borrow::Cow;
use std::convert::TryInto;
@@ -44,7 +44,7 @@ impl From<BeaconStateError> for Error {
pub fn get_pubkey_from_state<'a, T>(
state: &'a BeaconState<T>,
validator_index: usize,
) -> Option<Cow<'a, G1Point>>
) -> Option<Cow<'a, PublicKey>>
where
T: EthSpec,
{
@@ -55,7 +55,7 @@ where
let pk: Option<PublicKey> = (&v.pubkey).try_into().ok();
pk
})
.map(|pk| Cow::Owned(pk.into_point()))
.map(Cow::Owned)
}
/// A signature set that is valid if a block was signed by the expected block producer.
@@ -65,10 +65,10 @@ pub fn block_proposal_signature_set<'a, T, F>(
signed_block: &'a SignedBeaconBlock<T>,
block_root: Option<Hash256>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let block = &signed_block.message;
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
@@ -103,10 +103,10 @@ pub fn randao_signature_set<'a, T, F>(
get_pubkey: F,
block: &'a BeaconBlock<T>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
@@ -132,10 +132,10 @@ pub fn proposer_slashing_signature_set<'a, T, F>(
get_pubkey: F,
proposer_slashing: &'a ProposerSlashing,
spec: &'a ChainSpec,
) -> Result<(SignatureSet<'a>, SignatureSet<'a>)>
) -> Result<(SignatureSet, SignatureSet)>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let proposer_index = proposer_slashing.signed_header_1.message.proposer_index as usize;
@@ -161,9 +161,9 @@ where
fn block_header_signature_set<'a, T: EthSpec>(
state: &'a BeaconState<T>,
signed_header: &'a SignedBeaconBlockHeader,
pubkey: Cow<'a, G1Point>,
pubkey: Cow<'a, PublicKey>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> {
) -> Result<SignatureSet> {
let domain = spec.get_domain(
signed_header.message.slot.epoch(T::slots_per_epoch()),
Domain::BeaconProposer,
@@ -191,10 +191,10 @@ pub fn indexed_attestation_signature_set<'a, 'b, T, F>(
signature: &'a AggregateSignature,
indexed_attestation: &'b IndexedAttestation<T>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let pubkeys = indexed_attestation
.attesting_indices
@@ -213,9 +213,9 @@ where
);
let message = indexed_attestation.data.signing_root(domain);
let signed_message = SignedMessage::new(pubkeys, message.as_bytes().to_vec());
let message = message.as_bytes().to_vec();
Ok(SignatureSet::new(signature, vec![signed_message]))
Ok(SignatureSet::new(signature, pubkeys, message))
}
/// Returns the signature set for the given `indexed_attestation` but pubkeys are supplied directly
@@ -227,10 +227,10 @@ pub fn indexed_attestation_signature_set_from_pubkeys<'a, 'b, T, F>(
fork: &Fork,
genesis_validators_root: Hash256,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let pubkeys = indexed_attestation
.attesting_indices
@@ -249,9 +249,9 @@ where
);
let message = indexed_attestation.data.signing_root(domain);
let signed_message = SignedMessage::new(pubkeys, message.as_bytes().to_vec());
let message = message.as_bytes().to_vec();
Ok(SignatureSet::new(signature, vec![signed_message]))
Ok(SignatureSet::new(signature, pubkeys, message))
}
/// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`.
@@ -260,10 +260,10 @@ pub fn attester_slashing_signature_sets<'a, T, F>(
get_pubkey: F,
attester_slashing: &'a AttesterSlashing<T>,
spec: &'a ChainSpec,
) -> Result<(SignatureSet<'a>, SignatureSet<'a>)>
) -> Result<(SignatureSet, SignatureSet)>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>> + Clone,
F: Fn(usize) -> Option<Cow<'a, PublicKey>> + Clone,
{
Ok((
indexed_attestation_signature_set(
@@ -305,12 +305,12 @@ pub fn deposit_pubkey_signature_message(
/// `deposit_pubkey_signature_message`.
pub fn deposit_signature_set<'a>(
pubkey_signature_message: &'a (PublicKey, Signature, Vec<u8>),
) -> SignatureSet<'a> {
) -> SignatureSet {
let (pubkey, signature, message) = pubkey_signature_message;
// Note: Deposits are valid across forks, thus the deposit domain is computed
// with the fork zeroed.
SignatureSet::single(signature, pubkey.g1_ref(), message.clone())
// with the fok zeroed.
SignatureSet::single(&signature, Cow::Borrowed(pubkey), message.clone())
}
/// Returns a signature set that is valid if the `SignedVoluntaryExit` was signed by the indicated
@@ -320,10 +320,10 @@ pub fn exit_signature_set<'a, T, F>(
get_pubkey: F,
signed_exit: &'a SignedVoluntaryExit,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, G1Point>>,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let exit = &signed_exit.message;
let proposer_index = exit.validator_index as usize;

View File

@@ -3,6 +3,7 @@ use crate::per_block_processing::signature_sets::{
deposit_pubkey_signature_message, deposit_signature_set,
};
use merkle_proof::verify_merkle_proof;
use safe_arith::SafeArith;
use tree_hash::TreeHash;
use types::*;
@@ -59,7 +60,7 @@ pub fn verify_deposit_merkle_proof<T: EthSpec>(
verify_merkle_proof(
leaf,
&deposit.proof[..],
spec.deposit_contract_tree_depth as usize + 1,
spec.deposit_contract_tree_depth.safe_add(1)? as usize,
deposit_index as usize,
state.eth1_data.deposit_root,
),

View File

@@ -1,4 +1,5 @@
use errors::EpochProcessingError as Error;
use safe_arith::SafeArith;
use tree_hash::TreeHash;
use types::*;
@@ -90,7 +91,11 @@ pub fn process_justification_and_finalization<T: EthSpec>(
state.previous_justified_checkpoint = state.current_justified_checkpoint.clone();
state.justification_bits.shift_up(1)?;
if total_balances.previous_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
if total_balances
.previous_epoch_target_attesters()
.safe_mul(3)?
>= total_balances.current_epoch().safe_mul(2)?
{
state.current_justified_checkpoint = Checkpoint {
epoch: previous_epoch,
root: *state.get_block_root_at_epoch(previous_epoch)?,
@@ -98,7 +103,11 @@ pub fn process_justification_and_finalization<T: EthSpec>(
state.justification_bits.set(1, true)?;
}
// If the current epoch gets justified, fill the last bit.
if total_balances.current_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 {
if total_balances
.current_epoch_target_attesters()
.safe_mul(3)?
>= total_balances.current_epoch().safe_mul(2)?
{
state.current_justified_checkpoint = Checkpoint {
epoch: current_epoch,
root: *state.get_block_root_at_epoch(current_epoch)?,
@@ -152,17 +161,19 @@ pub fn process_final_updates<T: EthSpec>(
}
// Update effective balances with hysteresis (lag).
let hysteresis_increment = spec.effective_balance_increment / spec.hysteresis_quotient;
let downward_threshold = hysteresis_increment * spec.hysteresis_downward_multiplier;
let upward_threshold = hysteresis_increment * spec.hysteresis_upward_multiplier;
let hysteresis_increment = spec
.effective_balance_increment
.safe_div(spec.hysteresis_quotient)?;
let downward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_downward_multiplier)?;
let upward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_upward_multiplier)?;
for (index, validator) in state.validators.iter_mut().enumerate() {
let balance = state.balances[index];
if balance + downward_threshold < validator.effective_balance
|| validator.effective_balance + upward_threshold < balance
if balance.safe_add(downward_threshold)? < validator.effective_balance
|| validator.effective_balance.safe_add(upward_threshold)? < balance
{
validator.effective_balance = std::cmp::min(
balance - balance % spec.effective_balance_increment,
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
spec.max_effective_balance,
);
}
@@ -175,7 +186,11 @@ pub fn process_final_updates<T: EthSpec>(
state.set_randao_mix(next_epoch, *state.get_randao_mix(current_epoch)?)?;
// Set historical root accumulator
if next_epoch.as_u64() % (T::SlotsPerHistoricalRoot::to_u64() / T::slots_per_epoch()) == 0 {
if next_epoch
.as_u64()
.safe_rem(T::SlotsPerHistoricalRoot::to_u64().safe_div(T::slots_per_epoch())?)?
== 0
{
let historical_batch = state.historical_batch();
state
.historical_roots

View File

@@ -1,6 +1,7 @@
use super::super::common::get_base_reward;
use super::validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses};
use super::Error;
use safe_arith::SafeArith;
use types::*;
@@ -13,21 +14,21 @@ pub struct Delta {
impl Delta {
/// Reward the validator with the `reward`.
pub fn reward(&mut self, reward: u64) {
self.rewards += reward;
pub fn reward(&mut self, reward: u64) -> Result<(), Error> {
self.rewards = self.rewards.safe_add(reward)?;
Ok(())
}
/// Penalize the validator with the `penalty`.
pub fn penalize(&mut self, penalty: u64) {
self.penalties += penalty;
}
pub fn penalize(&mut self, penalty: u64) -> Result<(), Error> {
self.penalties = self.penalties.safe_add(penalty)?;
Ok(())
}
impl std::ops::AddAssign for Delta {
/// Use wrapping addition as that is how it's defined in the spec.
fn add_assign(&mut self, other: Delta) {
self.rewards += other.rewards;
self.penalties += other.penalties;
/// Combine two deltas.
fn combine(&mut self, other: Delta) -> Result<(), Error> {
self.reward(other.rewards)?;
self.penalize(other.penalties)
}
}
@@ -56,9 +57,10 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
get_proposer_deltas(&mut deltas, state, validator_statuses, spec)?;
// Apply the deltas, over-flowing but not under-flowing (saturating at 0 instead).
// Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0
// instead).
for (i, delta) in deltas.iter().enumerate() {
state.balances[i] += delta.rewards;
state.balances[i] = state.balances[i].safe_add(delta.rewards)?;
state.balances[i] = state.balances[i].saturating_sub(delta.penalties);
}
@@ -91,7 +93,8 @@ fn get_proposer_deltas<T: EthSpec>(
return Err(Error::ValidatorStatusesInconsistent);
}
deltas[inclusion.proposer_index].reward(base_reward / spec.proposer_reward_quotient);
deltas[inclusion.proposer_index]
.reward(base_reward.safe_div(spec.proposer_reward_quotient)?)?;
}
}
@@ -123,9 +126,9 @@ fn get_attestation_deltas<T: EthSpec>(
base_reward,
finality_delay,
spec,
);
)?;
deltas[index] += delta;
deltas[index].combine(delta)?;
}
Ok(())
@@ -140,7 +143,7 @@ fn get_attestation_delta<T: EthSpec>(
base_reward: u64,
finality_delay: u64,
spec: &ChainSpec,
) -> Delta {
) -> Result<Delta, Error> {
let mut delta = Delta::default();
// Is this validator eligible to be rewarded or penalized?
@@ -149,7 +152,7 @@ fn get_attestation_delta<T: EthSpec>(
|| (validator.is_slashed && !validator.is_withdrawable_in_current_epoch);
if !is_eligible {
return delta;
return Ok(delta);
}
// Handle integer overflow by dividing these quantities by EFFECTIVE_BALANCE_INCREMENT
@@ -157,59 +160,78 @@ fn get_attestation_delta<T: EthSpec>(
// - increment = EFFECTIVE_BALANCE_INCREMENT
// - reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
// - rewards[index] = reward_numerator // (total_balance // increment)
let total_balance_ebi = total_balances.current_epoch() / spec.effective_balance_increment;
let total_attesting_balance_ebi =
total_balances.previous_epoch_attesters() / spec.effective_balance_increment;
let matching_target_balance_ebi =
total_balances.previous_epoch_target_attesters() / spec.effective_balance_increment;
let matching_head_balance_ebi =
total_balances.previous_epoch_head_attesters() / spec.effective_balance_increment;
let total_balance_ebi = total_balances
.current_epoch()
.safe_div(spec.effective_balance_increment)?;
let total_attesting_balance_ebi = total_balances
.previous_epoch_attesters()
.safe_div(spec.effective_balance_increment)?;
let matching_target_balance_ebi = total_balances
.previous_epoch_target_attesters()
.safe_div(spec.effective_balance_increment)?;
let matching_head_balance_ebi = total_balances
.previous_epoch_head_attesters()
.safe_div(spec.effective_balance_increment)?;
// Expected FFG source.
// Spec:
// - validator index in `get_unslashed_attesting_indices(state, matching_source_attestations)`
if validator.is_previous_epoch_attester && !validator.is_slashed {
delta.reward(base_reward * total_attesting_balance_ebi / total_balance_ebi);
delta.reward(
base_reward
.safe_mul(total_attesting_balance_ebi)?
.safe_div(total_balance_ebi)?,
)?;
// Inclusion speed bonus
let proposer_reward = base_reward / spec.proposer_reward_quotient;
let max_attester_reward = base_reward - proposer_reward;
let proposer_reward = base_reward.safe_div(spec.proposer_reward_quotient)?;
let max_attester_reward = base_reward.safe_sub(proposer_reward)?;
let inclusion = validator
.inclusion_info
.expect("It is a logic error for an attester not to have an inclusion delay.");
delta.reward(max_attester_reward / inclusion.delay);
delta.reward(max_attester_reward.safe_div(inclusion.delay)?)?;
} else {
delta.penalize(base_reward);
delta.penalize(base_reward)?;
}
// Expected FFG target.
// Spec:
// - validator index in `get_unslashed_attesting_indices(state, matching_target_attestations)`
if validator.is_previous_epoch_target_attester && !validator.is_slashed {
delta.reward(base_reward * matching_target_balance_ebi / total_balance_ebi);
delta.reward(
base_reward
.safe_mul(matching_target_balance_ebi)?
.safe_div(total_balance_ebi)?,
)?;
} else {
delta.penalize(base_reward);
delta.penalize(base_reward)?;
}
// Expected head.
// Spec:
// - validator index in `get_unslashed_attesting_indices(state, matching_head_attestations)`
if validator.is_previous_epoch_head_attester && !validator.is_slashed {
delta.reward(base_reward * matching_head_balance_ebi / total_balance_ebi);
delta.reward(
base_reward
.safe_mul(matching_head_balance_ebi)?
.safe_div(total_balance_ebi)?,
)?;
} else {
delta.penalize(base_reward);
delta.penalize(base_reward)?;
}
// Inactivity penalty
if finality_delay > spec.min_epochs_to_inactivity_penalty {
// All eligible validators are penalized
delta.penalize(spec.base_rewards_per_epoch * base_reward);
delta.penalize(spec.base_rewards_per_epoch.safe_mul(base_reward)?)?;
// Additionally, all validators whose FFG target didn't match are penalized extra
if !validator.is_previous_epoch_target_attester {
delta.penalize(
validator.current_epoch_effective_balance * finality_delay
/ spec.inactivity_penalty_quotient,
);
validator
.current_epoch_effective_balance
.safe_mul(finality_delay)?
.safe_div(spec.inactivity_penalty_quotient)?,
)?;
}
}
@@ -218,5 +240,5 @@ fn get_attestation_delta<T: EthSpec>(
// This function only computes the delta for a single validator, so it cannot also return a
// delta for a validator.
delta
Ok(delta)
}

View File

@@ -18,6 +18,7 @@ pub enum EpochProcessingError {
BeaconStateError(BeaconStateError),
InclusionError(InclusionError),
SszTypesError(ssz_types::Error),
ArithError(safe_arith::ArithError),
}
impl From<InclusionError> for EpochProcessingError {
@@ -38,6 +39,12 @@ impl From<ssz_types::Error> for EpochProcessingError {
}
}
impl From<safe_arith::ArithError> for EpochProcessingError {
fn from(e: safe_arith::ArithError) -> EpochProcessingError {
EpochProcessingError::ArithError(e)
}
}
#[derive(Debug, PartialEq)]
pub enum InclusionError {
/// The validator did not participate in an attestation in this period.

View File

@@ -1,3 +1,4 @@
use safe_arith::SafeArith;
use types::{BeaconStateError as Error, *};
/// Process slashings.
@@ -13,12 +14,17 @@ pub fn process_slashings<T: EthSpec>(
for (index, validator) in state.validators.iter().enumerate() {
if validator.slashed
&& epoch + T::EpochsPerSlashingsVector::to_u64() / 2 == validator.withdrawable_epoch
&& epoch + T::EpochsPerSlashingsVector::to_u64().safe_div(2)?
== validator.withdrawable_epoch
{
let increment = spec.effective_balance_increment;
let penalty_numerator = validator.effective_balance / increment
* std::cmp::min(sum_slashings * 3, total_balance);
let penalty = penalty_numerator / total_balance * increment;
let penalty_numerator = validator
.effective_balance
.safe_div(increment)?
.safe_mul(std::cmp::min(sum_slashings.safe_mul(3)?, total_balance))?;
let penalty = penalty_numerator
.safe_div(total_balance)?
.safe_mul(increment)?;
safe_sub_assign!(state.balances[index], penalty);
}

View File

@@ -1,4 +1,5 @@
use crate::common::get_attesting_indices;
use safe_arith::SafeArith;
use types::*;
/// Sets the boolean `var` on `self` to be true if it is true on `other`. Otherwise leaves `self`
@@ -198,12 +199,16 @@ impl ValidatorStatuses {
if validator.is_active_at(state.current_epoch()) {
status.is_active_in_current_epoch = true;
total_balances.current_epoch += effective_balance;
total_balances
.current_epoch
.safe_add_assign(effective_balance)?;
}
if validator.is_active_at(state.previous_epoch()) {
status.is_active_in_previous_epoch = true;
total_balances.previous_epoch += effective_balance;
total_balances
.previous_epoch
.safe_add_assign(effective_balance)?;
}
statuses.push(status);
@@ -275,19 +280,29 @@ impl ValidatorStatuses {
let validator_balance = state.get_effective_balance(index, spec)?;
if v.is_current_epoch_attester {
self.total_balances.current_epoch_attesters += validator_balance;
self.total_balances
.current_epoch_attesters
.safe_add_assign(validator_balance)?;
}
if v.is_current_epoch_target_attester {
self.total_balances.current_epoch_target_attesters += validator_balance;
self.total_balances
.current_epoch_target_attesters
.safe_add_assign(validator_balance)?;
}
if v.is_previous_epoch_attester {
self.total_balances.previous_epoch_attesters += validator_balance;
self.total_balances
.previous_epoch_attesters
.safe_add_assign(validator_balance)?;
}
if v.is_previous_epoch_target_attester {
self.total_balances.previous_epoch_target_attesters += validator_balance;
self.total_balances
.previous_epoch_target_attesters
.safe_add_assign(validator_balance)?;
}
if v.is_previous_epoch_head_attester {
self.total_balances.previous_epoch_head_attesters += validator_balance;
self.total_balances
.previous_epoch_head_attesters
.safe_add_assign(validator_balance)?;
}
}
}

View File

@@ -23,6 +23,7 @@ log = "0.4.8"
merkle_proof = { path = "../utils/merkle_proof" }
rayon = "1.2.0"
rand = "0.7.2"
safe_arith = { path = "../utils/safe_arith" }
serde = "1.0.102"
serde_derive = "1.0.102"
slog = "2.5.2"

View File

@@ -3,6 +3,7 @@ use super::{
Signature, SignedRoot, SubnetId,
};
use crate::{test_utils::TestRandom, Hash256};
use safe_arith::{ArithError, SafeArith};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -13,6 +14,7 @@ use tree_hash_derive::TreeHash;
pub enum Error {
SszTypesError(ssz_types::Error),
AlreadySigned(usize),
SubnetCountIsZero(ArithError),
}
/// Details an attestation that can be slashable.
@@ -86,8 +88,12 @@ impl<T: EthSpec> Attestation<T> {
///
/// Note, this will return the subnet id for an aggregated attestation. This is done
/// to avoid checking aggregate bits every time we wish to get an id.
pub fn subnet_id(&self) -> SubnetId {
SubnetId::new(self.data.index % T::default_spec().attestation_subnet_count)
pub fn subnet_id(&self, spec: &ChainSpec) -> Result<SubnetId, Error> {
self.data
.index
.safe_rem(spec.attestation_subnet_count)
.map(SubnetId::new)
.map_err(Error::SubnetCountIsZero)
}
}

View File

@@ -7,11 +7,13 @@ use compare_fields_derive::CompareFields;
use eth2_hashing::hash;
use int_to_bytes::{int_to_bytes4, int_to_bytes8};
use pubkey_cache::PubkeyCache;
use safe_arith::{ArithError, SafeArith};
use serde_derive::{Deserialize, Serialize};
use ssz::ssz_encode;
use ssz_derive::{Decode, Encode};
use ssz_types::{typenum::Unsigned, BitVector, FixedVector};
use std::convert::TryInto;
use std::fmt;
use swap_or_not_shuffle::compute_shuffled_index;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -76,6 +78,12 @@ pub enum Error {
deposit_count: u64,
deposit_index: u64,
},
/// An arithmetic operation occurred which would have overflowed or divided by 0.
///
/// This represents a serious bug in either the spec or Lighthouse!
ArithError(ArithError),
MissingBeaconBlock(SignedBeaconBlockHash),
MissingBeaconState(BeaconStateHash),
}
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.
@@ -94,6 +102,33 @@ impl AllowNextEpoch {
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct BeaconStateHash(Hash256);
impl fmt::Debug for BeaconStateHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "BeaconStateHash({:?})", self.0)
}
}
impl fmt::Display for BeaconStateHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Hash256> for BeaconStateHash {
fn from(hash: Hash256) -> BeaconStateHash {
BeaconStateHash(hash)
}
}
impl From<BeaconStateHash> for Hash256 {
fn from(beacon_state_hash: BeaconStateHash) -> Hash256 {
beacon_state_hash.0
}
}
/// The state of the `BeaconChain` at some slot.
///
/// Spec v0.11.1
@@ -413,7 +448,7 @@ impl<T: EthSpec> BeaconState<T> {
let mut i = 0;
loop {
let candidate_index = indices[compute_shuffled_index(
i % indices.len(),
i.safe_rem(indices.len())?,
indices.len(),
seed,
spec.shuffle_round_count,
@@ -421,17 +456,19 @@ impl<T: EthSpec> BeaconState<T> {
.ok_or(Error::UnableToShuffle)?];
let random_byte = {
let mut preimage = seed.to_vec();
preimage.append(&mut int_to_bytes8((i / 32) as u64));
preimage.append(&mut int_to_bytes8(i.safe_div(32)? as u64));
let hash = hash(&preimage);
hash[i % 32]
hash[i.safe_rem(32)?]
};
let effective_balance = self.validators[candidate_index].effective_balance;
if effective_balance * MAX_RANDOM_BYTE
>= spec.max_effective_balance * u64::from(random_byte)
if effective_balance.safe_mul(MAX_RANDOM_BYTE)?
>= spec
.max_effective_balance
.safe_mul(u64::from(random_byte))?
{
return Ok(candidate_index);
}
i += 1;
i.increment()?;
}
}
@@ -448,7 +485,7 @@ impl<T: EthSpec> BeaconState<T> {
let committee = self.get_beacon_committee(slot, index)?;
let modulo = std::cmp::max(
1,
committee.committee.len() as u64 / spec.target_aggregators_per_committee,
(committee.committee.len() as u64).safe_div(spec.target_aggregators_per_committee)?,
);
let signature_hash = hash(&slot_signature.as_bytes());
let signature_hash_int = u64::from_le_bytes(
@@ -456,7 +493,8 @@ impl<T: EthSpec> BeaconState<T> {
.try_into()
.expect("first 8 bytes of signature should always convert to fixed array"),
);
Ok(signature_hash_int % modulo == 0)
Ok(signature_hash_int.safe_rem(modulo)? == 0)
}
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`.
@@ -502,8 +540,8 @@ impl<T: EthSpec> BeaconState<T> {
///
/// Spec v0.11.1
fn get_latest_block_roots_index(&self, slot: Slot) -> Result<usize, Error> {
if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) {
Ok(slot.as_usize() % self.block_roots.len())
if slot < self.slot && self.slot <= slot + self.block_roots.len() as u64 {
Ok(slot.as_usize().safe_rem(self.block_roots.len())?)
} else {
Err(BeaconStateError::SlotOutOfBounds)
}
@@ -555,7 +593,7 @@ impl<T: EthSpec> BeaconState<T> {
let len = T::EpochsPerHistoricalVector::to_u64();
if current_epoch < epoch + len && epoch <= allow_next_epoch.upper_bound_of(current_epoch) {
Ok(epoch.as_usize() % len as usize)
Ok(epoch.as_usize().safe_rem(len as usize)?)
} else {
Err(Error::EpochOutOfBounds)
}
@@ -569,7 +607,9 @@ impl<T: EthSpec> BeaconState<T> {
///
/// Spec v0.11.1
pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> {
let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize();
let i = epoch
.as_usize()
.safe_rem(T::EpochsPerHistoricalVector::to_usize())?;
let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature)));
@@ -599,8 +639,8 @@ impl<T: EthSpec> BeaconState<T> {
///
/// Spec v0.11.1
fn get_latest_state_roots_index(&self, slot: Slot) -> Result<usize, Error> {
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) {
Ok(slot.as_usize() % self.state_roots.len())
if slot < self.slot && self.slot <= slot + self.state_roots.len() as u64 {
Ok(slot.as_usize().safe_rem(self.state_roots.len())?)
} else {
Err(BeaconStateError::SlotOutOfBounds)
}
@@ -631,6 +671,14 @@ impl<T: EthSpec> BeaconState<T> {
Ok(&self.block_roots[i])
}
pub fn get_block_state_roots(
&self,
slot: Slot,
) -> Result<(SignedBeaconBlockHash, BeaconStateHash), Error> {
let i = self.get_latest_block_roots_index(slot)?;
Ok((self.block_roots[i].into(), self.state_roots[i].into()))
}
/// Sets the latest state root for slot.
///
/// Spec v0.11.1
@@ -654,7 +702,9 @@ impl<T: EthSpec> BeaconState<T> {
if current_epoch < epoch + T::EpochsPerSlashingsVector::to_u64()
&& epoch <= allow_next_epoch.upper_bound_of(current_epoch)
{
Ok(epoch.as_usize() % T::EpochsPerSlashingsVector::to_usize())
Ok(epoch
.as_usize()
.safe_rem(T::EpochsPerSlashingsVector::to_usize())?)
} else {
Err(Error::EpochOutOfBounds)
}
@@ -713,20 +763,20 @@ impl<T: EthSpec> BeaconState<T> {
// == 0`.
let mix = {
let i = epoch + T::EpochsPerHistoricalVector::to_u64() - spec.min_seed_lookahead - 1;
self.randao_mixes[i.as_usize() % self.randao_mixes.len()]
self.randao_mixes[i.as_usize().safe_rem(self.randao_mixes.len())?]
};
let domain_bytes = int_to_bytes4(spec.get_domain_constant(domain_type));
let epoch_bytes = int_to_bytes8(epoch.as_u64());
const NUM_DOMAIN_BYTES: usize = 4;
const NUM_EPOCH_BYTES: usize = 8;
const MIX_OFFSET: usize = NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES;
const NUM_MIX_BYTES: usize = 32;
let mut preimage = [0; NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES + NUM_MIX_BYTES];
preimage[0..NUM_DOMAIN_BYTES].copy_from_slice(&domain_bytes);
preimage[NUM_DOMAIN_BYTES..NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES]
.copy_from_slice(&epoch_bytes);
preimage[NUM_DOMAIN_BYTES + NUM_EPOCH_BYTES..].copy_from_slice(mix.as_bytes());
preimage[NUM_DOMAIN_BYTES..MIX_OFFSET].copy_from_slice(&epoch_bytes);
preimage[MIX_OFFSET..].copy_from_slice(mix.as_bytes());
Ok(Hash256::from_slice(&hash(&preimage)))
}
@@ -760,9 +810,10 @@ impl<T: EthSpec> BeaconState<T> {
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
Ok(std::cmp::max(
spec.min_per_epoch_churn_limit,
self.committee_cache(RelativeEpoch::Current)?
.active_validator_count() as u64
/ spec.churn_limit_quotient,
(self
.committee_cache(RelativeEpoch::Current)?
.active_validator_count() as u64)
.safe_div(spec.churn_limit_quotient)?,
))
}
@@ -792,7 +843,7 @@ impl<T: EthSpec> BeaconState<T> {
) -> Result<u64, Error> {
validator_indices.iter().try_fold(0_u64, |acc, i| {
self.get_effective_balance(*i, spec)
.and_then(|bal| Ok(bal + acc))
.and_then(|bal| Ok(acc.safe_add(bal)?))
})
}
@@ -1098,3 +1149,9 @@ impl From<tree_hash::Error> for Error {
Error::TreeHashError(e)
}
}
impl From<ArithError> for Error {
fn from(e: ArithError) -> Error {
Error::ArithError(e)
}
}

View File

@@ -1,3 +1,5 @@
#![allow(clippy::integer_arithmetic)]
use super::BeaconState;
use crate::*;
use core::num::NonZeroUsize;
@@ -43,7 +45,7 @@ impl CommitteeCache {
}
let committees_per_slot =
T::get_committee_count_per_slot(active_validator_indices.len(), spec) as u64;
T::get_committee_count_per_slot(active_validator_indices.len(), spec)? as u64;
let seed = state.get_seed(epoch, Domain::BeaconAttester, spec)?;
@@ -56,7 +58,7 @@ impl CommitteeCache {
.ok_or_else(|| Error::UnableToShuffle)?;
// The use of `NonZeroUsize` reduces the maximum number of possible validators by one.
if state.validators.len() > usize::max_value() - 1 {
if state.validators.len() == usize::max_value() {
return Err(Error::TooManyValidators);
}

View File

@@ -1,4 +1,5 @@
use super::{BeaconStateError, ChainSpec, Epoch, Validator};
use safe_arith::SafeArith;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -50,7 +51,10 @@ impl ExitCache {
/// Must only be called once per exiting validator.
pub fn record_validator_exit(&mut self, exit_epoch: Epoch) -> Result<(), BeaconStateError> {
self.check_initialized()?;
*self.exits_per_epoch.entry(exit_epoch).or_insert(0) += 1;
self.exits_per_epoch
.entry(exit_epoch)
.or_insert(0)
.increment()?;
Ok(())
}

View File

@@ -23,6 +23,7 @@ impl PubkeyCache {
///
/// The added index must equal the number of validators already added to the map. This ensures
/// that an index is never skipped.
#[allow(clippy::integer_arithmetic)]
pub fn insert(&mut self, pubkey: PublicKeyBytes, index: ValidatorIndex) -> bool {
if index == self.len {
self.map.insert(pubkey, index);

Some files were not shown because too many files have changed in this diff Show More