mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
102 Commits
v1.0.0-bet
...
v1.0.0.rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e76b02bb7 | ||
|
|
519b003fc3 | ||
|
|
9a10462c64 | ||
|
|
ac60ff2bc2 | ||
|
|
f8a855d168 | ||
|
|
74c7733abf | ||
|
|
f63e89813d | ||
|
|
c021e2e8bc | ||
|
|
c3fc40907d | ||
|
|
3fb78ff575 | ||
|
|
7bd97546f0 | ||
|
|
5140ceec68 | ||
|
|
97ad5cd5fd | ||
|
|
4dc65c5787 | ||
|
|
1b012ccfa5 | ||
|
|
60cdd69b05 | ||
|
|
90a66df529 | ||
|
|
c4a1fe4d0d | ||
|
|
8a256de2dd | ||
|
|
c3451a6ce9 | ||
|
|
4b6441f626 | ||
|
|
eb7ab16f92 | ||
|
|
e6ecda5ebe | ||
|
|
095c4d5dd5 | ||
|
|
59d63087b1 | ||
|
|
e1dd532af3 | ||
|
|
cfed4fa1b5 | ||
|
|
7735a083b2 | ||
|
|
fec469291e | ||
|
|
acb47f2920 | ||
|
|
925fba0570 | ||
|
|
1a72733c53 | ||
|
|
2976bf7723 | ||
|
|
7c54cfea3f | ||
|
|
d3f8599d19 | ||
|
|
2034c662af | ||
|
|
ad5151f25d | ||
|
|
f75a8efc0d | ||
|
|
39817c0586 | ||
|
|
168cffb0dd | ||
|
|
194ee7c439 | ||
|
|
5889670cc7 | ||
|
|
7449eba612 | ||
|
|
d85cf028ef | ||
|
|
71c6164c42 | ||
|
|
83601245f2 | ||
|
|
758ec96d6d | ||
|
|
977e539fe9 | ||
|
|
f361450e8d | ||
|
|
0c9389a438 | ||
|
|
f200a16418 | ||
|
|
28ad21c410 | ||
|
|
da835afbaf | ||
|
|
16bccf05cf | ||
|
|
8dcdfea2a8 | ||
|
|
244d9633af | ||
|
|
d281ef9c56 | ||
|
|
58fcb52220 | ||
|
|
8d50fa10e6 | ||
|
|
21d4c8f3f8 | ||
|
|
5fdb916b4f | ||
|
|
18be4a4e3e | ||
|
|
e9136e9679 | ||
|
|
5f9239595b | ||
|
|
47daedaf11 | ||
|
|
52d850f355 | ||
|
|
d1b9f12a1e | ||
|
|
56fd535dd5 | ||
|
|
79d19ea438 | ||
|
|
ec2e677668 | ||
|
|
8e3c6e45ef | ||
|
|
a21a2c9e95 | ||
|
|
25118fb8dc | ||
|
|
06902c667d | ||
|
|
d3ca9985eb | ||
|
|
bd506bf4e8 | ||
|
|
1a05fcae3c | ||
|
|
3c5bf9bf72 | ||
|
|
24457e1aae | ||
|
|
660ed2d9a8 | ||
|
|
4290ba416c | ||
|
|
9e9a172248 | ||
|
|
2f11e55869 | ||
|
|
7f7d18e910 | ||
|
|
e22dd3758d | ||
|
|
8638e2c0b5 | ||
|
|
0fb465ba07 | ||
|
|
09e3f0360e | ||
|
|
7b0ee3adfe | ||
|
|
f57bab78aa | ||
|
|
ce75b2f684 | ||
|
|
742808c6cf | ||
|
|
b4bce7c726 | ||
|
|
9e9a913069 | ||
|
|
93c11e0e53 | ||
|
|
1b9911ccc3 | ||
|
|
be40e1a3b9 | ||
|
|
d4c954648c | ||
|
|
15706a36cb | ||
|
|
5995d2394c | ||
|
|
1c5d533c93 | ||
|
|
8cac198692 |
@@ -1 +1 @@
|
||||
3.2.0
|
||||
3.7.0
|
||||
|
||||
@@ -112,6 +112,7 @@ nogo(
|
||||
"//tools/analyzers/nop:go_tool_library",
|
||||
"//tools/analyzers/slicedirect:go_tool_library",
|
||||
"//tools/analyzers/ineffassign:go_tool_library",
|
||||
"//tools/analyzers/properpermissions:go_tool_library",
|
||||
] + select({
|
||||
# nogo checks that fail with coverage enabled.
|
||||
":coverage_enabled": [],
|
||||
|
||||
@@ -61,7 +61,7 @@ Example:
|
||||
|
||||
```bash
|
||||
go get github.com/prysmaticlabs/example@v1.2.3
|
||||
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%prysm_deps
|
||||
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%prysm_deps -prune=true
|
||||
```
|
||||
|
||||
The deps.bzl file should have been updated with the dependency and any transitive dependencies.
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Prysm: An Ethereum 2.0 Client Written in Go
|
||||
|
||||
[](https://buildkite.com/prysmatic-labs/prysm)
|
||||
[](https://github.com/ethereum/eth2.0-specs/tree/v0.12.3)
|
||||
[](https://goreportcard.com/report/github.com/prysmaticlabs/prysm)
|
||||
[](https://github.com/ethereum/eth2.0-specs/tree/v1.0.0)
|
||||
[](https://discord.gg/KSA7rPr)
|
||||
[](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the Ethereum 2.0 client specifications developed by [Prysmatic Labs](https://prysmaticlabs.com).
|
||||
|
||||
### Getting Started
|
||||
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the [official documentation portal](https://docs.prylabs.network). If you still have questions, feel free to stop by either our [Discord](https://discord.gg/KSA7rPr) or [Gitter](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) and a member of the team or our community will be happy to assist you.
|
||||
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the [official documentation portal](https://docs.prylabs.network). If you still have questions, feel free to stop by our [Discord](https://discord.gg/KSA7rPr).
|
||||
|
||||
### Come join the testnet!
|
||||
Participation is now open to the public for our Ethereum 2.0 phase 0 testnet release. Visit [prylabs.net](https://prylabs.net) for more information on the project or to sign up as a validator on the network. You can visualize the nodes in the network on [eth2stats.io](https://eth2stats.io), explore validator rewards/penalties via Bitfly's block explorer: [beaconcha.in](https://beaconcha.in), and follow the latest blocks added to the chain on [Etherscan](https://beacon.etherscan.io).
|
||||
Participation is now open to the public for our Ethereum 2.0 phase 0 testnet release. Visit [prylabs.net](https://prylabs.net) for more information on the project or to sign up as a validator on the network. You can visualize the nodes in the network on [eth2stats.io](https://eth2stats.io), explore validator rewards/penalties via Bitfly's block explorer: [beaconcha.in](https://beaconcha.in), and follow the latest blocks added to the chain on [beaconscan](https://beaconscan.com).
|
||||
|
||||
## Contributing
|
||||
Want to get involved? Check out our [Contribution Guide](https://docs.prylabs.network/docs/contribute/contribution-guidelines/) to learn more!
|
||||
|
||||
61
WORKSPACE
61
WORKSPACE
@@ -5,19 +5,19 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_toolchains",
|
||||
sha256 = "db48eed61552e25d36fe051a65d2a329cc0fb08442627e8f13960c5ab087a44e",
|
||||
strip_prefix = "bazel-toolchains-3.2.0",
|
||||
sha256 = "8e0633dfb59f704594f19ae996a35650747adc621ada5e8b9fb588f808c89cb0",
|
||||
strip_prefix = "bazel-toolchains-3.7.0",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.2.0/bazel-toolchains-3.2.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-toolchains/releases/download/3.2.0/bazel-toolchains-3.2.0.tar.gz",
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.7.0/bazel-toolchains-3.7.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-toolchains/releases/download/3.7.0/bazel-toolchains-3.7.0.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "com_grail_bazel_toolchain",
|
||||
sha256 = "0bec89e35d8a141c87f28cfc506d6d344785c8eb2ff3a453140a1fe972ada79d",
|
||||
strip_prefix = "bazel-toolchain-77a87103145f86f03f90475d19c2c8854398a444",
|
||||
urls = ["https://github.com/grailbio/bazel-toolchain/archive/77a87103145f86f03f90475d19c2c8854398a444.tar.gz"],
|
||||
sha256 = "b924b102adc0c3368d38a19bd971cb4fa75362a27bc363d0084b90ca6877d3f0",
|
||||
strip_prefix = "bazel-toolchain-0.5.7",
|
||||
urls = ["https://github.com/grailbio/bazel-toolchain/archive/0.5.7.tar.gz"],
|
||||
)
|
||||
|
||||
load("@com_grail_bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies")
|
||||
@@ -28,7 +28,7 @@ load("@com_grail_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain")
|
||||
|
||||
llvm_toolchain(
|
||||
name = "llvm_toolchain",
|
||||
llvm_version = "9.0.0",
|
||||
llvm_version = "10.0.0",
|
||||
)
|
||||
|
||||
load("@llvm_toolchain//:toolchains.bzl", "llvm_register_toolchains")
|
||||
@@ -47,10 +47,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44",
|
||||
sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -60,10 +60,10 @@ bazel_skylib_workspace()
|
||||
|
||||
http_archive(
|
||||
name = "bazel_gazelle",
|
||||
sha256 = "d8c45ee70ec39a57e7a05e5027c32b1576cc7f16d9dd37135b0eddde45cf1b10",
|
||||
sha256 = "1f4fc1d91826ec436ae04833430626f4cc02c20bb0a813c0c2f3c4c421307b1d",
|
||||
strip_prefix = "bazel-gazelle-e368a11b76e92932122d824970dc0ce5feb9c349",
|
||||
urls = [
|
||||
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/bazel-gazelle/releases/download/v0.20.0/bazel-gazelle-v0.20.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.20.0/bazel-gazelle-v0.20.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-gazelle/archive/e368a11b76e92932122d824970dc0ce5feb9c349.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -76,9 +76,9 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_docker",
|
||||
sha256 = "dc97fccceacd4c6be14e800b2a00693d5e8d07f69ee187babfd04a80a9f8e250",
|
||||
strip_prefix = "rules_docker-0.14.1",
|
||||
url = "https://github.com/bazelbuild/rules_docker/archive/v0.14.1.tar.gz",
|
||||
sha256 = "1698624e878b0607052ae6131aa216d45ebb63871ec497f26c67455b34119c80",
|
||||
strip_prefix = "rules_docker-0.15.0",
|
||||
urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.15.0/rules_docker-v0.15.0.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -89,10 +89,10 @@ http_archive(
|
||||
# nogo check fails for certain third_party dependencies.
|
||||
"//third_party:io_bazel_rules_go.patch",
|
||||
],
|
||||
sha256 = "7b9bbe3ea1fccb46dcfa6c3f3e29ba7ec740d8733370e21cdc8937467b4a4349",
|
||||
sha256 = "207fad3e6689135c5d8713e5a17ba9d1290238f47b9ba545b63d9303406209c6",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.22.4/rules_go-v0.22.4.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_go/releases/download/v0.22.4/rules_go-v0.22.4.tar.gz",
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.24.7/rules_go-v0.24.7.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_go/releases/download/v0.24.7/rules_go-v0.24.7.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -155,7 +155,10 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
|
||||
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains(nogo = "@//:nogo")
|
||||
go_register_toolchains(
|
||||
go_version = "1.15.5",
|
||||
nogo = "@//:nogo",
|
||||
)
|
||||
|
||||
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
|
||||
|
||||
@@ -219,8 +222,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "6b3498001de98c477aa2c256beffc20a85ce1b12b8e0f8e88502a5c3a18c01de",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.0.0-rc.0/general.tar.gz",
|
||||
sha256 = "ef5396e4b13995da9776eeb5ae346a2de90970c28da3c4f0dcaa4ab9f0ad1f93",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.0.0/general.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -235,8 +238,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "72c2f561db879ddcdf729fef93d10e0f9162b4cf3a697c513ef8935b93f6165a",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.12.3/minimal.tar.gz",
|
||||
sha256 = "170551b441e7d54b73248372ad9ce8cb6c148810b5f1364637117a63f4f1c085",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.0.0/minimal.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -251,8 +254,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "63eca02503692a0b6a2d7b70118e0dd62dff094153a3a542af6dbea721841b0d",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.12.3/mainnet.tar.gz",
|
||||
sha256 = "b541a9979b4703fa5ee5d2182b0b5313c38efc54ae7eaec2eef793230a52ec83",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.0.0/mainnet.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -268,9 +271,9 @@ buildifier_dependencies()
|
||||
|
||||
git_repository(
|
||||
name = "com_google_protobuf",
|
||||
commit = "4059c61f27eb1b06c4ee979546a238be792df0a4",
|
||||
commit = "fde7cf7358ec7cd69e8db9be4f1fa6a5c431386a", # v3.13.0
|
||||
remote = "https://github.com/protocolbuffers/protobuf",
|
||||
shallow_since = "1558721209 -0700",
|
||||
shallow_since = "1597443653 -0700",
|
||||
)
|
||||
|
||||
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test")
|
||||
load("@io_bazel_rules_docker//go:image.bzl", "go_image")
|
||||
load("@io_bazel_rules_docker//container:container.bzl", "container_bundle")
|
||||
load("@io_bazel_rules_docker//container:container.bzl", "container_bundle", "container_image")
|
||||
load("//tools:go_image.bzl", "go_image_alpine", "go_image_debug")
|
||||
load("@io_bazel_rules_docker//contrib:push-all.bzl", "docker_push")
|
||||
|
||||
@@ -35,49 +35,29 @@ go_library(
|
||||
|
||||
go_image(
|
||||
name = "image",
|
||||
srcs = [
|
||||
"main.go",
|
||||
"usage.go",
|
||||
],
|
||||
base = select({
|
||||
"//tools:base_image_alpine": "//tools:alpine_cc_image",
|
||||
"//tools:base_image_cc": "//tools:cc_image",
|
||||
"//conditions:default": "//tools:cc_image",
|
||||
}),
|
||||
goarch = "amd64",
|
||||
goos = "linux",
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain",
|
||||
race = "off",
|
||||
static = "off", # Static enabled binary seems to cause issues with DNS lookup with cgo.
|
||||
binary = ":beacon-chain",
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//beacon-chain/flags:go_default_library",
|
||||
"//beacon-chain/node:go_default_library",
|
||||
"//shared/tos:go_default_library",
|
||||
"//shared/cmd:go_default_library",
|
||||
"//shared/debug:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/journald:go_default_library",
|
||||
"//shared/logutil:go_default_library",
|
||||
"//shared/maxprocs:go_default_library",
|
||||
"//shared/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//log:go_default_library",
|
||||
"@com_github_ipfs_go_log_v2//:go_default_library",
|
||||
"@com_github_joonix_log//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
container_image(
|
||||
name = "image_with_creation_time",
|
||||
base = "image",
|
||||
stamp = True,
|
||||
)
|
||||
|
||||
container_bundle(
|
||||
name = "image_bundle",
|
||||
images = {
|
||||
"gcr.io/prysmaticlabs/prysm/beacon-chain:latest": ":image",
|
||||
"gcr.io/prysmaticlabs/prysm/beacon-chain:{DOCKER_TAG}": ":image",
|
||||
"index.docker.io/prysmaticlabs/prysm-beacon-chain:latest": ":image",
|
||||
"index.docker.io/prysmaticlabs/prysm-beacon-chain:{DOCKER_TAG}": ":image",
|
||||
"gcr.io/prysmaticlabs/prysm/beacon-chain:latest": ":image_with_creation_time",
|
||||
"gcr.io/prysmaticlabs/prysm/beacon-chain:{DOCKER_TAG}": ":image_with_creation_time",
|
||||
"index.docker.io/prysmaticlabs/prysm-beacon-chain:latest": ":image_with_creation_time",
|
||||
"index.docker.io/prysmaticlabs/prysm-beacon-chain:{DOCKER_TAG}": ":image_with_creation_time",
|
||||
},
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
@@ -77,6 +77,7 @@ go_test(
|
||||
name = "go_raceoff_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"blockchain_test.go",
|
||||
"chain_info_test.go",
|
||||
"head_test.go",
|
||||
"info_test.go",
|
||||
|
||||
19
beacon-chain/blockchain/blockchain_test.go
Normal file
19
beacon-chain/blockchain/blockchain_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
run := func() int {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
|
||||
return m.Run()
|
||||
}
|
||||
os.Exit(run())
|
||||
}
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
@@ -60,19 +62,19 @@ func TestFinalizedCheckpt_GenesisRootOk(t *testing.T) {
|
||||
func TestCurrentJustifiedCheckpt_CanRetrieve(t *testing.T) {
|
||||
db, sc := testDB.SetupDB(t)
|
||||
|
||||
cp := ðpb.Checkpoint{Epoch: 6, Root: bytesutil.PadTo([]byte("foo"), 32)}
|
||||
c := setupBeaconChain(t, db, sc)
|
||||
assert.Equal(t, params.BeaconConfig().ZeroHash, bytesutil.ToBytes32(c.CurrentJustifiedCheckpt().Root), "Unexpected justified epoch")
|
||||
cp := ðpb.Checkpoint{Epoch: 6, Root: bytesutil.PadTo([]byte("foo"), 32)}
|
||||
c.justifiedCheckpt = cp
|
||||
|
||||
assert.Equal(t, cp.Epoch, c.CurrentJustifiedCheckpt().Epoch, "Unexpected justified epoch")
|
||||
}
|
||||
|
||||
func TestJustifiedCheckpt_GenesisRootOk(t *testing.T) {
|
||||
db, sc := testDB.SetupDB(t)
|
||||
|
||||
c := setupBeaconChain(t, db, sc)
|
||||
genesisRoot := [32]byte{'B'}
|
||||
cp := ðpb.Checkpoint{Root: genesisRoot[:]}
|
||||
c := setupBeaconChain(t, db, sc)
|
||||
c.justifiedCheckpt = cp
|
||||
c.genesisRoot = genesisRoot
|
||||
assert.DeepEqual(t, c.genesisRoot[:], c.CurrentJustifiedCheckpt().Root)
|
||||
@@ -83,6 +85,7 @@ func TestPreviousJustifiedCheckpt_CanRetrieve(t *testing.T) {
|
||||
|
||||
cp := ðpb.Checkpoint{Epoch: 7, Root: bytesutil.PadTo([]byte("foo"), 32)}
|
||||
c := setupBeaconChain(t, db, sc)
|
||||
assert.Equal(t, params.BeaconConfig().ZeroHash, bytesutil.ToBytes32(c.CurrentJustifiedCheckpt().Root), "Unexpected justified epoch")
|
||||
c.prevJustifiedCheckpt = cp
|
||||
assert.Equal(t, cp.Epoch, c.PreviousJustifiedCheckpt().Epoch, "Unexpected previous justified epoch")
|
||||
}
|
||||
@@ -114,6 +117,21 @@ func TestHeadRoot_CanRetrieve(t *testing.T) {
|
||||
assert.Equal(t, [32]byte{'A'}, bytesutil.ToBytes32(r))
|
||||
}
|
||||
|
||||
func TestHeadRoot_UseDB(t *testing.T) {
|
||||
db, _ := testDB.SetupDB(t)
|
||||
c := &Service{beaconDB: db}
|
||||
c.head = &head{root: params.BeaconConfig().ZeroHash}
|
||||
b := testutil.NewBeaconBlock()
|
||||
br, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(context.Background(), b))
|
||||
require.NoError(t, db.SaveStateSummary(context.Background(), &pb.StateSummary{Root: br[:]}))
|
||||
require.NoError(t, db.SaveHeadBlockRoot(context.Background(), br))
|
||||
r, err := c.HeadRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, br, bytesutil.ToBytes32(r))
|
||||
}
|
||||
|
||||
func TestHeadBlock_CanRetrieve(t *testing.T) {
|
||||
b := testutil.NewBeaconBlock()
|
||||
b.Block.Slot = 1
|
||||
@@ -154,6 +172,17 @@ func TestCurrentFork_CanRetrieve(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentFork_NilHeadSTate(t *testing.T) {
|
||||
f := &pb.Fork{
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
}
|
||||
c := &Service{}
|
||||
if !proto.Equal(c.CurrentFork(), f) {
|
||||
t.Error("Received incorrect fork version")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesisValidatorRoot_CanRetrieve(t *testing.T) {
|
||||
// Should not panic if head state is nil.
|
||||
c := &Service{}
|
||||
@@ -201,3 +230,54 @@ func TestIsCanonical_Ok(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, can)
|
||||
}
|
||||
|
||||
func TestService_HeadValidatorsIndices(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisState(t, 10)
|
||||
c := &Service{}
|
||||
|
||||
c.head = &head{}
|
||||
indices, err := c.HeadValidatorsIndices(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(indices))
|
||||
|
||||
c.head = &head{state: s}
|
||||
indices, err = c.HeadValidatorsIndices(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 10, len(indices))
|
||||
}
|
||||
|
||||
func TestService_HeadSeed(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisState(t, 1)
|
||||
c := &Service{}
|
||||
seed, err := helpers.Seed(s, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
require.NoError(t, err)
|
||||
|
||||
c.head = &head{}
|
||||
root, err := c.HeadSeed(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{}, root)
|
||||
|
||||
c.head = &head{state: s}
|
||||
root, err = c.HeadSeed(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, seed, root)
|
||||
}
|
||||
|
||||
func TestService_HeadGenesisValidatorRoot(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisState(t, 1)
|
||||
c := &Service{}
|
||||
|
||||
c.head = &head{}
|
||||
root := c.HeadGenesisValidatorRoot()
|
||||
require.Equal(t, [32]byte{}, root)
|
||||
|
||||
c.head = &head{state: s}
|
||||
root = c.HeadGenesisValidatorRoot()
|
||||
require.DeepEqual(t, root[:], s.GenesisValidatorRoot())
|
||||
}
|
||||
|
||||
func TestService_ProtoArrayStore(t *testing.T) {
|
||||
c := &Service{forkChoiceStore: protoarray.New(0, 0, [32]byte{})}
|
||||
p := c.ProtoArrayStore()
|
||||
require.Equal(t, 0, int(p.FinalizedEpoch()))
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ func (s *Service) cacheJustifiedStateBalances(ctx context.Context, justifiedRoot
|
||||
epoch := helpers.CurrentEpoch(justifiedState)
|
||||
|
||||
justifiedBalances := make([]uint64, justifiedState.NumValidators())
|
||||
if err := justifiedState.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := justifiedState.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if helpers.IsActiveValidatorUsingTrie(val, epoch) {
|
||||
justifiedBalances[idx] = val.EffectiveBalance()
|
||||
} else {
|
||||
|
||||
@@ -78,7 +78,6 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "updates exit pool",
|
||||
args: args{
|
||||
@@ -93,14 +92,13 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
),
|
||||
},
|
||||
check: func(t *testing.T, s *Service) {
|
||||
var n int
|
||||
for i := uint64(0); int(i) < genesis.NumValidators(); i++ {
|
||||
if s.exitPool.HasBeenIncluded(i) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n != 3 {
|
||||
t.Errorf("Did not mark the correct number of exits. Got %d but wanted %d", n, 3)
|
||||
pending := s.exitPool.PendingExits(genesis, 1, true /* no limit */)
|
||||
if len(pending) != 0 {
|
||||
t.Errorf(
|
||||
"Did not mark the correct number of exits. Got %d pending but wanted %d",
|
||||
len(pending),
|
||||
0,
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -38,6 +38,10 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// headSyncMinEpochsAfterCheckpoint defines how many epochs should elapse after known finalization
|
||||
// checkpoint for head sync to be triggered.
|
||||
const headSyncMinEpochsAfterCheckpoint = 128
|
||||
|
||||
// Service represents a service that handles the internal
|
||||
// logic of managing the full PoS beacon chain.
|
||||
type Service struct {
|
||||
@@ -335,6 +339,9 @@ func (s *Service) Stop() error {
|
||||
// Status always returns nil unless there is an error condition that causes
|
||||
// this service to be unhealthy.
|
||||
func (s *Service) Status() error {
|
||||
if s.genesisRoot == params.BeaconConfig().ZeroHash {
|
||||
return errors.New("genesis state has not been created")
|
||||
}
|
||||
if runtime.NumGoroutine() > s.maxRoutines {
|
||||
return fmt.Errorf("too many goroutines %d", runtime.NumGoroutine())
|
||||
}
|
||||
@@ -418,29 +425,6 @@ func (s *Service) initializeChainInfo(ctx context.Context) error {
|
||||
}
|
||||
s.genesisRoot = genesisBlkRoot
|
||||
|
||||
if flags.Get().HeadSync {
|
||||
headBlock, err := s.beaconDB.HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head block")
|
||||
}
|
||||
headRoot, err := headBlock.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash head block")
|
||||
}
|
||||
finalizedState, err := s.stateGen.Resume(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized state from db")
|
||||
}
|
||||
log.Infof("Regenerating state from the last checkpoint at slot %d to current head slot of %d."+
|
||||
"This process may take a while, please wait.", finalizedState.Slot(), headBlock.Block.Slot)
|
||||
headState, err := s.stateGen.StateByRoot(ctx, headRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head state")
|
||||
}
|
||||
s.setHead(headRoot, headBlock, headState)
|
||||
return nil
|
||||
}
|
||||
|
||||
finalized, err := s.beaconDB.FinalizedCheckpoint(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint from db")
|
||||
@@ -458,6 +442,42 @@ func (s *Service) initializeChainInfo(ctx context.Context) error {
|
||||
return errors.Wrap(err, "could not get finalized state from db")
|
||||
}
|
||||
|
||||
if flags.Get().HeadSync {
|
||||
headBlock, err := s.beaconDB.HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head block")
|
||||
}
|
||||
headEpoch := helpers.SlotToEpoch(headBlock.Block.Slot)
|
||||
var epochsSinceFinality uint64
|
||||
if headEpoch > finalized.Epoch {
|
||||
epochsSinceFinality = headEpoch - finalized.Epoch
|
||||
}
|
||||
// Head sync when node is far enough beyond known finalized epoch,
|
||||
// this becomes really useful during long period of non-finality.
|
||||
if epochsSinceFinality >= headSyncMinEpochsAfterCheckpoint {
|
||||
headRoot, err := headBlock.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash head block")
|
||||
}
|
||||
finalizedState, err := s.stateGen.Resume(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized state from db")
|
||||
}
|
||||
log.Infof("Regenerating state from the last checkpoint at slot %d to current head slot of %d."+
|
||||
"This process may take a while, please wait.", finalizedState.Slot(), headBlock.Block.Slot)
|
||||
headState, err := s.stateGen.StateByRoot(ctx, headRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve head state")
|
||||
}
|
||||
s.setHead(headRoot, headBlock, headState)
|
||||
return nil
|
||||
} else {
|
||||
log.Warnf("Finalized checkpoint at slot %d is too close to the current head slot, "+
|
||||
"resetting head from the checkpoint ('--%s' flag is ignored).",
|
||||
finalizedState.Slot(), flags.HeadSync.Name)
|
||||
}
|
||||
}
|
||||
|
||||
finalizedBlock, err := s.beaconDB.Block(ctx, finalizedRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized block from db")
|
||||
|
||||
@@ -3,7 +3,6 @@ package blockchain
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
@@ -32,15 +32,9 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
type mockBeaconNode struct {
|
||||
stateFeed *event.Feed
|
||||
}
|
||||
@@ -323,6 +317,87 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
|
||||
assert.DeepEqual(t, genesis, c.head.block)
|
||||
}
|
||||
|
||||
func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
|
||||
resetFlags := flags.Get()
|
||||
flags.Init(&flags.GlobalFlags{
|
||||
HeadSync: true,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
}()
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
finalizedSlot := params.BeaconConfig().SlotsPerEpoch*2 + 1
|
||||
db, sc := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisBlock := testutil.NewBeaconBlock()
|
||||
genesisRoot, err := genesisBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisRoot))
|
||||
require.NoError(t, db.SaveBlock(ctx, genesisBlock))
|
||||
|
||||
finalizedBlock := testutil.NewBeaconBlock()
|
||||
finalizedBlock.Block.Slot = finalizedSlot
|
||||
finalizedBlock.Block.ParentRoot = genesisRoot[:]
|
||||
finalizedRoot, err := finalizedBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, finalizedBlock))
|
||||
|
||||
// Set head slot close to the finalization point, no head sync is triggered.
|
||||
headBlock := testutil.NewBeaconBlock()
|
||||
headBlock.Block.Slot = finalizedSlot + params.BeaconConfig().SlotsPerEpoch*5
|
||||
headBlock.Block.ParentRoot = finalizedRoot[:]
|
||||
headRoot, err := headBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, headBlock))
|
||||
|
||||
headState := testutil.NewBeaconState()
|
||||
require.NoError(t, headState.SetSlot(headBlock.Block.Slot))
|
||||
require.NoError(t, headState.SetGenesisValidatorRoot(params.BeaconConfig().ZeroHash[:]))
|
||||
require.NoError(t, db.SaveState(ctx, headState, genesisRoot))
|
||||
require.NoError(t, db.SaveState(ctx, headState, finalizedRoot))
|
||||
require.NoError(t, db.SaveState(ctx, headState, headRoot))
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, headRoot))
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{
|
||||
Epoch: helpers.SlotToEpoch(finalizedBlock.Block.Slot),
|
||||
Root: finalizedRoot[:],
|
||||
}))
|
||||
|
||||
c := &Service{beaconDB: db, stateGen: stategen.New(db, sc)}
|
||||
|
||||
require.NoError(t, c.initializeChainInfo(ctx))
|
||||
s, err := c.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
|
||||
assert.Equal(t, genesisRoot, c.genesisRoot, "Genesis block root incorrect")
|
||||
// Since head sync is not triggered, chain is initialized to the last finalization checkpoint.
|
||||
assert.DeepEqual(t, finalizedBlock, c.head.block)
|
||||
assert.LogsContain(t, hook, "resetting head from the checkpoint ('--head-sync' flag is ignored)")
|
||||
assert.LogsDoNotContain(t, hook, "Regenerating state from the last checkpoint at slot")
|
||||
|
||||
// Set head slot far beyond the finalization point, head sync should be triggered.
|
||||
headBlock = testutil.NewBeaconBlock()
|
||||
headBlock.Block.Slot = finalizedSlot + params.BeaconConfig().SlotsPerEpoch*headSyncMinEpochsAfterCheckpoint
|
||||
headBlock.Block.ParentRoot = finalizedRoot[:]
|
||||
headRoot, err = headBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, headBlock))
|
||||
require.NoError(t, db.SaveState(ctx, headState, headRoot))
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, headRoot))
|
||||
|
||||
hook.Reset()
|
||||
require.NoError(t, c.initializeChainInfo(ctx))
|
||||
s, err = c.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
|
||||
assert.Equal(t, genesisRoot, c.genesisRoot, "Genesis block root incorrect")
|
||||
// Head slot is far beyond the latest finalized checkpoint, head sync is triggered.
|
||||
assert.DeepEqual(t, headBlock, c.head.block)
|
||||
assert.LogsContain(t, hook, "Regenerating state from the last checkpoint at slot 225")
|
||||
assert.LogsDoNotContain(t, hook, "resetting head from the checkpoint ('--head-sync' flag is ignored)")
|
||||
}
|
||||
|
||||
func TestChainService_SaveHeadNoDB(t *testing.T) {
|
||||
db, sc := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
2
beacon-chain/cache/BUILD.bazel
vendored
2
beacon-chain/cache/BUILD.bazel
vendored
@@ -57,7 +57,7 @@ go_test(
|
||||
"checkpoint_state_test.go",
|
||||
"committee_fuzz_test.go",
|
||||
"committee_test.go",
|
||||
"feature_flag_test.go",
|
||||
"cache_test.go",
|
||||
"hot_state_cache_test.go",
|
||||
"skip_slot_cache_test.go",
|
||||
"subnet_ids_test.go",
|
||||
|
||||
17
beacon-chain/cache/cache_test.go
vendored
Normal file
17
beacon-chain/cache/cache_test.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
run := func() int {
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{EnableEth1DataVoteCache: true})
|
||||
defer resetCfg()
|
||||
return m.Run()
|
||||
}
|
||||
os.Exit(run())
|
||||
}
|
||||
@@ -50,12 +50,10 @@ type FinalizedDeposits struct {
|
||||
// stores all the deposit related data that is required by the beacon-node.
|
||||
type DepositCache struct {
|
||||
// Beacon chain deposits in memory.
|
||||
pendingDeposits []*dbpb.DepositContainer
|
||||
deposits []*dbpb.DepositContainer
|
||||
finalizedDeposits *FinalizedDeposits
|
||||
depositsLock sync.RWMutex
|
||||
chainStartPubkeys map[string]bool
|
||||
chainStartPubkeysLock sync.RWMutex
|
||||
pendingDeposits []*dbpb.DepositContainer
|
||||
deposits []*dbpb.DepositContainer
|
||||
finalizedDeposits *FinalizedDeposits
|
||||
depositsLock sync.RWMutex
|
||||
}
|
||||
|
||||
// New instantiates a new deposit cache
|
||||
@@ -71,7 +69,6 @@ func New() (*DepositCache, error) {
|
||||
pendingDeposits: []*dbpb.DepositContainer{},
|
||||
deposits: []*dbpb.DepositContainer{},
|
||||
finalizedDeposits: &FinalizedDeposits{Deposits: finalizedDepositsTrie, MerkleTrieIndex: -1},
|
||||
chainStartPubkeys: make(map[string]bool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -151,28 +148,6 @@ func (dc *DepositCache) AllDepositContainers(ctx context.Context) []*dbpb.Deposi
|
||||
return dc.deposits
|
||||
}
|
||||
|
||||
// MarkPubkeyForChainstart sets the pubkey deposit status to true.
|
||||
func (dc *DepositCache) MarkPubkeyForChainstart(ctx context.Context, pubkey string) {
|
||||
ctx, span := trace.StartSpan(ctx, "DepositsCache.MarkPubkeyForChainstart")
|
||||
defer span.End()
|
||||
dc.chainStartPubkeysLock.Lock()
|
||||
defer dc.chainStartPubkeysLock.Unlock()
|
||||
dc.chainStartPubkeys[pubkey] = true
|
||||
}
|
||||
|
||||
// PubkeyInChainstart returns bool for whether the pubkey passed in has deposited.
|
||||
func (dc *DepositCache) PubkeyInChainstart(ctx context.Context, pubkey string) bool {
|
||||
ctx, span := trace.StartSpan(ctx, "DepositsCache.PubkeyInChainstart")
|
||||
defer span.End()
|
||||
dc.chainStartPubkeysLock.RLock()
|
||||
defer dc.chainStartPubkeysLock.RUnlock()
|
||||
if dc.chainStartPubkeys != nil {
|
||||
return dc.chainStartPubkeys[pubkey]
|
||||
}
|
||||
dc.chainStartPubkeys = make(map[string]bool)
|
||||
return false
|
||||
}
|
||||
|
||||
// AllDeposits returns a list of historical deposits until the given block number
|
||||
// (inclusive). If no block is specified then this method returns all historical deposits.
|
||||
func (dc *DepositCache) AllDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit {
|
||||
@@ -269,7 +244,7 @@ func (dc *DepositCache) PruneProofs(ctx context.Context, untilDepositIndex int64
|
||||
dc.depositsLock.Lock()
|
||||
defer dc.depositsLock.Unlock()
|
||||
|
||||
if untilDepositIndex > int64(len(dc.deposits)) {
|
||||
if untilDepositIndex >= int64(len(dc.deposits)) {
|
||||
untilDepositIndex = int64(len(dc.deposits) - 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -700,6 +700,49 @@ func TestPruneProofs_PruneAllWhenDepositIndexTooBig(t *testing.T) {
|
||||
assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[3].Deposit.Proof)
|
||||
}
|
||||
|
||||
func TestPruneProofs_CorrectlyHandleLastIndex(t *testing.T) {
|
||||
dc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
deposits := []struct {
|
||||
blkNum uint64
|
||||
deposit *ethpb.Deposit
|
||||
index int64
|
||||
}{
|
||||
{
|
||||
blkNum: 0,
|
||||
deposit: ðpb.Deposit{Proof: makeDepositProof()},
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
blkNum: 0,
|
||||
deposit: ðpb.Deposit{Proof: makeDepositProof()},
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
blkNum: 0,
|
||||
deposit: ðpb.Deposit{Proof: makeDepositProof()},
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
blkNum: 0,
|
||||
deposit: ðpb.Deposit{Proof: makeDepositProof()},
|
||||
index: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, ins := range deposits {
|
||||
dc.InsertDeposit(context.Background(), ins.deposit, ins.blkNum, ins.index, [32]byte{})
|
||||
}
|
||||
|
||||
require.NoError(t, dc.PruneProofs(context.Background(), 4))
|
||||
|
||||
assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[0].Deposit.Proof)
|
||||
assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[1].Deposit.Proof)
|
||||
assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[2].Deposit.Proof)
|
||||
assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[3].Deposit.Proof)
|
||||
}
|
||||
|
||||
func makeDepositProof() [][]byte {
|
||||
proof := make([][]byte, int(params.BeaconConfig().DepositContractTreeDepth)+1)
|
||||
for i := range proof {
|
||||
|
||||
17
beacon-chain/cache/feature_flag_test.go
vendored
17
beacon-chain/cache/feature_flag_test.go
vendored
@@ -1,17 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{EnableEth1DataVoteCache: true})
|
||||
defer resetCfg()
|
||||
code := m.Run()
|
||||
// os.Exit will prevent defer from being called
|
||||
resetCfg()
|
||||
os.Exit(code)
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func ProcessAttesterSlashings(
|
||||
currentEpoch := helpers.SlotToEpoch(beaconState.Slot())
|
||||
var err error
|
||||
var slashedAny bool
|
||||
var val *stateTrie.ReadOnlyValidator
|
||||
var val stateTrie.ReadOnlyValidator
|
||||
for _, validatorIndex := range slashableIndices {
|
||||
val, err = beaconState.ValidatorAtIndexReadOnly(validatorIndex)
|
||||
if err != nil {
|
||||
|
||||
@@ -429,13 +429,13 @@ func TestFuzzProcessVoluntaryExitsNoVerify_10000(t *testing.T) {
|
||||
func TestFuzzVerifyExit_10000(t *testing.T) {
|
||||
fuzzer := fuzz.NewWithSeed(0)
|
||||
ve := ð.SignedVoluntaryExit{}
|
||||
val := &stateTrie.ReadOnlyValidator{}
|
||||
val := stateTrie.ReadOnlyValidator{}
|
||||
fork := &pb.Fork{}
|
||||
var slot uint64
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
fuzzer.Fuzz(ve)
|
||||
fuzzer.Fuzz(val)
|
||||
fuzzer.Fuzz(&val)
|
||||
fuzzer.Fuzz(fork)
|
||||
fuzzer.Fuzz(&slot)
|
||||
err := VerifyExitAndSignature(val, slot, fork, ve, params.BeaconConfig().ZeroHash[:])
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// ValidatorAlreadyExitedMsg defines a message saying that a validator has already exited.
|
||||
var ValidatorAlreadyExitedMsg = "validator has already submitted an exit, which will take place at epoch"
|
||||
var ValidatorAlreadyExitedMsg = "has already submitted an exit, which will take place at epoch"
|
||||
|
||||
// ValidatorCannotExitYetMsg defines a message saying that a validator cannot exit
|
||||
// because it has not been active long enough.
|
||||
@@ -125,7 +125,7 @@ func ProcessVoluntaryExitsNoVerifySignature(
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
||||
// assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
|
||||
func VerifyExitAndSignature(validator *stateTrie.ReadOnlyValidator, currentSlot uint64, fork *pb.Fork, signed *ethpb.SignedVoluntaryExit, genesisRoot []byte) error {
|
||||
func VerifyExitAndSignature(validator stateTrie.ReadOnlyValidator, currentSlot uint64, fork *pb.Fork, signed *ethpb.SignedVoluntaryExit, genesisRoot []byte) error {
|
||||
if signed == nil || signed.Exit == nil {
|
||||
return errors.New("nil exit")
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func VerifyExitAndSignature(validator *stateTrie.ReadOnlyValidator, currentSlot
|
||||
// assert get_current_epoch(state) >= exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
func verifyExitConditions(validator *stateTrie.ReadOnlyValidator, currentSlot uint64, exit *ethpb.VoluntaryExit) error {
|
||||
func verifyExitConditions(validator stateTrie.ReadOnlyValidator, currentSlot uint64, exit *ethpb.VoluntaryExit) error {
|
||||
currentEpoch := helpers.SlotToEpoch(currentSlot)
|
||||
// Verify the validator is active.
|
||||
if !helpers.IsActiveValidatorUsingTrie(validator, currentEpoch) {
|
||||
@@ -169,7 +169,7 @@ func verifyExitConditions(validator *stateTrie.ReadOnlyValidator, currentSlot ui
|
||||
}
|
||||
// Verify the validator has not yet submitted an exit.
|
||||
if validator.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
||||
return fmt.Errorf("%s: %v", ValidatorAlreadyExitedMsg, validator.ExitEpoch())
|
||||
return fmt.Errorf("validator with index %d %s: %v", exit.ValidatorIndex, ValidatorAlreadyExitedMsg, validator.ExitEpoch())
|
||||
}
|
||||
// Exits must specify an epoch when they become valid; they are not valid before then.
|
||||
if currentEpoch < exit.Epoch {
|
||||
|
||||
@@ -139,7 +139,7 @@ func TestProcessVoluntaryExits_ExitAlreadySubmitted(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
want := "validator has already submitted an exit, which will take place at epoch: 10"
|
||||
want := "validator with index 0 has already submitted an exit, which will take place at epoch: 10"
|
||||
_, err = blocks.ProcessVoluntaryExits(context.Background(), state, b)
|
||||
assert.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestRandaoSignatureSet_OK(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
set, _, err := blocks.RandaoSignatureSet(beaconState, block.Body)
|
||||
set, err := blocks.RandaoSignatureSet(beaconState, block.Body)
|
||||
require.NoError(t, err)
|
||||
verified, err := set.Verify()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -92,16 +92,16 @@ func BlockSignatureSet(beaconState *stateTrie.BeaconState, block *ethpb.SignedBe
|
||||
// from a block and its corresponding state.
|
||||
func RandaoSignatureSet(beaconState *stateTrie.BeaconState,
|
||||
body *ethpb.BeaconBlockBody,
|
||||
) (*bls.SignatureSet, *stateTrie.BeaconState, error) {
|
||||
) (*bls.SignatureSet, error) {
|
||||
buf, proposerPub, domain, err := randaoSigningData(beaconState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
set, err := retrieveSignatureSet(buf, proposerPub, body.RandaoReveal, domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
return set, beaconState, nil
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// retrieves the randao related signing data from the state.
|
||||
|
||||
@@ -366,7 +366,7 @@ func UnslashedAttestingIndices(state *stateTrie.BeaconState, atts []*pb.PendingA
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to look up validator")
|
||||
}
|
||||
if v != nil && v.Slashed() {
|
||||
if !v.IsNil() && v.Slashed() {
|
||||
setIndices = append(setIndices[:i], setIndices[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,9 +196,9 @@ func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9) / (1 * 1e9) * (3*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(29000000000), // 32 * 1e9 - 3000000000
|
||||
// penalty = validator balance / increment * (2*total_penalties) / total_balance * increment
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (1*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -212,9 +212,9 @@ func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (3*1e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
// penalty = validator balance / increment * (2*total_penalties) / total_balance * increment
|
||||
// 500000000 = (32 * 1e9) / (1 * 1e9) * (1*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(32000000000), // 32 * 1e9 - 500000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -229,8 +229,8 @@ func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
Slashings: []uint64{0, 2 * 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9) / (1 * 1e9) * (3*2e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(29000000000), // 32 * 1e9 - 3000000000
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (1*2e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -243,8 +243,8 @@ func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9 - 1*1e9) / (1 * 1e9) * (3*1e9) / (31*1e9) * (1 * 1e9)
|
||||
want: uint64(28000000000), // 31 * 1e9 - 3000000000
|
||||
// 2000000000 = (32 * 1e9 - 1*1e9) / (1 * 1e9) * (2*1e9) / (31*1e9) * (1 * 1e9)
|
||||
want: uint64(30000000000), // 32 * 1e9 - 2000000000
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ func New(ctx context.Context, state *stateTrie.BeaconState) ([]*Validator, *Bala
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
prevEpoch := helpers.PrevEpoch(state)
|
||||
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
// Was validator withdrawable or slashed
|
||||
withdrawable := prevEpoch+1 >= val.WithdrawableEpoch()
|
||||
pVal := &Validator{
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package precompute
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
@@ -28,10 +26,7 @@ func ProcessSlashingsPrecompute(state *stateTrie.BeaconState, pBal *Balance) err
|
||||
|
||||
var hasSlashing bool
|
||||
// Iterate through validator list in state, stop until a validator satisfies slashing condition of current epoch.
|
||||
err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if val == nil {
|
||||
return errors.New("nil validator in state")
|
||||
}
|
||||
err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
correctEpoch := epochToWithdraw == val.WithdrawableEpoch()
|
||||
if val.Slashed() && correctEpoch {
|
||||
hasSlashing = true
|
||||
|
||||
@@ -58,9 +58,9 @@ func TestProcessSlashingsPrecompute_SlashedLess(t *testing.T) {
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9) / (1 * 1e9) * (3*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(29000000000), // 32 * 1e9 - 3000000000
|
||||
// penalty = validator balance / increment * (2*total_penalties) / total_balance * increment
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (1*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -74,9 +74,9 @@ func TestProcessSlashingsPrecompute_SlashedLess(t *testing.T) {
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (3*1e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
// penalty = validator balance / increment * (2*total_penalties) / total_balance * increment
|
||||
// 500000000 = (32 * 1e9) / (1 * 1e9) * (1*1e9) / (32*1e9) * (1 * 1e9)
|
||||
want: uint64(32000000000), // 32 * 1e9 - 500000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -91,8 +91,8 @@ func TestProcessSlashingsPrecompute_SlashedLess(t *testing.T) {
|
||||
Slashings: []uint64{0, 2 * 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9) / (1 * 1e9) * (3*2e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(29000000000), // 32 * 1e9 - 3000000000
|
||||
// 1000000000 = (32 * 1e9) / (1 * 1e9) * (1*2e9) / (64*1e9) * (1 * 1e9)
|
||||
want: uint64(31000000000), // 32 * 1e9 - 1000000000
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
@@ -105,8 +105,8 @@ func TestProcessSlashingsPrecompute_SlashedLess(t *testing.T) {
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
// penalty = validator balance / increment * (3*total_penalties) / total_balance * increment
|
||||
// 3000000000 = (32 * 1e9 - 1*1e9) / (1 * 1e9) * (3*1e9) / (31*1e9) * (1 * 1e9)
|
||||
want: uint64(28000000000), // 31 * 1e9 - 3000000000
|
||||
// 2000000000 = (32 * 1e9 - 1*1e9) / (1 * 1e9) * (2*1e9) / (31*1e9) * (1 * 1e9)
|
||||
want: uint64(30000000000), // 32 * 1e9 - 2000000000
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -31,15 +31,15 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -65,14 +65,14 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -30,8 +30,8 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
@@ -63,8 +63,8 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
|
||||
@@ -8,12 +8,14 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
prevConfig := params.BeaconConfig().Copy()
|
||||
c := params.BeaconConfig()
|
||||
c.MinGenesisActiveValidatorCount = 16384
|
||||
params.OverrideBeaconConfig(c)
|
||||
run := func() int {
|
||||
prevConfig := params.BeaconConfig().Copy()
|
||||
defer params.OverrideBeaconConfig(prevConfig)
|
||||
c := params.BeaconConfig()
|
||||
c.MinGenesisActiveValidatorCount = 16384
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
retVal := m.Run()
|
||||
params.OverrideBeaconConfig(prevConfig)
|
||||
os.Exit(retVal)
|
||||
return m.Run()
|
||||
}
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
@@ -17,6 +19,9 @@ import (
|
||||
// assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||
// return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||
func BlockRootAtSlot(state *stateTrie.BeaconState, slot uint64) ([]byte, error) {
|
||||
if math.MaxUint64-slot < params.BeaconConfig().SlotsPerHistoricalRoot {
|
||||
return []byte{}, errors.New("slot overflows uint64")
|
||||
}
|
||||
if slot >= state.Slot() || state.Slot() > slot+params.BeaconConfig().SlotsPerHistoricalRoot {
|
||||
return []byte{}, errors.Errorf("slot %d out of bounds", slot)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package helpers_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -101,6 +102,11 @@ func TestBlockRootAtSlot_OutOfBounds(t *testing.T) {
|
||||
stateSlot: params.BeaconConfig().SlotsPerHistoricalRoot + 2,
|
||||
expectedErr: "slot 1 out of bounds",
|
||||
},
|
||||
{
|
||||
slot: math.MaxUint64 - 5,
|
||||
stateSlot: 0, // Doesn't matter
|
||||
expectedErr: "slot overflows uint64",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot
|
||||
|
||||
@@ -274,7 +274,7 @@ func ShuffledIndices(state *stateTrie.BeaconState, epoch uint64) ([]uint64, erro
|
||||
}
|
||||
|
||||
indices := make([]uint64, 0, state.NumValidators())
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, epoch) {
|
||||
indices = append(indices, uint64(idx))
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func TotalBalance(state *stateTrie.BeaconState, indices []uint64) uint64 {
|
||||
// return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
|
||||
func TotalActiveBalance(state *stateTrie.BeaconState) (uint64, error) {
|
||||
total := uint64(0)
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, SlotToEpoch(state.Slot())) {
|
||||
total += val.EffectiveBalance()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func IsActiveValidator(validator *ethpb.Validator, epoch uint64) bool {
|
||||
}
|
||||
|
||||
// IsActiveValidatorUsingTrie checks if a read only validator is active.
|
||||
func IsActiveValidatorUsingTrie(validator *stateTrie.ReadOnlyValidator, epoch uint64) bool {
|
||||
func IsActiveValidatorUsingTrie(validator stateTrie.ReadOnlyValidator, epoch uint64) bool {
|
||||
return checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func IsSlashableValidator(activationEpoch, withdrawableEpoch uint64, slashed boo
|
||||
}
|
||||
|
||||
// IsSlashableValidatorUsingTrie checks if a read only validator is slashable.
|
||||
func IsSlashableValidatorUsingTrie(val *stateTrie.ReadOnlyValidator, epoch uint64) bool {
|
||||
func IsSlashableValidatorUsingTrie(val stateTrie.ReadOnlyValidator, epoch uint64) bool {
|
||||
return checkValidatorSlashable(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), epoch)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func ActiveValidatorIndices(state *stateTrie.BeaconState, epoch uint64) ([]uint6
|
||||
return activeIndices, nil
|
||||
}
|
||||
var indices []uint64
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, epoch) {
|
||||
indices = append(indices, uint64(idx))
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func ActiveValidatorCount(state *stateTrie.BeaconState, epoch uint64) (uint64, e
|
||||
}
|
||||
|
||||
count := uint64(0)
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
if err := state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, epoch) {
|
||||
count++
|
||||
}
|
||||
@@ -319,7 +319,7 @@ func IsEligibleForActivationQueue(validator *ethpb.Validator) bool {
|
||||
|
||||
// IsEligibleForActivationQueueUsingTrie checks if the read-only validator is eligible to
|
||||
// be placed into the activation queue.
|
||||
func IsEligibleForActivationQueueUsingTrie(validator *stateTrie.ReadOnlyValidator) bool {
|
||||
func IsEligibleForActivationQueueUsingTrie(validator stateTrie.ReadOnlyValidator) bool {
|
||||
return isEligibileForActivationQueue(validator.ActivationEligibilityEpoch(), validator.EffectiveBalance())
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func IsEligibleForActivation(state *stateTrie.BeaconState, validator *ethpb.Vali
|
||||
}
|
||||
|
||||
// IsEligibleForActivationUsingTrie checks if the validator is eligible for activation.
|
||||
func IsEligibleForActivationUsingTrie(state *stateTrie.BeaconState, validator *stateTrie.ReadOnlyValidator) bool {
|
||||
func IsEligibleForActivationUsingTrie(state *stateTrie.BeaconState, validator stateTrie.ReadOnlyValidator) bool {
|
||||
cpt := state.FinalizedCheckpoint()
|
||||
if cpt == nil {
|
||||
return false
|
||||
|
||||
@@ -16,19 +16,6 @@ import (
|
||||
|
||||
var runAmount = 25
|
||||
|
||||
func TestExecuteStateTransition_FullBlock(t *testing.T) {
|
||||
benchutil.SetBenchmarkConfig()
|
||||
beaconState, err := benchutil.PreGenState1Epoch()
|
||||
require.NoError(t, err)
|
||||
block, err := benchutil.PreGenFullBlock()
|
||||
require.NoError(t, err)
|
||||
|
||||
oldSlot := beaconState.Slot()
|
||||
beaconState, err = state.ExecuteStateTransition(context.Background(), beaconState, block)
|
||||
require.NoError(t, err, "Failed to process block, benchmarks will fail")
|
||||
require.NotEqual(t, oldSlot, beaconState.Slot(), "Expected slots to be different")
|
||||
}
|
||||
|
||||
func BenchmarkExecuteStateTransition_FullBlock(b *testing.B) {
|
||||
benchutil.SetBenchmarkConfig()
|
||||
beaconState, err := benchutil.PreGenState1Epoch()
|
||||
|
||||
@@ -15,6 +15,7 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/fileutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
|
||||
@@ -2,12 +2,12 @@ package interop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
)
|
||||
|
||||
// WriteBlockToDisk as a block ssz. Writes to temp directory. Debug!
|
||||
@@ -27,7 +27,7 @@ func WriteBlockToDisk(block *ethpb.SignedBeaconBlock, failed bool) {
|
||||
log.WithError(err).Error("Failed to ssz encode block")
|
||||
return
|
||||
}
|
||||
if err := ioutil.WriteFile(fp, enc, 0664); err != nil {
|
||||
if err := fileutil.WriteFile(fp, enc); err != nil {
|
||||
log.WithError(err).Error("Failed to write to disk")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ package interop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
)
|
||||
|
||||
// WriteStateToDisk as a state ssz. Writes to temp directory. Debug!
|
||||
@@ -22,7 +22,7 @@ func WriteStateToDisk(state *stateTrie.BeaconState) {
|
||||
log.WithError(err).Error("Failed to ssz encode state")
|
||||
return
|
||||
}
|
||||
if err := ioutil.WriteFile(fp, enc, 0664); err != nil {
|
||||
if err := fileutil.WriteFile(fp, enc); err != nil {
|
||||
log.WithError(err).Error("Failed to write to disk")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["validator_index_map_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
|
||||
@@ -415,7 +415,7 @@ func ProcessBlockNoVerifyAnySig(
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not retrieve block signature set")
|
||||
}
|
||||
rSet, state, err := b.RandaoSignatureSet(state, signed.Block.Body)
|
||||
rSet, err := b.RandaoSignatureSet(state, signed.Block.Body)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not retrieve randao signature set")
|
||||
|
||||
@@ -45,7 +45,7 @@ func InitiateValidatorExit(state *stateTrie.BeaconState, idx uint64) (*stateTrie
|
||||
return state, nil
|
||||
}
|
||||
var exitEpochs []uint64
|
||||
err = state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
err = state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if val.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
||||
exitEpochs = append(exitEpochs, val.ExitEpoch())
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func InitiateValidatorExit(state *stateTrie.BeaconState, idx uint64) (*stateTrie
|
||||
|
||||
// We use the exit queue churn to determine if we have passed a churn limit.
|
||||
exitQueueChurn := uint64(0)
|
||||
err = state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
|
||||
err = state.ReadFromEveryValidator(func(idx int, val stateTrie.ReadOnlyValidator) error {
|
||||
if val.ExitEpoch() == exitQueueEpoch {
|
||||
exitQueueChurn++
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package kv
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -40,7 +39,7 @@ func (s *Store) Backup(ctx context.Context, outputDir string) error {
|
||||
return errors.New("no head block")
|
||||
}
|
||||
// Ensure the backups directory exists.
|
||||
if err := os.MkdirAll(backupsDir, params.BeaconIoConfig().ReadWriteExecutePermissions); err != nil {
|
||||
if err := fileutil.MkdirAll(backupsDir); err != nil {
|
||||
return err
|
||||
}
|
||||
backupPath := path.Join(backupsDir, fmt.Sprintf("prysm_beacondb_at_slot_%07d.backup", head.Block.Slot))
|
||||
|
||||
@@ -106,15 +106,13 @@ func TestStore_BlocksHandleZeroCase(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
numBlocks := 10
|
||||
totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks)
|
||||
blockRoots := make([][32]byte, 0)
|
||||
for i := 0; i < len(totalBlocks); i++ {
|
||||
b := testutil.NewBeaconBlock()
|
||||
b.Block.Slot = uint64(i)
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
totalBlocks[i] = b
|
||||
r, err := totalBlocks[i].Block.HashTreeRoot()
|
||||
_, err := totalBlocks[i].Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
blockRoots = append(blockRoots, r)
|
||||
}
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
zeroFilter := filters.NewFilter().SetStartSlot(0).SetEndSlot(0)
|
||||
@@ -128,16 +126,14 @@ func TestStore_BlocksHandleInvalidEndSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
numBlocks := 10
|
||||
totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks)
|
||||
blockRoots := make([][32]byte, 0)
|
||||
// Save blocks from slot 1 onwards.
|
||||
for i := 0; i < len(totalBlocks); i++ {
|
||||
b := testutil.NewBeaconBlock()
|
||||
b.Block.Slot = uint64(i) + 1
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
totalBlocks[i] = b
|
||||
r, err := totalBlocks[i].Block.HashTreeRoot()
|
||||
_, err := totalBlocks[i].Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
blockRoots = append(blockRoots, r)
|
||||
}
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
badFilter := filters.NewFilter().SetStartSlot(5).SetEndSlot(1)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
prombolt "github.com/prysmaticlabs/prombbolt"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
@@ -46,9 +47,15 @@ type Store struct {
|
||||
// path specified, creates the kv-buckets based on the schema, and stores
|
||||
// an open connection db object as a property of the Store struct.
|
||||
func NewKVStore(dirPath string, stateSummaryCache *cache.StateSummaryCache) (*Store, error) {
|
||||
if err := os.MkdirAll(dirPath, params.BeaconIoConfig().ReadWriteExecutePermissions); err != nil {
|
||||
hasDir, err := fileutil.HasDir(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !hasDir {
|
||||
if err := fileutil.MkdirAll(dirPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
datafile := path.Join(dirPath, databaseFileName)
|
||||
boltDB, err := bolt.Open(datafile, params.BeaconIoConfig().ReadWritePermissions, &bolt.Options{Timeout: 1 * time.Second, InitialMmapSize: 10e6})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
// setupDB instantiates and returns a Store instance.
|
||||
func setupDB(t testing.TB) *Store {
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
require.NoError(t, err, "Could not generate random file path")
|
||||
p := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
require.NoError(t, os.RemoveAll(p), "Failed to remove directory")
|
||||
db, err := NewKVStore(p, cache.NewStateSummaryCache())
|
||||
db, err := NewKVStore(t.TempDir(), cache.NewStateSummaryCache())
|
||||
require.NoError(t, err, "Failed to instantiate DB")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, db.Close(), "Failed to close database")
|
||||
require.NoError(t, os.RemoveAll(db.DatabasePath()), "Failed to remove directory")
|
||||
})
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
@@ -120,23 +121,6 @@ func (s *Store) HasState(ctx context.Context, blockRoot [32]byte) bool {
|
||||
func (s *Store) DeleteState(ctx context.Context, blockRoot [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteState")
|
||||
defer span.End()
|
||||
return s.DeleteStates(ctx, [][32]byte{blockRoot})
|
||||
}
|
||||
|
||||
// DeleteStates by block roots.
|
||||
//
|
||||
// Note: bkt.Delete(key) uses a binary search to find the item in the database. Iterating with a
|
||||
// cursor is faster when there are a large set of keys to delete. This method is O(n) deletion where
|
||||
// n is the number of keys in the database. The alternative of calling bkt.Delete on each key to
|
||||
// delete would be O(m*log(n)) which would be much slower given a large set of keys to delete.
|
||||
func (s *Store) DeleteStates(ctx context.Context, blockRoots [][32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteStates")
|
||||
defer span.End()
|
||||
|
||||
rootMap := make(map[[32]byte]bool, len(blockRoots))
|
||||
for _, blockRoot := range blockRoots {
|
||||
rootMap[blockRoot] = true
|
||||
}
|
||||
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
@@ -154,34 +138,38 @@ func (s *Store) DeleteStates(ctx context.Context, blockRoots [][32]byte) error {
|
||||
blockBkt := tx.Bucket(blocksBucket)
|
||||
headBlkRoot := blockBkt.Get(headBlockRootKey)
|
||||
bkt = tx.Bucket(stateBucket)
|
||||
c := bkt.Cursor()
|
||||
|
||||
for blockRoot, _ := c.First(); blockRoot != nil; blockRoot, _ = c.Next() {
|
||||
if !rootMap[bytesutil.ToBytes32(blockRoot)] {
|
||||
continue
|
||||
}
|
||||
// Safe guard against deleting genesis, finalized, head state.
|
||||
if bytes.Equal(blockRoot, checkpoint.Root) || bytes.Equal(blockRoot, genesisBlockRoot) || bytes.Equal(blockRoot, headBlkRoot) {
|
||||
return errors.New("cannot delete genesis, finalized, or head state")
|
||||
}
|
||||
|
||||
slot, err := slotByBlockRoot(ctx, tx, blockRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
indicesByBucket := createStateIndicesFromStateSlot(ctx, slot)
|
||||
if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot, tx); err != nil {
|
||||
return errors.Wrap(err, "could not delete root for DB indices")
|
||||
}
|
||||
|
||||
if err := c.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Safe guard against deleting genesis, finalized, head state.
|
||||
if bytes.Equal(blockRoot[:], checkpoint.Root) || bytes.Equal(blockRoot[:], genesisBlockRoot) || bytes.Equal(blockRoot[:], headBlkRoot) {
|
||||
return errors.New("cannot delete genesis, finalized, or head state")
|
||||
}
|
||||
return nil
|
||||
|
||||
slot, err := slotByBlockRoot(ctx, tx, blockRoot[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
indicesByBucket := createStateIndicesFromStateSlot(ctx, slot)
|
||||
if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
|
||||
return errors.Wrap(err, "could not delete root for DB indices")
|
||||
}
|
||||
|
||||
return bkt.Delete(blockRoot[:])
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteStates by block roots.
|
||||
func (s *Store) DeleteStates(ctx context.Context, blockRoots [][32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteStates")
|
||||
defer span.End()
|
||||
|
||||
for _, r := range blockRoots {
|
||||
if err := s.DeleteState(ctx, r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// creates state from marshaled proto state bytes.
|
||||
func createState(ctx context.Context, enc []byte) (*pb.BeaconState, error) {
|
||||
protoState := &pb.BeaconState{}
|
||||
@@ -327,6 +315,7 @@ func createStateIndicesFromStateSlot(ctx context.Context, slot uint64) map[strin
|
||||
// (e.g. archived_interval=2048, states with slots after 1365).
|
||||
// This is to tolerate skip slots. Not every state lays on the boundary.
|
||||
// 3.) state with current finalized root
|
||||
// 4.) unfinalized States
|
||||
func (s *Store) CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint uint64) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB. CleanUpDirtyStates")
|
||||
defer span.End()
|
||||
@@ -335,6 +324,10 @@ func (s *Store) CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint ui
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalizedSlot, err := helpers.StartSlot(f.Epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deletedRoots := make([][32]byte, 0)
|
||||
|
||||
err = s.db.View(func(tx *bolt.Tx) error {
|
||||
@@ -344,11 +337,13 @@ func (s *Store) CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint ui
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
finalized := bytesutil.ToBytes32(f.Root) == bytesutil.ToBytes32(v)
|
||||
finalizedChkpt := bytesutil.ToBytes32(f.Root) == bytesutil.ToBytes32(v)
|
||||
slot := bytesutil.BytesToUint64BigEndian(k)
|
||||
mod := slot % slotsPerArchivedPoint
|
||||
// The following conditions cover 1, 2, and 3 above.
|
||||
if mod != 0 && mod <= slotsPerArchivedPoint-slotsPerArchivedPoint/3 && !finalized {
|
||||
nonFinalized := slot > finalizedSlot
|
||||
|
||||
// The following conditions cover 1, 2, 3 and 4 above.
|
||||
if mod != 0 && mod <= slotsPerArchivedPoint-slotsPerArchivedPoint/3 && !finalizedChkpt && !nonFinalized {
|
||||
deletedRoots = append(deletedRoots, bytesutil.ToBytes32(v))
|
||||
}
|
||||
return nil
|
||||
@@ -358,6 +353,11 @@ func (s *Store) CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint ui
|
||||
return err
|
||||
}
|
||||
|
||||
// Length of to be deleted roots is 0. Nothing to do.
|
||||
if len(deletedRoots) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithField("count", len(deletedRoots)).Info("Cleaning up dirty states")
|
||||
if err := s.DeleteStates(ctx, deletedRoots); err != nil {
|
||||
return err
|
||||
|
||||
@@ -231,21 +231,30 @@ func TestStore_GenesisState_CanGetHighestBelow(t *testing.T) {
|
||||
func TestStore_CleanUpDirtyStates_AboveThreshold(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
genesisState := testutil.NewBeaconState()
|
||||
genesisRoot := [32]byte{'a'}
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot))
|
||||
require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot))
|
||||
|
||||
bRoots := make([][32]byte, 0)
|
||||
slotsPerArchivedPoint := uint64(128)
|
||||
prevRoot := genesisRoot
|
||||
for i := uint64(1); i <= slotsPerArchivedPoint; i++ {
|
||||
b := testutil.NewBeaconBlock()
|
||||
b.Block.Slot = i
|
||||
b.Block.ParentRoot = prevRoot[:]
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(context.Background(), b))
|
||||
bRoots = append(bRoots, r)
|
||||
prevRoot = r
|
||||
|
||||
st := testutil.NewBeaconState()
|
||||
require.NoError(t, st.SetSlot(i))
|
||||
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||
}
|
||||
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), ðpb.Checkpoint{Root: bRoots[len(bRoots)-1][:], Epoch: slotsPerArchivedPoint / params.BeaconConfig().SlotsPerEpoch}))
|
||||
require.NoError(t, db.CleanUpDirtyStates(context.Background(), slotsPerArchivedPoint))
|
||||
|
||||
for i, root := range bRoots {
|
||||
@@ -281,3 +290,33 @@ func TestStore_CleanUpDirtyStates_Finalized(t *testing.T) {
|
||||
require.NoError(t, db.CleanUpDirtyStates(context.Background(), params.BeaconConfig().SlotsPerEpoch))
|
||||
require.Equal(t, true, db.HasState(context.Background(), genesisRoot))
|
||||
}
|
||||
|
||||
func TestStore_CleanUpDirtyStates_DontDeleteNonFinalized(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
genesisState := testutil.NewBeaconState()
|
||||
genesisRoot := [32]byte{'a'}
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genesisRoot))
|
||||
require.NoError(t, db.SaveState(context.Background(), genesisState, genesisRoot))
|
||||
|
||||
unfinalizedRoots := [][32]byte{}
|
||||
for i := uint64(1); i <= params.BeaconConfig().SlotsPerEpoch; i++ {
|
||||
b := testutil.NewBeaconBlock()
|
||||
b.Block.Slot = i
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(context.Background(), b))
|
||||
unfinalizedRoots = append(unfinalizedRoots, r)
|
||||
|
||||
st := testutil.NewBeaconState()
|
||||
require.NoError(t, st.SetSlot(i))
|
||||
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||
}
|
||||
|
||||
require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), ðpb.Checkpoint{Root: genesisRoot[:]}))
|
||||
require.NoError(t, db.CleanUpDirtyStates(context.Background(), params.BeaconConfig().SlotsPerEpoch))
|
||||
|
||||
for _, rt := range unfinalizedRoots {
|
||||
require.Equal(t, true, db.HasState(context.Background(), rt))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,5 @@ go_library(
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//shared/rand:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -3,27 +3,17 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/shared/rand"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
// SetupDB instantiates and returns database backed by key value store.
|
||||
func SetupDB(t testing.TB) (db.Database, *cache.StateSummaryCache) {
|
||||
randPath := rand.NewDeterministicGenerator().Int()
|
||||
p := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
if err := os.RemoveAll(p); err != nil {
|
||||
t.Fatalf("failed to remove directory: %v", err)
|
||||
}
|
||||
sc := cache.NewStateSummaryCache()
|
||||
s, err := kv.NewKVStore(p, sc)
|
||||
s, err := kv.NewKVStore(t.TempDir(), sc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -31,9 +21,6 @@ func SetupDB(t testing.TB) (db.Database, *cache.StateSummaryCache) {
|
||||
if err := s.Close(); err != nil {
|
||||
t.Fatalf("failed to close database: %v", err)
|
||||
}
|
||||
if err := os.RemoveAll(s.DatabasePath()); err != nil {
|
||||
t.Fatalf("could not remove tmp db dir: %v", err)
|
||||
}
|
||||
})
|
||||
return s, sc
|
||||
}
|
||||
|
||||
@@ -130,6 +130,10 @@ var (
|
||||
Name: "enable-debug-rpc-endpoints",
|
||||
Usage: "Enables the debug rpc service, containing utility endpoints such as /eth/v1alpha1/beacon/state.",
|
||||
}
|
||||
SubscribeToAllSubnets = &cli.BoolFlag{
|
||||
Name: "subscribe-all-subnets",
|
||||
Usage: "Subscribe to all possible attestation subnets.",
|
||||
}
|
||||
// HistoricalSlasherNode is a set of beacon node flags required for performing historical detection with a slasher.
|
||||
HistoricalSlasherNode = &cli.BoolFlag{
|
||||
Name: "historical-slasher-node",
|
||||
|
||||
@@ -12,6 +12,7 @@ type GlobalFlags struct {
|
||||
HeadSync bool
|
||||
DisableSync bool
|
||||
DisableDiscv5 bool
|
||||
SubscribeToAllSubnets bool
|
||||
MinimumSyncPeers int
|
||||
BlockBatchLimit int
|
||||
BlockBatchLimitBurstFactor int
|
||||
@@ -44,6 +45,10 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
|
||||
log.Warn("Using Disable Sync flag, using this flag on a live network might lead to adverse consequences.")
|
||||
cfg.DisableSync = true
|
||||
}
|
||||
if ctx.Bool(SubscribeToAllSubnets.Name) {
|
||||
log.Warn("Subscribing to All Attestation Subnets")
|
||||
cfg.SubscribeToAllSubnets = true
|
||||
}
|
||||
cfg.DisableDiscv5 = ctx.Bool(DisableDiscv5.Name)
|
||||
cfg.BlockBatchLimit = ctx.Int(BlockBatchLimit.Name)
|
||||
cfg.BlockBatchLimitBurstFactor = ctx.Int(BlockBatchLimitBurstFactor.Name)
|
||||
|
||||
@@ -25,23 +25,10 @@ go_binary(
|
||||
|
||||
go_image(
|
||||
name = "image",
|
||||
srcs = [
|
||||
"main.go",
|
||||
],
|
||||
base = "//tools:go_image",
|
||||
goarch = "amd64",
|
||||
goos = "linux",
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/gateway/server",
|
||||
race = "off",
|
||||
binary = ":server",
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//beacon-chain/gateway:go_default_library",
|
||||
"@com_github_grpc_ecosystem_grpc_gateway//runtime:go_default_library",
|
||||
"@com_github_joonix_log//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"//shared/maxprocs:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
container_bundle(
|
||||
|
||||
@@ -203,12 +203,8 @@ func (s *Service) saveGenesisState(ctx context.Context, genesisState *stateTrie.
|
||||
return errors.Wrap(err, "could not save finalized checkpoint")
|
||||
}
|
||||
|
||||
pubKeys := make([][48]byte, 0, genesisState.NumValidators())
|
||||
indices := make([]uint64, 0, genesisState.NumValidators())
|
||||
for i := uint64(0); i < uint64(genesisState.NumValidators()); i++ {
|
||||
pk := genesisState.PubkeyAtIndex(i)
|
||||
pubKeys = append(pubKeys, pk)
|
||||
indices = append(indices, i)
|
||||
s.chainStartDeposits[i] = ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: pk[:],
|
||||
|
||||
@@ -50,6 +50,7 @@ var appFlags = []cli.Flag{
|
||||
flags.InteropGenesisTimeFlag,
|
||||
flags.SlotsPerArchivedPoint,
|
||||
flags.EnableDebugRPCEndpoints,
|
||||
flags.SubscribeToAllSubnets,
|
||||
flags.EnableBackupWebhookFlag,
|
||||
flags.BackupWebhookOutputDir,
|
||||
flags.HistoricalSlasherNode,
|
||||
|
||||
@@ -58,7 +58,6 @@ go_test(
|
||||
deps = [
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//shared/cmd:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
|
||||
@@ -602,6 +602,7 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
key := b.cliCtx.String(flags.KeyFlag.Name)
|
||||
mockEth1DataVotes := b.cliCtx.Bool(flags.InteropMockEth1DataVotesFlag.Name)
|
||||
enableDebugRPCEndpoints := b.cliCtx.Bool(flags.EnableDebugRPCEndpoints.Name)
|
||||
maxMsgSize := b.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
|
||||
p2pService := b.fetchP2P()
|
||||
rpcService := rpc.NewService(b.ctx, &rpc.Config{
|
||||
Host: host,
|
||||
@@ -634,6 +635,7 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
OperationNotifier: b,
|
||||
StateGen: b.stateGen,
|
||||
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
|
||||
MaxMsgSize: maxMsgSize,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rpcService)
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/cmd"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -26,8 +23,7 @@ var _ statefeed.Notifier = (*BeaconNode)(nil)
|
||||
func TestNodeClose_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
tmp := fmt.Sprintf("%s/datadirtest2", testutil.TempDir())
|
||||
require.NoError(t, os.RemoveAll(tmp))
|
||||
tmp := fmt.Sprintf("%s/datadirtest2", t.TempDir())
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
@@ -49,11 +45,8 @@ func TestNodeClose_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBootStrapNodeFile(t *testing.T) {
|
||||
file, err := ioutil.TempFile(testutil.TempDir(), "bootstrapFile")
|
||||
file, err := ioutil.TempFile(t.TempDir(), "bootstrapFile")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, os.Remove(file.Name()))
|
||||
}()
|
||||
|
||||
sampleNode0 := "- enr:-Ku4QMKVC_MowDsmEa20d5uGjrChI0h8_KsKXDmgVQbIbngZV0i" +
|
||||
"dV6_RL7fEtZGo-kTNZ5o7_EJI_vCPJ6scrhwX0Z4Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD" +
|
||||
@@ -74,10 +67,7 @@ func TestBootStrapNodeFile(t *testing.T) {
|
||||
func TestClearDB(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
require.NoError(t, err, "Could not generate random number for file path")
|
||||
tmp := filepath.Join(testutil.TempDir(), fmt.Sprintf("datadirtest%d", randPath))
|
||||
require.NoError(t, os.RemoveAll(tmp))
|
||||
tmp := filepath.Join(t.TempDir(), "datadirtest")
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
@@ -85,7 +75,7 @@ func TestClearDB(t *testing.T) {
|
||||
set.Bool(cmd.ForceClearDB.Name, true, "force clear db")
|
||||
|
||||
context := cli.NewContext(&app, set, nil)
|
||||
_, err = NewBeaconNode(context)
|
||||
_, err := NewBeaconNode(context)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.LogsContain(t, hook, "Removing database")
|
||||
|
||||
@@ -6,12 +6,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
numPendingAttesterSlashingFailedSigVerify = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "pending_attester_slashing_fail_sig_verify_total",
|
||||
Help: "Times an pending attester slashing fails sig verification",
|
||||
},
|
||||
)
|
||||
numPendingAttesterSlashings = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_pending_attester_slashings",
|
||||
@@ -24,18 +18,6 @@ var (
|
||||
Help: "Number of attester slashings included in blocks",
|
||||
},
|
||||
)
|
||||
attesterSlashingReattempts = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "attester_slashing_reattempts_total",
|
||||
Help: "Times an attester slashing for an already slashed validator is received",
|
||||
},
|
||||
)
|
||||
numPendingProposerSlashingFailedSigVerify = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "pending_proposer_slashing_fail_sig_verify_total",
|
||||
Help: "Times an pending proposer slashing fails sig verification",
|
||||
},
|
||||
)
|
||||
numPendingProposerSlashings = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_pending_proposer_slashings",
|
||||
@@ -48,10 +30,4 @@ var (
|
||||
Help: "Number of proposer slashings included in blocks",
|
||||
},
|
||||
)
|
||||
proposerSlashingReattempts = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "proposer_slashing_reattempts_total",
|
||||
Help: "Times a proposer slashing for an already slashed validator is received",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -124,7 +124,6 @@ func (p *Pool) InsertAttesterSlashing(
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyAttesterSlashing(ctx, state, slashing); err != nil {
|
||||
numPendingAttesterSlashingFailedSigVerify.Inc()
|
||||
return errors.Wrap(err, "could not verify attester slashing")
|
||||
}
|
||||
|
||||
@@ -139,7 +138,6 @@ func (p *Pool) InsertAttesterSlashing(
|
||||
// If the validator has already exited, has already been slashed, or if its index
|
||||
// has been recently included in the pool of slashings, skip including this indice.
|
||||
if !ok {
|
||||
attesterSlashingReattempts.Inc()
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
@@ -150,7 +148,6 @@ func (p *Pool) InsertAttesterSlashing(
|
||||
return p.pendingAttesterSlashing[i].validatorToSlash >= val
|
||||
})
|
||||
if found != len(p.pendingAttesterSlashing) && p.pendingAttesterSlashing[found].validatorToSlash == val {
|
||||
attesterSlashingReattempts.Inc()
|
||||
cantSlash = append(cantSlash, val)
|
||||
continue
|
||||
}
|
||||
@@ -185,7 +182,6 @@ func (p *Pool) InsertProposerSlashing(
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyProposerSlashing(state, slashing); err != nil {
|
||||
numPendingProposerSlashingFailedSigVerify.Inc()
|
||||
return errors.Wrap(err, "could not verify proposer slashing")
|
||||
}
|
||||
|
||||
@@ -198,7 +194,6 @@ func (p *Pool) InsertProposerSlashing(
|
||||
// has been recently included in the pool of slashings, do not process this new
|
||||
// slashing.
|
||||
if !ok {
|
||||
proposerSlashingReattempts.Inc()
|
||||
return fmt.Errorf("validator at index %d cannot be slashed", idx)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ go_test(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
|
||||
|
||||
@@ -12,20 +12,18 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// Pool implements a struct to maintain pending and recently included voluntary exits. This pool
|
||||
// Pool implements a struct to maintain pending and seen voluntary exits. This pool
|
||||
// is used by proposers to insert into new blocks.
|
||||
type Pool struct {
|
||||
lock sync.RWMutex
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
included map[uint64]bool
|
||||
lock sync.RWMutex
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
// NewPool accepts a head fetcher (for reading the validator set) and returns an initialized
|
||||
// voluntary exit pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
included: make(map[uint64]bool),
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,48 +44,48 @@ func (p *Pool) PendingExits(state *beaconstate.BeaconState, slot uint64, noLimit
|
||||
if e.Exit.Epoch > helpers.SlotToEpoch(slot) {
|
||||
continue
|
||||
}
|
||||
if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil && v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
||||
if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil &&
|
||||
v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch {
|
||||
pending = append(pending, e)
|
||||
if uint64(len(pending)) == maxExits {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if uint64(len(pending)) > maxExits {
|
||||
pending = pending[:maxExits]
|
||||
}
|
||||
return pending
|
||||
}
|
||||
|
||||
// InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists,
|
||||
// has been included recently, or the validator is already exited.
|
||||
// or the validator is already exited.
|
||||
func (p *Pool) InsertVoluntaryExit(ctx context.Context, state *beaconstate.BeaconState, exit *ethpb.SignedVoluntaryExit) {
|
||||
ctx, span := trace.StartSpan(ctx, "exitPool.InsertVoluntaryExit")
|
||||
defer span.End()
|
||||
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
// Has this validator index been included recently?
|
||||
if p.included[exit.Exit.ValidatorIndex] {
|
||||
// Prevent malformed messages from being inserted.
|
||||
if exit == nil || exit.Exit == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Has the validator been exited already?
|
||||
if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil || v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
||||
return
|
||||
}
|
||||
|
||||
// Does this validator exist in the list already? Use binary search to find the answer.
|
||||
if found := sort.Search(len(p.pending), func(i int) bool {
|
||||
e := p.pending[i].Exit
|
||||
return e.ValidatorIndex == exit.Exit.ValidatorIndex
|
||||
}); found != len(p.pending) {
|
||||
// If an exit exists with this validator index, prefer one with an earlier exit epoch.
|
||||
if p.pending[found].Exit.Epoch > exit.Exit.Epoch {
|
||||
p.pending[found] = exit
|
||||
existsInPending, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
|
||||
// If the item exists in the pending list and includes a more favorable, earlier
|
||||
// exit epoch, we replace it in the pending list. If it exists but the prior condition is false,
|
||||
// we simply return.
|
||||
if existsInPending {
|
||||
if exit.Exit.Epoch < p.pending[index].Exit.Epoch {
|
||||
p.pending[index] = exit
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Insert into pending list and sort again.
|
||||
// Has the validator been exited already?
|
||||
if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil ||
|
||||
v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
|
||||
return
|
||||
}
|
||||
|
||||
// Insert into pending list and sort.
|
||||
p.pending = append(p.pending, exit)
|
||||
sort.Slice(p.pending, func(i, j int) bool {
|
||||
return p.pending[i].Exit.ValidatorIndex < p.pending[j].Exit.ValidatorIndex
|
||||
@@ -95,22 +93,25 @@ func (p *Pool) InsertVoluntaryExit(ctx context.Context, state *beaconstate.Beaco
|
||||
}
|
||||
|
||||
// MarkIncluded is used when an exit has been included in a beacon block. Every block seen by this
|
||||
// node should call this method to include the exit.
|
||||
// node should call this method to include the exit. This will remove the exit from
|
||||
// the pending exits slice.
|
||||
func (p *Pool) MarkIncluded(exit *ethpb.SignedVoluntaryExit) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
i := sort.Search(len(p.pending), func(i int) bool {
|
||||
return p.pending[i].Exit.ValidatorIndex == exit.Exit.ValidatorIndex
|
||||
})
|
||||
if i != len(p.pending) {
|
||||
p.pending = append(p.pending[:i], p.pending[i+1:]...)
|
||||
exists, index := existsInList(p.pending, exit.Exit.ValidatorIndex)
|
||||
if exists {
|
||||
// Exit we want is present at p.pending[index], so we remove it.
|
||||
p.pending = append(p.pending[:index], p.pending[index+1:]...)
|
||||
}
|
||||
p.included[exit.Exit.ValidatorIndex] = true
|
||||
}
|
||||
|
||||
// HasBeenIncluded returns true if the pool has recorded that a validator index has been recorded.
|
||||
func (p *Pool) HasBeenIncluded(bIdx uint64) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.included[bIdx]
|
||||
// Binary search to check if the index exists in the list of pending exits.
|
||||
func existsInList(pending []*ethpb.SignedVoluntaryExit, searchingFor uint64) (bool, int) {
|
||||
i := sort.Search(len(pending), func(j int) bool {
|
||||
return pending[j].Exit.ValidatorIndex >= searchingFor
|
||||
})
|
||||
if i < len(pending) && pending[i].Exit.ValidatorIndex == searchingFor {
|
||||
return true, i
|
||||
}
|
||||
return false, -1
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@ import (
|
||||
beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
type fields struct {
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
included map[uint64]bool
|
||||
pending []*ethpb.SignedVoluntaryExit
|
||||
}
|
||||
type args struct {
|
||||
exit *ethpb.SignedVoluntaryExit
|
||||
@@ -28,11 +26,32 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
args args
|
||||
want []*ethpb.SignedVoluntaryExit
|
||||
}{
|
||||
{
|
||||
name: "Prevent inserting nil exit",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: nil,
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "Prevent inserting malformed exit",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: nil,
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
{
|
||||
name: "Empty list",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
included: make(map[uint64]bool),
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
@@ -62,7 +81,6 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
included: make(map[uint64]bool),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
@@ -82,7 +100,7 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate exit with lower epoch",
|
||||
name: "Duplicate exit in pending list",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
@@ -92,12 +110,11 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
included: make(map[uint64]bool),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 10,
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
@@ -105,7 +122,65 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 10,
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate validator index",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 20,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Duplicate received with more favorable exit epoch",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 4,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 4,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
@@ -114,8 +189,7 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
{
|
||||
name: "Exit for already exited validator",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{},
|
||||
included: make(map[uint64]bool),
|
||||
pending: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
@@ -144,7 +218,6 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
included: make(map[uint64]bool),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
@@ -175,24 +248,6 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Already included",
|
||||
fields: fields{
|
||||
pending: make([]*ethpb.SignedVoluntaryExit, 0),
|
||||
included: map[uint64]bool{
|
||||
1: true,
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 12,
|
||||
ValidatorIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*ethpb.SignedVoluntaryExit{},
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
validators := []*ethpb.Validator{
|
||||
@@ -212,8 +267,7 @@ func TestPool_InsertVoluntaryExit(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Pool{
|
||||
pending: tt.fields.pending,
|
||||
included: tt.fields.included,
|
||||
pending: tt.fields.pending,
|
||||
}
|
||||
s, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators})
|
||||
require.NoError(t, err)
|
||||
@@ -244,32 +298,6 @@ func TestPool_MarkIncluded(t *testing.T) {
|
||||
args args
|
||||
want fields
|
||||
}{
|
||||
{
|
||||
name: "Included, does not exist in pending",
|
||||
fields: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 2},
|
||||
},
|
||||
},
|
||||
included: make(map[uint64]bool),
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 3},
|
||||
},
|
||||
},
|
||||
want: fields{
|
||||
pending: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 2},
|
||||
},
|
||||
},
|
||||
included: map[uint64]bool{
|
||||
3: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Removes from pending list",
|
||||
fields: fields{
|
||||
@@ -284,9 +312,6 @@ func TestPool_MarkIncluded(t *testing.T) {
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 3},
|
||||
},
|
||||
},
|
||||
included: map[uint64]bool{
|
||||
0: true,
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
exit: ðpb.SignedVoluntaryExit{
|
||||
@@ -302,18 +327,13 @@ func TestPool_MarkIncluded(t *testing.T) {
|
||||
Exit: ðpb.VoluntaryExit{ValidatorIndex: 3},
|
||||
},
|
||||
},
|
||||
included: map[uint64]bool{
|
||||
0: true,
|
||||
2: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Pool{
|
||||
pending: tt.fields.pending,
|
||||
included: tt.fields.included,
|
||||
pending: tt.fields.pending,
|
||||
}
|
||||
p.MarkIncluded(tt.args.exit)
|
||||
if len(p.pending) != len(tt.want.pending) {
|
||||
@@ -324,7 +344,6 @@ func TestPool_MarkIncluded(t *testing.T) {
|
||||
t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want.pending[i])
|
||||
}
|
||||
}
|
||||
assert.DeepEqual(t, tt.want.included, p.included)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ go_library(
|
||||
"discovery.go",
|
||||
"doc.go",
|
||||
"fork.go",
|
||||
"gossip_scoring_params.go",
|
||||
"gossip_topic_mappings.go",
|
||||
"handshake.go",
|
||||
"info.go",
|
||||
@@ -48,6 +49,8 @@ go_library(
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/fileutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/iputils:go_default_library",
|
||||
"//shared/p2putils:go_default_library",
|
||||
|
||||
@@ -26,11 +26,15 @@ func (s *Service) InterceptPeerDial(_ peer.ID) (allow bool) {
|
||||
|
||||
// InterceptAddrDial tests whether we're permitted to dial the specified
|
||||
// multiaddr for the given peer.
|
||||
func (s *Service) InterceptAddrDial(_ peer.ID, m multiaddr.Multiaddr) (allow bool) {
|
||||
func (s *Service) InterceptAddrDial(pid peer.ID, m multiaddr.Multiaddr) (allow bool) {
|
||||
// Disallow bad peers from dialing in.
|
||||
if s.peers.IsBad(pid) {
|
||||
return false
|
||||
}
|
||||
return filterConnections(s.addrFilter, m)
|
||||
}
|
||||
|
||||
// InterceptAccept tests whether an incipient inbound connection is allowed.
|
||||
// InterceptAccept checks whether the incidental inbound connection is allowed.
|
||||
func (s *Service) InterceptAccept(n network.ConnMultiaddrs) (allow bool) {
|
||||
if !s.validateDial(n.RemoteMultiaddr()) {
|
||||
// Allow other go-routines to run in the event
|
||||
@@ -40,7 +44,6 @@ func (s *Service) InterceptAccept(n network.ConnMultiaddrs) (allow bool) {
|
||||
"reason": "exceeded dial limit"}).Trace("Not accepting inbound dial from ip address")
|
||||
return false
|
||||
}
|
||||
|
||||
if s.isPeerAtLimit() {
|
||||
log.WithFields(logrus.Fields{"peer": n.RemoteMultiaddr(),
|
||||
"reason": "at peer limit"}).Trace("Not accepting inbound dial")
|
||||
|
||||
@@ -64,6 +64,9 @@ func TestPeer_AtMaxLimit(t *testing.T) {
|
||||
func TestService_InterceptBannedIP(t *testing.T) {
|
||||
s := &Service{
|
||||
ipLimiter: leakybucket.NewCollector(ipLimit, ipBurst, false),
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
var err error
|
||||
s.addrFilter, err = configureFilter(&Config{})
|
||||
@@ -144,6 +147,9 @@ func TestPeerAllowList(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to p2p listen")
|
||||
s := &Service{
|
||||
ipLimiter: leakybucket.NewCollector(ipLimit, ipBurst, false),
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
s.addrFilter, err = configureFilter(&Config{AllowListCIDR: cidr})
|
||||
require.NoError(t, err)
|
||||
@@ -187,6 +193,9 @@ func TestPeerDenyList(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to p2p listen")
|
||||
s := &Service{
|
||||
ipLimiter: leakybucket.NewCollector(ipLimit, ipBurst, false),
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
s.addrFilter, err = configureFilter(&Config{DenyListCIDR: []string{cidr}})
|
||||
require.NoError(t, err)
|
||||
@@ -219,6 +228,9 @@ func TestPeerDenyList(t *testing.T) {
|
||||
func TestService_InterceptAddrDial_Allow(t *testing.T) {
|
||||
s := &Service{
|
||||
ipLimiter: leakybucket.NewCollector(ipLimit, ipBurst, false),
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
var err error
|
||||
cidr := "212.67.89.112/16"
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/iputils"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -36,7 +35,7 @@ func createAddrAndPrivKey(t *testing.T) (net.IP, *ecdsa.PrivateKey) {
|
||||
ip, err := iputils.ExternalIPv4()
|
||||
require.NoError(t, err, "Could not get ip")
|
||||
ipAddr := net.ParseIP(ip)
|
||||
temp := testutil.TempDir()
|
||||
temp := t.TempDir()
|
||||
randNum := rand.Int()
|
||||
tempPath := path.Join(temp, strconv.Itoa(randNum))
|
||||
require.NoError(t, os.Mkdir(tempPath, 0700))
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -234,7 +233,7 @@ func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) {
|
||||
forkEntry := enr.WithEntry(eth2ENRKey, enc)
|
||||
// In epoch 1 of current time, the fork version should be
|
||||
// {0, 0, 0, 1} according to the configuration override above.
|
||||
temp := testutil.TempDir()
|
||||
temp := t.TempDir()
|
||||
randNum := rand.Int()
|
||||
tempPath := path.Join(temp, strconv.Itoa(randNum))
|
||||
require.NoError(t, os.Mkdir(tempPath, 0700))
|
||||
@@ -260,7 +259,7 @@ func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddForkEntry_Genesis(t *testing.T) {
|
||||
temp := testutil.TempDir()
|
||||
temp := t.TempDir()
|
||||
randNum := rand.Int()
|
||||
tempPath := path.Join(temp, strconv.Itoa(randNum))
|
||||
require.NoError(t, os.Mkdir(tempPath, 0700))
|
||||
|
||||
185
beacon-chain/p2p/gossip_scoring_params.go
Normal file
185
beacon-chain/p2p/gossip_scoring_params.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
const (
|
||||
// beaconBlockWeight specifies the scoring weight that we apply to
|
||||
// our beacon block topic.
|
||||
beaconBlockWeight = 0.8
|
||||
// aggregateWeight specifies the scoring weight that we apply to
|
||||
// our aggregate topic.
|
||||
aggregateWeight = 0.5
|
||||
// attestationTotalWeight specifies the scoring weight that we apply to
|
||||
// our attestation subnet topic.
|
||||
attestationTotalWeight = 1
|
||||
// decayToZero specifies the terminal value that we will use when decaying
|
||||
// a value.
|
||||
decayToZero = 0.01
|
||||
)
|
||||
|
||||
func peerScoringParams() (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds) {
|
||||
thresholds := &pubsub.PeerScoreThresholds{
|
||||
GossipThreshold: -4000,
|
||||
PublishThreshold: -8000,
|
||||
GraylistThreshold: -16000,
|
||||
AcceptPXThreshold: 100,
|
||||
OpportunisticGraftThreshold: 5,
|
||||
}
|
||||
scoreParams := &pubsub.PeerScoreParams{
|
||||
Topics: make(map[string]*pubsub.TopicScoreParams),
|
||||
TopicScoreCap: 32.72,
|
||||
AppSpecificScore: func(p peer.ID) float64 {
|
||||
return 0
|
||||
},
|
||||
AppSpecificWeight: 1,
|
||||
IPColocationFactorWeight: -35.11,
|
||||
IPColocationFactorThreshold: 10,
|
||||
IPColocationFactorWhitelist: nil,
|
||||
BehaviourPenaltyWeight: -15.92,
|
||||
BehaviourPenaltyThreshold: 6,
|
||||
BehaviourPenaltyDecay: scoreDecay(10 * oneEpochDuration()),
|
||||
DecayInterval: 1 * oneSlotDuration(),
|
||||
DecayToZero: decayToZero,
|
||||
RetainScore: 100 * oneEpochDuration(),
|
||||
}
|
||||
return scoreParams, thresholds
|
||||
}
|
||||
|
||||
func topicScoreParams(topic string) *pubsub.TopicScoreParams {
|
||||
switch {
|
||||
case strings.Contains(topic, "beacon_block"):
|
||||
return defaultBlockTopicParams()
|
||||
case strings.Contains(topic, "beacon_aggregate_and_proof"):
|
||||
return defaultAggregateTopicParams()
|
||||
case strings.Contains(topic, "beacon_attestation"):
|
||||
return defaultAggregateSubnetTopicParams()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Based on Ben's tested parameters for lighthouse.
|
||||
// https://gist.github.com/blacktemplar/5c1862cb3f0e32a1a7fb0b25e79e6e2c
|
||||
|
||||
func defaultBlockTopicParams() *pubsub.TopicScoreParams {
|
||||
decayEpoch := time.Duration(5)
|
||||
blocksPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: beaconBlockWeight,
|
||||
TimeInMeshWeight: 0.0324,
|
||||
TimeInMeshQuantum: 1 * oneSlotDuration(),
|
||||
TimeInMeshCap: 300,
|
||||
FirstMessageDeliveriesWeight: 1,
|
||||
FirstMessageDeliveriesDecay: scoreDecay(20 * oneEpochDuration()),
|
||||
FirstMessageDeliveriesCap: 23,
|
||||
MeshMessageDeliveriesWeight: -0.717,
|
||||
MeshMessageDeliveriesDecay: scoreDecay(decayEpoch * oneEpochDuration()),
|
||||
MeshMessageDeliveriesCap: float64(blocksPerEpoch * uint64(decayEpoch)),
|
||||
MeshMessageDeliveriesThreshold: float64(blocksPerEpoch*uint64(decayEpoch)) / 10,
|
||||
MeshMessageDeliveriesWindow: 2 * time.Second,
|
||||
MeshMessageDeliveriesActivation: 4 * oneEpochDuration(),
|
||||
MeshFailurePenaltyWeight: -0.717,
|
||||
MeshFailurePenaltyDecay: scoreDecay(decayEpoch * oneEpochDuration()),
|
||||
InvalidMessageDeliveriesWeight: -140.4475,
|
||||
InvalidMessageDeliveriesDecay: scoreDecay(50 * oneEpochDuration()),
|
||||
}
|
||||
}
|
||||
|
||||
func defaultAggregateTopicParams() *pubsub.TopicScoreParams {
|
||||
aggPerEpoch := aggregatorsPerSlot() * params.BeaconConfig().SlotsPerEpoch
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: aggregateWeight,
|
||||
TimeInMeshWeight: 0.0324,
|
||||
TimeInMeshQuantum: 1 * oneSlotDuration(),
|
||||
TimeInMeshCap: 300,
|
||||
FirstMessageDeliveriesWeight: 0.128,
|
||||
FirstMessageDeliveriesDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
FirstMessageDeliveriesCap: 179,
|
||||
MeshMessageDeliveriesWeight: -0.064,
|
||||
MeshMessageDeliveriesDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
MeshMessageDeliveriesCap: float64(aggPerEpoch),
|
||||
MeshMessageDeliveriesThreshold: float64(aggPerEpoch / 50),
|
||||
MeshMessageDeliveriesWindow: 2 * time.Second,
|
||||
MeshMessageDeliveriesActivation: 32 * oneSlotDuration(),
|
||||
MeshFailurePenaltyWeight: -0.064,
|
||||
MeshFailurePenaltyDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
InvalidMessageDeliveriesWeight: -140.4475,
|
||||
InvalidMessageDeliveriesDecay: scoreDecay(50 * oneEpochDuration()),
|
||||
}
|
||||
}
|
||||
|
||||
func defaultAggregateSubnetTopicParams() *pubsub.TopicScoreParams {
|
||||
topicWeight := attestationTotalWeight / float64(params.BeaconNetworkConfig().AttestationSubnetCount)
|
||||
subnetWeight := activeValidators() / params.BeaconNetworkConfig().AttestationSubnetCount
|
||||
minimumWeight := subnetWeight / 50
|
||||
numPerSlot := time.Duration(subnetWeight / params.BeaconConfig().SlotsPerEpoch)
|
||||
comsPerSlot := committeeCountPerSlot()
|
||||
exceedsThreshold := comsPerSlot >= 2*params.BeaconNetworkConfig().AttestationSubnetCount/params.BeaconConfig().SlotsPerEpoch
|
||||
firstDecay := time.Duration(1)
|
||||
meshDecay := time.Duration(4)
|
||||
if exceedsThreshold {
|
||||
firstDecay = 4
|
||||
meshDecay = 16
|
||||
}
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: topicWeight,
|
||||
TimeInMeshWeight: 0.0324,
|
||||
TimeInMeshQuantum: numPerSlot,
|
||||
TimeInMeshCap: 300,
|
||||
FirstMessageDeliveriesWeight: 0.955,
|
||||
FirstMessageDeliveriesDecay: scoreDecay(firstDecay * oneEpochDuration()),
|
||||
FirstMessageDeliveriesCap: 24,
|
||||
MeshMessageDeliveriesWeight: -37.55,
|
||||
MeshMessageDeliveriesDecay: scoreDecay(meshDecay * oneEpochDuration()),
|
||||
MeshMessageDeliveriesCap: float64(subnetWeight),
|
||||
MeshMessageDeliveriesThreshold: float64(minimumWeight),
|
||||
MeshMessageDeliveriesWindow: 2 * time.Second,
|
||||
MeshMessageDeliveriesActivation: 17 * oneSlotDuration(),
|
||||
MeshFailurePenaltyWeight: -37.55,
|
||||
MeshFailurePenaltyDecay: scoreDecay(meshDecay * oneEpochDuration()),
|
||||
InvalidMessageDeliveriesWeight: -4544,
|
||||
InvalidMessageDeliveriesDecay: scoreDecay(50 * oneEpochDuration()),
|
||||
}
|
||||
}
|
||||
|
||||
func oneSlotDuration() time.Duration {
|
||||
return time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
}
|
||||
|
||||
func oneEpochDuration() time.Duration {
|
||||
return time.Duration(params.BeaconConfig().SlotsPerEpoch) * oneSlotDuration()
|
||||
}
|
||||
|
||||
func scoreDecay(totalDurationDecay time.Duration) float64 {
|
||||
numOfTimes := totalDurationDecay / oneSlotDuration()
|
||||
return math.Pow(decayToZero, 1/float64(numOfTimes))
|
||||
}
|
||||
|
||||
// Default to the min-genesis for the current moment, as p2p service
|
||||
// has no access to the chain service.
|
||||
func activeValidators() uint64 {
|
||||
return params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
}
|
||||
|
||||
func committeeCountPerSlot() uint64 {
|
||||
// Use a static parameter for now rather than a dynamic one, we can use
|
||||
// the actual parameter later when we have figured out how to fix a circular
|
||||
// dependency in service startup order.
|
||||
return helpers.SlotCommitteeCount(activeValidators())
|
||||
}
|
||||
|
||||
// Uses a very rough gauge for total aggregator size per slot.
|
||||
func aggregatorsPerSlot() uint64 {
|
||||
comms := committeeCountPerSlot()
|
||||
totalAggs := comms * params.BeaconConfig().TargetAggregatorsPerCommittee
|
||||
return totalAggs
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func peerMultiaddrString(conn network.Conn) string {
|
||||
// AddConnectionHandler adds a callback function which handles the connection with a
|
||||
// newly added peer. It performs a handshake with that peer by sending a hello request
|
||||
// and validating the response from the peer.
|
||||
func (s *Service) AddConnectionHandler(reqFunc func(ctx context.Context, id peer.ID) error) {
|
||||
func (s *Service) AddConnectionHandler(reqFunc, goodByeFunc func(ctx context.Context, id peer.ID) error) {
|
||||
// Peer map and lock to keep track of current connection attempts.
|
||||
peerMap := make(map[peer.ID]bool)
|
||||
peerLock := new(sync.Mutex)
|
||||
@@ -61,8 +61,11 @@ func (s *Service) AddConnectionHandler(reqFunc func(ctx context.Context, id peer
|
||||
remotePeer := conn.RemotePeer()
|
||||
disconnectFromPeer := func() {
|
||||
s.peers.SetConnectionState(remotePeer, peers.PeerDisconnecting)
|
||||
if err := s.Disconnect(remotePeer); err != nil {
|
||||
log.WithError(err).Error("Unable to disconnect from peer")
|
||||
// Only attempt a goodbye if we are still connected to the peer.
|
||||
if s.host.Network().Connectedness(remotePeer) == network.Connected {
|
||||
if err := goodByeFunc(context.TODO(), remotePeer); err != nil {
|
||||
log.WithError(err).Error("Unable to disconnect from peer")
|
||||
}
|
||||
}
|
||||
s.peers.SetConnectionState(remotePeer, peers.PeerDisconnected)
|
||||
}
|
||||
@@ -81,6 +84,7 @@ func (s *Service) AddConnectionHandler(reqFunc func(ctx context.Context, id peer
|
||||
return
|
||||
}
|
||||
s.peers.Add(nil /* ENR */, remotePeer, conn.RemoteMultiaddr(), conn.Stat().Direction)
|
||||
// Defensive check in the event we still get a bad peer.
|
||||
if s.peers.IsBad(remotePeer) {
|
||||
log.WithField("reason", "bad peer").Trace("Ignoring connection request")
|
||||
disconnectFromPeer()
|
||||
@@ -93,7 +97,7 @@ func (s *Service) AddConnectionHandler(reqFunc func(ctx context.Context, id peer
|
||||
"direction": conn.Stat().Direction,
|
||||
"multiAddr": peerMultiaddrString(conn),
|
||||
"activePeers": len(s.peers.Active()),
|
||||
}).Info("Peer connected")
|
||||
}).Debug("Peer connected")
|
||||
}
|
||||
|
||||
// Do not perform handshake on inbound dials.
|
||||
@@ -168,7 +172,7 @@ func (s *Service) AddDisconnectionHandler(handler func(ctx context.Context, id p
|
||||
s.peers.SetConnectionState(conn.RemotePeer(), peers.PeerDisconnected)
|
||||
// Only log disconnections if we were fully connected.
|
||||
if priorState == peers.PeerConnected {
|
||||
log.WithField("activePeers", len(s.peers.Active())).Info("Peer disconnected")
|
||||
log.WithField("activePeers", len(s.peers.Active())).Debug("Peer disconnected")
|
||||
}
|
||||
}()
|
||||
},
|
||||
|
||||
@@ -51,7 +51,8 @@ type PubSubTopicUser interface {
|
||||
|
||||
// ConnectionHandler configures p2p to handle connections with a peer.
|
||||
type ConnectionHandler interface {
|
||||
AddConnectionHandler(f func(ctx context.Context, id peer.ID) error)
|
||||
AddConnectionHandler(f func(ctx context.Context, id peer.ID) error,
|
||||
j func(ctx context.Context, id peer.ID) error)
|
||||
AddDisconnectionHandler(f func(ctx context.Context, id peer.ID) error)
|
||||
connmgr.ConnectionGater
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
gethCrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -14,17 +13,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestPrivateKeyLoading(t *testing.T) {
|
||||
file, err := ioutil.TempFile(testutil.TempDir(), "key")
|
||||
file, err := ioutil.TempFile(t.TempDir(), "key")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, os.Remove(file.Name()))
|
||||
}()
|
||||
key, _, err := crypto.GenerateSecp256k1Key(rand.Reader)
|
||||
require.NoError(t, err, "Could not generate key")
|
||||
raw, err := key.Raw()
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
// overlay parameters
|
||||
gossipSubD = 6 // topic stable mesh target count
|
||||
gossipSubDlo = 5 // topic stable mesh low watermark
|
||||
gossipSubD = 8 // topic stable mesh target count
|
||||
gossipSubDlo = 6 // topic stable mesh low watermark
|
||||
gossipSubDhi = 12 // topic stable mesh high watermark
|
||||
|
||||
// gossip parameters
|
||||
@@ -42,10 +40,7 @@ func TestGossipParameters(t *testing.T) {
|
||||
setPubSubParameters()
|
||||
assert.Equal(t, gossipSubMcacheLen, pubsub.GossipSubHistoryLength, "gossipSubMcacheLen")
|
||||
assert.Equal(t, gossipSubMcacheGossip, pubsub.GossipSubHistoryGossip, "gossipSubMcacheGossip")
|
||||
val := (params.BeaconConfig().SlotsPerEpoch * params.BeaconConfig().SecondsPerSlot * 1000) /
|
||||
uint64(pubsub.GossipSubHeartbeatInterval.Milliseconds())
|
||||
roundedUp := math.Round(float64(val) / 10)
|
||||
assert.Equal(t, gossipSubSeenTTL, int(roundedUp)*10, "gossipSubSeenTtl")
|
||||
assert.Equal(t, gossipSubSeenTTL, int(pubsub.TimeCacheDuration.Milliseconds()/pubsub.GossipSubHeartbeatInterval.Milliseconds()), "gossipSubSeenTtl")
|
||||
}
|
||||
|
||||
func TestFanoutParameters(t *testing.T) {
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
|
||||
"@com_github_multiformats_go_multiaddr//:go_default_library",
|
||||
"@com_github_multiformats_go_multiaddr//net:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -19,8 +19,8 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["store_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
|
||||
|
||||
@@ -48,10 +48,11 @@ type PeerData struct {
|
||||
Enr *enr.Record
|
||||
NextValidTime time.Time
|
||||
// Chain related data.
|
||||
ChainState *pb.Status
|
||||
MetaData *pb.MetaData
|
||||
ChainStateLastUpdated time.Time
|
||||
// Scorers related data.
|
||||
MetaData *pb.MetaData
|
||||
ChainState *pb.Status
|
||||
ChainStateLastUpdated time.Time
|
||||
ChainStateValidationError error
|
||||
// Scorers internal data.
|
||||
BadResponses int
|
||||
ProcessedBlocks uint64
|
||||
BlockProviderUpdated time.Time
|
||||
|
||||
@@ -11,25 +11,24 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
run := func() int {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
EnablePeerScorer: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
EnablePeerScorer: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
resetFlags := flags.Get()
|
||||
flags.Init(&flags.GlobalFlags{
|
||||
BlockBatchLimit: 64,
|
||||
BlockBatchLimitBurstFactor: 10,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
}()
|
||||
code := m.Run()
|
||||
// os.Exit will prevent defer from being called
|
||||
resetCfg()
|
||||
flags.Init(resetFlags)
|
||||
os.Exit(code)
|
||||
resetFlags := flags.Get()
|
||||
flags.Init(&flags.GlobalFlags{
|
||||
BlockBatchLimit: 64,
|
||||
BlockBatchLimitBurstFactor: 10,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
}()
|
||||
return m.Run()
|
||||
}
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
srcs = [
|
||||
"bad_responses.go",
|
||||
"block_providers.go",
|
||||
"peer_status.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers",
|
||||
@@ -13,6 +14,8 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/flags:go_default_library",
|
||||
"//beacon-chain/p2p/peers/peerdata:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/rand:go_default_library",
|
||||
"//shared/timeutils:go_default_library",
|
||||
@@ -25,14 +28,17 @@ go_test(
|
||||
srcs = [
|
||||
"bad_responses_test.go",
|
||||
"block_providers_test.go",
|
||||
"peer_status_test.go",
|
||||
"scorers_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/flags:go_default_library",
|
||||
"//beacon-chain/p2p/peers:go_default_library",
|
||||
"//beacon-chain/p2p/peers/peerdata:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/rand:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
|
||||
)
|
||||
|
||||
var _ Scorer = (*BadResponsesScorer)(nil)
|
||||
|
||||
const (
|
||||
// DefaultBadResponsesThreshold defines how many bad responses to tolerate before peer is deemed bad.
|
||||
DefaultBadResponsesThreshold = 6
|
||||
// DefaultBadResponsesWeight is a default weight. Since score represents penalty, it has negative weight.
|
||||
DefaultBadResponsesWeight = -1.0
|
||||
// DefaultBadResponsesDecayInterval defines how often to decay previous statistics.
|
||||
// Every interval bad responses counter will be decremented by 1.
|
||||
DefaultBadResponsesDecayInterval = time.Hour
|
||||
@@ -20,7 +19,6 @@ const (
|
||||
|
||||
// BadResponsesScorer represents bad responses scoring service.
|
||||
type BadResponsesScorer struct {
|
||||
ctx context.Context
|
||||
config *BadResponsesScorerConfig
|
||||
store *peerdata.Store
|
||||
}
|
||||
@@ -29,29 +27,22 @@ type BadResponsesScorer struct {
|
||||
type BadResponsesScorerConfig struct {
|
||||
// Threshold specifies number of bad responses tolerated, before peer is banned.
|
||||
Threshold int
|
||||
// Weight defines weight of bad response/threshold ratio on overall score.
|
||||
Weight float64
|
||||
// DecayInterval specifies how often bad response stats should be decayed.
|
||||
DecayInterval time.Duration
|
||||
}
|
||||
|
||||
// newBadResponsesScorer creates new bad responses scoring service.
|
||||
func newBadResponsesScorer(
|
||||
ctx context.Context, store *peerdata.Store, config *BadResponsesScorerConfig) *BadResponsesScorer {
|
||||
func newBadResponsesScorer(store *peerdata.Store, config *BadResponsesScorerConfig) *BadResponsesScorer {
|
||||
if config == nil {
|
||||
config = &BadResponsesScorerConfig{}
|
||||
}
|
||||
scorer := &BadResponsesScorer{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
store: store,
|
||||
}
|
||||
if scorer.config.Threshold == 0 {
|
||||
scorer.config.Threshold = DefaultBadResponsesThreshold
|
||||
}
|
||||
if scorer.config.Weight == 0.0 {
|
||||
scorer.config.Weight = DefaultBadResponsesWeight
|
||||
}
|
||||
if scorer.config.DecayInterval == 0 {
|
||||
scorer.config.DecayInterval = DefaultBadResponsesDecayInterval
|
||||
}
|
||||
@@ -65,8 +56,11 @@ func (s *BadResponsesScorer) Score(pid peer.ID) float64 {
|
||||
return s.score(pid)
|
||||
}
|
||||
|
||||
// score is a lock-free version of ScoreBadResponses.
|
||||
// score is a lock-free version of Score.
|
||||
func (s *BadResponsesScorer) score(pid peer.ID) float64 {
|
||||
if s.isBadPeer(pid) {
|
||||
return BadPeerScore
|
||||
}
|
||||
score := float64(0)
|
||||
peerData, ok := s.store.PeerData(pid)
|
||||
if !ok {
|
||||
@@ -74,7 +68,8 @@ func (s *BadResponsesScorer) score(pid peer.ID) float64 {
|
||||
}
|
||||
if peerData.BadResponses > 0 {
|
||||
score = float64(peerData.BadResponses) / float64(s.config.Threshold)
|
||||
score = score * s.config.Weight
|
||||
// Since score represents a penalty, negate it.
|
||||
score *= -1
|
||||
}
|
||||
return score
|
||||
}
|
||||
@@ -131,7 +126,7 @@ func (s *BadResponsesScorer) isBadPeer(pid peer.ID) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// BadPeers returns the peers that are bad.
|
||||
// BadPeers returns the peers that are considered bad.
|
||||
func (s *BadResponsesScorer) BadPeers() []peer.ID {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
|
||||
@@ -86,7 +86,6 @@ func TestScorers_BadResponses_Decay(t *testing.T) {
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: maxBadResponses,
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
@@ -15,6 +14,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
)
|
||||
|
||||
var _ Scorer = (*BlockProviderScorer)(nil)
|
||||
|
||||
const (
|
||||
// DefaultBlockProviderProcessedBatchWeight is a default reward weight of a processed batch of blocks.
|
||||
DefaultBlockProviderProcessedBatchWeight = float64(0.1)
|
||||
@@ -35,7 +36,6 @@ const (
|
||||
|
||||
// BlockProviderScorer represents block provider scoring service.
|
||||
type BlockProviderScorer struct {
|
||||
ctx context.Context
|
||||
config *BlockProviderScorerConfig
|
||||
store *peerdata.Store
|
||||
// maxScore is a cached value for maximum attainable block provider score.
|
||||
@@ -62,13 +62,11 @@ type BlockProviderScorerConfig struct {
|
||||
}
|
||||
|
||||
// newBlockProviderScorer creates block provider scoring service.
|
||||
func newBlockProviderScorer(
|
||||
ctx context.Context, store *peerdata.Store, config *BlockProviderScorerConfig) *BlockProviderScorer {
|
||||
func newBlockProviderScorer(store *peerdata.Store, config *BlockProviderScorerConfig) *BlockProviderScorer {
|
||||
if config == nil {
|
||||
config = &BlockProviderScorerConfig{}
|
||||
}
|
||||
scorer := &BlockProviderScorer{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
store: store,
|
||||
}
|
||||
@@ -176,6 +174,20 @@ func (s *BlockProviderScorer) processedBlocks(pid peer.ID) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsBadPeer states if the peer is to be considered bad.
|
||||
// Block provider scorer cannot guarantee that lower score of a peer is indeed a sign of a bad peer.
|
||||
// Therefore this scorer never marks peers as bad, and relies on scores to probabilistically sort
|
||||
// out low-scorers (see WeightSorted method).
|
||||
func (s *BlockProviderScorer) IsBadPeer(_ peer.ID) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// BadPeers returns the peers that are considered bad.
|
||||
// No peers are considered bad by block providers scorer.
|
||||
func (s *BlockProviderScorer) BadPeers() []peer.ID {
|
||||
return []peer.ID{}
|
||||
}
|
||||
|
||||
// Decay updates block provider counters by decaying them.
|
||||
// This urges peers to keep up the performance to continue getting a high score (and allows
|
||||
// new peers to contest previously high scoring ones).
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/rand"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
@@ -438,17 +439,20 @@ func TestScorers_BlockProvider_FormatScorePretty(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
peerStatusGen := func() *peers.Status {
|
||||
return peers.NewStatus(ctx, &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{
|
||||
BlockProviderScorerConfig: &scorers.BlockProviderScorerConfig{
|
||||
ProcessedBatchWeight: 0.05,
|
||||
ProcessedBlocksCap: 20 * batchSize,
|
||||
Decay: 10 * batchSize,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(ctx, &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{
|
||||
BlockProviderScorerConfig: &scorers.BlockProviderScorerConfig{
|
||||
ProcessedBatchWeight: 0.05,
|
||||
ProcessedBlocksCap: 20 * batchSize,
|
||||
Decay: 10 * batchSize,
|
||||
},
|
||||
},
|
||||
})
|
||||
peerStatuses := peerStatusGen()
|
||||
scorer := peerStatuses.Scorers().BlockProviderScorer()
|
||||
if tt.update != nil {
|
||||
tt.update(scorer)
|
||||
@@ -456,4 +460,29 @@ func TestScorers_BlockProvider_FormatScorePretty(t *testing.T) {
|
||||
tt.check(scorer)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("peer scorer disabled", func(t *testing.T) {
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
EnablePeerScorer: false,
|
||||
})
|
||||
defer resetCfg()
|
||||
peerStatuses := peerStatusGen()
|
||||
scorer := peerStatuses.Scorers().BlockProviderScorer()
|
||||
assert.Equal(t, "disabled", scorer.FormatScorePretty("peer1"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestScorers_BlockProvider_BadPeerMarking(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
peerStatuses := peers.NewStatus(ctx, &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
scorer := peerStatuses.Scorers().BlockProviderScorer()
|
||||
|
||||
assert.Equal(t, false, scorer.IsBadPeer("peer1"), "Unexpected status for unregistered peer")
|
||||
scorer.IncrementProcessedBlocks("peer1", 64)
|
||||
assert.Equal(t, false, scorer.IsBadPeer("peer1"))
|
||||
assert.Equal(t, 0, len(scorer.BadPeers()))
|
||||
}
|
||||
|
||||
143
beacon-chain/p2p/peers/scorers/peer_status.go
Normal file
143
beacon-chain/p2p/peers/scorers/peer_status.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
|
||||
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
)
|
||||
|
||||
var _ Scorer = (*PeerStatusScorer)(nil)
|
||||
|
||||
// PeerStatusScorer represents scorer that evaluates peers based on their statuses.
|
||||
// Peer statuses are updated by regularly polling peers (see sync/rpc_status.go).
|
||||
type PeerStatusScorer struct {
|
||||
config *PeerStatusScorerConfig
|
||||
store *peerdata.Store
|
||||
ourHeadSlot uint64
|
||||
highestPeerHeadSlot uint64
|
||||
}
|
||||
|
||||
// PeerStatusScorerConfig holds configuration parameters for peer status scoring service.
|
||||
type PeerStatusScorerConfig struct{}
|
||||
|
||||
// newPeerStatusScorer creates new peer status scoring service.
|
||||
func newPeerStatusScorer(store *peerdata.Store, config *PeerStatusScorerConfig) *PeerStatusScorer {
|
||||
if config == nil {
|
||||
config = &PeerStatusScorerConfig{}
|
||||
}
|
||||
return &PeerStatusScorer{
|
||||
config: config,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
// Score returns calculated peer score.
|
||||
func (s *PeerStatusScorer) Score(pid peer.ID) float64 {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
return s.score(pid)
|
||||
}
|
||||
|
||||
// score is a lock-free version of Score.
|
||||
func (s *PeerStatusScorer) score(pid peer.ID) float64 {
|
||||
if s.isBadPeer(pid) {
|
||||
return BadPeerScore
|
||||
}
|
||||
score := float64(0)
|
||||
peerData, ok := s.store.PeerData(pid)
|
||||
if !ok || peerData.ChainState == nil {
|
||||
return score
|
||||
}
|
||||
if peerData.ChainState.HeadSlot < s.ourHeadSlot {
|
||||
return score
|
||||
}
|
||||
// Calculate score as a ratio to the known maximum head slot.
|
||||
// The closer the current peer's head slot to the maximum, the higher is the calculated score.
|
||||
if s.highestPeerHeadSlot > 0 {
|
||||
score = float64(peerData.ChainState.HeadSlot) / float64(s.highestPeerHeadSlot)
|
||||
return math.Round(score*ScoreRoundingFactor) / ScoreRoundingFactor
|
||||
}
|
||||
return score
|
||||
}
|
||||
|
||||
// IsBadPeer states if the peer is to be considered bad.
|
||||
func (s *PeerStatusScorer) IsBadPeer(pid peer.ID) bool {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
return s.isBadPeer(pid)
|
||||
}
|
||||
|
||||
// isBadPeer is lock-free version of IsBadPeer.
|
||||
func (s *PeerStatusScorer) isBadPeer(pid peer.ID) bool {
|
||||
peerData, ok := s.store.PeerData(pid)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Mark peer as bad, if the latest error is one of the terminal ones.
|
||||
terminalErrs := []error{
|
||||
p2ptypes.ErrWrongForkDigestVersion,
|
||||
}
|
||||
for _, err := range terminalErrs {
|
||||
if errors.Is(peerData.ChainStateValidationError, err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BadPeers returns the peers that are considered bad.
|
||||
func (s *PeerStatusScorer) BadPeers() []peer.ID {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
|
||||
badPeers := make([]peer.ID, 0)
|
||||
for pid := range s.store.Peers() {
|
||||
if s.isBadPeer(pid) {
|
||||
badPeers = append(badPeers, pid)
|
||||
}
|
||||
}
|
||||
return badPeers
|
||||
}
|
||||
|
||||
// SetPeerStatus sets chain state data for a given peer.
|
||||
func (s *PeerStatusScorer) SetPeerStatus(pid peer.ID, chainState *pb.Status, validationError error) {
|
||||
s.store.Lock()
|
||||
defer s.store.Unlock()
|
||||
|
||||
peerData := s.store.PeerDataGetOrCreate(pid)
|
||||
peerData.ChainState = chainState
|
||||
peerData.ChainStateLastUpdated = timeutils.Now()
|
||||
peerData.ChainStateValidationError = validationError
|
||||
|
||||
// Update maximum known head slot (scores will be calculated with respect to that maximum value).
|
||||
if chainState != nil && chainState.HeadSlot > s.highestPeerHeadSlot {
|
||||
s.highestPeerHeadSlot = chainState.HeadSlot
|
||||
}
|
||||
}
|
||||
|
||||
// PeerStatus gets the chain state of the given remote peer.
|
||||
// This can return nil if there is no known chain state for the peer.
|
||||
// This will error if the peer does not exist.
|
||||
func (s *PeerStatusScorer) PeerStatus(pid peer.ID) (*pb.Status, error) {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
return s.peerStatus(pid)
|
||||
}
|
||||
|
||||
// peerStatus lock-free version of PeerStatus.
|
||||
func (s *PeerStatusScorer) peerStatus(pid peer.ID) (*pb.Status, error) {
|
||||
if peerData, ok := s.store.PeerData(pid); ok {
|
||||
return peerData.ChainState, nil
|
||||
}
|
||||
return nil, peerdata.ErrPeerUnknown
|
||||
}
|
||||
|
||||
// SetHeadSlot updates known head slot.
|
||||
func (s *PeerStatusScorer) SetHeadSlot(slot uint64) {
|
||||
s.ourHeadSlot = slot
|
||||
}
|
||||
197
beacon-chain/p2p/peers/scorers/peer_status_test.go
Normal file
197
beacon-chain/p2p/peers/scorers/peer_status_test.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package scorers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
|
||||
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestScorers_PeerStatus_Score(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
update func(scorer *scorers.PeerStatusScorer)
|
||||
check func(scorer *scorers.PeerStatusScorer)
|
||||
}{
|
||||
{
|
||||
name: "nonexistent peer",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
scorer.SetHeadSlot(64)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, 0.0, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent bad peer",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
scorer.SetHeadSlot(0)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: 64,
|
||||
}, p2ptypes.ErrWrongForkDigestVersion)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, scorers.BadPeerScore, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent peer no head slot for the host node is known",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
scorer.SetHeadSlot(0)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: 64,
|
||||
}, nil)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, 1.0, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent peer head is before ours",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
scorer.SetHeadSlot(128)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: 64,
|
||||
}, nil)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, 0.0, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent peer partial score",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
headSlot := uint64(128)
|
||||
scorer.SetHeadSlot(headSlot)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: headSlot + 64,
|
||||
}, nil)
|
||||
// Set another peer to a higher score.
|
||||
scorer.SetPeerStatus("peer2", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: headSlot + 128,
|
||||
}, nil)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
headSlot := uint64(128)
|
||||
assert.Equal(t, float64(headSlot+64)/float64(headSlot+128), scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent peer full score",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
headSlot := uint64(128)
|
||||
scorer.SetHeadSlot(headSlot)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: headSlot + 64,
|
||||
}, nil)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, 1.0, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existent peer no max known slot",
|
||||
update: func(scorer *scorers.PeerStatusScorer) {
|
||||
scorer.SetHeadSlot(0)
|
||||
scorer.SetPeerStatus("peer1", &pb.Status{
|
||||
HeadRoot: make([]byte, 32),
|
||||
HeadSlot: 0,
|
||||
}, nil)
|
||||
},
|
||||
check: func(scorer *scorers.PeerStatusScorer) {
|
||||
assert.Equal(t, 0.0, scorer.Score("peer1"), "Unexpected score")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(ctx, &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
scorer := peerStatuses.Scorers().PeerStatusScorer()
|
||||
if tt.update != nil {
|
||||
tt.update(scorer)
|
||||
}
|
||||
tt.check(scorer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScorers_PeerStatus_IsBadPeer(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
pid := peer.ID("peer1")
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer(pid))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid))
|
||||
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus(pid, &pb.Status{}, p2ptypes.ErrWrongForkDigestVersion)
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer(pid))
|
||||
assert.Equal(t, true, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid))
|
||||
}
|
||||
|
||||
func TestScorers_PeerStatus_BadPeers(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
pid1 := peer.ID("peer1")
|
||||
pid2 := peer.ID("peer2")
|
||||
pid3 := peer.ID("peer3")
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer(pid1))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid1))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer(pid2))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid2))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer(pid3))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid3))
|
||||
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus(pid1, &pb.Status{}, p2ptypes.ErrWrongForkDigestVersion)
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus(pid2, &pb.Status{}, nil)
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus(pid3, &pb.Status{}, p2ptypes.ErrWrongForkDigestVersion)
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer(pid1))
|
||||
assert.Equal(t, true, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid1))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer(pid2))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid2))
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer(pid3))
|
||||
assert.Equal(t, true, peerStatuses.Scorers().PeerStatusScorer().IsBadPeer(pid3))
|
||||
assert.Equal(t, 2, len(peerStatuses.Scorers().PeerStatusScorer().BadPeers()))
|
||||
assert.Equal(t, 2, len(peerStatuses.Scorers().BadPeers()))
|
||||
}
|
||||
|
||||
func TestScorers_PeerStatus_PeerStatus(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
status, err := peerStatuses.Scorers().PeerStatusScorer().PeerStatus("peer1")
|
||||
require.ErrorContains(t, peerdata.ErrPeerUnknown.Error(), err)
|
||||
assert.Equal(t, (*pb.Status)(nil), status)
|
||||
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus("peer1", &pb.Status{
|
||||
HeadSlot: 128,
|
||||
}, nil)
|
||||
peerStatuses.Scorers().PeerStatusScorer().SetPeerStatus("peer2", &pb.Status{
|
||||
HeadSlot: 128,
|
||||
}, p2ptypes.ErrInvalidEpoch)
|
||||
status, err = peerStatuses.Scorers().PeerStatusScorer().PeerStatus("peer1")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(128), status.HeadSlot)
|
||||
assert.Equal(t, nil, peerStatuses.Scorers().ValidationError("peer1"))
|
||||
assert.ErrorContains(t, p2ptypes.ErrInvalidEpoch.Error(), peerStatuses.Scorers().ValidationError("peer2"))
|
||||
assert.Equal(t, nil, peerStatuses.Scorers().ValidationError("peer3"))
|
||||
}
|
||||
@@ -13,27 +13,26 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
run := func() int {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
EnablePeerScorer: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{
|
||||
EnablePeerScorer: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
resetFlags := flags.Get()
|
||||
flags.Init(&flags.GlobalFlags{
|
||||
BlockBatchLimit: 64,
|
||||
BlockBatchLimitBurstFactor: 10,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
}()
|
||||
code := m.Run()
|
||||
// os.Exit will prevent defer from being called
|
||||
resetCfg()
|
||||
flags.Init(resetFlags)
|
||||
os.Exit(code)
|
||||
resetFlags := flags.Get()
|
||||
flags.Init(&flags.GlobalFlags{
|
||||
BlockBatchLimit: 64,
|
||||
BlockBatchLimitBurstFactor: 10,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
}()
|
||||
return m.Run()
|
||||
}
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
// roundScore returns score rounded in accordance with the score manager's rounding factor.
|
||||
|
||||
@@ -9,35 +9,58 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
|
||||
)
|
||||
|
||||
var _ Scorer = (*Service)(nil)
|
||||
|
||||
// ScoreRoundingFactor defines how many digits to keep in decimal part.
|
||||
// This parameter is used in math.Round(score*ScoreRoundingFactor) / ScoreRoundingFactor.
|
||||
const ScoreRoundingFactor = 10000
|
||||
|
||||
// BadPeerScore defines score that is returned for a bad peer (all other metrics are ignored).
|
||||
const BadPeerScore = -1.00
|
||||
|
||||
// Scorer defines minimum set of methods every peer scorer must expose.
|
||||
type Scorer interface {
|
||||
Score(pid peer.ID) float64
|
||||
IsBadPeer(pid peer.ID) bool
|
||||
BadPeers() []peer.ID
|
||||
}
|
||||
|
||||
// Service manages peer scorers that are used to calculate overall peer score.
|
||||
type Service struct {
|
||||
ctx context.Context
|
||||
store *peerdata.Store
|
||||
scorers struct {
|
||||
badResponsesScorer *BadResponsesScorer
|
||||
blockProviderScorer *BlockProviderScorer
|
||||
peerStatusScorer *PeerStatusScorer
|
||||
}
|
||||
weights map[Scorer]float64
|
||||
totalWeight float64
|
||||
}
|
||||
|
||||
// Config holds configuration parameters for scoring service.
|
||||
type Config struct {
|
||||
BadResponsesScorerConfig *BadResponsesScorerConfig
|
||||
BlockProviderScorerConfig *BlockProviderScorerConfig
|
||||
PeerStatusScorerConfig *PeerStatusScorerConfig
|
||||
}
|
||||
|
||||
// NewService provides fully initialized peer scoring service.
|
||||
func NewService(ctx context.Context, store *peerdata.Store, config *Config) *Service {
|
||||
s := &Service{
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
store: store,
|
||||
weights: make(map[Scorer]float64),
|
||||
}
|
||||
s.scorers.badResponsesScorer = newBadResponsesScorer(ctx, store, config.BadResponsesScorerConfig)
|
||||
s.scorers.blockProviderScorer = newBlockProviderScorer(ctx, store, config.BlockProviderScorerConfig)
|
||||
go s.loop(s.ctx)
|
||||
|
||||
// Register scorers.
|
||||
s.scorers.badResponsesScorer = newBadResponsesScorer(store, config.BadResponsesScorerConfig)
|
||||
s.setScorerWeight(s.scorers.badResponsesScorer, 1.0)
|
||||
s.scorers.blockProviderScorer = newBlockProviderScorer(store, config.BlockProviderScorerConfig)
|
||||
s.setScorerWeight(s.scorers.blockProviderScorer, 1.0)
|
||||
s.scorers.peerStatusScorer = newPeerStatusScorer(store, config.PeerStatusScorerConfig)
|
||||
s.setScorerWeight(s.scorers.peerStatusScorer, 0.0)
|
||||
|
||||
// Start background tasks.
|
||||
go s.loop(ctx)
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -52,6 +75,22 @@ func (s *Service) BlockProviderScorer() *BlockProviderScorer {
|
||||
return s.scorers.blockProviderScorer
|
||||
}
|
||||
|
||||
// PeerStatusScorer exposes peer chain status scoring service.
|
||||
func (s *Service) PeerStatusScorer() *PeerStatusScorer {
|
||||
return s.scorers.peerStatusScorer
|
||||
}
|
||||
|
||||
// ActiveScorersCount returns number of scorers that can affect score (have non-zero weight).
|
||||
func (s *Service) ActiveScorersCount() int {
|
||||
cnt := 0
|
||||
for _, w := range s.weights {
|
||||
if w > 0 {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
// Score returns calculated peer score across all tracked metrics.
|
||||
func (s *Service) Score(pid peer.ID) float64 {
|
||||
s.store.RLock()
|
||||
@@ -61,11 +100,57 @@ func (s *Service) Score(pid peer.ID) float64 {
|
||||
if _, ok := s.store.PeerData(pid); !ok {
|
||||
return 0
|
||||
}
|
||||
score += s.scorers.badResponsesScorer.score(pid)
|
||||
score += s.scorers.blockProviderScorer.score(pid)
|
||||
score += s.scorers.badResponsesScorer.score(pid) * s.scorerWeight(s.scorers.badResponsesScorer)
|
||||
score += s.scorers.blockProviderScorer.score(pid) * s.scorerWeight(s.scorers.blockProviderScorer)
|
||||
score += s.scorers.peerStatusScorer.score(pid) * s.scorerWeight(s.scorers.peerStatusScorer)
|
||||
return math.Round(score*ScoreRoundingFactor) / ScoreRoundingFactor
|
||||
}
|
||||
|
||||
// IsBadPeer traverses all the scorers to see if any of them classifies peer as bad.
|
||||
func (s *Service) IsBadPeer(pid peer.ID) bool {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
return s.isBadPeer(pid)
|
||||
}
|
||||
|
||||
// isBadPeer is a lock-free version of isBadPeer.
|
||||
func (s *Service) isBadPeer(pid peer.ID) bool {
|
||||
if s.scorers.badResponsesScorer.isBadPeer(pid) {
|
||||
return true
|
||||
}
|
||||
if s.scorers.peerStatusScorer.isBadPeer(pid) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BadPeers returns the peers that are considered bad by any of registered scorers.
|
||||
func (s *Service) BadPeers() []peer.ID {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
|
||||
badPeers := make([]peer.ID, 0)
|
||||
for pid := range s.store.Peers() {
|
||||
if s.isBadPeer(pid) {
|
||||
badPeers = append(badPeers, pid)
|
||||
}
|
||||
}
|
||||
return badPeers
|
||||
}
|
||||
|
||||
// ValidationError returns peer data validation error, which potentially provides more information
|
||||
// why peer is considered bad.
|
||||
func (s *Service) ValidationError(pid peer.ID) error {
|
||||
s.store.RLock()
|
||||
defer s.store.RUnlock()
|
||||
|
||||
peerData, ok := s.store.PeerData(pid)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return peerData.ChainStateValidationError
|
||||
}
|
||||
|
||||
// loop handles background tasks.
|
||||
func (s *Service) loop(ctx context.Context) {
|
||||
decayBadResponsesStats := time.NewTicker(s.scorers.badResponsesScorer.Params().DecayInterval)
|
||||
@@ -84,3 +169,14 @@ func (s *Service) loop(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setScorerWeight adds scorer to map of known scorers.
|
||||
func (s *Service) setScorerWeight(scorer Scorer, weight float64) {
|
||||
s.weights[scorer] = weight
|
||||
s.totalWeight += s.weights[scorer]
|
||||
}
|
||||
|
||||
// scorerWeight calculates contribution percentage of a given scorer in total score.
|
||||
func (s *Service) scorerWeight(scorer Scorer) float64 {
|
||||
return s.weights[scorer] / s.totalWeight
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func TestScorers_Service_Init(t *testing.T) {
|
||||
t.Run("bad responses scorer", func(t *testing.T) {
|
||||
params := peerStatuses.Scorers().BadResponsesScorer().Params()
|
||||
assert.Equal(t, scorers.DefaultBadResponsesThreshold, params.Threshold, "Unexpected threshold value")
|
||||
assert.Equal(t, scorers.DefaultBadResponsesWeight, params.Weight, "Unexpected weight value")
|
||||
assert.Equal(t, scorers.DefaultBadResponsesDecayInterval, params.DecayInterval, "Unexpected decay interval value")
|
||||
assert.Equal(t, scorers.DefaultBadResponsesDecayInterval,
|
||||
params.DecayInterval, "Unexpected decay interval value")
|
||||
})
|
||||
|
||||
t.Run("block providers scorer", func(t *testing.T) {
|
||||
@@ -48,7 +48,6 @@ func TestScorers_Service_Init(t *testing.T) {
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: 2,
|
||||
Weight: -1,
|
||||
DecayInterval: 1 * time.Minute,
|
||||
},
|
||||
BlockProviderScorerConfig: &scorers.BlockProviderScorerConfig{
|
||||
@@ -64,7 +63,6 @@ func TestScorers_Service_Init(t *testing.T) {
|
||||
t.Run("bad responses scorer", func(t *testing.T) {
|
||||
params := peerStatuses.Scorers().BadResponsesScorer().Params()
|
||||
assert.Equal(t, 2, params.Threshold, "Unexpected threshold value")
|
||||
assert.Equal(t, -1.0, params.Weight, "Unexpected weight value")
|
||||
assert.Equal(t, 1*time.Minute, params.DecayInterval, "Unexpected decay interval value")
|
||||
})
|
||||
|
||||
@@ -119,7 +117,8 @@ func TestScorers_Service_Score(t *testing.T) {
|
||||
for _, pid := range pids {
|
||||
peerStatuses.Add(nil, pid, nil, network.DirUnknown)
|
||||
// Not yet used peer gets boosted score.
|
||||
assert.Equal(t, s.BlockProviderScorer().MaxScore(), s.Score(pid), "Unexpected score for not yet used peer")
|
||||
startScore := s.BlockProviderScorer().MaxScore()
|
||||
assert.Equal(t, startScore/float64(s.ActiveScorersCount()), s.Score(pid), "Unexpected score for not yet used peer")
|
||||
}
|
||||
return s, pids
|
||||
}
|
||||
@@ -136,27 +135,29 @@ func TestScorers_Service_Score(t *testing.T) {
|
||||
|
||||
t.Run("bad responses score", func(t *testing.T) {
|
||||
s, pids := setupScorer()
|
||||
zeroScore := s.BlockProviderScorer().MaxScore()
|
||||
// Peers start with boosted start score (new peers are boosted by block provider).
|
||||
startScore := s.BlockProviderScorer().MaxScore() / float64(s.ActiveScorersCount())
|
||||
penalty := (-1 / float64(s.BadResponsesScorer().Params().Threshold)) / float64(s.ActiveScorersCount())
|
||||
|
||||
// Update peers' stats and test the effect on peer order.
|
||||
s.BadResponsesScorer().Increment("peer2")
|
||||
assert.DeepEqual(t, pack(s, zeroScore, zeroScore-0.2, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, startScore, startScore+penalty, startScore), peerScores(s, pids))
|
||||
s.BadResponsesScorer().Increment("peer1")
|
||||
s.BadResponsesScorer().Increment("peer1")
|
||||
assert.DeepEqual(t, pack(s, zeroScore-0.4, zeroScore-0.2, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, startScore+2*penalty, startScore+penalty, startScore), peerScores(s, pids))
|
||||
|
||||
// See how decaying affects order of peers.
|
||||
s.BadResponsesScorer().Decay()
|
||||
assert.DeepEqual(t, pack(s, zeroScore-0.2, zeroScore, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, startScore+penalty, startScore, startScore), peerScores(s, pids))
|
||||
s.BadResponsesScorer().Decay()
|
||||
assert.DeepEqual(t, pack(s, zeroScore, zeroScore, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, startScore, startScore, startScore), peerScores(s, pids))
|
||||
})
|
||||
|
||||
t.Run("block providers score", func(t *testing.T) {
|
||||
s, pids := setupScorer()
|
||||
s1 := s.BlockProviderScorer()
|
||||
zeroScore := s.BlockProviderScorer().MaxScore()
|
||||
batchWeight := s1.Params().ProcessedBatchWeight
|
||||
startScore := s.BlockProviderScorer().MaxScore() / 2
|
||||
batchWeight := s1.Params().ProcessedBatchWeight / 2
|
||||
|
||||
// Partial batch.
|
||||
s1.IncrementProcessedBlocks("peer1", batchSize/4)
|
||||
@@ -164,11 +165,11 @@ func TestScorers_Service_Score(t *testing.T) {
|
||||
|
||||
// Single batch.
|
||||
s1.IncrementProcessedBlocks("peer1", batchSize)
|
||||
assert.DeepEqual(t, pack(s, batchWeight, zeroScore, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, batchWeight, startScore, startScore), peerScores(s, pids), "Unexpected scores")
|
||||
|
||||
// Multiple batches.
|
||||
s1.IncrementProcessedBlocks("peer2", batchSize*4)
|
||||
assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, zeroScore), peerScores(s, pids), "Unexpected scores")
|
||||
assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, startScore), peerScores(s, pids), "Unexpected scores")
|
||||
|
||||
// Partial batch.
|
||||
s1.IncrementProcessedBlocks("peer3", batchSize/2)
|
||||
@@ -187,25 +188,22 @@ func TestScorers_Service_Score(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("overall score", func(t *testing.T) {
|
||||
// Full score, no penalty.
|
||||
s, _ := setupScorer()
|
||||
s1 := s.BlockProviderScorer()
|
||||
s2 := s.BadResponsesScorer()
|
||||
batchWeight := s1.Params().ProcessedBatchWeight
|
||||
batchWeight := s1.Params().ProcessedBatchWeight / float64(s.ActiveScorersCount())
|
||||
penalty := (-1 / float64(s.BadResponsesScorer().Params().Threshold)) / float64(s.ActiveScorersCount())
|
||||
|
||||
// Full score, no penalty.
|
||||
s1.IncrementProcessedBlocks("peer1", batchSize*5)
|
||||
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"))
|
||||
assert.Equal(t, roundScore(batchWeight*5), s.Score("peer1"))
|
||||
// Now, adjust score by introducing penalty for bad responses.
|
||||
s2.Increment("peer1")
|
||||
s2.Increment("peer1")
|
||||
assert.Equal(t, -0.4, s2.Score("peer1"), "Unexpected bad responses score")
|
||||
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"), "Unexpected block provider score")
|
||||
assert.Equal(t, roundScore(batchWeight*5-0.4), s.Score("peer1"), "Unexpected overall score")
|
||||
assert.Equal(t, roundScore(batchWeight*5+2*penalty), s.Score("peer1"), "Unexpected overall score")
|
||||
// If peer continues to misbehave, score becomes negative.
|
||||
s2.Increment("peer1")
|
||||
assert.Equal(t, -0.6, s2.Score("peer1"), "Unexpected bad responses score")
|
||||
assert.Equal(t, roundScore(batchWeight*5), s1.Score("peer1"), "Unexpected block provider score")
|
||||
assert.Equal(t, roundScore(batchWeight*5-0.6), s.Score("peer1"), "Unexpected overall score")
|
||||
assert.Equal(t, roundScore(batchWeight*5+3*penalty), s.Score("peer1"), "Unexpected overall score")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -218,7 +216,6 @@ func TestScorers_Service_loop(t *testing.T) {
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: 5,
|
||||
Weight: -0.5,
|
||||
DecayInterval: 50 * time.Millisecond,
|
||||
},
|
||||
BlockProviderScorerConfig: &scorers.BlockProviderScorerConfig{
|
||||
@@ -264,3 +261,45 @@ func TestScorers_Service_loop(t *testing.T) {
|
||||
assert.Equal(t, false, s1.IsBadPeer(pid1), "Peer should not be marked as bad")
|
||||
assert.Equal(t, uint64(0), s2.ProcessedBlocks("peer1"), "No blocks are expected")
|
||||
}
|
||||
|
||||
func TestScorers_Service_IsBadPeer(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
PeerLimit: 30,
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: 2,
|
||||
DecayInterval: 50 * time.Second,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer("peer1"))
|
||||
peerStatuses.Scorers().BadResponsesScorer().Increment("peer1")
|
||||
peerStatuses.Scorers().BadResponsesScorer().Increment("peer1")
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer("peer1"))
|
||||
}
|
||||
|
||||
func TestScorers_Service_BadPeers(t *testing.T) {
|
||||
peerStatuses := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
PeerLimit: 30,
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: 2,
|
||||
DecayInterval: 50 * time.Second,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer("peer1"))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer("peer2"))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer("peer3"))
|
||||
assert.Equal(t, 0, len(peerStatuses.Scorers().BadPeers()))
|
||||
for _, pid := range []peer.ID{"peer1", "peer3"} {
|
||||
peerStatuses.Scorers().BadResponsesScorer().Increment(pid)
|
||||
peerStatuses.Scorers().BadResponsesScorer().Increment(pid)
|
||||
}
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer("peer1"))
|
||||
assert.Equal(t, false, peerStatuses.Scorers().IsBadPeer("peer2"))
|
||||
assert.Equal(t, true, peerStatuses.Scorers().IsBadPeer("peer3"))
|
||||
assert.Equal(t, 2, len(peerStatuses.Scorers().BadPeers()))
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata"
|
||||
@@ -52,14 +53,20 @@ const (
|
||||
PeerConnecting
|
||||
)
|
||||
|
||||
// Additional buffer beyond current peer limit, from which we can store the relevant peer statuses.
|
||||
const maxLimitBuffer = 150
|
||||
const (
|
||||
// ColocationLimit restricts how many peer identities we can see from a single ip or ipv6 subnet.
|
||||
ColocationLimit = 5
|
||||
|
||||
// Additional buffer beyond current peer limit, from which we can store the relevant peer statuses.
|
||||
maxLimitBuffer = 150
|
||||
)
|
||||
|
||||
// Status is the structure holding the peer status information.
|
||||
type Status struct {
|
||||
ctx context.Context
|
||||
scorers *scorers.Service
|
||||
store *peerdata.Store
|
||||
ctx context.Context
|
||||
scorers *scorers.Service
|
||||
store *peerdata.Store
|
||||
ipTracker map[string]uint64
|
||||
}
|
||||
|
||||
// StatusConfig represents peer status service params.
|
||||
@@ -76,9 +83,10 @@ func NewStatus(ctx context.Context, config *StatusConfig) *Status {
|
||||
MaxPeers: maxLimitBuffer + config.PeerLimit,
|
||||
})
|
||||
return &Status{
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
scorers: scorers.NewService(ctx, store, config.ScorerParams),
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
scorers: scorers.NewService(ctx, store, config.ScorerParams),
|
||||
ipTracker: map[string]uint64{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +108,15 @@ func (p *Status) Add(record *enr.Record, pid peer.ID, address ma.Multiaddr, dire
|
||||
|
||||
if peerData, ok := p.store.PeerData(pid); ok {
|
||||
// Peer already exists, just update its address info.
|
||||
prevAddress := peerData.Address
|
||||
peerData.Address = address
|
||||
peerData.Direction = direction
|
||||
if record != nil {
|
||||
peerData.Enr = record
|
||||
}
|
||||
if !sameIP(prevAddress, address) {
|
||||
p.addIpToTracker(pid)
|
||||
}
|
||||
return
|
||||
}
|
||||
peerData := &peerdata.PeerData{
|
||||
@@ -117,6 +129,7 @@ func (p *Status) Add(record *enr.Record, pid peer.ID, address ma.Multiaddr, dire
|
||||
peerData.Enr = record
|
||||
}
|
||||
p.store.SetPeerData(pid, peerData)
|
||||
p.addIpToTracker(pid)
|
||||
}
|
||||
|
||||
// Address returns the multiaddress of the given remote peer.
|
||||
@@ -156,25 +169,14 @@ func (p *Status) ENR(pid peer.ID) (*enr.Record, error) {
|
||||
|
||||
// SetChainState sets the chain state of the given remote peer.
|
||||
func (p *Status) SetChainState(pid peer.ID, chainState *pb.Status) {
|
||||
p.store.Lock()
|
||||
defer p.store.Unlock()
|
||||
|
||||
peerData := p.store.PeerDataGetOrCreate(pid)
|
||||
peerData.ChainState = chainState
|
||||
peerData.ChainStateLastUpdated = timeutils.Now()
|
||||
p.scorers.PeerStatusScorer().SetPeerStatus(pid, chainState, nil)
|
||||
}
|
||||
|
||||
// ChainState gets the chain state of the given remote peer.
|
||||
// This can return nil if there is no known chain state for the peer.
|
||||
// This will error if the peer does not exist.
|
||||
func (p *Status) ChainState(pid peer.ID) (*pb.Status, error) {
|
||||
p.store.RLock()
|
||||
defer p.store.RUnlock()
|
||||
|
||||
if peerData, ok := p.store.PeerData(pid); ok {
|
||||
return peerData.ChainState, nil
|
||||
}
|
||||
return nil, peerdata.ErrPeerUnknown
|
||||
return p.scorers.PeerStatusScorer().PeerStatus(pid)
|
||||
}
|
||||
|
||||
// IsActive checks if a peers is active and returns the result appropriately.
|
||||
@@ -277,10 +279,10 @@ func (p *Status) ChainStateLastUpdated(pid peer.ID) (time.Time, error) {
|
||||
return timeutils.Now(), peerdata.ErrPeerUnknown
|
||||
}
|
||||
|
||||
// IsBad states if the peer is to be considered bad.
|
||||
// IsBad states if the peer is to be considered bad (by *any* of the registered scorers).
|
||||
// If the peer is unknown this will return `false`, which makes using this function easier than returning an error.
|
||||
func (p *Status) IsBad(pid peer.ID) bool {
|
||||
return p.scorers.BadResponsesScorer().IsBadPeer(pid)
|
||||
return p.isfromBadIP(pid) || p.scorers.IsBadPeer(pid)
|
||||
}
|
||||
|
||||
// NextValidTime gets the earliest possible time it is to contact/dial
|
||||
@@ -463,6 +465,7 @@ func (p *Status) Prune() {
|
||||
for _, peerData := range peersToPrune {
|
||||
p.store.DeletePeerData(peerData.pid)
|
||||
}
|
||||
p.tallyIPTracker()
|
||||
}
|
||||
|
||||
// BestFinalized returns the highest finalized epoch equal to or higher than ours that is agreed
|
||||
@@ -579,6 +582,88 @@ func (p *Status) HighestEpoch() uint64 {
|
||||
return helpers.SlotToEpoch(highestSlot)
|
||||
}
|
||||
|
||||
func (p *Status) isfromBadIP(pid peer.ID) bool {
|
||||
p.store.RLock()
|
||||
defer p.store.RUnlock()
|
||||
|
||||
peerData, ok := p.store.PeerData(pid)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if peerData.Address == nil {
|
||||
return false
|
||||
}
|
||||
ip, err := manet.ToIP(peerData.Address)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if val, ok := p.ipTracker[ip.String()]; ok {
|
||||
if val > ColocationLimit {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Status) addIpToTracker(pid peer.ID) {
|
||||
data, ok := p.store.PeerData(pid)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if data.Address == nil {
|
||||
return
|
||||
}
|
||||
ip, err := manet.ToIP(data.Address)
|
||||
if err != nil {
|
||||
// Should never happen, it is
|
||||
// assumed every IP coming in
|
||||
// is a valid ip.
|
||||
return
|
||||
}
|
||||
// Ignore loopback addresses.
|
||||
if ip.IsLoopback() {
|
||||
return
|
||||
}
|
||||
stringIP := ip.String()
|
||||
p.ipTracker[stringIP] += 1
|
||||
}
|
||||
|
||||
func (p *Status) tallyIPTracker() {
|
||||
tracker := map[string]uint64{}
|
||||
// Iterate through all peers.
|
||||
for _, peerData := range p.store.Peers() {
|
||||
if peerData.Address == nil {
|
||||
continue
|
||||
}
|
||||
ip, err := manet.ToIP(peerData.Address)
|
||||
if err != nil {
|
||||
// Should never happen, it is
|
||||
// assumed every IP coming in
|
||||
// is a valid ip.
|
||||
continue
|
||||
}
|
||||
stringIP := ip.String()
|
||||
tracker[stringIP] += 1
|
||||
}
|
||||
p.ipTracker = tracker
|
||||
}
|
||||
|
||||
func sameIP(firstAddr, secondAddr ma.Multiaddr) bool {
|
||||
// Exit early if we do get nil multiaddresses
|
||||
if firstAddr == nil || secondAddr == nil {
|
||||
return false
|
||||
}
|
||||
firstIP, err := manet.ToIP(firstAddr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
secondIP, err := manet.ToIP(secondAddr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return firstIP.Equal(secondIP)
|
||||
}
|
||||
|
||||
func retrieveIndicesFromBitfield(bitV bitfield.Bitvector64) []uint64 {
|
||||
committeeIdxs := make([]uint64, 0, bitV.Count())
|
||||
for i := uint64(0); i < 64; i++ {
|
||||
|
||||
@@ -3,6 +3,7 @@ package peers_test
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -517,6 +518,45 @@ func TestPrune(t *testing.T) {
|
||||
assert.ErrorContains(t, "peer unknown", err)
|
||||
}
|
||||
|
||||
func TestPeerIPTracker(t *testing.T) {
|
||||
maxBadResponses := 2
|
||||
p := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
PeerLimit: 30,
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: maxBadResponses,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
badIP := "211.227.218.116"
|
||||
badPeers := []peer.ID{}
|
||||
for i := 0; i < peers.ColocationLimit+10; i++ {
|
||||
port := strconv.Itoa(3000 + i)
|
||||
addr, err := ma.NewMultiaddr("/ip4/" + badIP + "/tcp/" + port)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
badPeers = append(badPeers, createPeer(t, p, addr))
|
||||
}
|
||||
for _, pr := range badPeers {
|
||||
assert.Equal(t, true, p.IsBad(pr), "peer with bad ip is not bad")
|
||||
}
|
||||
|
||||
// Add in bad peers, so that our records are trimmed out
|
||||
// from the peer store.
|
||||
for i := 0; i < p.MaxPeerLimit()+100; i++ {
|
||||
// Peer added to peer handler.
|
||||
pid := addPeer(t, p, peers.PeerConnected)
|
||||
p.Scorers().BadResponsesScorer().Increment(pid)
|
||||
}
|
||||
p.Prune()
|
||||
|
||||
for _, pr := range badPeers {
|
||||
assert.Equal(t, false, p.IsBad(pr), "peer with good ip is regarded as bad")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrimmedOrderedPeers(t *testing.T) {
|
||||
p := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
PeerLimit: 30,
|
||||
@@ -833,3 +873,15 @@ func addPeer(t *testing.T, p *peers.Status, state peerdata.PeerConnectionState)
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
func createPeer(t *testing.T, p *peers.Status, addr ma.Multiaddr) peer.ID {
|
||||
mhBytes := []byte{0x11, 0x04}
|
||||
idBytes := make([]byte, 4)
|
||||
_, err := rand.Read(idBytes)
|
||||
require.NoError(t, err)
|
||||
mhBytes = append(mhBytes, idBytes...)
|
||||
id, err := peer.IDFromBytes(mhBytes)
|
||||
require.NoError(t, err)
|
||||
p.Add(new(enr.Record), id, addr, network.DirUnknown)
|
||||
return id
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
@@ -71,9 +73,24 @@ func (s *Service) SubscribeToTopic(topic string, opts ...pubsub.SubOpt) (*pubsub
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if featureconfig.Get().EnablePeerScorer {
|
||||
scoringParams := topicScoreParams(topic)
|
||||
if scoringParams != nil {
|
||||
if err = topicHandle.SetScoreParams(scoringParams); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicHandle.Subscribe(opts...)
|
||||
}
|
||||
|
||||
// peerInspector will scrape all the relevant scoring data and add it to our
|
||||
// peer handler.
|
||||
// TODO(#6043): Add hooks to add in peer inspector to our global peer handler.
|
||||
func (s *Service) peerInspector(peerMap map[peer.ID]*pubsub.PeerScoreSnapshot) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
// Content addressable ID function.
|
||||
//
|
||||
// ETH2 spec defines the message ID as:
|
||||
@@ -98,8 +115,20 @@ func msgIDFunction(pmsg *pubsub_pb.Message) string {
|
||||
}
|
||||
|
||||
func setPubSubParameters() {
|
||||
pubsub.GossipSubDlo = 5
|
||||
pubsub.GossipSubHeartbeatInterval = 700 * time.Millisecond
|
||||
heartBeatInterval := 700 * time.Millisecond
|
||||
pubsub.GossipSubDlo = 6
|
||||
pubsub.GossipSubD = 8
|
||||
pubsub.GossipSubHeartbeatInterval = heartBeatInterval
|
||||
pubsub.GossipSubHistoryLength = 6
|
||||
pubsub.GossipSubHistoryGossip = 3
|
||||
pubsub.TimeCacheDuration = 550 * heartBeatInterval
|
||||
|
||||
// Set a larger gossip history to ensure that slower
|
||||
// messages have a longer time to be propagated. This
|
||||
// comes with the tradeoff of larger memory usage and
|
||||
// size of the seen message cache.
|
||||
if featureconfig.Get().EnableLargerGossipHistory {
|
||||
pubsub.GossipSubHistoryLength = 12
|
||||
pubsub.GossipSubHistoryLength = 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
@@ -156,6 +157,13 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
|
||||
pubsub.WithMessageIdFn(msgIDFunction),
|
||||
pubsub.WithSubscriptionFilter(s),
|
||||
}
|
||||
// Add gossip scoring options.
|
||||
if featureconfig.Get().EnablePeerScorer {
|
||||
psOpts = append(
|
||||
psOpts,
|
||||
pubsub.WithPeerScore(peerScoringParams()),
|
||||
pubsub.WithPeerScoreInspect(s.peerInspector, time.Minute))
|
||||
}
|
||||
// Set the pubsub global parameters that we require.
|
||||
setPubSubParameters()
|
||||
|
||||
@@ -171,7 +179,6 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
|
||||
ScorerParams: &scorers.Config{
|
||||
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
|
||||
Threshold: maxBadResponses,
|
||||
Weight: -100,
|
||||
DecayInterval: time.Hour,
|
||||
},
|
||||
},
|
||||
@@ -432,7 +439,7 @@ func (s *Service) connectWithPeer(ctx context.Context, info peer.AddrInfo) error
|
||||
return nil
|
||||
}
|
||||
if s.Peers().IsBad(info.ID) {
|
||||
return nil
|
||||
return errors.New("refused to connect to bad peer")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, maxDialTimeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
@@ -313,3 +315,48 @@ func initializeStateWithForkDigest(ctx context.Context, t *testing.T, ef *event.
|
||||
|
||||
return fd
|
||||
}
|
||||
|
||||
func TestService_connectWithPeer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
peers *peers.Status
|
||||
info peer.AddrInfo
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "bad peer",
|
||||
peers: func() *peers.Status {
|
||||
ps := peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
})
|
||||
for i := 0; i < 10; i++ {
|
||||
ps.Scorers().BadResponsesScorer().Increment("bad")
|
||||
}
|
||||
return ps
|
||||
}(),
|
||||
info: peer.AddrInfo{ID: "bad"},
|
||||
wantErr: "refused to connect to bad peer",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h, _, _ := createHost(t, 34567)
|
||||
defer func() {
|
||||
if err := h.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
ctx := context.Background()
|
||||
s := &Service{
|
||||
host: h,
|
||||
peers: tt.peers,
|
||||
}
|
||||
err := s.connectWithPeer(ctx, tt.info)
|
||||
if len(tt.wantErr) > 0 {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
)
|
||||
|
||||
func TestStartDiscV5_DiscoverPeersWithSubnets(t *testing.T) {
|
||||
// This test needs to be entirely rewritten and should be done in a follow up PR from #7885.
|
||||
t.Skip("This test is now failing after PR 7885 due to false positive")
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
genesisTime := time.Now()
|
||||
|
||||
@@ -33,7 +33,7 @@ func (p *FakeP2P) Encoding() encoder.NetworkEncoding {
|
||||
}
|
||||
|
||||
// AddConnectionHandler -- fake.
|
||||
func (p *FakeP2P) AddConnectionHandler(_ func(ctx context.Context, id peer.ID) error) {
|
||||
func (p *FakeP2P) AddConnectionHandler(_, _ func(ctx context.Context, id peer.ID) error) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ func (p *TestP2P) ENR() *enr.Record {
|
||||
}
|
||||
|
||||
// AddConnectionHandler handles the connection with a newly connected peer.
|
||||
func (p *TestP2P) AddConnectionHandler(f func(ctx context.Context, id peer.ID) error) {
|
||||
func (p *TestP2P) AddConnectionHandler(f, _ func(ctx context.Context, id peer.ID) error) {
|
||||
p.BHost.Network().Notify(&network.NotifyBundle{
|
||||
ConnectedF: func(net network.Network, conn network.Conn) {
|
||||
// Must be handled in a goroutine as this callback cannot be blocking.
|
||||
|
||||
@@ -3,7 +3,11 @@ load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["types.go"],
|
||||
srcs = [
|
||||
"rpc_errors.go",
|
||||
"rpc_goodbye_codes.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
|
||||
15
beacon-chain/p2p/types/rpc_errors.go
Normal file
15
beacon-chain/p2p/types/rpc_errors.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package types
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrWrongForkDigestVersion = errors.New("wrong fork digest version")
|
||||
ErrInvalidEpoch = errors.New("invalid epoch")
|
||||
ErrInvalidFinalizedRoot = errors.New("invalid finalized root")
|
||||
ErrInvalidSequenceNum = errors.New("invalid sequence number provided")
|
||||
ErrGeneric = errors.New("internal service error")
|
||||
ErrInvalidParent = errors.New("mismatched parent root")
|
||||
ErrRateLimited = errors.New("rate limited")
|
||||
ErrIODeadline = errors.New("i/o deadline exceeded")
|
||||
ErrInvalidRequest = errors.New("invalid range, step or count")
|
||||
)
|
||||
40
beacon-chain/p2p/types/rpc_goodbye_codes.go
Normal file
40
beacon-chain/p2p/types/rpc_goodbye_codes.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package types
|
||||
|
||||
// RPCGoodbyeCode represents goodbye code, used in sync package.
|
||||
type RPCGoodbyeCode = SSZUint64
|
||||
|
||||
const (
|
||||
// Spec defined codes.
|
||||
GoodbyeCodeClientShutdown RPCGoodbyeCode = iota
|
||||
GoodbyeCodeWrongNetwork
|
||||
GoodbyeCodeGenericError
|
||||
|
||||
// Teku specific codes
|
||||
GoodbyeCodeUnableToVerifyNetwork = RPCGoodbyeCode(128)
|
||||
|
||||
// Lighthouse specific codes
|
||||
GoodbyeCodeTooManyPeers = RPCGoodbyeCode(129)
|
||||
GoodbyeCodeBadScore = RPCGoodbyeCode(250)
|
||||
GoodbyeCodeBanned = RPCGoodbyeCode(251)
|
||||
)
|
||||
|
||||
// GoodbyeCodeMessages defines a mapping between goodbye codes and string messages.
|
||||
var GoodbyeCodeMessages = map[RPCGoodbyeCode]string{
|
||||
GoodbyeCodeClientShutdown: "client shutdown",
|
||||
GoodbyeCodeWrongNetwork: "irrelevant network",
|
||||
GoodbyeCodeGenericError: "fault/error",
|
||||
GoodbyeCodeUnableToVerifyNetwork: "unable to verify network",
|
||||
GoodbyeCodeTooManyPeers: "client has too many peers",
|
||||
GoodbyeCodeBadScore: "peer score too low",
|
||||
GoodbyeCodeBanned: "client banned this node",
|
||||
}
|
||||
|
||||
// ErrToGoodbyeCode converts given error to RPC goodbye code.
|
||||
func ErrToGoodbyeCode(err error) RPCGoodbyeCode {
|
||||
switch err {
|
||||
case ErrWrongForkDigestVersion:
|
||||
return GoodbyeCodeWrongNetwork
|
||||
default:
|
||||
return GoodbyeCodeGenericError
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user