mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Compare commits
306 Commits
c6c9414d8b
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b3ce46d28 | ||
|
|
d0ef6dd7f3 | ||
|
|
5be922abe4 | ||
|
|
97b9176411 | ||
|
|
76ca77bc1b | ||
|
|
78d17f07c6 | ||
|
|
882c1e6bf3 | ||
|
|
4b8490a4dd | ||
|
|
9daff21027 | ||
|
|
e5648f46c1 | ||
|
|
0d8165aa98 | ||
|
|
971d8bdcce | ||
|
|
a5d483aa50 | ||
|
|
cdd2e35152 | ||
|
|
5f9f496f64 | ||
|
|
1514f7849a | ||
|
|
35de6cc816 | ||
|
|
67c083460e | ||
|
|
7c4b5eba09 | ||
|
|
921b56522d | ||
|
|
ddbecc81bf | ||
|
|
58ca70acfa | ||
|
|
ec1aa897bf | ||
|
|
2b57cf3bac | ||
|
|
8b47f2ad1a | ||
|
|
fbfbb126a1 | ||
|
|
b04d4c466a | ||
|
|
ee826991a3 | ||
|
|
9e589a6b62 | ||
|
|
afc2bad4d7 | ||
|
|
18d7f909e2 | ||
|
|
b926ae0667 | ||
|
|
9eb22c4f52 | ||
|
|
af142a4119 | ||
|
|
c6372c109e | ||
|
|
a9d88725fe | ||
|
|
0acf164ebd | ||
|
|
af0d9d1c63 | ||
|
|
f34ef8c54c | ||
|
|
320e7affd6 | ||
|
|
e9931a6fe0 | ||
|
|
c029fed293 | ||
|
|
4b4441ed8e | ||
|
|
8773bf881a | ||
|
|
1cf1e081ac | ||
|
|
5ae798549a | ||
|
|
175ad3c9b1 | ||
|
|
064f6cba4d | ||
|
|
add9647b7b | ||
|
|
2e145dd3ae | ||
|
|
3cb5203f2b | ||
|
|
8674cc3d26 | ||
|
|
79c7307095 | ||
|
|
be35f0297b | ||
|
|
4ba541dea4 | ||
|
|
f612b816f6 | ||
|
|
24f630d70b | ||
|
|
2d09a1e539 | ||
|
|
61fafd0523 | ||
|
|
de5df510e4 | ||
|
|
5af9774a41 | ||
|
|
69e46a9628 | ||
|
|
d3cbad3608 | ||
|
|
878540bfc5 | ||
|
|
8b024d58ac | ||
|
|
059c1daff8 | ||
|
|
4b94d88f3d | ||
|
|
06aae90f15 | ||
|
|
74a2198a22 | ||
|
|
230810533e | ||
|
|
237f0db6de | ||
|
|
697a3dcb04 | ||
|
|
db5463e7fa | ||
|
|
b87b730f60 | ||
|
|
c5a4778ef9 | ||
|
|
816d14f31b | ||
|
|
598916566e | ||
|
|
50e63f0cf0 | ||
|
|
50eae63112 | ||
|
|
e083bb5086 | ||
|
|
7bc42b3c6b | ||
|
|
d4be71cbe0 | ||
|
|
bfd50c3f10 | ||
|
|
12bc617b62 | ||
|
|
c081179cdd | ||
|
|
f59563cfbe | ||
|
|
c6f119bd08 | ||
|
|
85ae64855c | ||
|
|
4a487fca6a | ||
|
|
2b20455037 | ||
|
|
1def1d8d32 | ||
|
|
540b568bd3 | ||
|
|
ea9de682f9 | ||
|
|
7fb2ebf3f1 | ||
|
|
35b67a3dcf | ||
|
|
b4b00fd41c | ||
|
|
650383d117 | ||
|
|
882d067144 | ||
|
|
06d1161bd2 | ||
|
|
7563db0538 | ||
|
|
f61688a08c | ||
|
|
55ea83066c | ||
|
|
eecec35b03 | ||
|
|
b2ddee1ae8 | ||
|
|
ae0a501646 | ||
|
|
2dfc2de155 | ||
|
|
8078917d8c | ||
|
|
d07ed6427e | ||
|
|
f02afa20ab | ||
|
|
5217749c4f | ||
|
|
5ff4139f0e | ||
|
|
5bb7ad31a8 | ||
|
|
69d1cb40ac | ||
|
|
fda0310e42 | ||
|
|
01c646b2bc | ||
|
|
64162a51fa | ||
|
|
3411cb9d6d | ||
|
|
3c6c3b9999 | ||
|
|
d48e6b17d0 | ||
|
|
9798f83dc6 | ||
|
|
5f9c2edebd | ||
|
|
8d9960d912 | ||
|
|
ca427dcb66 | ||
|
|
df00a73b0c | ||
|
|
d9719b2a83 | ||
|
|
26d275568d | ||
|
|
7a0f4c0ed2 | ||
|
|
bd0f18281a | ||
|
|
8b2184dd9c | ||
|
|
eb76044fff | ||
|
|
f01dc69a35 | ||
|
|
03abcd5497 | ||
|
|
0e035c787e | ||
|
|
e3d4cff19e | ||
|
|
4e82811264 | ||
|
|
76a21207bb | ||
|
|
185dade934 | ||
|
|
bf441a8ec8 | ||
|
|
ccbe0720fd | ||
|
|
0b476749fb | ||
|
|
f2c31004de | ||
|
|
da0e6bb28b | ||
|
|
32975baa3c | ||
|
|
4a0b3710ef | ||
|
|
82dc94e023 | ||
|
|
348579780e | ||
|
|
8373d75033 | ||
|
|
76fa1e5adb | ||
|
|
b899305a69 | ||
|
|
b88d7a841e | ||
|
|
0c33dc472f | ||
|
|
ae11795617 | ||
|
|
94cb85d847 | ||
|
|
87ff6a0773 | ||
|
|
d9a109ad37 | ||
|
|
013aee6958 | ||
|
|
0f43b261bd | ||
|
|
d29a2be222 | ||
|
|
ee3a38821a | ||
|
|
1fb02639b0 | ||
|
|
13c9432d37 | ||
|
|
1cbe622859 | ||
|
|
b0db321a57 | ||
|
|
98f8331649 | ||
|
|
131823511e | ||
|
|
5fd97e9ed2 | ||
|
|
52ef370817 | ||
|
|
1877c2e441 | ||
|
|
66a922dab2 | ||
|
|
c25ed02bb3 | ||
|
|
ebff47bde1 | ||
|
|
5c1410b224 | ||
|
|
f747de5dc8 | ||
|
|
518e129d46 | ||
|
|
c504931c3b | ||
|
|
53dcc5e8d7 | ||
|
|
9ecd5d4c4e | ||
|
|
d21dbca1af | ||
|
|
67878ec06a | ||
|
|
ddf7a96aad | ||
|
|
7beeec460c | ||
|
|
744affa75b | ||
|
|
86ff17337e | ||
|
|
b09ecec75e | ||
|
|
b26fe09351 | ||
|
|
e181666982 | ||
|
|
3e63695e43 | ||
|
|
233adc9bcd | ||
|
|
07b5e52b52 | ||
|
|
dc3e73ee5a | ||
|
|
c774917749 | ||
|
|
57bc1070f6 | ||
|
|
67902e1977 | ||
|
|
cb63eca4f2 | ||
|
|
6a26fb2465 | ||
|
|
1c0aa9438c | ||
|
|
129762a65f | ||
|
|
d77c3e2dd6 | ||
|
|
6fcf25c811 | ||
|
|
508c36a430 | ||
|
|
7d1b304e8c | ||
|
|
5f1ec70e2b | ||
|
|
71eea358a1 | ||
|
|
ebc0d67d1a | ||
|
|
ed2fadc738 | ||
|
|
60aba6cebf | ||
|
|
f4ebbff4af | ||
|
|
cd1033c7f9 | ||
|
|
5a04d0d6bb | ||
|
|
901966c031 | ||
|
|
2fb1f3d817 | ||
|
|
085962a848 | ||
|
|
988b1fbfc5 | ||
|
|
5b4651748a | ||
|
|
d36019d5ea | ||
|
|
9d1989d473 | ||
|
|
a685b461e3 | ||
|
|
7d5e7172d6 | ||
|
|
e099432551 | ||
|
|
a562ece1c1 | ||
|
|
18332ad929 | ||
|
|
32a0b8a5da | ||
|
|
d3d6223b68 | ||
|
|
e7db3b2aa8 | ||
|
|
6bef43cda6 | ||
|
|
6c0b9264e4 | ||
|
|
371c38916c | ||
|
|
65fe416b19 | ||
|
|
6c1e74b3de | ||
|
|
bc25747275 | ||
|
|
b9eab2a2cc | ||
|
|
c34f2911ff | ||
|
|
6d86d0b3b9 | ||
|
|
855bf7cec9 | ||
|
|
c0c2671c5f | ||
|
|
126e8d4487 | ||
|
|
1127fc656e | ||
|
|
d8aada8258 | ||
|
|
967f3efc50 | ||
|
|
0fa4197a9b | ||
|
|
95bc2ca224 | ||
|
|
ab35814d39 | ||
|
|
3c9b099ae5 | ||
|
|
a62acd965d | ||
|
|
aec8f398d9 | ||
|
|
f0e1235910 | ||
|
|
4f3ab898bc | ||
|
|
fc325255fb | ||
|
|
097b147921 | ||
|
|
98cc1e98c6 | ||
|
|
216469c8a0 | ||
|
|
9bf2874f0f | ||
|
|
20a543507b | ||
|
|
70ad16f58e | ||
|
|
5ae4ced152 | ||
|
|
169ffcd66f | ||
|
|
ab72752422 | ||
|
|
f8118e4599 | ||
|
|
23de5ca6c4 | ||
|
|
83b13a1602 | ||
|
|
c4c76c747c | ||
|
|
d54bd42230 | ||
|
|
46fde662c8 | ||
|
|
59ae4260bf | ||
|
|
54184eb108 | ||
|
|
417a7ddf7a | ||
|
|
6a2591ff34 | ||
|
|
fde0a7819b | ||
|
|
4cec1b1e74 | ||
|
|
2452266033 | ||
|
|
49c70769cb | ||
|
|
f0c45f5011 | ||
|
|
c87f71b303 | ||
|
|
191cc9d1c5 | ||
|
|
c1fdfcd63c | ||
|
|
6a245babba | ||
|
|
1af61109fb | ||
|
|
bb703e4ab7 | ||
|
|
7a44bd2873 | ||
|
|
dd09c5ed06 | ||
|
|
5d4780b746 | ||
|
|
f9dbbb8496 | ||
|
|
4318d21872 | ||
|
|
6cdf27c356 | ||
|
|
cffe93e7b0 | ||
|
|
4a71a37d34 | ||
|
|
0035a5f783 | ||
|
|
f319ec7eb2 | ||
|
|
50708c961a | ||
|
|
792b52a03c | ||
|
|
14698aee6b | ||
|
|
fbdeae1792 | ||
|
|
4fc37fcf41 | ||
|
|
89cf5f5645 | ||
|
|
4172198ff4 | ||
|
|
bf97c6e1a2 | ||
|
|
e2ecbc3575 | ||
|
|
17147c7449 | ||
|
|
bb28b50c81 | ||
|
|
a85612d155 | ||
|
|
01f61664d8 | ||
|
|
bae78a7f2c | ||
|
|
e913c73813 | ||
|
|
2cabf15525 | ||
|
|
2b887bc01a | ||
|
|
92c6e854d5 |
@@ -2,8 +2,7 @@
|
||||
# across machines, developers, and workspaces.
|
||||
#
|
||||
# This config is loaded from https://github.com/bazelbuild/bazel-toolchains/blob/master/bazelrc/latest.bazelrc
|
||||
build:remote-cache --remote_cache=remotebuildexecution.googleapis.com
|
||||
build:remote-cache --tls_enabled=true
|
||||
build:remote-cache --remote_cache=grpcs://remotebuildexecution.googleapis.com
|
||||
build:remote-cache --remote_timeout=3600
|
||||
build:remote-cache --auth_enabled=true
|
||||
build:remote-cache --spawn_strategy=standalone
|
||||
@@ -11,12 +10,22 @@ build:remote-cache --strategy=Javac=standalone
|
||||
build:remote-cache --strategy=Closure=standalone
|
||||
build:remote-cache --strategy=Genrule=standalone
|
||||
|
||||
# Build results backend.
|
||||
build:remote-cache --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||
build:remote-cache --bes_backend=buildeventservice.googleapis.com
|
||||
build:remote-cache --bes_timeout=60s
|
||||
build:remote-cache --project_id=prysmaticlabs
|
||||
build:remote-cache --bes_upload_mode=fully_async
|
||||
|
||||
# Prysm specific remote-cache properties.
|
||||
build:remote-cache --disk_cache=
|
||||
build:remote-cache --jobs=50
|
||||
build:remote-cache --host_platform_remote_properties_override='properties:{name:\"cache-silo-key\" value:\"prysm\"}'
|
||||
build:remote-cache --remote_instance_name=projects/prysmaticlabs/instances/default_instance
|
||||
|
||||
build:remote-cache --experimental_remote_download_outputs=minimal
|
||||
build:remote-cache --experimental_inmemory_jdeps_files
|
||||
build:remote-cache --experimental_inmemory_dotd_files
|
||||
|
||||
# Import workspace options.
|
||||
import %workspace%/.bazelrc
|
||||
|
||||
@@ -31,5 +40,7 @@ build --curses=yes --color=yes
|
||||
build --keep_going
|
||||
build --test_output=errors
|
||||
build --flaky_test_attempts=5
|
||||
build --jobs=50
|
||||
test --local_test_jobs=2
|
||||
# Disabled race detection due to unstable test results under constrained environment build kite
|
||||
# build --features=race
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": "solium:recommended",
|
||||
"plugins": [
|
||||
"security"
|
||||
],
|
||||
"rules": {
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"security/no-inline-assembly": ["warning"],
|
||||
|
||||
"indentation": [
|
||||
"error",
|
||||
4
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
# Prysm: Ethereum 'Serenity' 2.0 Go Implementation
|
||||
|
||||
[](https://buildkite.com/prysmatic-labs/prysm)
|
||||
[](https://github.com/ethereum/eth2.0-specs/commit/8d324b7497bcb558e0183a30002d78d18704e3fa)
|
||||
[](https://discord.gg/KSA7rPr)
|
||||
[](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
|
||||
69
WORKSPACE
69
WORKSPACE
@@ -1,4 +1,5 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
@@ -33,12 +34,6 @@ http_archive(
|
||||
url = "https://github.com/bazelbuild/rules_docker/archive/v0.7.0.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "1db950bbd27fb2581866e307c0130983471d4c3cd49c46063a2503ca7b6770a4",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.29.0/rules_nodejs-0.29.0.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_k8s",
|
||||
sha256 = "f37af27b3825dbaa811bcf4c3fcab581437fc0bd777e86468f19604ca2e99c6b",
|
||||
@@ -46,6 +41,21 @@ http_archive(
|
||||
url = "https://github.com/bazelbuild/rules_k8s/archive/60571086ea6e10b1ddd2512d5c0fd32d01fa5701.tar.gz",
|
||||
)
|
||||
|
||||
# Override default import in rules_go with special patch until
|
||||
# https://github.com/gogo/protobuf/pull/582 is merged.
|
||||
git_repository(
|
||||
name = "com_github_gogo_protobuf",
|
||||
commit = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c", # v1.2.1, as of 2019-03-03
|
||||
patch_args = ["-p1"],
|
||||
patches = [
|
||||
"@io_bazel_rules_go//third_party:com_github_gogo_protobuf-gazelle.patch",
|
||||
"//third_party:com_github_gogo_protobuf-equal.patch",
|
||||
],
|
||||
remote = "https://github.com/gogo/protobuf",
|
||||
shallow_since = "1550471403 +0200",
|
||||
# gazelle args: -go_prefix github.com/gogo/protobuf -proto legacy
|
||||
)
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_docker//repositories:repositories.bzl",
|
||||
container_repositories = "repositories",
|
||||
@@ -53,16 +63,6 @@ load(
|
||||
|
||||
container_repositories()
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "yarn_install")
|
||||
|
||||
node_repositories()
|
||||
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
package_json = "//:package.json",
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
# This requires rules_docker to be fully instantiated before it is pulled in.
|
||||
load("@io_bazel_rules_k8s//k8s:k8s.bzl", "k8s_defaults", "k8s_repositories")
|
||||
|
||||
@@ -140,8 +140,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "56847989737e816ab7d23f3bb2422347dfa81271bae81a94de512c01461fab25",
|
||||
url = "https://github.com/prysmaticlabs/eth2.0-spec-tests/releases/download/v0.7.1/base64_encoded_archive.tar.gz",
|
||||
sha256 = "4be3a1a065abd46534b5b67d77be76de6ce6dbed27bffe8fcf1b3b6f4a11d9d5",
|
||||
url = "https://github.com/prysmaticlabs/eth2.0-spec-tests/releases/download/v0.8.0/base64_encoded_archive.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -154,17 +154,6 @@ load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_depen
|
||||
|
||||
buildifier_dependencies()
|
||||
|
||||
http_archive(
|
||||
name = "com_github_prysmaticlabs_go_ssz",
|
||||
sha256 = "f6fd5d623a988337810b956ddaf612dce771d9d0f9256934c8f4b1379f1cb2f6",
|
||||
strip_prefix = "go-ssz-2e84733edbac32aca6d47feafc4441e43b10047f",
|
||||
url = "https://github.com/prysmaticlabs/go-ssz/archive/2e84733edbac32aca6d47feafc4441e43b10047f.tar.gz",
|
||||
)
|
||||
|
||||
load("@com_github_prysmaticlabs_go_ssz//:deps.bzl", "go_ssz_dependencies")
|
||||
|
||||
go_ssz_dependencies()
|
||||
|
||||
go_repository(
|
||||
name = "com_github_golang_mock",
|
||||
commit = "51421b967af1f557f93a59e0057aaf15ca02e29c", # v1.2.0
|
||||
@@ -186,6 +175,12 @@ go_repository(
|
||||
vcs = "git",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_prysmaticlabs_go_ssz",
|
||||
commit = "08374e459d08fc6abeb43011a206ad54602e71b6",
|
||||
importpath = "github.com/prysmaticlabs/go-ssz",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_urfave_cli",
|
||||
commit = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1", # v1.20.0
|
||||
@@ -1103,3 +1098,19 @@ go_repository(
|
||||
commit = "4afad1f6206cb9222914f2ec6ab9d0b414705c54",
|
||||
importpath = "github.com/libp2p/go-eventbus",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_d4l3k_messagediff_v1",
|
||||
commit = "29f32d820d112dbd66e58492a6ffb7cc3106312b",
|
||||
importpath = "gopkg.in/d4l3k/messagediff.v1",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_prysmaticlabs_go_bitfield",
|
||||
commit = "ec88cc4d1d143cad98308da54b73d0cdb04254eb",
|
||||
importpath = "github.com/prysmaticlabs/go-bitfield",
|
||||
)
|
||||
|
||||
load("@com_github_prysmaticlabs_go_ssz//:deps.bzl", "go_ssz_dependencies")
|
||||
|
||||
go_ssz_dependencies()
|
||||
|
||||
@@ -64,7 +64,7 @@ container_push(
|
||||
image = ":image",
|
||||
registry = "gcr.io",
|
||||
repository = "prysmaticlabs/prysm/beacon-chain",
|
||||
tag = "latest",
|
||||
tag = "{DOCKER_TAG}",
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
@@ -9,15 +9,11 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/attestation",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bitutil:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/messagehandler:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
@@ -33,13 +29,13 @@ go_test(
|
||||
srcs = ["service_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
|
||||
@@ -4,26 +4,20 @@ package attestation
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bitutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
handler "github.com/prysmaticlabs/prysm/shared/messagehandler"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "attestation")
|
||||
var committeeCache = cache.NewCommitteesCache()
|
||||
|
||||
// TargetHandler provides an interface for fetching latest attestation targets
|
||||
// and updating attestations in batches.
|
||||
@@ -87,7 +81,7 @@ func (a *Service) Stop() error {
|
||||
}
|
||||
|
||||
// Status always returns nil.
|
||||
// TODO(1201): Add service health checks.
|
||||
// TODO(#1201): Add service health checks.
|
||||
func (a *Service) Status() error {
|
||||
return nil
|
||||
}
|
||||
@@ -106,10 +100,10 @@ func (a *Service) IncomingAttestationFeed() *event.Feed {
|
||||
// BeaconBlock` be the target block in the attestation
|
||||
// `get_latest_attestation(store, validator_index)`.
|
||||
func (a *Service) LatestAttestationTarget(beaconState *pb.BeaconState, index uint64) (*pb.AttestationTarget, error) {
|
||||
if index >= uint64(len(beaconState.ValidatorRegistry)) {
|
||||
if index >= uint64(len(beaconState.Validators)) {
|
||||
return nil, fmt.Errorf("invalid validator index %d", index)
|
||||
}
|
||||
validator := beaconState.ValidatorRegistry[index]
|
||||
validator := beaconState.Validators[index]
|
||||
|
||||
pubKey := bytesutil.ToBytes48(validator.Pubkey)
|
||||
a.store.RLock()
|
||||
@@ -122,7 +116,7 @@ func (a *Service) LatestAttestationTarget(beaconState *pb.BeaconState, index uin
|
||||
if attestation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
targetRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRootHash32)
|
||||
targetRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRoot)
|
||||
if !a.beaconDB.HasBlock(targetRoot) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -162,11 +156,14 @@ func (a *Service) handleAttestation(ctx context.Context, msg proto.Message) erro
|
||||
// This sets the pool limit, once the old pool is cleared out. It does by using the number of active
|
||||
// validators per slot as an estimate. The active indices here are not used in the actual processing
|
||||
// of attestations.
|
||||
activeIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
attPerSlot := len(activeIndices) / int(params.BeaconConfig().SlotsPerEpoch)
|
||||
count, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attPerSlot := count / params.BeaconConfig().SlotsPerEpoch
|
||||
// we only set the limit at 70% of the calculated amount to be safe so that relevant attestations
|
||||
// arent carried over to the next batch.
|
||||
a.poolLimit = attPerSlot * 7 / 10
|
||||
a.poolLimit = int(attPerSlot) * 7 / 10
|
||||
if a.poolLimit == 0 {
|
||||
a.poolLimit++
|
||||
}
|
||||
@@ -190,15 +187,7 @@ func (a *Service) UpdateLatestAttestation(ctx context.Context, attestation *pb.A
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
head, err := a.beaconDB.ChainHead()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headRoot, err := hashutil.HashBeaconBlock(head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.updateAttestation(ctx, headRoot, beaconState, attestation)
|
||||
return a.updateAttestation(beaconState, attestation)
|
||||
}
|
||||
|
||||
// BatchUpdateLatestAttestation updates multiple attestations and adds them into the attestation store
|
||||
@@ -214,19 +203,9 @@ func (a *Service) BatchUpdateLatestAttestation(ctx context.Context, attestations
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
head, err := a.beaconDB.ChainHead()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headRoot, err := hashutil.HashBeaconBlock(head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attestations = a.sortAttestations(attestations)
|
||||
|
||||
for _, attestation := range attestations {
|
||||
if err := a.updateAttestation(ctx, headRoot, beaconState, attestation); err != nil {
|
||||
if err := a.updateAttestation(beaconState, attestation); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -242,91 +221,53 @@ func (a *Service) InsertAttestationIntoStore(pubkey [48]byte, att *pb.Attestatio
|
||||
a.store.m[pubkey] = att
|
||||
}
|
||||
|
||||
func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beaconState *pb.BeaconState,
|
||||
attestation *pb.Attestation) error {
|
||||
func (a *Service) updateAttestation(beaconState *pb.BeaconState, attestation *pb.Attestation) error {
|
||||
totalAttestationSeen.Inc()
|
||||
|
||||
slot := attestation.Data.Slot
|
||||
var committee []uint64
|
||||
var cachedCommittees *cache.CommitteesInSlot
|
||||
var err error
|
||||
|
||||
for beaconState.Slot < slot {
|
||||
beaconState, err = state.ExecuteStateTransition(
|
||||
ctx, beaconState, nil /* block */, headRoot, state.DefaultConfig(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not execute head transition: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
cachedCommittees, err = committeeCache.CommitteesInfoBySlot(slot)
|
||||
committee, err := helpers.CrosslinkCommittee(beaconState, helpers.CurrentEpoch(beaconState), attestation.Data.Crosslink.Shard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cachedCommittees == nil {
|
||||
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(beaconState, slot, false /* registryChange */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cachedCommittees = helpers.ToCommitteeCache(slot, crosslinkCommittees)
|
||||
if err := committeeCache.AddCommittees(cachedCommittees); err != nil {
|
||||
return err
|
||||
}
|
||||
slot, err := helpers.AttestationDataSlot(beaconState, attestation.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get attestation slot: %v", err)
|
||||
}
|
||||
|
||||
// Find committee for shard.
|
||||
for _, v := range cachedCommittees.Committees {
|
||||
if v.Shard == attestation.Data.Shard {
|
||||
committee = v.Committee
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"attestationSlot": attestation.Data.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"attestationShard": attestation.Data.Shard,
|
||||
"committeesShard": cachedCommittees.Committees[0].Shard,
|
||||
"committeesList": cachedCommittees.Committees[0].Committee,
|
||||
"lengthOfCommittees": len(cachedCommittees.Committees),
|
||||
"attestationSlot": slot,
|
||||
"attestationShard": attestation.Data.Crosslink.Shard,
|
||||
"committeesList": committee,
|
||||
"lengthOfCommittees": len(committee),
|
||||
}).Debug("Updating latest attestation")
|
||||
|
||||
// The participation bitfield from attestation is represented in bytes,
|
||||
// here we multiply by 8 to get an accurate validator count in bits.
|
||||
bitfield := attestation.AggregationBitfield
|
||||
totalBits := len(bitfield) * 8
|
||||
|
||||
// Check each bit of participation bitfield to find out which
|
||||
// attester has submitted new attestation.
|
||||
// This is has O(n) run time and could be optimized down the line.
|
||||
for i := 0; i < totalBits; i++ {
|
||||
bitSet, err := bitutil.CheckBit(bitfield, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bitSet {
|
||||
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
|
||||
if !attestation.AggregationBits.BitAt(i) {
|
||||
continue
|
||||
}
|
||||
|
||||
if i >= len(committee) {
|
||||
log.Debugf("bitfield points to an invalid index in the committee: bitfield %08b", bitfield)
|
||||
if i >= uint64(len(committee)) {
|
||||
// This should never happen.
|
||||
log.Warnf("bitfield points to an invalid index in the committee: bitfield %08b", attestation.AggregationBits)
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(committee[i]) >= len(beaconState.ValidatorRegistry) {
|
||||
log.Debugf("index doesn't exist in validator registry: index %d", committee[i])
|
||||
if int(committee[i]) >= len(beaconState.Validators) {
|
||||
// This should never happen.
|
||||
log.Warnf("index doesn't exist in validator registry: index %d", committee[i])
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the attestation came from this attester. We use the slot committee to find the
|
||||
// validator's actual index.
|
||||
pubkey := bytesutil.ToBytes48(beaconState.ValidatorRegistry[committee[i]].Pubkey)
|
||||
newAttestationSlot := attestation.Data.Slot
|
||||
pubkey := bytesutil.ToBytes48(beaconState.Validators[committee[i]].Pubkey)
|
||||
newAttestationSlot := slot
|
||||
currentAttestationSlot := uint64(0)
|
||||
a.store.Lock()
|
||||
defer a.store.Unlock()
|
||||
if _, exists := a.store.m[pubkey]; exists {
|
||||
currentAttestationSlot = a.store.m[pubkey].Data.Slot
|
||||
currentAttestationSlot = slot
|
||||
}
|
||||
// If the attestation is newer than this attester's one in pool.
|
||||
if newAttestationSlot > currentAttestationSlot {
|
||||
@@ -334,12 +275,12 @@ func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beac
|
||||
|
||||
log.WithFields(
|
||||
logrus.Fields{
|
||||
"attestationSlot": attestation.Data.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"justifiedEpoch": attestation.Data.JustifiedEpoch - params.BeaconConfig().GenesisEpoch,
|
||||
"attestationSlot": slot,
|
||||
"sourceEpoch": attestation.Data.Source.Epoch,
|
||||
},
|
||||
).Debug("Attestation store updated")
|
||||
|
||||
blockRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRootHash32)
|
||||
blockRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRoot)
|
||||
votedBlock, err := a.beaconDB.Block(blockRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -349,12 +290,3 @@ func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortAttestations sorts attestations by their slot number in ascending order.
|
||||
func (a *Service) sortAttestations(attestations []*pb.Attestation) []*pb.Attestation {
|
||||
sort.SliceStable(attestations, func(i, j int) bool {
|
||||
return attestations[i].Data.Slot < attestations[j].Data.Slot
|
||||
})
|
||||
|
||||
return attestations
|
||||
}
|
||||
|
||||
@@ -4,15 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -34,63 +33,67 @@ func TestUpdateLatestAttestation_UpdatesLatest(t *testing.T) {
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
AggregationBits: bitfield.Bitlist{0x03},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: &pb.Checkpoint{},
|
||||
Source: &pb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
pubkey := bytesutil.ToBytes48([]byte{byte(3)})
|
||||
if service.store.m[pubkey].Data.Slot !=
|
||||
attestation.Data.Slot {
|
||||
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
|
||||
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
|
||||
pubkey := bytesutil.ToBytes48(beaconState.Validators[10].Pubkey)
|
||||
if service.store.m[pubkey].Data.Crosslink.Shard !=
|
||||
attestation.Data.Crosslink.Shard {
|
||||
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
|
||||
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
|
||||
}
|
||||
|
||||
beaconState = &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 36,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 36,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
|
||||
attestation.Data.Slot = params.BeaconConfig().GenesisSlot + 36
|
||||
attestation.Data.Shard = 36
|
||||
attestation.Data.Crosslink.Shard = 36
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
if service.store.m[pubkey].Data.Slot !=
|
||||
attestation.Data.Slot {
|
||||
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
|
||||
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
|
||||
if service.store.m[pubkey].Data.Crosslink.Shard !=
|
||||
attestation.Data.Crosslink.Shard {
|
||||
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
|
||||
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,31 +106,31 @@ func TestAttestationPool_UpdatesAttestationPool(t *testing.T) {
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
AggregationBits: bitfield.Bitlist{0x80, 0x01},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -142,8 +145,7 @@ func TestLatestAttestationTarget_CantGetAttestation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
if err := beaconDB.SaveState(ctx, &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{{}},
|
||||
LatestBlock: &pb.BeaconBlock{Slot: params.BeaconConfig().GenesisSlot},
|
||||
Validators: []*pb.Validator{{}},
|
||||
}); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
@@ -167,8 +169,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
|
||||
|
||||
pubKey := []byte{'A'}
|
||||
if err := beaconDB.SaveState(ctx, &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{{Pubkey: pubKey}},
|
||||
LatestBlock: &pb.BeaconBlock{Slot: params.BeaconConfig().GenesisSlot},
|
||||
Validators: []*pb.Validator{{Pubkey: pubKey}},
|
||||
}); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
@@ -177,7 +178,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatalf("could not save block: %v", err)
|
||||
}
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
log.Fatalf("could not hash block: %v", err)
|
||||
}
|
||||
@@ -193,7 +194,7 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
|
||||
|
||||
attestation := &pb.Attestation{
|
||||
Data: &pb.AttestationData{
|
||||
BeaconBlockRootHash32: blockRoot[:],
|
||||
BeaconBlockRoot: blockRoot[:],
|
||||
}}
|
||||
pubKey48 := bytesutil.ToBytes48(pubKey)
|
||||
service.store.m[pubKey48] = attestation
|
||||
@@ -213,159 +214,6 @@ func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLatestAttestation_CacheEnabledAndMiss(t *testing.T) {
|
||||
|
||||
beaconDB := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*pb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
},
|
||||
}
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
pubkey := bytesutil.ToBytes48([]byte{byte(3)})
|
||||
if service.store.m[pubkey].Data.Slot !=
|
||||
attestation.Data.Slot {
|
||||
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
|
||||
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
|
||||
}
|
||||
|
||||
attestation.Data.Slot = params.BeaconConfig().GenesisSlot + 36
|
||||
attestation.Data.Shard = 36
|
||||
|
||||
beaconState = &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 36,
|
||||
ValidatorRegistry: validators,
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
if service.store.m[pubkey].Data.Slot !=
|
||||
attestation.Data.Slot {
|
||||
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
|
||||
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
|
||||
}
|
||||
|
||||
// Verify the committee for attestation's data slot was cached.
|
||||
fetchedCommittees, err := committeeCache.CommitteesInfoBySlot(attestation.Data.Slot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantedCommittee := []uint64{38}
|
||||
if !reflect.DeepEqual(wantedCommittee, fetchedCommittees.Committees[0].Committee) {
|
||||
t.Errorf(
|
||||
"Result indices was an unexpected value. Wanted %d, got %d",
|
||||
wantedCommittee,
|
||||
fetchedCommittees.Committees[0].Committee,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLatestAttestation_CacheEnabledAndHit(t *testing.T) {
|
||||
|
||||
var validators []*pb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconDB := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 2,
|
||||
ValidatorRegistry: validators,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 2,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
|
||||
slot := params.BeaconConfig().GenesisSlot + 2
|
||||
shard := uint64(3)
|
||||
index := uint64(4)
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: slot,
|
||||
Shard: shard,
|
||||
},
|
||||
}
|
||||
|
||||
csInSlot := &cache.CommitteesInSlot{
|
||||
Slot: slot,
|
||||
Committees: []*cache.CommitteeInfo{
|
||||
{Shard: shard, Committee: []uint64{index, 999}},
|
||||
}}
|
||||
|
||||
if err := committeeCache.AddCommittees(csInSlot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
pubkey := bytesutil.ToBytes48([]byte{byte(index)})
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
|
||||
if service.store.m[pubkey].Data.Slot !=
|
||||
attestation.Data.Slot {
|
||||
t.Errorf("Incorrect slot stored, wanted: %d, got: %d",
|
||||
attestation.Data.Slot, service.store.m[pubkey].Data.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) {
|
||||
beaconDB := internal.SetupDB(t)
|
||||
hook := logTest.NewGlobal()
|
||||
@@ -376,31 +224,35 @@ func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) {
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 1,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0xC0},
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0x01},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: &pb.Checkpoint{},
|
||||
Source: &pb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -419,25 +271,30 @@ func TestBatchUpdate_FromSync(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*pb.Validator
|
||||
var latestRandaoMixes [][]byte
|
||||
var latestActiveIndexRoots [][]byte
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
latestRandaoMixes = append(latestRandaoMixes, []byte{'A'})
|
||||
latestActiveIndexRoots = append(latestActiveIndexRoots, []byte{'B'})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: latestRandaoMixes,
|
||||
ActiveIndexRoots: latestActiveIndexRoots,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -445,10 +302,13 @@ func TestBatchUpdate_FromSync(t *testing.T) {
|
||||
service.poolLimit = 9
|
||||
for i := 0; i < 10; i++ {
|
||||
attestation := &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
AggregationBits: bitfield.Bitlist{0x80},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
Target: &pb.Checkpoint{Epoch: 2},
|
||||
Source: &pb.Checkpoint{},
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := service.handleAttestation(ctx, attestation); err != nil {
|
||||
@@ -469,22 +329,23 @@ func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) {
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, &pb.Validator{
|
||||
Pubkey: []byte{byte(i)},
|
||||
ActivationEpoch: params.BeaconConfig().GenesisEpoch,
|
||||
ExitEpoch: params.BeaconConfig().GenesisEpoch + 10,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
ValidatorRegistry: validators,
|
||||
Slot: 1,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -492,10 +353,13 @@ func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) {
|
||||
attestations := make([]*pb.Attestation, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
attestations = append(attestations, &pb.Attestation{
|
||||
AggregationBitfield: []byte{0x80},
|
||||
AggregationBits: bitfield.Bitlist{0x80, 0x01},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 1,
|
||||
Shard: 1,
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: &pb.Checkpoint{},
|
||||
Source: &pb.Checkpoint{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -37,7 +36,6 @@ func reportVoteMetrics(index uint64, block *pb.BeaconBlock) {
|
||||
return
|
||||
}
|
||||
|
||||
s := params.BeaconConfig().GenesisSlot
|
||||
validatorLastVoteGauge.WithLabelValues(
|
||||
"v" + strconv.Itoa(int(index))).Set(float64(block.Slot - s))
|
||||
"v" + strconv.Itoa(int(index))).Set(float64(block.Slot))
|
||||
}
|
||||
|
||||
@@ -23,12 +23,11 @@ go_library(
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/p2p:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
@@ -36,7 +35,7 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"block_processing_test.go",
|
||||
"fork_choice_reorg_test.go",
|
||||
@@ -59,7 +58,6 @@ go_test(
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/forkutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/p2p:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
@@ -69,6 +67,7 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
@@ -14,8 +15,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -33,7 +32,7 @@ type BlockReceiver interface {
|
||||
// to beacon blocks and generating a new beacon state from the Ethereum 2.0 core primitives.
|
||||
type BlockProcessor interface {
|
||||
VerifyBlockValidity(ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState) error
|
||||
ApplyBlockStateTransition(ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState) (*pb.BeaconState, error)
|
||||
AdvanceState(ctx context.Context, beaconState *pb.BeaconState, block *pb.BeaconBlock) (*pb.BeaconState, error)
|
||||
CleanupBlockOperations(ctx context.Context, block *pb.BeaconBlock) error
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
|
||||
defer c.receiveBlockLock.Unlock()
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlock")
|
||||
defer span.End()
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parent, err := c.beaconDB.Block(parentRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent block: %v", err)
|
||||
@@ -70,9 +69,8 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve beacon state: %v", err)
|
||||
}
|
||||
saveLatestBlock := beaconState.LatestBlock
|
||||
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash beacon block")
|
||||
}
|
||||
@@ -85,15 +83,15 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
|
||||
if err := c.SaveAndBroadcastBlock(ctx, block); err != nil {
|
||||
return beaconState, fmt.Errorf(
|
||||
"could not save and broadcast beacon block with slot %d: %v",
|
||||
block.Slot-params.BeaconConfig().GenesisSlot, err,
|
||||
block.Slot, err,
|
||||
)
|
||||
}
|
||||
|
||||
log.WithField("slotNumber", block.Slot-params.BeaconConfig().GenesisSlot).Info(
|
||||
log.WithField("slotNumber", block.Slot).Info(
|
||||
"Executing state transition")
|
||||
|
||||
// We then apply the block state transition accordingly to obtain the resulting beacon state.
|
||||
beaconState, err = c.ApplyBlockStateTransition(ctx, block, beaconState)
|
||||
beaconState, err = c.AdvanceState(ctx, beaconState, block)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *BlockFailedProcessingErr:
|
||||
@@ -109,21 +107,18 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"slotNumber": block.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"currentEpoch": helpers.SlotToEpoch(block.Slot) - params.BeaconConfig().GenesisEpoch,
|
||||
"slotNumber": block.Slot,
|
||||
"currentEpoch": helpers.SlotToEpoch(block.Slot),
|
||||
}).Info("State transition complete")
|
||||
|
||||
// Check state root
|
||||
if featureconfig.FeatureConfig().EnableCheckBlockStateRoot {
|
||||
// Calc state hash with previous block
|
||||
beaconState.LatestBlock = saveLatestBlock
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash beacon state: %v", err)
|
||||
}
|
||||
beaconState.LatestBlock = block
|
||||
if !bytes.Equal(block.StateRootHash32, stateRoot[:]) {
|
||||
return nil, fmt.Errorf("beacon state root is not equal to block state root: %#x != %#x", stateRoot, block.StateRootHash32)
|
||||
if !bytes.Equal(block.StateRoot, stateRoot[:]) {
|
||||
return nil, fmt.Errorf("beacon state root is not equal to block state root: %#x != %#x", stateRoot, block.StateRoot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,53 +128,7 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock)
|
||||
return beaconState, fmt.Errorf("could not process block deposits, attestations, and other operations: %v", err)
|
||||
}
|
||||
|
||||
log.WithField("slot", block.Slot-params.BeaconConfig().GenesisSlot).Info("Finished processing beacon block")
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// ApplyBlockStateTransition runs the Ethereum 2.0 state transition function
|
||||
// to produce a new beacon state and also accounts for skip slots occurring.
|
||||
//
|
||||
// def apply_block_state_transition(block):
|
||||
// # process skipped slots
|
||||
// while (state.slot < block.slot - 1):
|
||||
// state = slot_state_transition(state, block=None)
|
||||
//
|
||||
// # process slot with block
|
||||
// state = slot_state_transition(state, block)
|
||||
//
|
||||
// # check state root
|
||||
// if block.state_root == hash(state):
|
||||
// return state, error
|
||||
// else:
|
||||
// return nil, error # or throw or whatever
|
||||
//
|
||||
func (c *ChainService) ApplyBlockStateTransition(
|
||||
ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState,
|
||||
) (*pb.BeaconState, error) {
|
||||
// Retrieve the last processed beacon block's hash root.
|
||||
headRoot, err := c.ChainHeadRoot()
|
||||
if err != nil {
|
||||
return beaconState, fmt.Errorf("could not retrieve chain head root: %v", err)
|
||||
}
|
||||
|
||||
// Check for skipped slots.
|
||||
numSkippedSlots := 0
|
||||
for beaconState.Slot < block.Slot-1 {
|
||||
beaconState, err = c.runStateTransition(ctx, headRoot, nil, beaconState)
|
||||
if err != nil {
|
||||
return beaconState, err
|
||||
}
|
||||
numSkippedSlots++
|
||||
}
|
||||
if numSkippedSlots > 0 {
|
||||
log.Warnf("Processed %d skipped slots", numSkippedSlots)
|
||||
}
|
||||
|
||||
beaconState, err = c.runStateTransition(ctx, headRoot, block, beaconState)
|
||||
if err != nil {
|
||||
return beaconState, err
|
||||
}
|
||||
log.WithField("slot", block.Slot).Info("Finished processing beacon block")
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
@@ -194,9 +143,9 @@ func (c *ChainService) VerifyBlockValidity(
|
||||
block *pb.BeaconBlock,
|
||||
beaconState *pb.BeaconState,
|
||||
) error {
|
||||
if block.Slot == params.BeaconConfig().GenesisSlot {
|
||||
if block.Slot == 0 {
|
||||
return fmt.Errorf("cannot process a genesis block: received block with slot %d",
|
||||
block.Slot-params.BeaconConfig().GenesisSlot)
|
||||
block.Slot)
|
||||
}
|
||||
powBlockFetcher := c.web3Service.Client().BlockByHash
|
||||
if err := b.IsValidBlock(ctx, beaconState, block,
|
||||
@@ -210,7 +159,7 @@ func (c *ChainService) VerifyBlockValidity(
|
||||
// peers via p2p. Blocks which have already been saved are not processed again via p2p, which is why
|
||||
// the order of operations is important in this function to prevent infinite p2p loops.
|
||||
func (c *ChainService) SaveAndBroadcastBlock(ctx context.Context, block *pb.BeaconBlock) error {
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not tree hash incoming block: %v", err)
|
||||
}
|
||||
@@ -220,7 +169,7 @@ func (c *ChainService) SaveAndBroadcastBlock(ctx context.Context, block *pb.Beac
|
||||
if err := c.beaconDB.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: block.Slot,
|
||||
BlockRoot: blockRoot[:],
|
||||
ParentRoot: block.ParentRootHash32,
|
||||
ParentRoot: block.ParentRoot,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to save attestation target: %v", err)
|
||||
}
|
||||
@@ -253,21 +202,19 @@ func (c *ChainService) CleanupBlockOperations(ctx context.Context, block *pb.Bea
|
||||
return nil
|
||||
}
|
||||
|
||||
// runStateTransition executes the Ethereum 2.0 core state transition for the beacon chain and
|
||||
// AdvanceState executes the Ethereum 2.0 core state transition for the beacon chain and
|
||||
// updates important checkpoints and local persistent data during epoch transitions. It serves as a wrapper
|
||||
// around the more low-level, core state transition function primitive.
|
||||
func (c *ChainService) runStateTransition(
|
||||
func (c *ChainService) AdvanceState(
|
||||
ctx context.Context,
|
||||
headRoot [32]byte,
|
||||
block *pb.BeaconBlock,
|
||||
beaconState *pb.BeaconState,
|
||||
block *pb.BeaconBlock,
|
||||
) (*pb.BeaconState, error) {
|
||||
finalizedEpoch := beaconState.FinalizedEpoch
|
||||
finalizedEpoch := beaconState.FinalizedCheckpoint.Epoch
|
||||
newState, err := state.ExecuteStateTransition(
|
||||
ctx,
|
||||
beaconState,
|
||||
block,
|
||||
headRoot,
|
||||
&state.TransitionConfig{
|
||||
VerifySignatures: false, // We disable signature verification for now.
|
||||
Logging: true, // We enable logging in this state transition call.
|
||||
@@ -276,20 +223,22 @@ func (c *ChainService) runStateTransition(
|
||||
if err != nil {
|
||||
return beaconState, &BlockFailedProcessingErr{err}
|
||||
}
|
||||
// Prune the block cache on every new finalized epoch.
|
||||
if newState.FinalizedEpoch > finalizedEpoch {
|
||||
// Prune the block cache and helper caches on every new finalized epoch.
|
||||
if newState.FinalizedCheckpoint.Epoch > finalizedEpoch {
|
||||
helpers.ClearAllCaches()
|
||||
c.beaconDB.ClearBlockCache()
|
||||
}
|
||||
|
||||
log.WithField(
|
||||
"slotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
|
||||
"slotsSinceGenesis", newState.Slot,
|
||||
).Info("Slot transition successfully processed")
|
||||
|
||||
if block != nil {
|
||||
log.WithField(
|
||||
"slotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
|
||||
"slotsSinceGenesis", newState.Slot,
|
||||
).Info("Block transition successfully processed")
|
||||
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -313,7 +262,7 @@ func (c *ChainService) runStateTransition(
|
||||
return newState, fmt.Errorf("could not update FFG checkpts: %v", err)
|
||||
}
|
||||
log.WithField(
|
||||
"SlotsSinceGenesis", newState.Slot-params.BeaconConfig().GenesisSlot,
|
||||
"SlotsSinceGenesis", newState.Slot,
|
||||
).Info("Epoch transition successfully processed")
|
||||
}
|
||||
return newState, nil
|
||||
@@ -329,11 +278,11 @@ func (c *ChainService) saveValidatorIdx(state *pb.BeaconState) error {
|
||||
for _, idx := range activatedValidators {
|
||||
// If for some reason the activated validator indices is not in state,
|
||||
// we skip them and save them to process for next epoch.
|
||||
if int(idx) >= len(state.ValidatorRegistry) {
|
||||
if int(idx) >= len(state.Validators) {
|
||||
idxNotInState = append(idxNotInState, idx)
|
||||
continue
|
||||
}
|
||||
pubKey := state.ValidatorRegistry[idx].Pubkey
|
||||
pubKey := state.Validators[idx].Pubkey
|
||||
if err := c.beaconDB.SaveValidatorIndex(pubKey, int(idx)); err != nil {
|
||||
return fmt.Errorf("could not save validator index: %v", err)
|
||||
}
|
||||
@@ -351,7 +300,7 @@ func (c *ChainService) saveValidatorIdx(state *pb.BeaconState) error {
|
||||
func (c *ChainService) deleteValidatorIdx(state *pb.BeaconState) error {
|
||||
exitedValidators := validators.ExitedValFromEpoch(helpers.CurrentEpoch(state) + 1)
|
||||
for _, idx := range exitedValidators {
|
||||
pubKey := state.ValidatorRegistry[idx].Pubkey
|
||||
pubKey := state.Validators[idx].Pubkey
|
||||
if err := c.beaconDB.DeleteValidatorIndex(pubKey); err != nil {
|
||||
return fmt.Errorf("could not delete validator index: %v", err)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
@@ -28,7 +30,7 @@ import (
|
||||
var _ = BlockProcessor(&ChainService{})
|
||||
|
||||
func initBlockStateRoot(t *testing.T, block *pb.BeaconBlock, chainService *ChainService) {
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parent, err := chainService.beaconDB.Block(parentRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -37,19 +39,18 @@ func initBlockStateRoot(t *testing.T, block *pb.BeaconBlock, chainService *Chain
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve state %v", err)
|
||||
}
|
||||
saveLatestBlock := beaconState.LatestBlock
|
||||
|
||||
computedState, err := chainService.ApplyBlockStateTransition(context.Background(), block, beaconState)
|
||||
computedState, err := chainService.AdvanceState(context.Background(), beaconState, block)
|
||||
if err != nil {
|
||||
t.Fatalf("could not apply block state transition: %v", err)
|
||||
}
|
||||
|
||||
computedState.LatestBlock = saveLatestBlock
|
||||
stateRoot, err := hashutil.HashProto(computedState)
|
||||
stateRoot, err := ssz.HashTreeRoot(computedState)
|
||||
if err != nil {
|
||||
t.Fatalf("could not tree hash state: %v", err)
|
||||
}
|
||||
block.StateRootHash32 = stateRoot[:]
|
||||
|
||||
block.StateRoot = stateRoot[:]
|
||||
t.Logf("state root after block: %#x", stateRoot)
|
||||
}
|
||||
|
||||
@@ -58,8 +59,8 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
|
||||
defer internal.TeardownDB(t, db)
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
unixTime := uint64(time.Now().Unix())
|
||||
deposits, _ := setupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
|
||||
Slot: 1,
|
||||
}
|
||||
|
||||
parentRoot, err := hashutil.HashBeaconBlock(parentBlock)
|
||||
parentRoot, err := ssz.SigningRoot(parentBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to tree hash block %v", err)
|
||||
}
|
||||
@@ -81,11 +82,13 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
|
||||
}
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: 2,
|
||||
ParentRootHash32: parentRoot[:],
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
Slot: 2,
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -98,35 +101,41 @@ func TestReceiveBlock_FaultyPOWChain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
EnableCheckBlockStateRoot: false,
|
||||
})
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
if err := chainService.beaconDB.SaveBlock(genesis); err != nil {
|
||||
t.Fatalf("Could not save block to db: %v", err)
|
||||
}
|
||||
parentHash, err := hashutil.HashBeaconBlock(genesis)
|
||||
parentRoot, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get tree hash root of canonical head: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
|
||||
if err := db.SaveHistoricalState(ctx, beaconState, parentRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -135,24 +144,25 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconState.Slot++
|
||||
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
},
|
||||
Slot: beaconState.Slot,
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
RandaoReveal: randaoReveal,
|
||||
Attestations: nil,
|
||||
},
|
||||
}
|
||||
|
||||
initBlockStateRoot(t, block, chainService)
|
||||
|
||||
if err := chainService.beaconDB.SaveJustifiedBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -165,31 +175,36 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
|
||||
if _, err := chainService.ReceiveBlock(context.Background(), block); err != nil {
|
||||
t.Errorf("Block failed processing: %v", err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Finished processing beacon block")
|
||||
}
|
||||
|
||||
func TestReceiveBlock_UsesParentBlockState(t *testing.T) {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
EnableCheckBlockStateRoot: false,
|
||||
})
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, _ := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
|
||||
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
if err := chainService.beaconDB.UpdateChainHead(ctx, genesisBlock, beaconState); err != nil {
|
||||
@@ -198,21 +213,24 @@ func TestReceiveBlock_UsesParentBlockState(t *testing.T) {
|
||||
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// We ensure the block uses the right state parent if its ancestor is not block.Slot-1.
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 4,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: []byte{},
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
},
|
||||
Slot: beaconState.Slot + 4,
|
||||
StateRoot: []byte{},
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
RandaoReveal: []byte{},
|
||||
Attestations: nil,
|
||||
},
|
||||
}
|
||||
initBlockStateRoot(t, block, chainService)
|
||||
if err := chainService.beaconDB.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -230,19 +248,25 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
|
||||
defer internal.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, _ := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
attsService := attestation.NewAttestationService(
|
||||
context.Background(),
|
||||
&attestation.Config{BeaconDB: db})
|
||||
chainService := setupBeaconChain(t, db, attsService)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
|
||||
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
@@ -255,27 +279,29 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
|
||||
|
||||
beaconState.Slot++
|
||||
|
||||
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: []byte{},
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
},
|
||||
Slot: beaconState.Slot,
|
||||
StateRoot: []byte{},
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Attestations: []*pb.Attestation{
|
||||
{
|
||||
Data: &pb.AttestationData{
|
||||
JustifiedEpoch: params.BeaconConfig().GenesisSlot * 100,
|
||||
},
|
||||
},
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
RandaoReveal: []byte{},
|
||||
Attestations: []*pb.Attestation{{
|
||||
Data: &pb.AttestationData{
|
||||
Target: &pb.Checkpoint{Epoch: 5},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -285,7 +311,7 @@ func TestReceiveBlock_DeletesBadBlock(t *testing.T) {
|
||||
case *BlockFailedProcessingErr:
|
||||
t.Log("Block failed processing as expected")
|
||||
default:
|
||||
t.Errorf("Unexpected block processing error: %v", err)
|
||||
t.Errorf("Expected block processing to fail, received: %v", err)
|
||||
}
|
||||
|
||||
savedBlock, err := db.Block(blockRoot)
|
||||
@@ -314,16 +340,23 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
|
||||
context.Background(),
|
||||
&attestation.Config{BeaconDB: db})
|
||||
chainService := setupBeaconChain(t, db, attsService)
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -334,11 +367,23 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconState.Slot++
|
||||
parentRoot, err := ssz.SigningRoot(genesisBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
goodStateBlock := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot,
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
RandaoReveal: randaoReveal,
|
||||
},
|
||||
}
|
||||
beaconState.Slot--
|
||||
initBlockStateRoot(t, goodStateBlock, chainService)
|
||||
@@ -357,17 +402,25 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) {
|
||||
func TestReceiveBlock_CheckBlockStateRoot_BadState(t *testing.T) {
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
ctx := context.Background()
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
if err := chainService.beaconDB.SaveHistoricalState(ctx, beaconState, parentHash); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -378,12 +431,24 @@ func TestReceiveBlock_CheckBlockStateRoot_BadState(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconState.Slot++
|
||||
parentRoot, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
invalidStateBlock := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
StateRootHash32: []byte{'b', 'a', 'd', ' ', 'h', 'a', 's', 'h'},
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot,
|
||||
StateRoot: []byte{'b', 'a', 'd', ' ', 'h', 'a', 's', 'h'},
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
RandaoReveal: randaoReveal,
|
||||
},
|
||||
}
|
||||
beaconState.Slot--
|
||||
|
||||
@@ -406,15 +471,24 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
|
||||
context.Background(),
|
||||
&attestation.Config{BeaconDB: db})
|
||||
chainService := setupBeaconChain(t, db, attsService)
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
beaconState.Eth1Data.DepositCount = 1
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
if err := chainService.beaconDB.SaveJustifiedState(beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -422,7 +496,7 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
}
|
||||
@@ -432,57 +506,69 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
currentSlot := params.BeaconConfig().GenesisSlot
|
||||
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
|
||||
currentSlot := uint64(0)
|
||||
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pendingDeposits := []*pb.Deposit{
|
||||
createPreChainStartDeposit(t, []byte{'F'}, beaconState.DepositIndex),
|
||||
createPreChainStartDeposit([]byte{'F'}),
|
||||
}
|
||||
pendingDepositsData := make([][]byte, len(pendingDeposits))
|
||||
for i, pd := range pendingDeposits {
|
||||
pendingDepositsData[i] = pd.DepositData
|
||||
h, err := hashutil.DepositHash(pd.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pendingDepositsData[i] = h[:]
|
||||
}
|
||||
depositTrie, err := trieutil.GenerateTrieFromItems(pendingDepositsData, int(params.BeaconConfig().DepositContractTreeDepth))
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate deposit trie: %v", err)
|
||||
}
|
||||
for i := range pendingDeposits {
|
||||
pendingDeposits[i].MerkleTreeIndex = 0
|
||||
proof, err := depositTrie.MerkleProof(int(pendingDeposits[i].MerkleTreeIndex))
|
||||
proof, err := depositTrie.MerkleProof(0)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate proof: %v", err)
|
||||
}
|
||||
pendingDeposits[i].MerkleProofHash32S = proof
|
||||
pendingDeposits[i].Proof = proof
|
||||
}
|
||||
depositRoot := depositTrie.Root()
|
||||
beaconState.LatestEth1Data.DepositRootHash32 = depositRoot[:]
|
||||
beaconState.Eth1Data.DepositRoot = depositRoot[:]
|
||||
if err := db.SaveHistoricalState(context.Background(), beaconState, parentHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentRoot, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: currentSlot + 1,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
},
|
||||
Slot: currentSlot + 1,
|
||||
StateRoot: stateRoot[:],
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Deposits: pendingDeposits,
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
RandaoReveal: randaoReveal,
|
||||
Deposits: pendingDeposits,
|
||||
},
|
||||
}
|
||||
|
||||
beaconState.LatestBlock = block
|
||||
beaconState.Slot--
|
||||
beaconState.DepositIndex = 0
|
||||
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
if err := chainService.beaconDB.SaveState(ctx, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initBlockStateRoot(t, block, chainService)
|
||||
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
log.Fatalf("could not hash block: %v", err)
|
||||
}
|
||||
@@ -495,7 +581,7 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, dep := range pendingDeposits {
|
||||
db.InsertPendingDeposit(chainService.ctx, dep, big.NewInt(0))
|
||||
db.InsertPendingDeposit(chainService.ctx, dep, big.NewInt(0), 0, [32]byte{})
|
||||
}
|
||||
|
||||
if len(db.PendingDeposits(chainService.ctx, nil)) != len(pendingDeposits) || len(pendingDeposits) == 0 {
|
||||
@@ -513,11 +599,11 @@ func TestReceiveBlock_RemovesPendingDeposits(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < len(beaconState.ValidatorRegistry); i++ {
|
||||
pubKey := bytesutil.ToBytes48(beaconState.ValidatorRegistry[i].Pubkey)
|
||||
for i := 0; i < len(beaconState.Validators); i++ {
|
||||
pubKey := bytesutil.ToBytes48(beaconState.Validators[i].Pubkey)
|
||||
attsService.InsertAttestationIntoStore(pubKey, &pb.Attestation{
|
||||
Data: &pb.AttestationData{
|
||||
BeaconBlockRootHash32: blockRoot[:],
|
||||
BeaconBlockRoot: blockRoot[:],
|
||||
}},
|
||||
)
|
||||
}
|
||||
@@ -569,56 +655,74 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
beaconState.Eth1DepositIndex = 0
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
}
|
||||
parentHash, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
beaconState.LatestBlock = genesisBlock
|
||||
_, genesisBlock := setupGenesisBlock(t, chainService)
|
||||
if err := db.UpdateChainHead(ctx, genesisBlock, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveFinalizedState(beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genesisSlot := params.BeaconConfig().GenesisSlot
|
||||
genesisSlot := uint64(0)
|
||||
|
||||
parentRoot, err := ssz.SigningRoot(genesisBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Top chain slots (see graph)
|
||||
blockSlots := []uint64{1, 2, 3, 5, 8}
|
||||
for _, slot := range blockSlots {
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: genesisSlot + slot,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentHash[:],
|
||||
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: genesisSlot + slot,
|
||||
StateRoot: stateRoot[:],
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
RandaoReveal: randaoReveal,
|
||||
},
|
||||
}
|
||||
initBlockStateRoot(t, block, chainService)
|
||||
computedState, err := chainService.ReceiveBlock(ctx, block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stateRoot, err = hashutil.HashProto(computedState)
|
||||
stateRoot, err = ssz.HashTreeRoot(computedState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = db.SaveBlock(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
computedState.LatestBlock = block
|
||||
if err = db.UpdateChainHead(ctx, block, computedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentHash, err = hashutil.HashBeaconBlock(block)
|
||||
parentRoot, err = ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -630,39 +734,48 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentHash, err = hashutil.HashBeaconBlock(commonAncestor)
|
||||
parentRoot, err = ssz.SigningRoot(commonAncestor)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
beaconState, err = db.HistoricalStateFromSlot(ctx, commonAncestor.Slot, parentHash)
|
||||
beaconState, err = db.HistoricalStateFromSlot(ctx, commonAncestor.Slot, parentRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stateRoot, err = hashutil.HashProto(beaconState)
|
||||
stateRoot, err = ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
epoch = helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err = helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Then we receive the block `f` from slot 6
|
||||
blockF := &pb.BeaconBlock{
|
||||
Slot: genesisSlot + 6,
|
||||
ParentRootHash32: parentHash[:],
|
||||
StateRootHash32: stateRoot[:],
|
||||
RandaoReveal: createRandaoReveal(t, beaconState, privKeys),
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: genesisSlot + 6,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: stateRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
RandaoReveal: randaoReveal,
|
||||
},
|
||||
}
|
||||
rootF, _ := hashutil.HashBeaconBlock(blockF)
|
||||
rootF, _ := ssz.SigningRoot(blockF)
|
||||
if err := db.SaveHistoricalState(ctx, beaconState, rootF); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initBlockStateRoot(t, blockF, chainService)
|
||||
|
||||
initBlockStateRoot(t, blockF, chainService)
|
||||
computedState, err := chainService.ReceiveBlock(ctx, blockF)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateRoot, err = hashutil.HashProto(computedState)
|
||||
stateRoot, err = ssz.HashTreeRoot(computedState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -671,18 +784,26 @@ func TestReceiveBlock_OnChainSplit(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentHash, err = hashutil.HashBeaconBlock(blockF)
|
||||
parentRoot, err = ssz.SigningRoot(blockF)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
epoch = helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err = helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Then we apply block `g` from slot 7
|
||||
blockG := &pb.BeaconBlock{
|
||||
Slot: genesisSlot + 7,
|
||||
ParentRootHash32: parentHash[:],
|
||||
StateRootHash32: stateRoot[:],
|
||||
RandaoReveal: createRandaoReveal(t, computedState, privKeys),
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: genesisSlot + 7,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: stateRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
RandaoReveal: randaoReveal,
|
||||
},
|
||||
}
|
||||
initBlockStateRoot(t, blockG, chainService)
|
||||
|
||||
@@ -703,63 +824,79 @@ func TestIsBlockReadyForProcessing_ValidBlock(t *testing.T) {
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
unixTime := uint64(time.Now().Unix())
|
||||
deposits, privKeys := setupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
|
||||
deposits, privKeys := testutil.SetupInitialDeposits(t, 100, true)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get genesis state: %v", err)
|
||||
}
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
Slot: genesis.Slot,
|
||||
ParentRoot: genesis.ParentRoot,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
ParentRootHash32: []byte{'a'},
|
||||
ParentRoot: []byte{'a'},
|
||||
}
|
||||
|
||||
if err := chainService.VerifyBlockValidity(ctx, block, beaconState); err == nil {
|
||||
t.Fatal("block processing succeeded despite block having no parent saved")
|
||||
}
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 10
|
||||
beaconState.Slot = 10
|
||||
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not tree hash state: %v", err)
|
||||
}
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
if err := chainService.beaconDB.SaveBlock(genesis); err != nil {
|
||||
t.Fatalf("cannot save block: %v", err)
|
||||
}
|
||||
parentRoot, err := hashutil.HashBeaconBlock(genesis)
|
||||
parentRoot, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get root of canonical head: %v", err)
|
||||
}
|
||||
|
||||
beaconState.LatestEth1Data = &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{2},
|
||||
BlockHash32: []byte{3},
|
||||
beaconState.Eth1Data = &pb.Eth1Data{
|
||||
DepositRoot: []byte{2},
|
||||
BlockHash: []byte{3},
|
||||
}
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot
|
||||
beaconState.Slot = 0
|
||||
|
||||
currentSlot := params.BeaconConfig().GenesisSlot + 1
|
||||
attestationSlot := params.BeaconConfig().GenesisSlot
|
||||
currentSlot := uint64(1)
|
||||
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := helpers.CreateRandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
randaoReveal := createRandaoReveal(t, beaconState, privKeys)
|
||||
block2 := &pb.BeaconBlock{
|
||||
Slot: currentSlot,
|
||||
StateRootHash32: stateRoot[:],
|
||||
ParentRootHash32: parentRoot[:],
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte("a"),
|
||||
BlockHash32: []byte("b"),
|
||||
},
|
||||
Slot: currentSlot,
|
||||
StateRoot: stateRoot[:],
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: []byte("a"),
|
||||
BlockHash: []byte("b"),
|
||||
},
|
||||
RandaoReveal: randaoReveal,
|
||||
Attestations: []*pb.Attestation{{
|
||||
AggregationBitfield: []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
AggregationBits: []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Data: &pb.AttestationData{
|
||||
Slot: attestationSlot,
|
||||
JustifiedBlockRootHash32: parentRoot[:],
|
||||
Source: &pb.Checkpoint{Root: parentRoot[:]},
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 960,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -785,8 +922,8 @@ func TestDeleteValidatorIdx_DeleteWorks(t *testing.T) {
|
||||
})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
}
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
if err := chainService.saveValidatorIdx(state); err != nil {
|
||||
@@ -827,8 +964,8 @@ func TestSaveValidatorIdx_SaveRetrieveWorks(t *testing.T) {
|
||||
})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
}
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
if err := chainService.saveValidatorIdx(state); err != nil {
|
||||
@@ -854,7 +991,7 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
|
||||
defer internal.TeardownDB(t, db)
|
||||
epoch := uint64(100)
|
||||
|
||||
// Tried to insert 5 active indices to DB with only 3 validators in state.
|
||||
// Tried to insert 5 active indices to DB with only 3 validators in state
|
||||
v.InsertActivatedIndices(epoch+1, []uint64{0, 1, 2, 3, 4})
|
||||
var validators []*pb.Validator
|
||||
for i := 0; i < 3; i++ {
|
||||
@@ -865,8 +1002,8 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
|
||||
})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: validators,
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
}
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
if err := chainService.saveValidatorIdx(state); err != nil {
|
||||
@@ -886,7 +1023,7 @@ func TestSaveValidatorIdx_IdxNotInState(t *testing.T) {
|
||||
t.Errorf("Activated validators mapping for epoch %d still there", epoch)
|
||||
}
|
||||
|
||||
// Verify the skipped validators are included in the next epoch.
|
||||
// Verify the skipped validators are included in the next epoch
|
||||
if !reflect.DeepEqual(v.ActivatedValFromEpoch(epoch+2), []uint64{3, 4}) {
|
||||
t.Error("Did not get wanted validator from activation queue")
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ import (
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -43,7 +42,7 @@ type TargetsFetcher interface {
|
||||
// are not older than the ones just processed in state. If it's older, we update
|
||||
// the db with the latest FFG check points, both justification and finalization.
|
||||
func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconState) error {
|
||||
lastJustifiedSlot := helpers.StartSlot(state.JustifiedEpoch)
|
||||
lastJustifiedSlot := helpers.StartSlot(state.CurrentJustifiedCheckpoint.Epoch)
|
||||
savedJustifiedBlock, err := c.beaconDB.JustifiedBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,7 +59,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
|
||||
// until we can get a block.
|
||||
lastAvailBlkSlot := lastJustifiedSlot
|
||||
for newJustifiedBlock == nil {
|
||||
log.WithField("slot", lastAvailBlkSlot-params.BeaconConfig().GenesisSlot).Debug("Missing block in DB, looking one slot back")
|
||||
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
|
||||
lastAvailBlkSlot--
|
||||
newJustifiedBlock, err = c.beaconDB.CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
|
||||
if err != nil {
|
||||
@@ -68,7 +67,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
|
||||
}
|
||||
}
|
||||
|
||||
newJustifiedRoot, err := hashutil.HashBeaconBlock(newJustifiedBlock)
|
||||
newJustifiedRoot, err := ssz.SigningRoot(newJustifiedBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,7 +84,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
|
||||
}
|
||||
}
|
||||
|
||||
lastFinalizedSlot := helpers.StartSlot(state.FinalizedEpoch)
|
||||
lastFinalizedSlot := helpers.StartSlot(state.FinalizedCheckpoint.Epoch)
|
||||
savedFinalizedBlock, err := c.beaconDB.FinalizedBlock()
|
||||
// If the last processed finalized slot in state is greater than
|
||||
// the slot of finalized block saved in DB.
|
||||
@@ -102,7 +101,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
|
||||
// until we can get a block.
|
||||
lastAvailBlkSlot := lastFinalizedSlot
|
||||
for newFinalizedBlock == nil {
|
||||
log.WithField("slot", lastAvailBlkSlot-params.BeaconConfig().GenesisSlot).Debug("Missing block in DB, looking one slot back")
|
||||
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
|
||||
lastAvailBlkSlot--
|
||||
newFinalizedBlock, err = c.beaconDB.CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
|
||||
if err != nil {
|
||||
@@ -110,7 +109,7 @@ func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconSt
|
||||
}
|
||||
}
|
||||
|
||||
newFinalizedRoot, err := hashutil.HashBeaconBlock(newFinalizedBlock)
|
||||
newFinalizedRoot, err := ssz.SigningRoot(newFinalizedBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -160,7 +159,7 @@ func (c *ChainService) ApplyForkChoiceRule(
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not run fork choice: %v", err)
|
||||
}
|
||||
newHeadRoot, err := hashutil.HashBeaconBlock(newHead)
|
||||
newHeadRoot, err := ssz.SigningRoot(newHead)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not hash new head block: %v", err)
|
||||
}
|
||||
@@ -172,7 +171,7 @@ func (c *ChainService) ApplyForkChoiceRule(
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve chain head: %v", err)
|
||||
}
|
||||
currentHeadRoot, err := hashutil.HashBeaconBlock(currentHead)
|
||||
currentHeadRoot, err := ssz.SigningRoot(currentHead)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not hash current head block: %v", err)
|
||||
}
|
||||
@@ -185,9 +184,9 @@ func (c *ChainService) ApplyForkChoiceRule(
|
||||
newState := postState
|
||||
if !isDescendant && !proto.Equal(currentHead, newHead) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"currentSlot": currentHead.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"currentSlot": currentHead.Slot,
|
||||
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
|
||||
"newSlot": newHead.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"newSlot": newHead.Slot,
|
||||
"newRoot": fmt.Sprintf("%#x", bytesutil.Trunc(newHeadRoot[:])),
|
||||
}).Warn("Reorg happened")
|
||||
// Only regenerate head state if there was a reorg.
|
||||
@@ -204,7 +203,7 @@ func (c *ChainService) ApplyForkChoiceRule(
|
||||
|
||||
if proto.Equal(currentHead, newHead) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"currentSlot": currentHead.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"currentSlot": currentHead.Slot,
|
||||
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
|
||||
}).Warn("Head did not change after fork choice, current head has the most votes")
|
||||
}
|
||||
@@ -220,14 +219,14 @@ func (c *ChainService) ApplyForkChoiceRule(
|
||||
if err := c.beaconDB.UpdateChainHead(ctx, newHead, newState); err != nil {
|
||||
return fmt.Errorf("failed to update chain: %v", err)
|
||||
}
|
||||
h, err := hashutil.HashBeaconBlock(newHead)
|
||||
h, err := ssz.SigningRoot(newHead)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not hash head: %v", err)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(h[:])),
|
||||
"headSlot": newHead.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"stateSlot": newState.Slot - params.BeaconConfig().GenesisSlot,
|
||||
"headSlot": newHead.Slot,
|
||||
"stateSlot": newState.Slot,
|
||||
}).Info("Chain head block and state updated")
|
||||
|
||||
return nil
|
||||
@@ -288,11 +287,11 @@ func (c *ChainService) lmdGhost(
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to determine vote count for block: %v", err)
|
||||
}
|
||||
maxChildRoot, err := hashutil.HashBeaconBlock(maxChild)
|
||||
maxChildRoot, err := ssz.SigningRoot(maxChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidateChildRoot, err := hashutil.HashBeaconBlock(children[i])
|
||||
candidateChildRoot, err := ssz.SigningRoot(children[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -318,7 +317,7 @@ func (c *ChainService) lmdGhost(
|
||||
// get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]
|
||||
// returns the child blocks of the given block.
|
||||
func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock, highestSlot uint64) ([]*pb.BeaconBlock, error) {
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -334,7 +333,7 @@ func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock,
|
||||
|
||||
filteredChildren := []*pb.BeaconBlock{}
|
||||
for _, kid := range children {
|
||||
parentRoot := bytesutil.ToBytes32(kid.ParentRootHash32)
|
||||
parentRoot := bytesutil.ToBytes32(kid.ParentRoot)
|
||||
if blockRoot == parentRoot {
|
||||
filteredChildren = append(filteredChildren, kid)
|
||||
}
|
||||
@@ -344,15 +343,15 @@ func (c *ChainService) BlockChildren(ctx context.Context, block *pb.BeaconBlock,
|
||||
|
||||
// isDescendant checks if the new head block is a descendant block of the current head.
|
||||
func (c *ChainService) isDescendant(currentHead *pb.BeaconBlock, newHead *pb.BeaconBlock) (bool, error) {
|
||||
currentHeadRoot, err := hashutil.HashBeaconBlock(currentHead)
|
||||
currentHeadRoot, err := ssz.SigningRoot(currentHead)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
for newHead.Slot > currentHead.Slot {
|
||||
if bytesutil.ToBytes32(newHead.ParentRootHash32) == currentHeadRoot {
|
||||
if bytesutil.ToBytes32(newHead.ParentRoot) == currentHeadRoot {
|
||||
return true, nil
|
||||
}
|
||||
newHead, err = c.beaconDB.Block(bytesutil.ToBytes32(newHead.ParentRootHash32))
|
||||
newHead, err = c.beaconDB.Block(bytesutil.ToBytes32(newHead.ParentRoot))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -367,7 +366,11 @@ func (c *ChainService) isDescendant(currentHead *pb.BeaconBlock, newHead *pb.Bea
|
||||
// each attestation target consists of validator index and its attestation target (i.e. the block
|
||||
// which the validator attested to)
|
||||
func (c *ChainService) AttestationTargets(state *pb.BeaconState) (map[uint64]*pb.AttestationTarget, error) {
|
||||
indices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
indices, err := helpers.ActiveValidatorIndices(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attestationTargets := make(map[uint64]*pb.AttestationTarget)
|
||||
for i, index := range indices {
|
||||
target, err := c.attsService.LatestAttestationTarget(state, index)
|
||||
@@ -397,7 +400,7 @@ func VoteCount(block *pb.BeaconBlock, state *pb.BeaconState, targets map[uint64]
|
||||
var ancestorRoot []byte
|
||||
var err error
|
||||
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -417,7 +420,7 @@ func VoteCount(block *pb.BeaconBlock, state *pb.BeaconState, targets map[uint64]
|
||||
}
|
||||
|
||||
if bytes.Equal(blockRoot[:], ancestorRoot) {
|
||||
balances += int(helpers.EffectiveBalance(state, validatorIndex))
|
||||
balances += int(state.Validators[validatorIndex].EffectiveBalance)
|
||||
}
|
||||
}
|
||||
return balances, nil
|
||||
@@ -454,7 +457,7 @@ func BlockAncestor(targetBlock *pb.AttestationTarget, slot uint64, beaconDB *db.
|
||||
newTarget := &pb.AttestationTarget{
|
||||
Slot: parent.Slot,
|
||||
BlockRoot: parentRoot[:],
|
||||
ParentRoot: parent.ParentRootHash32,
|
||||
ParentRoot: parent.ParentRoot,
|
||||
}
|
||||
return BlockAncestor(newTarget, slot, beaconDB)
|
||||
}
|
||||
@@ -485,7 +488,7 @@ func cachedAncestor(target *pb.AttestationTarget, height uint64, beaconDB *db.Be
|
||||
ancestorTarget := &pb.AttestationTarget{
|
||||
Slot: ancestor.Slot,
|
||||
BlockRoot: ancestorRoot,
|
||||
ParentRoot: ancestor.ParentRootHash32,
|
||||
ParentRoot: ancestor.ParentRoot,
|
||||
}
|
||||
if err := blkAncestorCache.AddBlockAncestor(&cache.AncestorInfo{
|
||||
Height: height,
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -27,20 +27,22 @@ func (m *mockAttestationHandler) BatchUpdateLatestAttestation(ctx context.Contex
|
||||
}
|
||||
|
||||
func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
// TODO(#2307): Fix test once v0.6 is merged.
|
||||
t.Skip()
|
||||
hook := logTest.NewGlobal()
|
||||
beaconDB := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, beaconDB)
|
||||
|
||||
ctx := context.Background()
|
||||
deposits, _ := setupInitialDeposits(t, 100)
|
||||
eth1Data := &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{},
|
||||
BlockHash32: []byte{},
|
||||
}
|
||||
justifiedState, err := state.GenesisBeaconState(deposits, 0, eth1Data)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
justifiedState, err := state.GenesisBeaconState(deposits, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
justifiedState.StateRoots = make([][]byte, params.BeaconConfig().HistoricalRootsLimit)
|
||||
justifiedState.LatestBlockHeader = &pb.BeaconBlockHeader{
|
||||
StateRoot: []byte{},
|
||||
}
|
||||
|
||||
chainService := setupBeaconChain(t, beaconDB, nil)
|
||||
|
||||
@@ -54,7 +56,6 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
if err := chainService.beaconDB.SaveBlock(blocks[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedState.LatestBlock = blocks[0]
|
||||
if err := chainService.beaconDB.SaveJustifiedState(justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -67,7 +68,7 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
canonicalBlockIndices := []int{1, 3, 5}
|
||||
postState := proto.Clone(justifiedState).(*pb.BeaconState)
|
||||
for _, canonicalIndex := range canonicalBlockIndices {
|
||||
postState, err = chainService.ApplyBlockStateTransition(ctx, blocks[canonicalIndex], postState)
|
||||
postState, err = chainService.AdvanceState(ctx, postState, blocks[canonicalIndex])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -86,8 +87,8 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
if chainHead.Slot != justifiedState.Slot+5 {
|
||||
t.Errorf(
|
||||
"Expected chain head with slot %d, received %d",
|
||||
justifiedState.Slot+5-params.BeaconConfig().GenesisSlot,
|
||||
chainHead.Slot-params.BeaconConfig().GenesisSlot,
|
||||
justifiedState.Slot+5,
|
||||
chainHead.Slot,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
forkedBlockIndices := []int{2, 4}
|
||||
forkState := proto.Clone(justifiedState).(*pb.BeaconState)
|
||||
for _, forkIndex := range forkedBlockIndices {
|
||||
forkState, err = chainService.ApplyBlockStateTransition(ctx, blocks[forkIndex], forkState)
|
||||
forkState, err = chainService.AdvanceState(ctx, forkState, blocks[forkIndex])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -113,13 +114,13 @@ func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
voteTargets[0] = &pb.AttestationTarget{
|
||||
Slot: blocks[5].Slot,
|
||||
BlockRoot: roots[5][:],
|
||||
ParentRoot: blocks[5].ParentRootHash32,
|
||||
ParentRoot: blocks[5].ParentRoot,
|
||||
}
|
||||
for i := 1; i < len(deposits); i++ {
|
||||
voteTargets[uint64(i)] = &pb.AttestationTarget{
|
||||
Slot: blocks[4].Slot,
|
||||
BlockRoot: roots[4][:],
|
||||
ParentRoot: blocks[4].ParentRootHash32,
|
||||
ParentRoot: blocks[4].ParentRoot,
|
||||
}
|
||||
}
|
||||
attHandler := &mockAttestationHandler{
|
||||
@@ -159,61 +160,73 @@ func constructForkedChain(t *testing.T, beaconState *pb.BeaconState) ([]*pb.Beac
|
||||
roots := make([][32]byte, 6)
|
||||
var err error
|
||||
blocks[0] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
ParentRootHash32: []byte{'A'},
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot,
|
||||
ParentRoot: []byte{'A'},
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[0], err = hashutil.HashBeaconBlock(blocks[0])
|
||||
roots[0], err = ssz.SigningRoot(blocks[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[1] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 2,
|
||||
ParentRootHash32: roots[0][:],
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot + 2,
|
||||
ParentRoot: roots[0][:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[1], err = hashutil.HashBeaconBlock(blocks[1])
|
||||
roots[1], err = ssz.SigningRoot(blocks[1])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[2] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 1,
|
||||
ParentRootHash32: roots[0][:],
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot + 1,
|
||||
ParentRoot: roots[0][:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[2], err = hashutil.HashBeaconBlock(blocks[2])
|
||||
roots[2], err = ssz.SigningRoot(blocks[2])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[3] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 3,
|
||||
ParentRootHash32: roots[1][:],
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot + 3,
|
||||
ParentRoot: roots[1][:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[3], err = hashutil.HashBeaconBlock(blocks[3])
|
||||
roots[3], err = ssz.SigningRoot(blocks[3])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[4] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 4,
|
||||
ParentRootHash32: roots[2][:],
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot + 4,
|
||||
ParentRoot: roots[2][:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[4], err = hashutil.HashBeaconBlock(blocks[4])
|
||||
roots[4], err = ssz.SigningRoot(blocks[4])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[5] = &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 5,
|
||||
ParentRootHash32: roots[3][:],
|
||||
Body: &pb.BeaconBlockBody{},
|
||||
Slot: beaconState.Slot + 5,
|
||||
ParentRoot: roots[3][:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Eth1Data: &pb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[5], err = hashutil.HashBeaconBlock(blocks[5])
|
||||
roots[5], err = ssz.SigningRoot(blocks[5])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
@@ -18,7 +19,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2p"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -95,7 +95,7 @@ func (c *ChainService) Start() {
|
||||
if beaconState != nil {
|
||||
log.Info("Beacon chain data already exists, starting service")
|
||||
c.genesisTime = time.Unix(int64(beaconState.GenesisTime), 0)
|
||||
c.finalizedEpoch = beaconState.FinalizedEpoch
|
||||
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
|
||||
} else {
|
||||
log.Info("Waiting for ChainStart log from the Validator Deposit Contract to start the beacon chain...")
|
||||
if c.web3Service == nil {
|
||||
@@ -114,17 +114,12 @@ func (c *ChainService) Start() {
|
||||
// processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
|
||||
// deposit contract, initializes the beacon chain's state, and kicks off the beacon chain.
|
||||
func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSub event.Subscription) {
|
||||
initialDepositsData := c.web3Service.ChainStartDeposits()
|
||||
initialDeposits := make([]*pb.Deposit, len(initialDepositsData))
|
||||
for i := range initialDepositsData {
|
||||
initialDeposits[i] = &pb.Deposit{DepositData: initialDepositsData[i]}
|
||||
}
|
||||
|
||||
initialDeposits := c.web3Service.ChainStartDeposits()
|
||||
beaconState, err := c.initializeBeaconChain(genesisTime, initialDeposits, c.web3Service.ChainStartETH1Data())
|
||||
if err != nil {
|
||||
log.Fatalf("Could not initialize beacon chain: %v", err)
|
||||
}
|
||||
c.finalizedEpoch = beaconState.FinalizedEpoch
|
||||
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
|
||||
c.stateInitializedFeed.Send(genesisTime)
|
||||
chainStartSub.Unsubscribe()
|
||||
}
|
||||
@@ -132,8 +127,7 @@ func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSu
|
||||
// initializes the state and genesis block of the beacon chain to persistent storage
|
||||
// based on a genesis timestamp value obtained from the ChainStart event emitted
|
||||
// by the ETH1.0 Deposit Contract and the POWChain service of the node.
|
||||
func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*pb.Deposit,
|
||||
eth1data *pb.Eth1Data) (*pb.BeaconState, error) {
|
||||
func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*pb.Deposit, eth1data *pb.Eth1Data) (*pb.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "beacon-chain.ChainService.initializeBeaconChain")
|
||||
defer span.End()
|
||||
log.Info("ChainStart time reached, starting the beacon chain!")
|
||||
@@ -147,26 +141,23 @@ func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*
|
||||
return nil, fmt.Errorf("could not attempt fetch beacon state: %v", err)
|
||||
}
|
||||
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash beacon state: %v", err)
|
||||
}
|
||||
genBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
genBlockRoot, err := hashutil.HashBeaconBlock(genBlock)
|
||||
genBlockRoot, err := ssz.SigningRoot(genBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash beacon block: %v", err)
|
||||
}
|
||||
|
||||
// TODO(#2011): Remove this in state caching.
|
||||
beaconState.LatestBlock = genBlock
|
||||
|
||||
if err := c.beaconDB.SaveBlock(genBlock); err != nil {
|
||||
return nil, fmt.Errorf("could not save genesis block to disk: %v", err)
|
||||
}
|
||||
if err := c.beaconDB.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: genBlock.Slot,
|
||||
BlockRoot: genBlockRoot[:],
|
||||
ParentRoot: genBlock.ParentRootHash32,
|
||||
ParentRoot: genBlock.ParentRoot,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("failed to save attestation target: %v", err)
|
||||
}
|
||||
@@ -225,7 +216,7 @@ func (c *ChainService) ChainHeadRoot() ([32]byte, error) {
|
||||
return [32]byte{}, fmt.Errorf("could not retrieve chain head: %v", err)
|
||||
}
|
||||
|
||||
root, err := hashutil.HashBeaconBlock(head)
|
||||
root, err := ssz.SigningRoot(head)
|
||||
if err != nil {
|
||||
return [32]byte{}, fmt.Errorf("could not tree hash parent block: %v", err)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
@@ -14,18 +12,15 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/forkutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
@@ -156,54 +151,13 @@ func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) {
|
||||
|
||||
var _ = p2p.Broadcaster(&mockBroadcaster{})
|
||||
|
||||
func setupInitialDeposits(t *testing.T, numDeposits int) ([]*pb.Deposit, []*bls.SecretKey) {
|
||||
privKeys := make([]*bls.SecretKey, numDeposits)
|
||||
deposits := make([]*pb.Deposit, numDeposits)
|
||||
for i := 0; i < len(deposits); i++ {
|
||||
priv, err := bls.RandKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
depositInput := &pb.DepositInput{
|
||||
Pubkey: priv.PublicKey().Marshal(),
|
||||
}
|
||||
balance := params.BeaconConfig().MaxDepositAmount
|
||||
depositData, err := helpers.EncodeDepositData(depositInput, balance, time.Now().Unix())
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot encode data: %v", err)
|
||||
}
|
||||
deposits[i] = &pb.Deposit{
|
||||
DepositData: depositData,
|
||||
MerkleTreeIndex: uint64(i),
|
||||
}
|
||||
privKeys[i] = priv
|
||||
}
|
||||
return deposits, privKeys
|
||||
}
|
||||
func createPreChainStartDeposit(pk []byte) *pb.Deposit {
|
||||
balance := params.BeaconConfig().MaxEffectiveBalance
|
||||
depositData := &pb.DepositData{Pubkey: pk, Amount: balance, Signature: make([]byte, 96)}
|
||||
|
||||
func createPreChainStartDeposit(t *testing.T, pk []byte, index uint64) *pb.Deposit {
|
||||
depositInput := &pb.DepositInput{Pubkey: pk}
|
||||
balance := params.BeaconConfig().MaxDepositAmount
|
||||
depositData, err := helpers.EncodeDepositData(depositInput, balance, time.Now().Unix())
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot encode data: %v", err)
|
||||
return &pb.Deposit{
|
||||
Data: depositData,
|
||||
}
|
||||
return &pb.Deposit{DepositData: depositData, MerkleTreeIndex: index}
|
||||
}
|
||||
|
||||
func createRandaoReveal(t *testing.T, beaconState *pb.BeaconState, privKeys []*bls.SecretKey) []byte {
|
||||
// We fetch the proposer's index as that is whom the RANDAO will be verified against.
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(beaconState, beaconState.Slot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch := helpers.SlotToEpoch(beaconState.Slot)
|
||||
buf := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(buf, epoch)
|
||||
domain := forkutil.DomainVersion(beaconState.Fork, epoch, params.BeaconConfig().DomainRandao)
|
||||
// We make the previous validator's index sign the message instead of the proposer.
|
||||
epochSignature := privKeys[proposerIdx].Sign(buf, domain)
|
||||
return epochSignature.Marshal()
|
||||
}
|
||||
|
||||
func setupGenesisBlock(t *testing.T, cs *ChainService) ([32]byte, *pb.BeaconBlock) {
|
||||
@@ -211,7 +165,7 @@ func setupGenesisBlock(t *testing.T, cs *ChainService) ([32]byte, *pb.BeaconBloc
|
||||
if err := cs.beaconDB.SaveBlock(genesis); err != nil {
|
||||
t.Fatalf("could not save block to db: %v", err)
|
||||
}
|
||||
parentHash, err := hashutil.HashBeaconBlock(genesis)
|
||||
parentHash, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get tree hash root of canonical head: %v", err)
|
||||
}
|
||||
@@ -289,7 +243,7 @@ func TestChainStartStop_Uninitialized(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if beaconState == nil || beaconState.Slot != params.BeaconConfig().GenesisSlot {
|
||||
if beaconState == nil || beaconState.Slot != 0 {
|
||||
t.Error("Expected canonical state feed to send a state with genesis block")
|
||||
}
|
||||
if err := chainService.Stop(); err != nil {
|
||||
@@ -311,8 +265,8 @@ func TestChainStartStop_Initialized(t *testing.T) {
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
|
||||
unixTime := uint64(time.Now().Unix())
|
||||
deposits, _ := setupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, &pb.Eth1Data{}); err != nil {
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
setupGenesisBlock(t, chainService)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["state_generator.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/stategenerator",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["state_generator_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/chaintest/backend:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,179 +0,0 @@
|
||||
package stategenerator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "stategenerator")
|
||||
|
||||
// GenerateStateFromBlock generates state from the last finalized state to the input slot.
|
||||
// Ex:
|
||||
// 1A - 2B(finalized) - 3C - 4 - 5D - 6 - 7F (letters mean there's a block).
|
||||
// Input: slot 6.
|
||||
// Output: resulting state of state transition function after applying block C and D.
|
||||
// along with skipped slot 4 and 6.
|
||||
func GenerateStateFromBlock(ctx context.Context, db *db.BeaconDB, slot uint64) (*pb.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.GenerateStateFromBlock")
|
||||
defer span.End()
|
||||
fState, err := db.HistoricalStateFromSlot(ctx, slot, [32]byte{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return finalized state if it's the same as input slot.
|
||||
if fState.Slot == slot {
|
||||
return fState, nil
|
||||
}
|
||||
|
||||
// input slot can't be smaller than last finalized state's slot.
|
||||
if fState.Slot > slot {
|
||||
return nil, fmt.Errorf(
|
||||
"requested slot %d < current slot %d in the finalized beacon state",
|
||||
slot-params.BeaconConfig().GenesisSlot,
|
||||
fState.Slot-params.BeaconConfig().GenesisSlot,
|
||||
)
|
||||
}
|
||||
|
||||
if fState.LatestBlock == nil {
|
||||
return nil, fmt.Errorf("latest head in state is nil %v", err)
|
||||
}
|
||||
|
||||
fRoot, err := hashutil.HashBeaconBlock(fState.LatestBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get block root %v", err)
|
||||
}
|
||||
|
||||
// from input slot, retrieve its corresponding block and call that the most recent block.
|
||||
mostRecentBlocks, err := db.BlocksBySlot(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mostRecentBlock := mostRecentBlocks[0]
|
||||
|
||||
// if the most recent block is a skip block, we get its parent block.
|
||||
// ex:
|
||||
// 1A - 2B - 3C - 4 - 5 (letters mean there's a block).
|
||||
// input slot is 5, but slots 4 and 5 are skipped, we get block C from slot 3.
|
||||
lastSlot := slot
|
||||
for mostRecentBlock == nil {
|
||||
lastSlot--
|
||||
blocks, err := db.BlocksBySlot(ctx, lastSlot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mostRecentBlock = blocks[0]
|
||||
}
|
||||
|
||||
// retrieve the block list to recompute state of the input slot.
|
||||
blocks, err := blocksSinceFinalized(ctx, db, mostRecentBlock, fRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to look up block ancestors %v", err)
|
||||
}
|
||||
|
||||
log.Infof("Recompute state starting last finalized slot %d and ending slot %d",
|
||||
fState.Slot-params.BeaconConfig().GenesisSlot, slot-params.BeaconConfig().GenesisSlot)
|
||||
postState := fState
|
||||
root := fRoot
|
||||
// this recomputes state up to the last available block.
|
||||
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (C is the last block).
|
||||
// input slot 8, this recomputes state to slot 6.
|
||||
for i := len(blocks); i > 0; i-- {
|
||||
block := blocks[i-1]
|
||||
if block.Slot <= postState.Slot {
|
||||
continue
|
||||
}
|
||||
// running state transitions for skipped slots.
|
||||
for block.Slot != fState.Slot+1 {
|
||||
postState, err = state.ExecuteStateTransition(
|
||||
ctx,
|
||||
postState,
|
||||
nil,
|
||||
root,
|
||||
&state.TransitionConfig{
|
||||
VerifySignatures: false,
|
||||
Logging: false,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute state transition %v", err)
|
||||
}
|
||||
}
|
||||
postState, err = state.ExecuteStateTransition(
|
||||
ctx,
|
||||
postState,
|
||||
block,
|
||||
root,
|
||||
&state.TransitionConfig{
|
||||
VerifySignatures: false,
|
||||
Logging: false,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute state transition %v", err)
|
||||
}
|
||||
|
||||
root, err = hashutil.HashBeaconBlock(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get block root %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// this recomputes state from last block to last slot if there's skipp slots after.
|
||||
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (7 and 8 are skipped slots).
|
||||
// input slot 8, this recomputes state from 6C to 8.
|
||||
for i := postState.Slot; i < slot; i++ {
|
||||
postState, err = state.ExecuteStateTransition(
|
||||
ctx,
|
||||
postState,
|
||||
nil,
|
||||
root,
|
||||
&state.TransitionConfig{
|
||||
VerifySignatures: false,
|
||||
Logging: false,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute state transition %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Finished recompute state with slot %d and finalized epoch %d",
|
||||
postState.Slot-params.BeaconConfig().GenesisSlot, postState.FinalizedEpoch-params.BeaconConfig().GenesisEpoch)
|
||||
|
||||
return postState, nil
|
||||
}
|
||||
|
||||
// blocksSinceFinalized will return a list of linked blocks that's
|
||||
// between the input block and the last finalized block in the db.
|
||||
// The input block is also returned in the list.
|
||||
// Ex:
|
||||
// A -> B(finalized) -> C -> D -> E -> D.
|
||||
// Input: E, output: [E, D, C, B].
|
||||
func blocksSinceFinalized(ctx context.Context, db *db.BeaconDB, block *pb.BeaconBlock,
|
||||
finalizedBlockRoot [32]byte) ([]*pb.BeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.blocksSinceFinalized")
|
||||
defer span.End()
|
||||
blockAncestors := make([]*pb.BeaconBlock, 0)
|
||||
blockAncestors = append(blockAncestors, block)
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
|
||||
// looking up ancestors, until the finalized block.
|
||||
for parentRoot != finalizedBlockRoot {
|
||||
retblock, err := db.Block(parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockAncestors = append(blockAncestors, retblock)
|
||||
parentRoot = bytesutil.ToBytes32(retblock.ParentRootHash32)
|
||||
}
|
||||
return blockAncestors, nil
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package stategenerator_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain/stategenerator"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
CacheTreeHash: false,
|
||||
})
|
||||
}
|
||||
func TestGenerateState_OK(t *testing.T) {
|
||||
b, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a new simulated backend %v", err)
|
||||
}
|
||||
privKeys, err := b.SetupBackend(100)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not set up backend %v", err)
|
||||
}
|
||||
beaconDb := b.DB()
|
||||
defer b.Shutdown()
|
||||
defer db.TeardownDB(beaconDb)
|
||||
ctx := context.Background()
|
||||
|
||||
slotLimit := uint64(30)
|
||||
|
||||
// Run the simulated chain for 30 slots, to get a state that we can save as finalized.
|
||||
for i := uint64(0); i < slotLimit; i++ {
|
||||
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
|
||||
}
|
||||
inMemBlocks := b.InMemoryBlocks()
|
||||
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.SaveFinalizedBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save finalized state: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := beaconDb.SaveFinalizedState(b.State()); err != nil {
|
||||
t.Fatalf("Unable to save finalized state: %v", err)
|
||||
}
|
||||
|
||||
// Run the chain for another 30 slots so that we can have this at the current head.
|
||||
for i := uint64(0); i < slotLimit; i++ {
|
||||
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
|
||||
}
|
||||
inMemBlocks := b.InMemoryBlocks()
|
||||
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ran 30 slots to save finalized slot then ran another 30 slots.
|
||||
slotToGenerateTill := params.BeaconConfig().GenesisSlot + slotLimit*2
|
||||
newState, err := stategenerator.GenerateStateFromBlock(context.Background(), beaconDb, slotToGenerateTill)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to generate new state from previous finalized state %v", err)
|
||||
}
|
||||
|
||||
if newState.Slot != b.State().Slot {
|
||||
t.Fatalf("The generated state and the current state do not have the same slot, expected: %d but got %d",
|
||||
b.State().Slot, newState.Slot)
|
||||
}
|
||||
|
||||
if !proto.Equal(newState, b.State()) {
|
||||
t.Error("Generated and saved states are unequal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateState_WithNilBlocksOK(t *testing.T) {
|
||||
b, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a new simulated backend %v", err)
|
||||
}
|
||||
privKeys, err := b.SetupBackend(100)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not set up backend %v", err)
|
||||
}
|
||||
beaconDb := b.DB()
|
||||
defer b.Shutdown()
|
||||
defer db.TeardownDB(beaconDb)
|
||||
ctx := context.Background()
|
||||
|
||||
slotLimit := uint64(30)
|
||||
|
||||
// Run the simulated chain for 30 slots, to get a state that we can save as finalized.
|
||||
for i := uint64(0); i < slotLimit; i++ {
|
||||
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
|
||||
}
|
||||
inMemBlocks := b.InMemoryBlocks()
|
||||
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.SaveFinalizedBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save finalized state: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := beaconDb.SaveFinalizedState(b.State()); err != nil {
|
||||
t.Fatalf("Unable to save finalized state")
|
||||
}
|
||||
|
||||
slotsWithNil := uint64(10)
|
||||
|
||||
// Run the chain for 10 slots with nil blocks.
|
||||
for i := uint64(0); i < slotsWithNil; i++ {
|
||||
if err := b.GenerateNilBlockAndAdvanceChain(); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
|
||||
}
|
||||
}
|
||||
|
||||
for i := uint64(0); i < slotLimit-slotsWithNil; i++ {
|
||||
if err := b.GenerateBlockAndAdvanceChain(&backend.SimulatedObjects{}, privKeys); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, b.State().Slot+1)
|
||||
}
|
||||
inMemBlocks := b.InMemoryBlocks()
|
||||
if err := beaconDb.SaveBlock(inMemBlocks[len(inMemBlocks)-1]); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
if err := beaconDb.UpdateChainHead(ctx, inMemBlocks[len(inMemBlocks)-1], b.State()); err != nil {
|
||||
t.Fatalf("Unable to save block %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ran 30 slots to save finalized slot then ran another 10 slots w/o blocks and 20 slots w/ blocks.
|
||||
slotToGenerateTill := params.BeaconConfig().GenesisSlot + slotLimit*2
|
||||
newState, err := stategenerator.GenerateStateFromBlock(context.Background(), beaconDb, slotToGenerateTill)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to generate new state from previous finalized state %v", err)
|
||||
}
|
||||
|
||||
if newState.Slot != b.State().Slot {
|
||||
t.Fatalf("The generated state and the current state do not have the same slot, expected: %d but got %d",
|
||||
b.State().Slot, newState.Slot)
|
||||
}
|
||||
|
||||
if !proto.Equal(newState, b.State()) {
|
||||
t.Error("generated and saved states are unequal")
|
||||
}
|
||||
}
|
||||
20
beacon-chain/cache/BUILD.bazel
vendored
20
beacon-chain/cache/BUILD.bazel
vendored
@@ -3,9 +3,17 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"active_balance.go",
|
||||
"active_count.go",
|
||||
"active_indices.go",
|
||||
"attestation_data.go",
|
||||
"block.go",
|
||||
"committee.go",
|
||||
"common.go",
|
||||
"eth1_data.go",
|
||||
"seed.go",
|
||||
"shuffled_indices.go",
|
||||
"start_shard.go",
|
||||
"total_balance.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
@@ -23,15 +31,23 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"active_balance_test.go",
|
||||
"active_count_test.go",
|
||||
"active_indices_test.go",
|
||||
"attestation_data_test.go",
|
||||
"block_test.go",
|
||||
"committee_test.go",
|
||||
"eth1_data_test.go",
|
||||
"seed_test.go",
|
||||
"shuffled_indices_test.go",
|
||||
"start_shard_test.go",
|
||||
"total_balance_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
race = "on",
|
||||
deps = [
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
98
beacon-chain/cache/active_balance.go
vendored
Normal file
98
beacon-chain/cache/active_balance.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotActiveBalanceInfo will be returned when a cache object is not a pointer to
|
||||
// a ActiveBalanceByEpoch struct.
|
||||
ErrNotActiveBalanceInfo = errors.New("object is not a active balance obj")
|
||||
|
||||
// maxActiveBalanceListSize defines the max number of active balance can cache.
|
||||
maxActiveBalanceListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
activeBalanceCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_balance_cache_miss",
|
||||
Help: "The number of active balance requests that aren't present in the cache.",
|
||||
})
|
||||
activeBalanceCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_balance_cache_hit",
|
||||
Help: "The number of active balance requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// ActiveBalanceByEpoch defines the active validator balance per epoch.
|
||||
type ActiveBalanceByEpoch struct {
|
||||
Epoch uint64
|
||||
ActiveBalance uint64
|
||||
}
|
||||
|
||||
// ActiveBalanceCache is a struct with 1 queue for looking up active balance by epoch.
|
||||
type ActiveBalanceCache struct {
|
||||
activeBalanceCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// activeBalanceKeyFn takes the epoch as the key for the active balance of a given epoch.
|
||||
func activeBalanceKeyFn(obj interface{}) (string, error) {
|
||||
tInfo, ok := obj.(*ActiveBalanceByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotActiveBalanceInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(tInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewActiveBalanceCache creates a new active balance cache for storing/accessing active validator balance.
|
||||
func NewActiveBalanceCache() *ActiveBalanceCache {
|
||||
return &ActiveBalanceCache{
|
||||
activeBalanceCache: cache.NewFIFO(activeBalanceKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveBalanceInEpoch fetches ActiveBalanceByEpoch by epoch. Returns true with a
|
||||
// reference to the ActiveBalanceInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ActiveBalanceCache) ActiveBalanceInEpoch(epoch uint64) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.activeBalanceCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return params.BeaconConfig().FarFutureEpoch, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
activeBalanceCacheHit.Inc()
|
||||
} else {
|
||||
activeBalanceCacheMiss.Inc()
|
||||
return params.BeaconConfig().FarFutureEpoch, nil
|
||||
}
|
||||
|
||||
tInfo, ok := obj.(*ActiveBalanceByEpoch)
|
||||
if !ok {
|
||||
return params.BeaconConfig().FarFutureEpoch, ErrNotActiveBalanceInfo
|
||||
}
|
||||
|
||||
return tInfo.ActiveBalance, nil
|
||||
}
|
||||
|
||||
// AddActiveBalance adds ActiveBalanceByEpoch object to the cache. This method also trims the least
|
||||
// recently added ActiveBalanceByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *ActiveBalanceCache) AddActiveBalance(activeBalance *ActiveBalanceByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.activeBalanceCache.AddIfNotPresent(activeBalance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.activeBalanceCache, maxActiveBalanceListSize)
|
||||
return nil
|
||||
}
|
||||
83
beacon-chain/cache/active_balance_test.go
vendored
Normal file
83
beacon-chain/cache/active_balance_test.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestActiveBalanceKeyFn_OK(t *testing.T) {
|
||||
tInfo := &ActiveBalanceByEpoch{
|
||||
Epoch: 45,
|
||||
ActiveBalance: 7456,
|
||||
}
|
||||
|
||||
key, err := activeBalanceKeyFn(tInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(tInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveBalanceKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := activeBalanceKeyFn("bad")
|
||||
if err != ErrNotActiveBalanceInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotActiveBalanceInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveBalanceCache_ActiveBalanceByEpoch(t *testing.T) {
|
||||
cache := NewActiveBalanceCache()
|
||||
|
||||
tInfo := &ActiveBalanceByEpoch{
|
||||
Epoch: 16511,
|
||||
ActiveBalance: 4456547,
|
||||
}
|
||||
activeBalance, err := cache.ActiveBalanceInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if activeBalance != params.BeaconConfig().FarFutureEpoch {
|
||||
t.Error("Expected active balance not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddActiveBalance(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
activeBalance, err = cache.ActiveBalanceInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(activeBalance, tInfo.ActiveBalance) {
|
||||
t.Errorf(
|
||||
"Expected fetched active balance to be %v, got %v",
|
||||
tInfo.ActiveBalance,
|
||||
activeBalance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveBalance_MaxSize(t *testing.T) {
|
||||
cache := NewActiveBalanceCache()
|
||||
|
||||
for i := uint64(0); i < 1001; i++ {
|
||||
tInfo := &ActiveBalanceByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddActiveBalance(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.activeBalanceCache.ListKeys()) != maxActiveBalanceListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxActiveBalanceListSize,
|
||||
len(cache.activeBalanceCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
98
beacon-chain/cache/active_count.go
vendored
Normal file
98
beacon-chain/cache/active_count.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotActiveCountInfo will be returned when a cache object is not a pointer to
|
||||
// a ActiveCountByEpoch struct.
|
||||
ErrNotActiveCountInfo = errors.New("object is not a active count obj")
|
||||
|
||||
// maxActiveCountListSize defines the max number of active count can cache.
|
||||
maxActiveCountListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
activeCountCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_validator_count_cache_miss",
|
||||
Help: "The number of active validator count requests that aren't present in the cache.",
|
||||
})
|
||||
activeCountCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_validator_count_cache_hit",
|
||||
Help: "The number of active validator count requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// ActiveCountByEpoch defines the active validator count per epoch.
|
||||
type ActiveCountByEpoch struct {
|
||||
Epoch uint64
|
||||
ActiveCount uint64
|
||||
}
|
||||
|
||||
// ActiveCountCache is a struct with 1 queue for looking up active count by epoch.
|
||||
type ActiveCountCache struct {
|
||||
activeCountCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// activeCountKeyFn takes the epoch as the key for the active count of a given epoch.
|
||||
func activeCountKeyFn(obj interface{}) (string, error) {
|
||||
aInfo, ok := obj.(*ActiveCountByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotActiveCountInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(aInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewActiveCountCache creates a new active count cache for storing/accessing active validator count.
|
||||
func NewActiveCountCache() *ActiveCountCache {
|
||||
return &ActiveCountCache{
|
||||
activeCountCache: cache.NewFIFO(activeCountKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveCountInEpoch fetches ActiveCountByEpoch by epoch. Returns true with a
|
||||
// reference to the ActiveCountInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ActiveCountCache) ActiveCountInEpoch(epoch uint64) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.activeCountCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return params.BeaconConfig().FarFutureEpoch, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
activeCountCacheHit.Inc()
|
||||
} else {
|
||||
activeCountCacheMiss.Inc()
|
||||
return params.BeaconConfig().FarFutureEpoch, nil
|
||||
}
|
||||
|
||||
aInfo, ok := obj.(*ActiveCountByEpoch)
|
||||
if !ok {
|
||||
return params.BeaconConfig().FarFutureEpoch, ErrNotActiveCountInfo
|
||||
}
|
||||
|
||||
return aInfo.ActiveCount, nil
|
||||
}
|
||||
|
||||
// AddActiveCount adds ActiveCountByEpoch object to the cache. This method also trims the least
|
||||
// recently added ActiveCountByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *ActiveCountCache) AddActiveCount(activeCount *ActiveCountByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.activeCountCache.AddIfNotPresent(activeCount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.activeCountCache, maxActiveCountListSize)
|
||||
return nil
|
||||
}
|
||||
83
beacon-chain/cache/active_count_test.go
vendored
Normal file
83
beacon-chain/cache/active_count_test.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestActiveCountKeyFn_OK(t *testing.T) {
|
||||
aInfo := &ActiveCountByEpoch{
|
||||
Epoch: 999,
|
||||
ActiveCount: 10,
|
||||
}
|
||||
|
||||
key, err := activeCountKeyFn(aInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(aInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(aInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveCountKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := activeCountKeyFn("bad")
|
||||
if err != ErrNotActiveCountInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotActiveCountInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveCountCache_ActiveCountByEpoch(t *testing.T) {
|
||||
cache := NewActiveCountCache()
|
||||
|
||||
aInfo := &ActiveCountByEpoch{
|
||||
Epoch: 99,
|
||||
ActiveCount: 11,
|
||||
}
|
||||
activeCount, err := cache.ActiveCountInEpoch(aInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if activeCount != params.BeaconConfig().FarFutureEpoch {
|
||||
t.Error("Expected active count not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddActiveCount(aInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
activeCount, err = cache.ActiveCountInEpoch(aInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(activeCount, aInfo.ActiveCount) {
|
||||
t.Errorf(
|
||||
"Expected fetched active count to be %v, got %v",
|
||||
aInfo.ActiveCount,
|
||||
activeCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveCount_MaxSize(t *testing.T) {
|
||||
cache := NewActiveCountCache()
|
||||
|
||||
for i := uint64(0); i < 1001; i++ {
|
||||
aInfo := &ActiveCountByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddActiveCount(aInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.activeCountCache.ListKeys()) != maxActiveCountListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxActiveCountListSize,
|
||||
len(cache.activeCountCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
102
beacon-chain/cache/active_indices.go
vendored
Normal file
102
beacon-chain/cache/active_indices.go
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotActiveIndicesInfo will be returned when a cache object is not a pointer to
|
||||
// a ActiveIndicesByEpoch struct.
|
||||
ErrNotActiveIndicesInfo = errors.New("object is not a active indices list")
|
||||
|
||||
// maxActiveIndicesListSize defines the max number of active indices can cache.
|
||||
maxActiveIndicesListSize = 4
|
||||
|
||||
// Metrics.
|
||||
activeIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_validator_indices_cache_miss",
|
||||
Help: "The number of active validator indices requests that aren't present in the cache.",
|
||||
})
|
||||
activeIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "active_validator_indices_cache_hit",
|
||||
Help: "The number of active validator indices requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// ActiveIndicesByEpoch defines the active validator indices per epoch.
|
||||
type ActiveIndicesByEpoch struct {
|
||||
Epoch uint64
|
||||
ActiveIndices []uint64
|
||||
}
|
||||
|
||||
// ActiveIndicesCache is a struct with 1 queue for looking up active indices by epoch.
|
||||
type ActiveIndicesCache struct {
|
||||
activeIndicesCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// activeIndicesKeyFn takes the epoch as the key for the active indices of a given epoch.
|
||||
func activeIndicesKeyFn(obj interface{}) (string, error) {
|
||||
aInfo, ok := obj.(*ActiveIndicesByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotActiveIndicesInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(aInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewActiveIndicesCache creates a new active indices cache for storing/accessing active validator indices.
|
||||
func NewActiveIndicesCache() *ActiveIndicesCache {
|
||||
return &ActiveIndicesCache{
|
||||
activeIndicesCache: cache.NewFIFO(activeIndicesKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveIndicesInEpoch fetches ActiveIndicesByEpoch by epoch. Returns true with a
|
||||
// reference to the ActiveIndicesInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ActiveIndicesCache) ActiveIndicesInEpoch(epoch uint64) ([]uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.activeIndicesCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
activeIndicesCacheHit.Inc()
|
||||
} else {
|
||||
activeIndicesCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
aInfo, ok := obj.(*ActiveIndicesByEpoch)
|
||||
if !ok {
|
||||
return nil, ErrNotActiveIndicesInfo
|
||||
}
|
||||
|
||||
return aInfo.ActiveIndices, nil
|
||||
}
|
||||
|
||||
// AddActiveIndicesList adds ActiveIndicesByEpoch object to the cache. This method also trims the least
|
||||
// recently added ActiveIndicesByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *ActiveIndicesCache) AddActiveIndicesList(activeIndices *ActiveIndicesByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.activeIndicesCache.AddIfNotPresent(activeIndices); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.activeIndicesCache, maxActiveIndicesListSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActiveIndicesKeys returns the keys of the active indices cache.
|
||||
func (c *ActiveIndicesCache) ActiveIndicesKeys() []string {
|
||||
return c.activeIndicesCache.ListKeys()
|
||||
}
|
||||
82
beacon-chain/cache/active_indices_test.go
vendored
Normal file
82
beacon-chain/cache/active_indices_test.go
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestActiveIndicesKeyFn_OK(t *testing.T) {
|
||||
aInfo := &ActiveIndicesByEpoch{
|
||||
Epoch: 999,
|
||||
ActiveIndices: []uint64{1, 2, 3, 4, 5},
|
||||
}
|
||||
|
||||
key, err := activeIndicesKeyFn(aInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(aInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(aInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveIndicesKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := activeIndicesKeyFn("bad")
|
||||
if err != ErrNotActiveIndicesInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotActiveIndicesInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveIndicesCache_ActiveIndicesByEpoch(t *testing.T) {
|
||||
cache := NewActiveIndicesCache()
|
||||
|
||||
aInfo := &ActiveIndicesByEpoch{
|
||||
Epoch: 99,
|
||||
ActiveIndices: []uint64{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
activeIndices, err := cache.ActiveIndicesInEpoch(aInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if activeIndices != nil {
|
||||
t.Error("Expected active indices not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddActiveIndicesList(aInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
activeIndices, err = cache.ActiveIndicesInEpoch(aInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(activeIndices, aInfo.ActiveIndices) {
|
||||
t.Errorf(
|
||||
"Expected fetched active indices to be %v, got %v",
|
||||
aInfo.ActiveIndices,
|
||||
activeIndices,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveIndices_MaxSize(t *testing.T) {
|
||||
cache := NewActiveIndicesCache()
|
||||
|
||||
for i := uint64(0); i < 100; i++ {
|
||||
aInfo := &ActiveIndicesByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddActiveIndicesList(aInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.activeIndicesCache.ListKeys()) != maxActiveIndicesListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxActiveIndicesListSize,
|
||||
len(cache.activeIndicesCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
15
beacon-chain/cache/attestation_data.go
vendored
15
beacon-chain/cache/attestation_data.go
vendored
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
@@ -57,7 +58,7 @@ func NewAttestationCache() *AttestationCache {
|
||||
|
||||
// Get waits for any in progress calculation to complete before returning a
|
||||
// cached response, if any.
|
||||
func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationDataRequest) (*pb.AttestationDataResponse, error) {
|
||||
func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationRequest) (*pbp2p.AttestationData, error) {
|
||||
if req == nil {
|
||||
return nil, errors.New("nil attestation data request")
|
||||
}
|
||||
@@ -105,7 +106,7 @@ func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationDataReque
|
||||
|
||||
// MarkInProgress a request so that any other similar requests will block on
|
||||
// Get until MarkNotInProgress is called.
|
||||
func (c *AttestationCache) MarkInProgress(req *pb.AttestationDataRequest) error {
|
||||
func (c *AttestationCache) MarkInProgress(req *pb.AttestationRequest) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
s, e := reqToKey(req)
|
||||
@@ -121,7 +122,7 @@ func (c *AttestationCache) MarkInProgress(req *pb.AttestationDataRequest) error
|
||||
|
||||
// MarkNotInProgress will release the lock on a given request. This should be
|
||||
// called after put.
|
||||
func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationDataRequest) error {
|
||||
func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationRequest) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
s, e := reqToKey(req)
|
||||
@@ -133,7 +134,7 @@ func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationDataRequest) err
|
||||
}
|
||||
|
||||
// Put the response in the cache.
|
||||
func (c *AttestationCache) Put(ctx context.Context, req *pb.AttestationDataRequest, res *pb.AttestationDataResponse) error {
|
||||
func (c *AttestationCache) Put(ctx context.Context, req *pb.AttestationRequest, res *pbp2p.AttestationData) error {
|
||||
data := &attestationReqResWrapper{
|
||||
req,
|
||||
res,
|
||||
@@ -158,11 +159,11 @@ func wrapperToKey(i interface{}) (string, error) {
|
||||
return reqToKey(w.req)
|
||||
}
|
||||
|
||||
func reqToKey(req *pb.AttestationDataRequest) (string, error) {
|
||||
func reqToKey(req *pb.AttestationRequest) (string, error) {
|
||||
return fmt.Sprintf("%d-%d", req.Shard, req.Slot), nil
|
||||
}
|
||||
|
||||
type attestationReqResWrapper struct {
|
||||
req *pb.AttestationDataRequest
|
||||
res *pb.AttestationDataResponse
|
||||
req *pb.AttestationRequest
|
||||
res *pbp2p.AttestationData
|
||||
}
|
||||
|
||||
7
beacon-chain/cache/attestation_data_test.go
vendored
7
beacon-chain/cache/attestation_data_test.go
vendored
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
)
|
||||
|
||||
@@ -13,7 +14,7 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := cache.NewAttestationCache()
|
||||
|
||||
req := &pb.AttestationDataRequest{
|
||||
req := &pb.AttestationRequest{
|
||||
Shard: 0,
|
||||
Slot: 1,
|
||||
}
|
||||
@@ -31,8 +32,8 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res := &pb.AttestationDataResponse{
|
||||
HeadSlot: 5,
|
||||
res := &pbp2p.AttestationData{
|
||||
Target: &pbp2p.Checkpoint{Epoch: 5},
|
||||
}
|
||||
|
||||
if err = c.Put(ctx, req, res); err != nil {
|
||||
|
||||
2
beacon-chain/cache/block.go
vendored
2
beacon-chain/cache/block.go
vendored
@@ -82,7 +82,7 @@ func (a *AncestorBlockCache) AncestorBySlot(blockHash []byte, height uint64) (*A
|
||||
|
||||
aInfo, ok := obj.(*AncestorInfo)
|
||||
if !ok {
|
||||
return nil, ErrNotACommitteeInfo
|
||||
return nil, ErrNotAncestorCacheObj
|
||||
}
|
||||
|
||||
return aInfo, nil
|
||||
|
||||
127
beacon-chain/cache/committee.go
vendored
127
beacon-chain/cache/committee.go
vendored
@@ -1,127 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotACommitteeInfo will be returned when a cache object is not a pointer to
|
||||
// a committeeInfo struct.
|
||||
ErrNotACommitteeInfo = errors.New("object is not an committee info")
|
||||
|
||||
// maxCacheSize is 4x of the epoch length for additional cache padding.
|
||||
// Requests should be only accessing committees within defined epoch length.
|
||||
maxCacheSize = int(4 * params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
// Metrics
|
||||
committeeCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "committee_cache_miss",
|
||||
Help: "The number of committee requests that aren't present in the cache.",
|
||||
})
|
||||
committeeCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "committee_cache_hit",
|
||||
Help: "The number of committee requests that are present in the cache.",
|
||||
})
|
||||
committeeCacheSize = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "committee_cache_size",
|
||||
Help: "The number of committees in the committee cache",
|
||||
})
|
||||
)
|
||||
|
||||
// CommitteeInfo defines the validator committee of slot and shard combinations.
|
||||
type CommitteeInfo struct {
|
||||
Committee []uint64
|
||||
Shard uint64
|
||||
}
|
||||
|
||||
// CommitteesInSlot specifies how many CommitteeInfos are in a given slot.
|
||||
type CommitteesInSlot struct {
|
||||
Slot uint64
|
||||
Committees []*CommitteeInfo
|
||||
}
|
||||
|
||||
// CommitteesCache structs with 1 queue for looking up committees by slot.
|
||||
type CommitteesCache struct {
|
||||
committeesCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// slotKeyFn takes the string representation of the slot number as the key
|
||||
// for the committees of a given slot (CommitteesInSlot).
|
||||
func slotKeyFn(obj interface{}) (string, error) {
|
||||
cInfo, ok := obj.(*CommitteesInSlot)
|
||||
if !ok {
|
||||
return "", ErrNotACommitteeInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(cInfo.Slot)), nil
|
||||
}
|
||||
|
||||
// NewCommitteesCache creates a new committee cache for storing/accessing blockInfo from
|
||||
// memory.
|
||||
func NewCommitteesCache() *CommitteesCache {
|
||||
return &CommitteesCache{
|
||||
committeesCache: cache.NewFIFO(slotKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// CommitteesInfoBySlot fetches CommitteesInSlot by slot. Returns true with a
|
||||
// reference to the committees info, if exists. Otherwise returns false, nil.
|
||||
func (c *CommitteesCache) CommitteesInfoBySlot(slot uint64) (*CommitteesInSlot, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
obj, exists, err := c.committeesCache.GetByKey(strconv.Itoa(int(slot)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
committeeCacheHit.Inc()
|
||||
} else {
|
||||
committeeCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cInfo, ok := obj.(*CommitteesInSlot)
|
||||
if !ok {
|
||||
return nil, ErrNotACommitteeInfo
|
||||
}
|
||||
|
||||
return cInfo, nil
|
||||
}
|
||||
|
||||
// AddCommittees adds CommitteesInSlot object to the cache. This method also trims the least
|
||||
// recently added committeeInfo object if the cache size has ready the max cache size limit.
|
||||
func (c *CommitteesCache) AddCommittees(committees *CommitteesInSlot) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if err := c.committeesCache.AddIfNotPresent(committees); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.committeesCache, maxCacheSize)
|
||||
committeeCacheSize.Set(float64(len(c.committeesCache.ListKeys())))
|
||||
return nil
|
||||
}
|
||||
|
||||
// trim the FIFO queue to the maxSize.
|
||||
func trim(queue *cache.FIFO, maxSize int) {
|
||||
for s := len(queue.ListKeys()); s > maxSize; s-- {
|
||||
// #nosec G104 popProcessNoopFunc never returns an error
|
||||
_, _ = queue.Pop(popProcessNoopFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// popProcessNoopFunc is a no-op function that never returns an error.
|
||||
func popProcessNoopFunc(obj interface{}) error {
|
||||
return nil
|
||||
}
|
||||
96
beacon-chain/cache/committee_test.go
vendored
96
beacon-chain/cache/committee_test.go
vendored
@@ -1,96 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSlotKeyFn_OK(t *testing.T) {
|
||||
cInfo := &CommitteesInSlot{
|
||||
Slot: 999,
|
||||
Committees: []*CommitteeInfo{
|
||||
{Shard: 1, Committee: []uint64{1, 2, 3}},
|
||||
{Shard: 1, Committee: []uint64{4, 5, 6}},
|
||||
},
|
||||
}
|
||||
|
||||
key, err := slotKeyFn(cInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
strSlot := strconv.Itoa(int(cInfo.Slot))
|
||||
if key != strSlot {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strSlot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlotKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := slotKeyFn("bad")
|
||||
if err != ErrNotACommitteeInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotACommitteeInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitteesCache_CommitteesInfoBySlot(t *testing.T) {
|
||||
cache := NewCommitteesCache()
|
||||
|
||||
cInfo := &CommitteesInSlot{
|
||||
Slot: 123,
|
||||
Committees: []*CommitteeInfo{{Shard: 456}},
|
||||
}
|
||||
|
||||
fetchedInfo, err := cache.CommitteesInfoBySlot(cInfo.Slot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fetchedInfo != nil {
|
||||
t.Error("Expected committees info not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddCommittees(cInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fetchedInfo, err = cache.CommitteesInfoBySlot(cInfo.Slot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fetchedInfo == nil {
|
||||
t.Error("Expected committee info to exist")
|
||||
}
|
||||
if fetchedInfo.Slot != cInfo.Slot {
|
||||
t.Errorf(
|
||||
"Expected fetched slot number to be %d, got %d",
|
||||
cInfo.Slot,
|
||||
fetchedInfo.Slot,
|
||||
)
|
||||
}
|
||||
if !reflect.DeepEqual(fetchedInfo.Committees, cInfo.Committees) {
|
||||
t.Errorf(
|
||||
"Expected fetched info committee to be %v, got %v",
|
||||
cInfo.Committees,
|
||||
fetchedInfo.Committees,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockCache_maxSize(t *testing.T) {
|
||||
cache := NewCommitteesCache()
|
||||
|
||||
for i := 0; i < maxCacheSize+10; i++ {
|
||||
cInfo := &CommitteesInSlot{
|
||||
Slot: uint64(i),
|
||||
}
|
||||
if err := cache.AddCommittees(cInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.committeesCache.ListKeys()) != maxCacheSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxCacheSize,
|
||||
len(cache.committeesCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
25
beacon-chain/cache/common.go
vendored
Normal file
25
beacon-chain/cache/common.go
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// maxCacheSize is 4x of the epoch length for additional cache padding.
|
||||
// Requests should be only accessing committees within defined epoch length.
|
||||
maxCacheSize = int(4 * params.BeaconConfig().SlotsPerEpoch)
|
||||
)
|
||||
|
||||
// trim the FIFO queue to the maxSize.
|
||||
func trim(queue *cache.FIFO, maxSize int) {
|
||||
for s := len(queue.ListKeys()); s > maxSize; s-- {
|
||||
// #nosec G104 popProcessNoopFunc never returns an error
|
||||
_, _ = queue.Pop(popProcessNoopFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// popProcessNoopFunc is a no-op function that never returns an error.
|
||||
func popProcessNoopFunc(obj interface{}) error {
|
||||
return nil
|
||||
}
|
||||
121
beacon-chain/cache/eth1_data.go
vendored
Normal file
121
beacon-chain/cache/eth1_data.go
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotEth1DataVote will be returned when a cache object is not a pointer to
|
||||
// a Eth1DataVote struct.
|
||||
ErrNotEth1DataVote = errors.New("object is not a eth1 data vote obj")
|
||||
|
||||
// maxEth1DataVoteSize defines the max number of eth1 data votes can cache.
|
||||
maxEth1DataVoteSize = 1000
|
||||
|
||||
// Metrics.
|
||||
eth1DataVoteCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "eth1_data_vote_cache_miss",
|
||||
Help: "The number of eth1 data vote count requests that aren't present in the cache.",
|
||||
})
|
||||
eth1DataVoteCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "eth1_data_vote_cache_hit",
|
||||
Help: "The number of eth1 data vote count requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// Eth1DataVote defines the struct which keeps track of the vote count of individual deposit root.
|
||||
type Eth1DataVote struct {
|
||||
DepositRoot []byte
|
||||
VoteCount uint64
|
||||
}
|
||||
|
||||
// Eth1DataVoteCache is a struct with 1 queue for looking up eth1 data vote count by deposit root.
|
||||
type Eth1DataVoteCache struct {
|
||||
eth1DataVoteCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// eth1DataVoteKeyFn takes the deposit root as the key for the eth1 data vote count of a given root.
|
||||
func eth1DataVoteKeyFn(obj interface{}) (string, error) {
|
||||
eInfo, ok := obj.(*Eth1DataVote)
|
||||
if !ok {
|
||||
return "", ErrNotEth1DataVote
|
||||
}
|
||||
|
||||
return string(eInfo.DepositRoot), nil
|
||||
}
|
||||
|
||||
// NewEth1DataVoteCache creates a new eth1 data vote count cache for storing/accessing Eth1DataVote.
|
||||
func NewEth1DataVoteCache() *Eth1DataVoteCache {
|
||||
return &Eth1DataVoteCache{
|
||||
eth1DataVoteCache: cache.NewFIFO(eth1DataVoteKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// Eth1DataVote fetches eth1 data vote count by deposit root. Returns vote count,
|
||||
// if exists. Otherwise returns false, nil.
|
||||
func (c *Eth1DataVoteCache) Eth1DataVote(depositRoot []byte) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.eth1DataVoteCache.GetByKey(string(depositRoot))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
eth1DataVoteCacheHit.Inc()
|
||||
} else {
|
||||
eth1DataVoteCacheMiss.Inc()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
eInfo, ok := obj.(*Eth1DataVote)
|
||||
if !ok {
|
||||
return 0, ErrNotEth1DataVote
|
||||
}
|
||||
|
||||
return eInfo.VoteCount, nil
|
||||
}
|
||||
|
||||
// AddEth1DataVote adds eth1 data vote object to the cache. This method also trims the least
|
||||
// recently added Eth1DataVoteByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *Eth1DataVoteCache) AddEth1DataVote(eth1DataVote *Eth1DataVote) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.eth1DataVoteCache.Add(eth1DataVote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.eth1DataVoteCache, maxEth1DataVoteSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementEth1DataVote increments the existing eth1 data object's vote count by 1,
|
||||
// and returns the vote count.
|
||||
func (c *Eth1DataVoteCache) IncrementEth1DataVote(depositRoot []byte) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.eth1DataVoteCache.GetByKey(string(depositRoot))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !exists {
|
||||
return 0, errors.New("eth1 data vote object does not exist")
|
||||
}
|
||||
|
||||
eth1DataVoteCacheHit.Inc()
|
||||
|
||||
eInfo, _ := obj.(*Eth1DataVote)
|
||||
eInfo.VoteCount++
|
||||
|
||||
if err := c.eth1DataVoteCache.Add(eInfo); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return eInfo.VoteCount, nil
|
||||
}
|
||||
108
beacon-chain/cache/eth1_data_test.go
vendored
Normal file
108
beacon-chain/cache/eth1_data_test.go
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEth1DataVoteKeyFn_OK(t *testing.T) {
|
||||
eInfo := &Eth1DataVote{
|
||||
VoteCount: 44,
|
||||
DepositRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
key, err := eth1DataVoteKeyFn(eInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != string(eInfo.DepositRoot) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, string(eInfo.DepositRoot))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEth1DataVoteKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := eth1DataVoteKeyFn("bad")
|
||||
if err != ErrNotEth1DataVote {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotEth1DataVote, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEth1DataVoteCache_CanAdd(t *testing.T) {
|
||||
cache := NewEth1DataVoteCache()
|
||||
|
||||
eInfo := &Eth1DataVote{
|
||||
VoteCount: 55,
|
||||
DepositRoot: []byte{'B'},
|
||||
}
|
||||
count, err := cache.Eth1DataVote(eInfo.DepositRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Error("Expected seed not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddEth1DataVote(eInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
count, err = cache.Eth1DataVote(eInfo.DepositRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != eInfo.VoteCount {
|
||||
t.Errorf(
|
||||
"Expected vote count to be %d, got %d",
|
||||
eInfo.VoteCount,
|
||||
count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEth1DataVoteCache_CanIncrement(t *testing.T) {
|
||||
cache := NewEth1DataVoteCache()
|
||||
|
||||
eInfo := &Eth1DataVote{
|
||||
VoteCount: 55,
|
||||
DepositRoot: []byte{'B'},
|
||||
}
|
||||
|
||||
if err := cache.AddEth1DataVote(eInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := cache.IncrementEth1DataVote(eInfo.DepositRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _ = cache.IncrementEth1DataVote(eInfo.DepositRoot)
|
||||
count, _ := cache.IncrementEth1DataVote(eInfo.DepositRoot)
|
||||
|
||||
if count != 58 {
|
||||
t.Errorf(
|
||||
"Expected vote count to be %d, got %d",
|
||||
58,
|
||||
count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEth1Data_MaxSize(t *testing.T) {
|
||||
cache := NewEth1DataVoteCache()
|
||||
|
||||
for i := 0; i < maxEth1DataVoteSize+1; i++ {
|
||||
eInfo := &Eth1DataVote{
|
||||
DepositRoot: []byte(strconv.Itoa(i)),
|
||||
}
|
||||
if err := cache.AddEth1DataVote(eInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.eth1DataVoteCache.ListKeys()) != maxEth1DataVoteSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxEth1DataVoteSize,
|
||||
len(cache.eth1DataVoteCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
97
beacon-chain/cache/seed.go
vendored
Normal file
97
beacon-chain/cache/seed.go
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotSeedInfo will be returned when a cache object is not a pointer to
|
||||
// a SeedByEpoch struct.
|
||||
ErrNotSeedInfo = errors.New("object is not a seed obj")
|
||||
|
||||
// maxSeedListSize defines the max number of seed can cache.
|
||||
maxSeedListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
seedCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "seed_cache_miss",
|
||||
Help: "The number of seed requests that aren't present in the cache.",
|
||||
})
|
||||
seedCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "seed_cache_hit",
|
||||
Help: "The number of seed requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// SeedByEpoch defines the seed of the epoch.
|
||||
type SeedByEpoch struct {
|
||||
Epoch uint64
|
||||
Seed []byte
|
||||
}
|
||||
|
||||
// SeedCache is a struct with 1 queue for looking up seed by epoch.
|
||||
type SeedCache struct {
|
||||
seedCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// seedKeyFn takes the epoch as the key for the seed of a given epoch.
|
||||
func seedKeyFn(obj interface{}) (string, error) {
|
||||
sInfo, ok := obj.(*SeedByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotSeedInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(sInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewSeedCache creates a new seed cache for storing/accessing seed.
|
||||
func NewSeedCache() *SeedCache {
|
||||
return &SeedCache{
|
||||
seedCache: cache.NewFIFO(seedKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// SeedInEpoch fetches SeedByEpoch by epoch. Returns true with a
|
||||
// reference to the SeedInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *SeedCache) SeedInEpoch(epoch uint64) ([]byte, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.seedCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
seedCacheHit.Inc()
|
||||
} else {
|
||||
seedCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sInfo, ok := obj.(*SeedByEpoch)
|
||||
if !ok {
|
||||
return nil, ErrNotSeedInfo
|
||||
}
|
||||
|
||||
return sInfo.Seed, nil
|
||||
}
|
||||
|
||||
// AddSeed adds SeedByEpoch object to the cache. This method also trims the least
|
||||
// recently added SeedByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *SeedCache) AddSeed(seed *SeedByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.seedCache.AddIfNotPresent(seed); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.seedCache, maxSeedListSize)
|
||||
return nil
|
||||
}
|
||||
83
beacon-chain/cache/seed_test.go
vendored
Normal file
83
beacon-chain/cache/seed_test.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestSeedKeyFn_OK(t *testing.T) {
|
||||
tInfo := &SeedByEpoch{
|
||||
Epoch: 44,
|
||||
Seed: []byte{'A'},
|
||||
}
|
||||
|
||||
key, err := seedKeyFn(tInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(tInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := seedKeyFn("bad")
|
||||
if err != ErrNotSeedInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotSeedInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedCache_SeedByEpoch(t *testing.T) {
|
||||
cache := NewSeedCache()
|
||||
|
||||
tInfo := &SeedByEpoch{
|
||||
Epoch: 55,
|
||||
Seed: []byte{'B'},
|
||||
}
|
||||
seed, err := cache.SeedInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if seed != nil {
|
||||
t.Error("Expected seed not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddSeed(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seed, err = cache.SeedInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(seed, tInfo.Seed) {
|
||||
t.Errorf(
|
||||
"Expected fetched seed to be %v, got %v",
|
||||
tInfo.Seed,
|
||||
seed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeed_MaxSize(t *testing.T) {
|
||||
cache := NewSeedCache()
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector+100; i++ {
|
||||
tInfo := &SeedByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddSeed(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.seedCache.ListKeys()) != maxSeedListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxSeedListSize,
|
||||
len(cache.seedCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
99
beacon-chain/cache/shuffled_indices.go
vendored
Normal file
99
beacon-chain/cache/shuffled_indices.go
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotValidatorListInfo will be returned when a cache object is not a pointer to
|
||||
// a ValidatorList struct.
|
||||
ErrNotValidatorListInfo = errors.New("object is not a shuffled validator list")
|
||||
|
||||
// maxShuffledListSize defines the max number of shuffled list can cache.
|
||||
maxShuffledListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
shuffledIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "shuffled_validators_cache_miss",
|
||||
Help: "The number of shuffled validators requests that aren't present in the cache.",
|
||||
})
|
||||
shuffledIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "shuffled_validators_cache_hit",
|
||||
Help: "The number of shuffled validators requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// IndicesByIndexSeed defines the shuffled validator indices per randao seed.
|
||||
type IndicesByIndexSeed struct {
|
||||
Index uint64
|
||||
Seed []byte
|
||||
ShuffledIndices []uint64
|
||||
}
|
||||
|
||||
// ShuffledIndicesCache is a struct with 1 queue for looking up shuffled validators by seed.
|
||||
type ShuffledIndicesCache struct {
|
||||
shuffledIndicesCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// slotKeyFn takes the randao seed as the key for the shuffled validators of a given epoch.
|
||||
func shuffleKeyFn(obj interface{}) (string, error) {
|
||||
sInfo, ok := obj.(*IndicesByIndexSeed)
|
||||
if !ok {
|
||||
return "", ErrNotValidatorListInfo
|
||||
}
|
||||
|
||||
return string(sInfo.Seed) + strconv.Itoa(int(sInfo.Index)), nil
|
||||
}
|
||||
|
||||
// NewShuffledIndicesCache creates a new shuffled validators cache for storing/accessing shuffled validator indices
|
||||
func NewShuffledIndicesCache() *ShuffledIndicesCache {
|
||||
return &ShuffledIndicesCache{
|
||||
shuffledIndicesCache: cache.NewFIFO(shuffleKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// IndicesByIndexSeed fetches IndicesByIndexSeed by epoch and seed. Returns true with a
|
||||
// reference to the ShuffledIndicesInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ShuffledIndicesCache) IndicesByIndexSeed(index uint64, seed []byte) ([]uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
key := string(seed) + strconv.Itoa(int(index))
|
||||
obj, exists, err := c.shuffledIndicesCache.GetByKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
shuffledIndicesCacheHit.Inc()
|
||||
} else {
|
||||
shuffledIndicesCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cInfo, ok := obj.(*IndicesByIndexSeed)
|
||||
if !ok {
|
||||
return nil, ErrNotValidatorListInfo
|
||||
}
|
||||
|
||||
return cInfo.ShuffledIndices, nil
|
||||
}
|
||||
|
||||
// AddShuffledValidatorList adds IndicesByIndexSeed object to the cache. This method also trims the least
|
||||
// recently added IndicesByIndexSeed object if the cache size has ready the max cache size limit.
|
||||
func (c *ShuffledIndicesCache) AddShuffledValidatorList(shuffledIndices *IndicesByIndexSeed) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.shuffledIndicesCache.AddIfNotPresent(shuffledIndices); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.shuffledIndicesCache, maxShuffledListSize)
|
||||
return nil
|
||||
}
|
||||
85
beacon-chain/cache/shuffled_indices_test.go
vendored
Normal file
85
beacon-chain/cache/shuffled_indices_test.go
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShuffleKeyFn_OK(t *testing.T) {
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: 999,
|
||||
Seed: []byte{'A'},
|
||||
ShuffledIndices: []uint64{1, 2, 3, 4, 5},
|
||||
}
|
||||
|
||||
key, err := shuffleKeyFn(sInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffleKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := shuffleKeyFn("bad")
|
||||
if err != ErrNotValidatorListInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotValidatorListInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffledIndicesCache_ShuffledIndicesBySeed2(t *testing.T) {
|
||||
cache := NewShuffledIndicesCache()
|
||||
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: 99,
|
||||
Seed: []byte{'A'},
|
||||
ShuffledIndices: []uint64{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
shuffledIndices, err := cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if shuffledIndices != nil {
|
||||
t.Error("Expected shuffled indices not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shuffledIndices, err = cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(shuffledIndices, sInfo.ShuffledIndices) {
|
||||
t.Errorf(
|
||||
"Expected fetched info committee to be %v, got %v",
|
||||
sInfo.ShuffledIndices,
|
||||
shuffledIndices,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffledIndices_MaxSize(t *testing.T) {
|
||||
cache := NewShuffledIndicesCache()
|
||||
|
||||
for i := uint64(0); i < 1001; i++ {
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: i,
|
||||
Seed: []byte{byte(i)},
|
||||
}
|
||||
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.shuffledIndicesCache.ListKeys()) != maxShuffledListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxShuffledListSize,
|
||||
len(cache.shuffledIndicesCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
98
beacon-chain/cache/start_shard.go
vendored
Normal file
98
beacon-chain/cache/start_shard.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotStartShardInfo will be returned when a cache object is not a pointer to
|
||||
// a StartShardByEpoch struct.
|
||||
ErrNotStartShardInfo = errors.New("object is not a start shard obj")
|
||||
|
||||
// maxStartShardListSize defines the max number of start shard can cache.
|
||||
maxStartShardListSize = int(params.BeaconConfig().ShardCount)
|
||||
|
||||
// Metrics.
|
||||
startShardCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "start_shard_cache_miss",
|
||||
Help: "The number of start shard requests that aren't present in the cache.",
|
||||
})
|
||||
startShardCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "start_shard_cache_hit",
|
||||
Help: "The number of start shard requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// StartShardByEpoch defines the start shard of the epoch.
|
||||
type StartShardByEpoch struct {
|
||||
Epoch uint64
|
||||
StartShard uint64
|
||||
}
|
||||
|
||||
// StartShardCache is a struct with 1 queue for looking up start shard by epoch.
|
||||
type StartShardCache struct {
|
||||
startShardCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// startShardKeyFn takes the epoch as the key for the start shard of a given epoch.
|
||||
func startShardKeyFn(obj interface{}) (string, error) {
|
||||
sInfo, ok := obj.(*StartShardByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotStartShardInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(sInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewStartShardCache creates a new start shard cache for storing/accessing start shard.
|
||||
func NewStartShardCache() *StartShardCache {
|
||||
return &StartShardCache{
|
||||
startShardCache: cache.NewFIFO(startShardKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// StartShardInEpoch fetches StartShardByEpoch by epoch. Returns true with a
|
||||
// reference to the StartShardInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *StartShardCache) StartShardInEpoch(epoch uint64) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.startShardCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return params.BeaconConfig().FarFutureEpoch, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
startShardCacheHit.Inc()
|
||||
} else {
|
||||
startShardCacheMiss.Inc()
|
||||
return params.BeaconConfig().FarFutureEpoch, nil
|
||||
}
|
||||
|
||||
sInfo, ok := obj.(*StartShardByEpoch)
|
||||
if !ok {
|
||||
return params.BeaconConfig().FarFutureEpoch, ErrNotStartShardInfo
|
||||
}
|
||||
|
||||
return sInfo.StartShard, nil
|
||||
}
|
||||
|
||||
// AddStartShard adds StartShardByEpoch object to the cache. This method also trims the least
|
||||
// recently added StartShardByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *StartShardCache) AddStartShard(startShard *StartShardByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.startShardCache.AddIfNotPresent(startShard); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.startShardCache, maxStartShardListSize)
|
||||
return nil
|
||||
}
|
||||
83
beacon-chain/cache/start_shard_test.go
vendored
Normal file
83
beacon-chain/cache/start_shard_test.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestStartShardKeyFn_OK(t *testing.T) {
|
||||
tInfo := &StartShardByEpoch{
|
||||
Epoch: 44,
|
||||
StartShard: 3,
|
||||
}
|
||||
|
||||
key, err := startShardKeyFn(tInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(tInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartShardKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := startShardKeyFn("bad")
|
||||
if err != ErrNotStartShardInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotStartShardInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartShardCache_StartShardByEpoch(t *testing.T) {
|
||||
cache := NewStartShardCache()
|
||||
|
||||
tInfo := &StartShardByEpoch{
|
||||
Epoch: 55,
|
||||
StartShard: 3,
|
||||
}
|
||||
startShard, err := cache.StartShardInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if startShard != params.BeaconConfig().FarFutureEpoch {
|
||||
t.Error("Expected start shard not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddStartShard(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
startShard, err = cache.StartShardInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(startShard, tInfo.StartShard) {
|
||||
t.Errorf(
|
||||
"Expected fetched start shard to be %v, got %v",
|
||||
tInfo.StartShard,
|
||||
startShard,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartShard_MaxSize(t *testing.T) {
|
||||
cache := NewStartShardCache()
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount+1; i++ {
|
||||
tInfo := &StartShardByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddStartShard(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.startShardCache.ListKeys()) != maxStartShardListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxStartShardListSize,
|
||||
len(cache.startShardCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
98
beacon-chain/cache/total_balance.go
vendored
Normal file
98
beacon-chain/cache/total_balance.go
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotTotalBalanceInfo will be returned when a cache object is not a pointer to
|
||||
// a TotalBalanceByEpoch struct.
|
||||
ErrNotTotalBalanceInfo = errors.New("object is not a total balance obj")
|
||||
|
||||
// maxTotalBalanceListSize defines the max number of total balance can cache.
|
||||
maxTotalBalanceListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
totalBalanceCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "total_balance_cache_miss",
|
||||
Help: "The number of total balance requests that aren't present in the cache.",
|
||||
})
|
||||
totalBalanceCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "total_balance_cache_hit",
|
||||
Help: "The number of total balance requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// TotalBalanceByEpoch defines the total validator balance per epoch.
|
||||
type TotalBalanceByEpoch struct {
|
||||
Epoch uint64
|
||||
TotalBalance uint64
|
||||
}
|
||||
|
||||
// TotalBalanceCache is a struct with 1 queue for looking up total balance by epoch.
|
||||
type TotalBalanceCache struct {
|
||||
totalBalanceCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// totalBalanceKeyFn takes the epoch as the key for the total balance of a given epoch.
|
||||
func totalBalanceKeyFn(obj interface{}) (string, error) {
|
||||
tInfo, ok := obj.(*TotalBalanceByEpoch)
|
||||
if !ok {
|
||||
return "", ErrNotTotalBalanceInfo
|
||||
}
|
||||
|
||||
return strconv.Itoa(int(tInfo.Epoch)), nil
|
||||
}
|
||||
|
||||
// NewTotalBalanceCache creates a new total balance cache for storing/accessing total validator balance.
|
||||
func NewTotalBalanceCache() *TotalBalanceCache {
|
||||
return &TotalBalanceCache{
|
||||
totalBalanceCache: cache.NewFIFO(totalBalanceKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// TotalBalanceInEpoch fetches TotalBalanceByEpoch by epoch. Returns true with a
|
||||
// reference to the TotalBalanceInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *TotalBalanceCache) TotalBalanceInEpoch(epoch uint64) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.totalBalanceCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return params.BeaconConfig().FarFutureEpoch, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
totalBalanceCacheHit.Inc()
|
||||
} else {
|
||||
totalBalanceCacheMiss.Inc()
|
||||
return params.BeaconConfig().FarFutureEpoch, nil
|
||||
}
|
||||
|
||||
tInfo, ok := obj.(*TotalBalanceByEpoch)
|
||||
if !ok {
|
||||
return params.BeaconConfig().FarFutureEpoch, ErrNotTotalBalanceInfo
|
||||
}
|
||||
|
||||
return tInfo.TotalBalance, nil
|
||||
}
|
||||
|
||||
// AddTotalBalance adds TotalBalanceByEpoch object to the cache. This method also trims the least
|
||||
// recently added TotalBalanceByEpoch object if the cache size has ready the max cache size limit.
|
||||
func (c *TotalBalanceCache) AddTotalBalance(totalBalance *TotalBalanceByEpoch) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.totalBalanceCache.AddIfNotPresent(totalBalance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.totalBalanceCache, maxTotalBalanceListSize)
|
||||
return nil
|
||||
}
|
||||
83
beacon-chain/cache/total_balance_test.go
vendored
Normal file
83
beacon-chain/cache/total_balance_test.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestTotalBalanceKeyFn_OK(t *testing.T) {
|
||||
tInfo := &TotalBalanceByEpoch{
|
||||
Epoch: 333,
|
||||
TotalBalance: 321321323,
|
||||
}
|
||||
|
||||
key, err := totalBalanceKeyFn(tInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != strconv.Itoa(int(tInfo.Epoch)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(int(tInfo.Epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalBalanceKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := totalBalanceKeyFn("bad")
|
||||
if err != ErrNotTotalBalanceInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotTotalBalanceInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalBalanceCache_TotalBalanceByEpoch(t *testing.T) {
|
||||
cache := NewTotalBalanceCache()
|
||||
|
||||
tInfo := &TotalBalanceByEpoch{
|
||||
Epoch: 111,
|
||||
TotalBalance: 345435435,
|
||||
}
|
||||
totalBalance, err := cache.TotalBalanceInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if totalBalance != params.BeaconConfig().FarFutureEpoch {
|
||||
t.Error("Expected total balance not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddTotalBalance(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
totalBalance, err = cache.TotalBalanceInEpoch(tInfo.Epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(totalBalance, tInfo.TotalBalance) {
|
||||
t.Errorf(
|
||||
"Expected fetched total balance to be %v, got %v",
|
||||
tInfo.TotalBalance,
|
||||
totalBalance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalBalance_MaxSize(t *testing.T) {
|
||||
cache := NewTotalBalanceCache()
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector+100; i++ {
|
||||
tInfo := &TotalBalanceByEpoch{
|
||||
Epoch: i,
|
||||
}
|
||||
if err := cache.AddTotalBalance(tInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.totalBalanceCache.ListKeys()) != maxTotalBalanceListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxTotalBalanceListSize,
|
||||
len(cache.totalBalanceCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["main.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/chaintest",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//beacon-chain/chaintest/backend:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"@com_github_go_yaml_yaml//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "chaintest",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["yaml_test.go"],
|
||||
data = glob(["tests/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/chaintest/backend:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,238 +0,0 @@
|
||||
# Ethereum 2.0 E2E Test Suite
|
||||
|
||||
This is a test-suite for conformity end-2-end tests for Prysm's implementation of the Ethereum 2.0 specification. Implementation teams have decided to utilize YAML as a general conformity test format for the current beacon chain's runtime functionality.
|
||||
|
||||
The test suite opts for YAML due to wide language support and support for inline comments.
|
||||
|
||||
# Testing Format
|
||||
|
||||
The testing format follows the official ETH2.0 Specification created [here](https://github.com/ethereum/eth2.0-specs/blob/master/specs/test-format.md)
|
||||
|
||||
## Stateful Tests
|
||||
|
||||
Chain tests check for conformity of a certain client to the beacon chain specification for items such as the fork choice rule and Casper FFG validator rewards & penalties. Stateful tests need to specify a certain configuration of a beacon chain, with items such as the number validators, in the YAML file. Sample tests will all required fields are shown below.
|
||||
|
||||
### State Transition
|
||||
|
||||
The most important use case for this test format is to verify the ins and outs of the Ethereum Phase 0 Beacon Chain state advancement. The specification details very strict guidelines for blocks to successfully trigger a state transition, including items such as Casper Proof of Stake slashing conditions of validators, pseudorandomness in the form of RANDAO, and attestation on shard blocks being processed all inside each incoming beacon block. The YAML configuration for this test type allows for configuring a state transition run over N slots, triggering slashing conditions, processing deposits of new validators, and more.
|
||||
|
||||
An example state transition test for testing slot and block processing will look as follows:
|
||||
|
||||
```yaml
|
||||
title: Sample Ethereum Serenity State Transition Tests
|
||||
summary: Testing full state transition block processing
|
||||
test_suite: prysm
|
||||
fork: sapphire
|
||||
version: 1.0
|
||||
test_cases:
|
||||
- config:
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 32 # Testing advancing state to slot < SlotsPerEpoch
|
||||
results:
|
||||
slot: 32
|
||||
num_validators: 1000
|
||||
- config:
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 16384
|
||||
num_slots: 64
|
||||
deposits:
|
||||
- slot: 1
|
||||
amount: 32
|
||||
merkle_index: 0
|
||||
pubkey: !!binary |
|
||||
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
- slot: 15
|
||||
amount: 32
|
||||
merkle_index: 1
|
||||
pubkey: !!binary |
|
||||
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
|
||||
- slot: 55
|
||||
amount: 32
|
||||
merkle_index: 2
|
||||
pubkey: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
proposer_slashings:
|
||||
- slot: 16 # At slot 16, we trigger a proposal slashing occurring
|
||||
proposer_index: 16385 # We penalize the proposer that was just added from slot 15
|
||||
proposal_1_shard: 0
|
||||
proposal_1_slot: 15
|
||||
proposal_1_root: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
proposal_2_shard: 0
|
||||
proposal_2_slot: 15
|
||||
proposal_2_root: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
attester_slashings:
|
||||
- slot: 59 # At slot 59, we trigger a attester slashing
|
||||
slashable_vote_data_1_slot: 55
|
||||
slashable_vote_data_2_slot: 55
|
||||
slashable_vote_data_1_justified_slot: 0
|
||||
slashable_vote_data_2_justified_slot: 1
|
||||
slashable_vote_data_1_custody_0_indices: [16386]
|
||||
slashable_vote_data_1_custody_1_indices: []
|
||||
slashable_vote_data_2_custody_0_indices: []
|
||||
slashable_vote_data_2_custody_1_indices: [16386]
|
||||
results:
|
||||
slot: 64
|
||||
num_validators: 16387
|
||||
penalized_validators: [16385, 16386] # We test that the validators at indices 16385, 16386 were indeed penalized
|
||||
- config:
|
||||
skip_slots: [10, 20]
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 128 # Testing advancing state's slot == 2*SlotsPerEpoch
|
||||
deposits:
|
||||
- slot: 10
|
||||
amount: 32
|
||||
merkle_index: 0
|
||||
pubkey: !!binary |
|
||||
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
- slot: 20
|
||||
amount: 32
|
||||
merkle_index: 1
|
||||
pubkey: !!binary |
|
||||
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
|
||||
results:
|
||||
slot: 128
|
||||
num_validators: 1000 # Validator registry should not have grown if slots 10 and 20 were skipped
|
||||
```
|
||||
|
||||
#### Test Configuration Options
|
||||
|
||||
The following configuration options are available for state transition tests:
|
||||
|
||||
**Config**
|
||||
|
||||
- **skip_slots**: `[int]` determines which slot numbers to simulate a proposer not submitting a block in the state transition TODO
|
||||
- **epoch_length**: `int` the number of slots in an epoch
|
||||
- **deposits_for_chain_start**: `int` the number of eth deposits needed for the beacon chain to initialize (this simulates an initial validator registry based on this number in the test)
|
||||
- **num_slots**: `int` the number of times we run a state transition in the test
|
||||
- **deposits**: `[Deposit Config]` trigger a new validator deposit into the beacon state based on configuration options
|
||||
- **proposer_slashings**: `[Proposer Slashing Config]` trigger a proposer slashing at a certain slot for a certain proposer index
|
||||
- **attester_slashings**: `[Casper Slashing Config]` trigger a attester slashing at a certain slot
|
||||
- **validator_exits**: `[Validator Exit Config]` trigger a voluntary validator exit at a certain slot for a validator index
|
||||
|
||||
**Deposit Config**
|
||||
|
||||
- **slot**: `int` a slot in which to trigger a deposit during a state transition test
|
||||
- **amount**: `int` the ETH deposit amount to trigger
|
||||
- **merkle_index**: `int` the index of the deposit in the validator deposit contract's Merkle trie
|
||||
- **pubkey**: `!!binary` the public key of the validator in the triggered deposit object
|
||||
|
||||
**Proposer Slashing Config**
|
||||
|
||||
- **slot**: `int` a slot in which to trigger a proposer slashing during a state transition test
|
||||
- **proposer_index**: `int` the proposer to penalize
|
||||
- **proposal_1_shard**: `int` the first proposal data's shard id
|
||||
- **proposal_1_slot**: `int` the first proposal data's slot
|
||||
- **proposal_1_root**: `!!binary` the second proposal data's block root
|
||||
- **proposal_2_shard**: `int` the second proposal data's shard id
|
||||
- **proposal_2_slot**: `int` the second proposal data's slot
|
||||
- **proposal_2_root**: `!!binary` the second proposal data's block root
|
||||
|
||||
**Casper Slashing Config**
|
||||
|
||||
- **slot**: `int` a slot in which to trigger a attester slashing during a state transition test
|
||||
- **slashable_vote_data_1_slot**: `int` the slot of the attestation data of slashableVoteData1
|
||||
- **slashable_vote_data_2_slot**: `int` the slot of the attestation data of slashableVoteData2
|
||||
- **slashable_vote_data_1_justified_slot**: `int` the justified slot of the attestation data of slashableVoteData1
|
||||
- **slashable_vote_data_2_justified_slot**: `int` the justified slot of the attestation data of slashableVoteData2
|
||||
- **slashable_vote_data_1_custody_0_indices**: `[int]` the custody indices 0 for slashableVoteData1
|
||||
- **slashable_vote_data_1_custody_1_indices**: `[int]` the custody indices 1 for slashableVoteData1
|
||||
- **slashable_vote_data_2_custody_0_indices**: `[int]` the custody indices 0 for slashableVoteData2
|
||||
- **slashable_vote_data_2_custody_1_indices**: `[int]` the custody indices 1 for slashableVoteData2
|
||||
|
||||
**Validator Exit Config**
|
||||
|
||||
- **slot**: `int` the slot at which a validator wants to voluntarily exit the validator registry
|
||||
- **validator_index**: `int` the index of the validator in the registry that is exiting
|
||||
|
||||
#### Test Results
|
||||
|
||||
The following are **mandatory** fields as they correspond to checks done at the end of the test run.
|
||||
|
||||
- **slot**: `int` check the slot of the state resulting from applying N state transitions in the test
|
||||
- **num_validators** `[int]` check the number of validators in the validator registry after applying N state transitions
|
||||
- **penalized_validators** `[int]` the list of validator indices we verify were penalized during the test
|
||||
- **exited_validators**: `[int]` the list of validator indices we verify voluntarily exited the registry during the test
|
||||
|
||||
## Stateless Tests
|
||||
|
||||
Stateless tests represent simple unit test definitions for important invariants in the ETH2.0 runtime. In particular, these test conformity across clients with respect to items such as Simple Serialize (SSZ), Signature Aggregation (BLS), and Validator Shuffling
|
||||
|
||||
**Simple Serialize**
|
||||
|
||||
TODO
|
||||
|
||||
**Signature Aggregation**
|
||||
|
||||
TODO
|
||||
|
||||
**Validator Shuffling**
|
||||
|
||||
```yaml
|
||||
title: Shuffling Algorithm Tests
|
||||
summary: Test vectors for shuffling a list based upon a seed using `shuffle`
|
||||
test_suite: shuffle
|
||||
fork: tchaikovsky
|
||||
version: 1.0
|
||||
|
||||
test_cases:
|
||||
- input: []
|
||||
output: []
|
||||
seed: !!binary ""
|
||||
- name: boring_list
|
||||
description: List with a single element, 0
|
||||
input: [0]
|
||||
output: [0]
|
||||
seed: !!binary ""
|
||||
- input: [255]
|
||||
output: [255]
|
||||
seed: !!binary ""
|
||||
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
|
||||
output: [1, 6, 4, 1, 6, 6, 2, 2, 4, 5]
|
||||
seed: !!binary ""
|
||||
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||
output: [4, 7, 10, 13, 3, 1, 2, 9, 12, 6, 11, 8, 5]
|
||||
seed: !!binary ""
|
||||
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
|
||||
output: [6, 65, 2, 5, 4, 2, 6, 6, 1, 1]
|
||||
seed: !!binary |
|
||||
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
```
|
||||
|
||||
# Using the Runner
|
||||
|
||||
First, create a directory containing the YAML files you wish to test (or use the default `./sampletests` directory included with Prysm).
|
||||
Then, make sure you have the following folder structure for the directory:
|
||||
|
||||
```
|
||||
yourtestdir/
|
||||
fork-choice-tests/
|
||||
*.yaml
|
||||
...
|
||||
shuffle-tests/
|
||||
*.yaml
|
||||
...
|
||||
state-tests/
|
||||
*.yaml
|
||||
...
|
||||
```
|
||||
|
||||
Then, navigate to the test runner's directory and use the go tool as follows:
|
||||
|
||||
```bash
|
||||
go run main.go -tests-dir /path/to/your/testsdir
|
||||
```
|
||||
|
||||
The runner will then start up a simulated backend and run all your specified YAML tests.
|
||||
|
||||
```bash
|
||||
[2018-11-06 15:01:44] INFO ----Running Chain Tests----
|
||||
[2018-11-06 15:01:44] INFO Running 4 YAML Tests
|
||||
[2018-11-06 15:01:44] INFO Title: Sample Ethereum 2.0 Beacon Chain Test
|
||||
[2018-11-06 15:01:44] INFO Summary: Basic, functioning fork choice rule for Ethereum 2.0
|
||||
[2018-11-06 15:01:44] INFO Test Suite: prysm
|
||||
[2018-11-06 15:01:44] INFO Test Runs Finished In: 0.000643545 Seconds
|
||||
```
|
||||
@@ -1,43 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"fork_choice_test_format.go",
|
||||
"helpers.go",
|
||||
"shuffle_test_format.go",
|
||||
"simulated_backend.go",
|
||||
"state_test_format.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/utils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/forkutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["simulated_backend_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,51 +0,0 @@
|
||||
package backend
|
||||
|
||||
// ForkChoiceTest --
|
||||
type ForkChoiceTest struct {
|
||||
Title string
|
||||
Summary string
|
||||
TestSuite string `yaml:"test_suite"`
|
||||
TestCases []*ForkChoiceTestCase `yaml:"test_cases"`
|
||||
}
|
||||
|
||||
// ForkChoiceTestCase --
|
||||
type ForkChoiceTestCase struct {
|
||||
Config *ForkChoiceTestConfig `yaml:"config"`
|
||||
Slots []*ForkChoiceTestSlot `yaml:"slots,flow"`
|
||||
Results *ForkChoiceTestResult `yaml:"results"`
|
||||
}
|
||||
|
||||
// ForkChoiceTestConfig --
|
||||
type ForkChoiceTestConfig struct {
|
||||
ValidatorCount uint64 `yaml:"validator_count"`
|
||||
CycleLength uint64 `yaml:"cycle_length"`
|
||||
ShardCount uint64 `yaml:"shard_count"`
|
||||
MinCommitteeSize uint64 `yaml:"min_committee_size"`
|
||||
}
|
||||
|
||||
// ForkChoiceTestSlot --
|
||||
type ForkChoiceTestSlot struct {
|
||||
SlotNumber uint64 `yaml:"slot_number"`
|
||||
NewBlock *TestBlock `yaml:"new_block"`
|
||||
Attestations []*TestAttestation `yaml:",flow"`
|
||||
}
|
||||
|
||||
// ForkChoiceTestResult --
|
||||
type ForkChoiceTestResult struct {
|
||||
Head string
|
||||
LastJustifiedBlock string `yaml:"last_justified_block"`
|
||||
LastFinalizedBlock string `yaml:"last_finalized_block"`
|
||||
}
|
||||
|
||||
// TestBlock --
|
||||
type TestBlock struct {
|
||||
ID string `yaml:"ID"`
|
||||
Parent string `yaml:"parent"`
|
||||
}
|
||||
|
||||
// TestAttestation --
|
||||
type TestAttestation struct {
|
||||
Block string `yaml:"block"`
|
||||
ValidatorRegistry string `yaml:"validators"`
|
||||
CommitteeSlot uint64 `yaml:"committee_slot"`
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/forkutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/trieutil"
|
||||
)
|
||||
|
||||
// Generates a simulated beacon block to use
|
||||
// in the next state transition given the current state,
|
||||
// the previous beacon block, and previous beacon block root.
|
||||
func generateSimulatedBlock(
|
||||
beaconState *pb.BeaconState,
|
||||
prevBlockRoot [32]byte,
|
||||
historicalDeposits []*pb.Deposit,
|
||||
simObjects *SimulatedObjects,
|
||||
privKeys []*bls.SecretKey,
|
||||
) (*pb.BeaconBlock, [32]byte, error) {
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not tree hash state: %v", err)
|
||||
}
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(beaconState, beaconState.Slot+1)
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, err
|
||||
}
|
||||
epoch := helpers.SlotToEpoch(beaconState.Slot + 1)
|
||||
buf := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(buf, epoch)
|
||||
domain := forkutil.DomainVersion(beaconState.Fork, epoch, params.BeaconConfig().DomainRandao)
|
||||
// We make the previous validator's index sign the message instead of the proposer.
|
||||
epochSignature := privKeys[proposerIdx].Sign(buf, domain)
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 1,
|
||||
RandaoReveal: epochSignature.Marshal(),
|
||||
ParentRootHash32: prevBlockRoot[:],
|
||||
StateRootHash32: stateRoot[:],
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{1},
|
||||
BlockHash32: []byte{2},
|
||||
},
|
||||
Body: &pb.BeaconBlockBody{
|
||||
ProposerSlashings: []*pb.ProposerSlashing{},
|
||||
AttesterSlashings: []*pb.AttesterSlashing{},
|
||||
Attestations: []*pb.Attestation{},
|
||||
Deposits: []*pb.Deposit{},
|
||||
VoluntaryExits: []*pb.VoluntaryExit{},
|
||||
},
|
||||
}
|
||||
if simObjects.simDeposit != nil {
|
||||
depositInput := &pb.DepositInput{
|
||||
Pubkey: []byte(simObjects.simDeposit.Pubkey),
|
||||
WithdrawalCredentialsHash32: make([]byte, 32),
|
||||
ProofOfPossession: make([]byte, 96),
|
||||
}
|
||||
|
||||
data, err := helpers.EncodeDepositData(depositInput, simObjects.simDeposit.Amount, time.Now().Unix())
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not encode deposit data: %v", err)
|
||||
}
|
||||
|
||||
// We then update the deposits Merkle trie with the deposit data and return
|
||||
// its Merkle branch leading up to the root of the trie.
|
||||
historicalDepositData := make([][]byte, len(historicalDeposits))
|
||||
for i := range historicalDeposits {
|
||||
historicalDepositData[i] = historicalDeposits[i].DepositData
|
||||
}
|
||||
newTrie, err := trieutil.GenerateTrieFromItems(append(historicalDepositData, data), int(params.BeaconConfig().DepositContractTreeDepth))
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not regenerate trie: %v", err)
|
||||
}
|
||||
proof, err := newTrie.MerkleProof(int(simObjects.simDeposit.MerkleIndex))
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not generate proof: %v", err)
|
||||
}
|
||||
|
||||
root := newTrie.Root()
|
||||
block.Eth1Data.DepositRootHash32 = root[:]
|
||||
block.Body.Deposits = append(block.Body.Deposits, &pb.Deposit{
|
||||
DepositData: data,
|
||||
MerkleProofHash32S: proof,
|
||||
MerkleTreeIndex: simObjects.simDeposit.MerkleIndex,
|
||||
})
|
||||
}
|
||||
if simObjects.simProposerSlashing != nil {
|
||||
block.Body.ProposerSlashings = append(block.Body.ProposerSlashings, &pb.ProposerSlashing{
|
||||
ProposerIndex: simObjects.simProposerSlashing.ProposerIndex,
|
||||
ProposalData_1: &pb.ProposalSignedData{
|
||||
Slot: simObjects.simProposerSlashing.Proposal1Slot,
|
||||
Shard: simObjects.simProposerSlashing.Proposal1Shard,
|
||||
BlockRootHash32: []byte(simObjects.simProposerSlashing.Proposal1Root),
|
||||
},
|
||||
ProposalData_2: &pb.ProposalSignedData{
|
||||
Slot: simObjects.simProposerSlashing.Proposal2Slot,
|
||||
Shard: simObjects.simProposerSlashing.Proposal2Shard,
|
||||
BlockRootHash32: []byte(simObjects.simProposerSlashing.Proposal2Root),
|
||||
},
|
||||
})
|
||||
}
|
||||
if simObjects.simAttesterSlashing != nil {
|
||||
block.Body.AttesterSlashings = append(block.Body.AttesterSlashings, &pb.AttesterSlashing{
|
||||
SlashableAttestation_1: &pb.SlashableAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: simObjects.simAttesterSlashing.SlashableAttestation1Slot,
|
||||
JustifiedEpoch: simObjects.simAttesterSlashing.SlashableAttestation1JustifiedEpoch,
|
||||
},
|
||||
CustodyBitfield: []byte(simObjects.simAttesterSlashing.SlashableAttestation1CustodyBitField),
|
||||
ValidatorIndices: simObjects.simAttesterSlashing.SlashableAttestation1ValidatorIndices,
|
||||
},
|
||||
SlashableAttestation_2: &pb.SlashableAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: simObjects.simAttesterSlashing.SlashableAttestation2Slot,
|
||||
JustifiedEpoch: simObjects.simAttesterSlashing.SlashableAttestation2JustifiedEpoch,
|
||||
},
|
||||
CustodyBitfield: []byte(simObjects.simAttesterSlashing.SlashableAttestation2CustodyBitField),
|
||||
ValidatorIndices: simObjects.simAttesterSlashing.SlashableAttestation2ValidatorIndices,
|
||||
},
|
||||
})
|
||||
}
|
||||
if simObjects.simValidatorExit != nil {
|
||||
block.Body.VoluntaryExits = append(block.Body.VoluntaryExits, &pb.VoluntaryExit{
|
||||
Epoch: simObjects.simValidatorExit.Epoch,
|
||||
ValidatorIndex: simObjects.simValidatorExit.ValidatorIndex,
|
||||
})
|
||||
}
|
||||
blockRoot, err := hashutil.HashBeaconBlock(block)
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not tree hash new block: %v", err)
|
||||
}
|
||||
return block, blockRoot, nil
|
||||
}
|
||||
|
||||
// generateInitialSimulatedDeposits generates initial deposits for creating a beacon state in the simulated
|
||||
// backend based on the yaml configuration.
|
||||
func generateInitialSimulatedDeposits(numDeposits uint64) ([]*pb.Deposit, []*bls.SecretKey, error) {
|
||||
genesisTime := time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC).Unix()
|
||||
deposits := make([]*pb.Deposit, numDeposits)
|
||||
privKeys := make([]*bls.SecretKey, numDeposits)
|
||||
for i := 0; i < len(deposits); i++ {
|
||||
priv, err := bls.RandKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not initialize key: %v", err)
|
||||
}
|
||||
depositInput := &pb.DepositInput{
|
||||
Pubkey: priv.PublicKey().Marshal(),
|
||||
WithdrawalCredentialsHash32: make([]byte, 32),
|
||||
ProofOfPossession: make([]byte, 96),
|
||||
}
|
||||
depositData, err := helpers.EncodeDepositData(
|
||||
depositInput,
|
||||
params.BeaconConfig().MaxDepositAmount,
|
||||
genesisTime,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not encode genesis block deposits: %v", err)
|
||||
}
|
||||
deposits[i] = &pb.Deposit{DepositData: depositData, MerkleTreeIndex: uint64(i)}
|
||||
privKeys[i] = priv
|
||||
}
|
||||
return deposits, privKeys, nil
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package backend
|
||||
|
||||
// ShuffleTest --
|
||||
type ShuffleTest struct {
|
||||
Title string `yaml:"title"`
|
||||
Summary string `yaml:"summary"`
|
||||
TestSuite string `yaml:"test_suite"`
|
||||
Fork string `yaml:"fork"`
|
||||
Version string `yaml:"version"`
|
||||
TestCases []*ShuffleTestCase `yaml:"test_cases"`
|
||||
}
|
||||
|
||||
// ShuffleTestCase --
|
||||
type ShuffleTestCase struct {
|
||||
Input []uint64 `yaml:"input,flow"`
|
||||
Output []uint64 `yaml:"output,flow"`
|
||||
Seed string
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
// Package backend contains utilities for simulating an entire
|
||||
// ETH 2.0 beacon chain for e2e tests and benchmarking
|
||||
// purposes.
|
||||
package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SimulatedBackend allowing for a programmatic advancement
|
||||
// of an in-memory beacon chain for client test runs
|
||||
// and other e2e use cases.
|
||||
type SimulatedBackend struct {
|
||||
chainService *blockchain.ChainService
|
||||
beaconDB *db.BeaconDB
|
||||
state *pb.BeaconState
|
||||
prevBlockRoots [][32]byte
|
||||
inMemoryBlocks []*pb.BeaconBlock
|
||||
historicalDeposits []*pb.Deposit
|
||||
}
|
||||
|
||||
// SimulatedObjects is a container to hold the
|
||||
// required primitives for generation of a beacon
|
||||
// block.
|
||||
type SimulatedObjects struct {
|
||||
simDeposit *StateTestDeposit
|
||||
simProposerSlashing *StateTestProposerSlashing
|
||||
simAttesterSlashing *StateTestAttesterSlashing
|
||||
simValidatorExit *StateTestValidatorExit
|
||||
}
|
||||
|
||||
// NewSimulatedBackend creates an instance by initializing a chain service
|
||||
// utilizing a mockDB which will act according to test run parameters specified
|
||||
// in the common ETH 2.0 client test YAML format.
|
||||
func NewSimulatedBackend() (*SimulatedBackend, error) {
|
||||
db, err := db.SetupDB()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not setup simulated backend db: %v", err)
|
||||
}
|
||||
cs, err := blockchain.NewChainService(context.Background(), &blockchain.Config{
|
||||
BeaconDB: db,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SimulatedBackend{
|
||||
chainService: cs,
|
||||
beaconDB: db,
|
||||
inMemoryBlocks: make([]*pb.BeaconBlock, 0),
|
||||
historicalDeposits: make([]*pb.Deposit, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetupBackend sets up the simulated backend with simulated deposits, and initializes the
|
||||
// state and genesis block.
|
||||
func (sb *SimulatedBackend) SetupBackend(numOfDeposits uint64) ([]*bls.SecretKey, error) {
|
||||
initialDeposits, privKeys, err := generateInitialSimulatedDeposits(numOfDeposits)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not simulate initial validator deposits: %v", err)
|
||||
}
|
||||
if err := sb.setupBeaconStateAndGenesisBlock(initialDeposits); err != nil {
|
||||
return nil, fmt.Errorf("could not set up beacon state and initialize genesis block %v", err)
|
||||
}
|
||||
return privKeys, nil
|
||||
}
|
||||
|
||||
// DB returns the underlying db instance in the simulated
|
||||
// backend.
|
||||
func (sb *SimulatedBackend) DB() *db.BeaconDB {
|
||||
return sb.beaconDB
|
||||
}
|
||||
|
||||
// GenerateBlockAndAdvanceChain generates a simulated block and runs that block though
|
||||
// state transition.
|
||||
func (sb *SimulatedBackend) GenerateBlockAndAdvanceChain(objects *SimulatedObjects, privKeys []*bls.SecretKey) error {
|
||||
prevBlockRoot := sb.prevBlockRoots[len(sb.prevBlockRoots)-1]
|
||||
// We generate a new block to pass into the state transition.
|
||||
newBlock, newBlockRoot, err := generateSimulatedBlock(
|
||||
sb.state,
|
||||
prevBlockRoot,
|
||||
sb.historicalDeposits,
|
||||
objects,
|
||||
privKeys,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate simulated beacon block %v", err)
|
||||
}
|
||||
newState := sb.state
|
||||
newState.LatestEth1Data = newBlock.Eth1Data
|
||||
newState, err = state.ExecuteStateTransition(
|
||||
context.Background(),
|
||||
sb.state,
|
||||
newBlock,
|
||||
prevBlockRoot,
|
||||
state.DefaultConfig(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not execute state transition: %v", err)
|
||||
}
|
||||
|
||||
sb.state = newState
|
||||
sb.prevBlockRoots = append(sb.prevBlockRoots, newBlockRoot)
|
||||
sb.inMemoryBlocks = append(sb.inMemoryBlocks, newBlock)
|
||||
if len(newBlock.Body.Deposits) > 0 {
|
||||
sb.historicalDeposits = append(sb.historicalDeposits, newBlock.Body.Deposits...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateNilBlockAndAdvanceChain would trigger a state transition with a nil block.
|
||||
func (sb *SimulatedBackend) GenerateNilBlockAndAdvanceChain() error {
|
||||
prevBlockRoot := sb.prevBlockRoots[len(sb.prevBlockRoots)-1]
|
||||
newState, err := state.ExecuteStateTransition(
|
||||
context.Background(),
|
||||
sb.state,
|
||||
nil,
|
||||
prevBlockRoot,
|
||||
state.DefaultConfig(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not execute state transition: %v", err)
|
||||
}
|
||||
sb.state = newState
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown closes the db associated with the simulated backend.
|
||||
func (sb *SimulatedBackend) Shutdown() error {
|
||||
return sb.beaconDB.Close()
|
||||
}
|
||||
|
||||
// State is a getter to return the current beacon state
|
||||
// of the backend.
|
||||
func (sb *SimulatedBackend) State() *pb.BeaconState {
|
||||
return sb.state
|
||||
}
|
||||
|
||||
// InMemoryBlocks returns the blocks that have been processed by the simulated
|
||||
// backend.
|
||||
func (sb *SimulatedBackend) InMemoryBlocks() []*pb.BeaconBlock {
|
||||
return sb.inMemoryBlocks
|
||||
}
|
||||
|
||||
// RunForkChoiceTest uses a parsed set of chaintests from a YAML file
|
||||
// according to the ETH 2.0 client chain test specification and runs them
|
||||
// against the simulated backend.
|
||||
func (sb *SimulatedBackend) RunForkChoiceTest(testCase *ForkChoiceTestCase) error {
|
||||
defer db.TeardownDB(sb.beaconDB)
|
||||
// Utilize the config parameters in the test case to setup
|
||||
// the DB and set global config parameters accordingly.
|
||||
// Config parameters include: ValidatorCount, ShardCount,
|
||||
// CycleLength, MinCommitteeSize, and more based on the YAML
|
||||
// test language specification.
|
||||
c := params.BeaconConfig()
|
||||
c.ShardCount = testCase.Config.ShardCount
|
||||
c.SlotsPerEpoch = testCase.Config.CycleLength
|
||||
c.TargetCommitteeSize = testCase.Config.MinCommitteeSize
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
// Then, we create the validators based on the custom test config.
|
||||
validators := make([]*pb.Validator, testCase.Config.ValidatorCount)
|
||||
for i := uint64(0); i < testCase.Config.ValidatorCount; i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().ActivationExitDelay,
|
||||
Pubkey: []byte{},
|
||||
}
|
||||
}
|
||||
// TODO(#718): Next step is to update and save the blocks specified
|
||||
// in the case case into the DB.
|
||||
//
|
||||
// Then, we call the updateHead routine and confirm the
|
||||
// chain's head is the expected result from the test case.
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunShuffleTest uses validator set specified from a YAML file, runs the validator shuffle
|
||||
// algorithm, then compare the output with the expected output from the YAML file.
|
||||
func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error {
|
||||
defer db.TeardownDB(sb.beaconDB)
|
||||
seed := common.BytesToHash([]byte(testCase.Seed))
|
||||
output, err := utils.ShuffleIndices(seed, testCase.Input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !reflect.DeepEqual(output, testCase.Output) {
|
||||
return fmt.Errorf("shuffle result error: expected %v, actual %v", testCase.Output, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunStateTransitionTest advances a beacon chain state transition an N amount of
|
||||
// slots from a genesis state, with a block being processed at every iteration
|
||||
// of the state transition function.
|
||||
func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) error {
|
||||
defer db.TeardownDB(sb.beaconDB)
|
||||
setTestConfig(testCase)
|
||||
|
||||
privKeys, err := sb.initializeStateTest(testCase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not initialize state test %v", err)
|
||||
}
|
||||
averageTimesPerTransition := []time.Duration{}
|
||||
startSlot := params.BeaconConfig().GenesisSlot
|
||||
for i := startSlot; i < startSlot+testCase.Config.NumSlots; i++ {
|
||||
|
||||
// If the slot is marked as skipped in the configuration options,
|
||||
// we simply run the state transition with a nil block argument.
|
||||
if sliceutil.IsInUint64(i, testCase.Config.SkipSlots) {
|
||||
if err := sb.GenerateNilBlockAndAdvanceChain(); err != nil {
|
||||
return fmt.Errorf("could not advance the chain with a nil block %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
simulatedObjects := sb.generateSimulatedObjects(testCase, i)
|
||||
startTime := time.Now()
|
||||
|
||||
if err := sb.GenerateBlockAndAdvanceChain(simulatedObjects, privKeys); err != nil {
|
||||
return fmt.Errorf("could not generate the block and advance the chain %v", err)
|
||||
}
|
||||
|
||||
endTime := time.Now()
|
||||
averageTimesPerTransition = append(averageTimesPerTransition, endTime.Sub(startTime))
|
||||
}
|
||||
|
||||
log.Infof(
|
||||
"with %d initial deposits, each state transition took average time = %v",
|
||||
testCase.Config.DepositsForChainStart,
|
||||
averageDuration(averageTimesPerTransition),
|
||||
)
|
||||
|
||||
if err := sb.compareTestCase(testCase); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// initializeStateTest sets up the environment by generating all the required objects in order
|
||||
// to proceed with the state test.
|
||||
func (sb *SimulatedBackend) initializeStateTest(testCase *StateTestCase) ([]*bls.SecretKey, error) {
|
||||
initialDeposits, privKeys, err := generateInitialSimulatedDeposits(testCase.Config.DepositsForChainStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not simulate initial validator deposits: %v", err)
|
||||
}
|
||||
if err := sb.setupBeaconStateAndGenesisBlock(initialDeposits); err != nil {
|
||||
return nil, fmt.Errorf("could not set up beacon state and initialize genesis block %v", err)
|
||||
}
|
||||
return privKeys, nil
|
||||
}
|
||||
|
||||
// setupBeaconStateAndGenesisBlock creates the initial beacon state and genesis block in order to
|
||||
// proceed with the test.
|
||||
func (sb *SimulatedBackend) setupBeaconStateAndGenesisBlock(initialDeposits []*pb.Deposit) error {
|
||||
var err error
|
||||
genesisTime := time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC).Unix()
|
||||
sb.state, err = state.GenesisBeaconState(initialDeposits, uint64(genesisTime), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not initialize simulated beacon state: %v", err)
|
||||
}
|
||||
sb.historicalDeposits = initialDeposits
|
||||
|
||||
// We do not expect hashing initial beacon state and genesis block to
|
||||
// fail, so we can safely ignore the error below.
|
||||
// #nosec G104
|
||||
stateRoot, err := hashutil.HashProto(sb.state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not tree hash state: %v", err)
|
||||
}
|
||||
genesisBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
genesisBlockRoot, err := hashutil.HashBeaconBlock(genesisBlock)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not tree hash genesis block: %v", err)
|
||||
}
|
||||
|
||||
// We now keep track of generated blocks for each state transition in
|
||||
// a slice.
|
||||
sb.prevBlockRoots = [][32]byte{genesisBlockRoot}
|
||||
sb.inMemoryBlocks = append(sb.inMemoryBlocks, genesisBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateSimulatedObjects generates the simulated objects depending on the testcase and current slot.
|
||||
func (sb *SimulatedBackend) generateSimulatedObjects(testCase *StateTestCase, slotNumber uint64) *SimulatedObjects {
|
||||
// If the slot is not skipped, we check if we are simulating a deposit at the current slot.
|
||||
var simulatedDeposit *StateTestDeposit
|
||||
for _, deposit := range testCase.Config.Deposits {
|
||||
if deposit.Slot == slotNumber {
|
||||
simulatedDeposit = deposit
|
||||
break
|
||||
}
|
||||
}
|
||||
var simulatedProposerSlashing *StateTestProposerSlashing
|
||||
for _, pSlashing := range testCase.Config.ProposerSlashings {
|
||||
if pSlashing.Slot == slotNumber {
|
||||
simulatedProposerSlashing = pSlashing
|
||||
break
|
||||
}
|
||||
}
|
||||
var simulatedAttesterSlashing *StateTestAttesterSlashing
|
||||
for _, cSlashing := range testCase.Config.AttesterSlashings {
|
||||
if cSlashing.Slot == slotNumber {
|
||||
simulatedAttesterSlashing = cSlashing
|
||||
break
|
||||
}
|
||||
}
|
||||
var simulatedValidatorExit *StateTestValidatorExit
|
||||
for _, exit := range testCase.Config.ValidatorExits {
|
||||
if exit.Epoch == slotNumber/params.BeaconConfig().SlotsPerEpoch {
|
||||
simulatedValidatorExit = exit
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &SimulatedObjects{
|
||||
simDeposit: simulatedDeposit,
|
||||
simProposerSlashing: simulatedProposerSlashing,
|
||||
simAttesterSlashing: simulatedAttesterSlashing,
|
||||
simValidatorExit: simulatedValidatorExit,
|
||||
}
|
||||
}
|
||||
|
||||
// compareTestCase compares the state in the simulated backend against the values in inputted test case. If
|
||||
// there are any discrepancies it returns an error.
|
||||
func (sb *SimulatedBackend) compareTestCase(testCase *StateTestCase) error {
|
||||
if sb.state.Slot != testCase.Results.Slot {
|
||||
return fmt.Errorf(
|
||||
"incorrect state slot after %d state transitions without blocks, wanted %d, received %d",
|
||||
testCase.Config.NumSlots,
|
||||
sb.state.Slot,
|
||||
testCase.Results.Slot,
|
||||
)
|
||||
}
|
||||
if len(sb.state.ValidatorRegistry) != testCase.Results.NumValidators {
|
||||
return fmt.Errorf(
|
||||
"incorrect num validators after %d state transitions without blocks, wanted %d, received %d",
|
||||
testCase.Config.NumSlots,
|
||||
testCase.Results.NumValidators,
|
||||
len(sb.state.ValidatorRegistry),
|
||||
)
|
||||
}
|
||||
for _, slashed := range testCase.Results.SlashedValidators {
|
||||
if sb.state.ValidatorRegistry[slashed].SlashedEpoch == params.BeaconConfig().FarFutureEpoch {
|
||||
return fmt.Errorf(
|
||||
"expected validator at index %d to have been slashed",
|
||||
slashed,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, exited := range testCase.Results.ExitedValidators {
|
||||
if sb.state.ValidatorRegistry[exited].StatusFlags != pb.Validator_INITIATED_EXIT {
|
||||
return fmt.Errorf(
|
||||
"expected validator at index %d to have exited",
|
||||
exited,
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTestConfig(testCase *StateTestCase) {
|
||||
// We setup the initial configuration for running state
|
||||
// transition tests below.
|
||||
c := params.BeaconConfig()
|
||||
c.SlotsPerEpoch = testCase.Config.SlotsPerEpoch
|
||||
c.DepositsForChainStart = testCase.Config.DepositsForChainStart
|
||||
params.OverrideBeaconConfig(c)
|
||||
}
|
||||
|
||||
func averageDuration(times []time.Duration) time.Duration {
|
||||
sum := int64(0)
|
||||
for _, t := range times {
|
||||
sum += t.Nanoseconds()
|
||||
}
|
||||
return time.Duration(sum / int64(len(times)))
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
EnableCrosslinks: true,
|
||||
})
|
||||
}
|
||||
|
||||
func TestSimulatedBackendStop_ShutsDown(t *testing.T) {
|
||||
|
||||
backend, err := NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a new simulated backedn %v", err)
|
||||
}
|
||||
if err := backend.Shutdown(); err != nil {
|
||||
t.Errorf("Could not successfully shutdown simulated backend %v", err)
|
||||
}
|
||||
|
||||
db.TeardownDB(backend.beaconDB)
|
||||
}
|
||||
|
||||
func TestGenerateBlockAndAdvanceChain_IncreasesSlot(t *testing.T) {
|
||||
backend, err := NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a new simulated backend %v", err)
|
||||
}
|
||||
|
||||
privKeys, err := backend.SetupBackend(100)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not set up backend %v", err)
|
||||
}
|
||||
defer backend.Shutdown()
|
||||
defer db.TeardownDB(backend.beaconDB)
|
||||
|
||||
slotLimit := params.BeaconConfig().SlotsPerEpoch + uint64(1)
|
||||
|
||||
for i := uint64(0); i < slotLimit; i++ {
|
||||
if err := backend.GenerateBlockAndAdvanceChain(&SimulatedObjects{}, privKeys); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, backend.state.Slot+1)
|
||||
}
|
||||
if backend.inMemoryBlocks[len(backend.inMemoryBlocks)-1].Slot != backend.state.Slot {
|
||||
t.Errorf("In memory Blocks do not have the same last slot as the state, expected %d but got %v",
|
||||
backend.state.Slot, backend.inMemoryBlocks[len(backend.inMemoryBlocks)-1])
|
||||
}
|
||||
}
|
||||
|
||||
if backend.state.Slot != params.BeaconConfig().GenesisSlot+uint64(slotLimit) {
|
||||
t.Errorf("Unequal state slot and expected slot %d %d", backend.state.Slot, slotLimit)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGenerateNilBlockAndAdvanceChain_IncreasesSlot(t *testing.T) {
|
||||
backend, err := NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a new simulated backedn %v", err)
|
||||
}
|
||||
|
||||
if _, err := backend.SetupBackend(100); err != nil {
|
||||
t.Fatalf("Could not set up backend %v", err)
|
||||
}
|
||||
defer backend.Shutdown()
|
||||
defer db.TeardownDB(backend.beaconDB)
|
||||
|
||||
slotLimit := params.BeaconConfig().SlotsPerEpoch + uint64(1)
|
||||
|
||||
for i := uint64(0); i < slotLimit; i++ {
|
||||
if err := backend.GenerateNilBlockAndAdvanceChain(); err != nil {
|
||||
t.Fatalf("Could not generate block and transition state successfully %v for slot %d", err, backend.state.Slot+1)
|
||||
}
|
||||
}
|
||||
|
||||
if backend.state.Slot != params.BeaconConfig().GenesisSlot+uint64(slotLimit) {
|
||||
t.Errorf("Unequal state slot and expected slot %d %d", backend.state.Slot, slotLimit)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package backend
|
||||
|
||||
// StateTest --
|
||||
type StateTest struct {
|
||||
Title string
|
||||
Summary string
|
||||
Fork string `yaml:"fork"`
|
||||
Version string `yaml:"version"`
|
||||
TestSuite string `yaml:"test_suite"`
|
||||
TestCases []*StateTestCase `yaml:"test_cases"`
|
||||
}
|
||||
|
||||
// StateTestCase --
|
||||
type StateTestCase struct {
|
||||
Config *StateTestConfig `yaml:"config"`
|
||||
Results *StateTestResults `yaml:"results"`
|
||||
}
|
||||
|
||||
// StateTestConfig --
|
||||
type StateTestConfig struct {
|
||||
SkipSlots []uint64 `yaml:"skip_slots"`
|
||||
DepositSlots []uint64 `yaml:"deposit_slots"`
|
||||
Deposits []*StateTestDeposit `yaml:"deposits"`
|
||||
ProposerSlashings []*StateTestProposerSlashing `yaml:"proposer_slashings"`
|
||||
AttesterSlashings []*StateTestAttesterSlashing `yaml:"attester_slashings"`
|
||||
ValidatorExits []*StateTestValidatorExit `yaml:"validator_exits"`
|
||||
SlotsPerEpoch uint64 `yaml:"slots_per_epoch"`
|
||||
ShardCount uint64 `yaml:"shard_count"`
|
||||
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
|
||||
NumSlots uint64 `yaml:"num_slots"`
|
||||
}
|
||||
|
||||
// StateTestDeposit --
|
||||
type StateTestDeposit struct {
|
||||
Slot uint64 `yaml:"slot"`
|
||||
Amount uint64 `yaml:"amount"`
|
||||
MerkleIndex uint64 `yaml:"merkle_index"`
|
||||
Pubkey string `yaml:"pubkey"`
|
||||
}
|
||||
|
||||
// StateTestProposerSlashing --
|
||||
type StateTestProposerSlashing struct {
|
||||
Slot uint64 `yaml:"slot"`
|
||||
ProposerIndex uint64 `yaml:"proposer_index"`
|
||||
Proposal1Shard uint64 `yaml:"proposal_1_shard"`
|
||||
Proposal2Shard uint64 `yaml:"proposal_2_shard"`
|
||||
Proposal1Slot uint64 `yaml:"proposal_1_slot"`
|
||||
Proposal2Slot uint64 `yaml:"proposal_2_slot"`
|
||||
Proposal1Root string `yaml:"proposal_1_root"`
|
||||
Proposal2Root string `yaml:"proposal_2_root"`
|
||||
}
|
||||
|
||||
// StateTestAttesterSlashing --
|
||||
type StateTestAttesterSlashing struct {
|
||||
Slot uint64 `yaml:"slot"`
|
||||
SlashableAttestation1Slot uint64 `yaml:"slashable_attestation_1_slot"`
|
||||
SlashableAttestation1JustifiedEpoch uint64 `yaml:"slashable_attestation_1_justified_epoch"`
|
||||
SlashableAttestation1ValidatorIndices []uint64 `yaml:"slashable_attestation_1_validator_indices"`
|
||||
SlashableAttestation1CustodyBitField string `yaml:"slashable_attestation_1_custody_bitfield"`
|
||||
SlashableAttestation2Slot uint64 `yaml:"slashable_attestation_2_slot"`
|
||||
SlashableAttestation2JustifiedEpoch uint64 `yaml:"slashable_attestation_2_justified_epoch"`
|
||||
SlashableAttestation2ValidatorIndices []uint64 `yaml:"slashable_attestation_2_validator_indices"`
|
||||
SlashableAttestation2CustodyBitField string `yaml:"slashable_attestation_2_custody_bitfield"`
|
||||
}
|
||||
|
||||
// StateTestValidatorExit --
|
||||
type StateTestValidatorExit struct {
|
||||
Epoch uint64 `yaml:"epoch"`
|
||||
ValidatorIndex uint64 `yaml:"validator_index"`
|
||||
}
|
||||
|
||||
// StateTestResults --
|
||||
type StateTestResults struct {
|
||||
Slot uint64
|
||||
NumValidators int `yaml:"num_validators"`
|
||||
SlashedValidators []uint64 `yaml:"slashed_validators"`
|
||||
ExitedValidators []uint64 `yaml:"exited_validators"`
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
prefixed "github.com/x-cray/logrus-prefixed-formatter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
EnableCrosslinks: false,
|
||||
})
|
||||
}
|
||||
|
||||
func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
|
||||
const forkChoiceTestsFolderName = "fork-choice-tests"
|
||||
const shuffleTestsFolderName = "shuffle-tests"
|
||||
const stateTestsFolderName = "state-tests"
|
||||
|
||||
var tests []interface{}
|
||||
|
||||
dirs, err := ioutil.ReadDir(yamlDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read YAML tests directory: %v", err)
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
files, err := ioutil.ReadDir(path.Join(yamlDir, dir.Name()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read YAML tests directory: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
filePath := path.Join(yamlDir, dir.Name(), file.Name())
|
||||
// #nosec G304
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read YAML file: %v", err)
|
||||
}
|
||||
switch dir.Name() {
|
||||
case forkChoiceTestsFolderName:
|
||||
decoded := &backend.ForkChoiceTest{}
|
||||
if err := yaml.Unmarshal(data, decoded); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
|
||||
}
|
||||
tests = append(tests, decoded)
|
||||
case shuffleTestsFolderName:
|
||||
decoded := &backend.ShuffleTest{}
|
||||
if err := yaml.Unmarshal(data, decoded); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
|
||||
}
|
||||
tests = append(tests, decoded)
|
||||
case stateTestsFolderName:
|
||||
decoded := &backend.StateTest{}
|
||||
if err := yaml.Unmarshal(data, decoded); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
|
||||
}
|
||||
tests = append(tests, decoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func runTests(tests []interface{}, sb *backend.SimulatedBackend) error {
|
||||
for _, tt := range tests {
|
||||
switch typedTest := tt.(type) {
|
||||
case *backend.ForkChoiceTest:
|
||||
log.Infof("Title: %v", typedTest.Title)
|
||||
log.Infof("Summary: %v", typedTest.Summary)
|
||||
log.Infof("Test Suite: %v", typedTest.TestSuite)
|
||||
for _, testCase := range typedTest.TestCases {
|
||||
if err := sb.RunForkChoiceTest(testCase); err != nil {
|
||||
return fmt.Errorf("chain test failed: %v", err)
|
||||
}
|
||||
}
|
||||
log.Info("Test PASSED")
|
||||
case *backend.ShuffleTest:
|
||||
log.Infof("Title: %v", typedTest.Title)
|
||||
log.Infof("Summary: %v", typedTest.Summary)
|
||||
log.Infof("Test Suite: %v", typedTest.TestSuite)
|
||||
log.Infof("Fork: %v", typedTest.Fork)
|
||||
log.Infof("Version: %v", typedTest.Version)
|
||||
for _, testCase := range typedTest.TestCases {
|
||||
if err := sb.RunShuffleTest(testCase); err != nil {
|
||||
return fmt.Errorf("chain test failed: %v", err)
|
||||
}
|
||||
}
|
||||
log.Info("Test PASSED")
|
||||
case *backend.StateTest:
|
||||
log.Infof("Title: %v", typedTest.Title)
|
||||
log.Infof("Summary: %v", typedTest.Summary)
|
||||
log.Infof("Test Suite: %v", typedTest.TestSuite)
|
||||
log.Infof("Fork: %v", typedTest.Fork)
|
||||
log.Infof("Version: %v", typedTest.Version)
|
||||
for _, testCase := range typedTest.TestCases {
|
||||
if err := sb.RunStateTransitionTest(testCase); err != nil {
|
||||
return fmt.Errorf("chain test failed: %v", err)
|
||||
}
|
||||
}
|
||||
log.Info("Test PASSED")
|
||||
default:
|
||||
return fmt.Errorf("receive unknown test type: %T", typedTest)
|
||||
}
|
||||
log.Info("-----------------------------")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var yamlDir = flag.String("tests-dir", "", "path to directory of yaml tests")
|
||||
flag.Parse()
|
||||
|
||||
customFormatter := new(prefixed.TextFormatter)
|
||||
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
customFormatter.FullTimestamp = true
|
||||
log.SetFormatter(customFormatter)
|
||||
|
||||
tests, err := readTestsFromYaml(*yamlDir)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to load tests from yaml: %v", err)
|
||||
}
|
||||
|
||||
sb, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create backend: %v", err)
|
||||
}
|
||||
|
||||
log.Info("----Running Tests----")
|
||||
startTime := time.Now()
|
||||
|
||||
err = runTests(tests, sb)
|
||||
if err != nil {
|
||||
log.Fatalf("Test failed %v", err)
|
||||
}
|
||||
|
||||
endTime := time.Now()
|
||||
log.Infof("Test Runs Finished In: %v", endTime.Sub(startTime))
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
# Credits to Danny Ryan (Ethereum Foundation)
|
||||
---
|
||||
|
||||
title: Sample Ethereum 2.0 Beacon Chain Test
|
||||
summary: Basic, functioning fork choice rule for Ethereum 2.0
|
||||
test_suite: prysm
|
||||
test_cases:
|
||||
- config:
|
||||
validator_count: 100
|
||||
cycle_length: 8
|
||||
shard_count: 64
|
||||
min_committee_size: 8
|
||||
slots:
|
||||
# "slot_number" has a minimum of 1
|
||||
- slot_number: 1
|
||||
new_block:
|
||||
id: A
|
||||
# "*" is used for the genesis block
|
||||
parent: "*"
|
||||
attestations:
|
||||
- block: A
|
||||
# the following is a shorthand string for [0, 1, 2, 3, 4, 5]
|
||||
validators: "0-5"
|
||||
- slot_number: 2
|
||||
new_block:
|
||||
id: B
|
||||
parent: A
|
||||
attestations:
|
||||
- block: B
|
||||
validators: "0-5"
|
||||
- slot_number: 3
|
||||
new_block:
|
||||
id: C
|
||||
parent: A
|
||||
attestations:
|
||||
# attestation "committee_slot" defaults to the slot during which the attestation occurs
|
||||
- block: C
|
||||
validators: "2-7"
|
||||
# default "committee_slot" can be directly overridden
|
||||
- block: C
|
||||
committee_slot: 2
|
||||
validators: "6, 7"
|
||||
- slot_number: 4
|
||||
new_block:
|
||||
id: D
|
||||
parent: C
|
||||
attestations:
|
||||
- block: D
|
||||
validators: "1-4"
|
||||
# slots can be skipped entirely (5 in this case)
|
||||
- slot_number: 6
|
||||
new_block:
|
||||
id: E
|
||||
parent: D
|
||||
attestations:
|
||||
- block: E
|
||||
validators: "0-4"
|
||||
- block: B
|
||||
validators: "5, 6, 7"
|
||||
results:
|
||||
head: E
|
||||
last_justified_block: "*"
|
||||
last_finalized_block: "*"
|
||||
@@ -1,44 +0,0 @@
|
||||
# Credits to Danny Ryan (Ethereum Foundation)
|
||||
---
|
||||
|
||||
title: Shuffling Algorithm Tests
|
||||
summary: Test vectors for shuffling a list based upon a seed using `shuffle`
|
||||
test_suite: shuffle
|
||||
fork: tchaikovsky
|
||||
version: 1.0
|
||||
|
||||
test_cases:
|
||||
- config:
|
||||
validator_count: 100
|
||||
cycle_length: 8
|
||||
shard_count: 32
|
||||
min_committee_size: 8
|
||||
- input: []
|
||||
output: []
|
||||
seed: !!binary ""
|
||||
- name: boring_list
|
||||
description: List with a single element, 0
|
||||
input: [0]
|
||||
output: [0]
|
||||
seed: !!binary ""
|
||||
- input: [255]
|
||||
output: [255]
|
||||
seed: !!binary ""
|
||||
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
|
||||
output: [2, 1, 6, 1, 4, 5, 6, 4, 6, 2]
|
||||
seed: !!binary ""
|
||||
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||
output: [4, 9, 1, 13, 8, 3, 5, 10, 7, 6, 11, 2, 12]
|
||||
seed: !!binary ""
|
||||
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
|
||||
output: [6, 1, 2, 2, 6, 6, 1, 5, 65, 4]
|
||||
seed: !!binary |
|
||||
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
- input: [35, 6, 2, 6, 1, 4, 6, 2, 1, 5, 7, 98, 3, 2, 11]
|
||||
output: [35, 1, 6, 4, 6, 6, 5, 11, 2, 3, 7, 1, 2, 2, 98]
|
||||
seed: !!binary |
|
||||
VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4=
|
||||
- input: [35, 6, 2, 6, 1, 4, 6, 2, 1, 5, 7, 98, 3, 2, 11]
|
||||
output: [98, 6, 6, 11, 5, 35, 2, 7, 2, 6, 4, 2, 1, 3, 1]
|
||||
seed: !!binary |
|
||||
rDTbe23J4UA0yLIurjbJqk49VcavAC0Nysas+l5MlwvLc0B/JqQ=
|
||||
@@ -1,82 +0,0 @@
|
||||
title: Sample Ethereum Serenity State Transition Tests
|
||||
summary: Testing full state transition block processing
|
||||
test_suite: prysm
|
||||
fork: sapphire
|
||||
version: 1.0
|
||||
test_cases:
|
||||
- config:
|
||||
slots_per_epoch: 64
|
||||
deposits_for_chain_start: 64
|
||||
num_slots: 32 # Testing advancing state to slot < SlotsPerEpoch
|
||||
results:
|
||||
slot: 9223372036854775840
|
||||
num_validators: 64
|
||||
- config:
|
||||
slots_per_epoch: 64
|
||||
deposits_for_chain_start: 64
|
||||
num_slots: 64 # Testing advancing state to exactly slot == SlotsPerEpoch
|
||||
deposits:
|
||||
- slot: 9223372036854775809
|
||||
amount: 32
|
||||
merkle_index: 64
|
||||
pubkey: !!binary |
|
||||
SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
- slot: 9223372036854775823
|
||||
amount: 32
|
||||
merkle_index: 65
|
||||
pubkey: !!binary |
|
||||
Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
|
||||
- slot: 9223372036854775863
|
||||
amount: 32
|
||||
merkle_index: 66
|
||||
pubkey: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
proposer_slashings:
|
||||
- slot: 9223372036854775824 # At slot 9223372036854775824, we trigger a proposal slashing occurring
|
||||
proposer_index: 50 # We penalize the proposer that was just added from slot 15
|
||||
proposal_1_shard: 0
|
||||
proposal_1_slot: 15
|
||||
proposal_1_root: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
proposal_2_shard: 0
|
||||
proposal_2_slot: 15
|
||||
proposal_2_root: !!binary |
|
||||
LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd
|
||||
attester_slashings:
|
||||
- slot: 9223372036854775868 # At slot 59, we trigger a attester slashing
|
||||
slashable_attestation_1_slot: 9223372036854775864
|
||||
slashable_attestation_2_slot: 9223372036854775864
|
||||
slashable_attestation_1_justified_epoch: 0
|
||||
slashable_attestation_2_justified_epoch: 1
|
||||
slashable_attestation_1_custody_bitfield: !binary "F"
|
||||
slashable_attestation_1_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51]
|
||||
slashable_attestation_2_custody_bitfield: !binary "F"
|
||||
slashable_attestation_2_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51]
|
||||
validator_exits:
|
||||
- epoch: 144115188075855872
|
||||
validator_index: 45 # At slot 9223372036854775868, validator at index 45 triggers a voluntary exit
|
||||
results:
|
||||
slot: 9223372036854775872
|
||||
num_validators: 67
|
||||
penalized_validators: [50, 51] # We test that the validators at indices were indeed penalized
|
||||
exited_validators: [45] # We confirm the indices of validators that willingly exited the registry
|
||||
# TODO(1387): Waiting for spec to stable to proceed with this test case
|
||||
# - config:
|
||||
# skip_slots: [10, 20]
|
||||
# slots_per_epoch: 64
|
||||
# deposits_for_chain_start: 1000
|
||||
# num_slots: 128 # Testing advancing state's slot == 2*SlotsPerEpoch
|
||||
# deposits:
|
||||
# - slot: 10
|
||||
# amount: 32
|
||||
# merkle_index: 0
|
||||
# pubkey: !!binary |
|
||||
# SlAAbShSkUg7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
|
||||
# - slot: 20
|
||||
# amount: 32
|
||||
# merkle_index: 1
|
||||
# pubkey: !!binary |
|
||||
# Oklajsjdkaklsdlkajsdjlajslkdjlkasjlkdjlajdsd
|
||||
# results:
|
||||
# slot: 128
|
||||
# num_validators: 1000 # Validator registry should not have grown if slots 10 and 20 were skipped
|
||||
@@ -1,49 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
)
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
EnableCrosslinks: true,
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromYaml_Pass(t *testing.T) {
|
||||
tests, err := readTestsFromYaml("./tests")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read yaml files: %v", err)
|
||||
}
|
||||
|
||||
sb, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create backend: %v", err)
|
||||
}
|
||||
|
||||
if err := runTests(tests, sb); err != nil {
|
||||
t.Errorf("Failed to run yaml tests %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStateTestFromYaml(b *testing.B) {
|
||||
tests, err := readTestsFromYaml("./tests")
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to read yaml files: %v", err)
|
||||
}
|
||||
|
||||
sb, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
b.Fatalf("Could not create backend: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := runTests(tests, sb); err != nil {
|
||||
b.Errorf("Failed to run yaml tests %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["rewards_penalties.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/balances",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["rewards_penalties_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,379 +0,0 @@
|
||||
// Package balances contains libraries to calculate reward and
|
||||
// penalty quotients. It computes new validator balances
|
||||
// for justifications, crosslinks and attestation inclusions. It
|
||||
// also computes penalties for the inactive validators.
|
||||
package balances
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
)
|
||||
|
||||
// ExpectedFFGSource applies rewards or penalties
|
||||
// for an expected FFG source. It uses total justified
|
||||
// attesting balances, total validator balances and base
|
||||
// reward quotient to calculate the reward amount.
|
||||
// Validators who voted for previous justified hash
|
||||
// will get a reward, everyone else will get a penalty.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any validator index in previous_epoch_justified_attester_indices
|
||||
// gains base_reward(state, index) * previous_epoch_justified_attesting_balance // total_balance.
|
||||
// Any active validator v not in previous_epoch_justified_attester_indices
|
||||
// loses base_reward(state, index).
|
||||
func ExpectedFFGSource(
|
||||
state *pb.BeaconState,
|
||||
justifiedAttesterIndices []uint64,
|
||||
justifiedAttestingBalance uint64,
|
||||
totalBalance uint64) *pb.BeaconState {
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
for _, index := range justifiedAttesterIndices {
|
||||
state.ValidatorBalances[index] +=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient) *
|
||||
justifiedAttestingBalance /
|
||||
totalBalance
|
||||
}
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(justifiedAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// ExpectedFFGTarget applies rewards or penalties
|
||||
// for an expected FFG target. It uses total boundary
|
||||
// attesting balances, total validator balances and base
|
||||
// reward quotient to calculate the reward amount.
|
||||
// Validators who voted for epoch boundary block
|
||||
// will get a reward, everyone else will get a penalty.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any validator index in previous_epoch_boundary_attester_indices gains
|
||||
// base_reward(state, index) * previous_epoch_boundary_attesting_balance // total_balance.
|
||||
// Any active validator index not in previous_epoch_boundary_attester_indices loses
|
||||
// base_reward(state, index).
|
||||
func ExpectedFFGTarget(
|
||||
state *pb.BeaconState,
|
||||
boundaryAttesterIndices []uint64,
|
||||
boundaryAttestingBalance uint64,
|
||||
totalBalance uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
for _, index := range boundaryAttesterIndices {
|
||||
state.ValidatorBalances[index] +=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient) *
|
||||
boundaryAttestingBalance /
|
||||
totalBalance
|
||||
}
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(boundaryAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// ExpectedBeaconChainHead applies rewards or penalties
|
||||
// for an expected beacon chain head. It uses total head
|
||||
// attesting balances, total validator balances and base
|
||||
// reward quotient to calculate the reward amount.
|
||||
// Validators who voted for the canonical head block
|
||||
// will get a reward, everyone else will get a penalty.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any validator index in previous_epoch_head_attester_indices gains
|
||||
// base_reward(state, index) * previous_epoch_head_attesting_balance // total_balance).
|
||||
// Any active validator index not in previous_epoch_head_attester_indices loses
|
||||
// base_reward(state, index).
|
||||
func ExpectedBeaconChainHead(
|
||||
state *pb.BeaconState,
|
||||
headAttesterIndices []uint64,
|
||||
headAttestingBalance uint64,
|
||||
totalBalance uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
for _, index := range headAttesterIndices {
|
||||
state.ValidatorBalances[index] +=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient) *
|
||||
headAttestingBalance /
|
||||
totalBalance
|
||||
}
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(headAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// InclusionDistance applies rewards based on
|
||||
// inclusion distance. It uses calculated inclusion distance
|
||||
// and base reward quotient to calculate the reward amount.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any validator index in previous_epoch_attester_indices gains
|
||||
// base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
|
||||
// inclusion_distance(state, index)
|
||||
func InclusionDistance(
|
||||
state *pb.BeaconState,
|
||||
attesterIndices []uint64,
|
||||
totalBalance uint64,
|
||||
inclusionDistanceByAttester map[uint64]uint64) (*pb.BeaconState, error) {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
for _, index := range attesterIndices {
|
||||
inclusionDistance, ok := inclusionDistanceByAttester[index]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get inclusion distance for attester: %d", index)
|
||||
}
|
||||
if inclusionDistance == 0 {
|
||||
return nil, errors.New("could not process inclusion distance: 0")
|
||||
}
|
||||
state.ValidatorBalances[index] +=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient) *
|
||||
params.BeaconConfig().MinAttestationInclusionDelay /
|
||||
inclusionDistance
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// InactivityFFGSource applies penalties to inactive
|
||||
// validators that missed to vote FFG source over an
|
||||
// extended of time. (epochs_since_finality > 4)
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any active validator index not in previous_epoch_justified_attester_indices,
|
||||
// loses inactivity_penalty(state, index, epochs_since_finality)
|
||||
func InactivityFFGSource(
|
||||
state *pb.BeaconState,
|
||||
justifiedAttesterIndices []uint64,
|
||||
totalBalance uint64,
|
||||
epochsSinceFinality uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(justifiedAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// InactivityFFGTarget applies penalties to inactive
|
||||
// validators that missed to vote FFG target over an
|
||||
// extended of time. (epochs_since_finality > 4)
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any active validator index not in previous_epoch_boundary_attester_indices,
|
||||
// loses inactivity_penalty(state, index, epochs_since_finality)
|
||||
func InactivityFFGTarget(
|
||||
state *pb.BeaconState,
|
||||
boundaryAttesterIndices []uint64,
|
||||
totalBalance uint64,
|
||||
epochsSinceFinality uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(boundaryAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// InactivityChainHead applies penalties to inactive validators
|
||||
// that missed to vote on canonical head over an extended of time.
|
||||
// (epochs_since_finality > 4)
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any active validator index not in previous_epoch_head_attester_indices,
|
||||
// loses base_reward(state, index)
|
||||
func InactivityChainHead(
|
||||
state *pb.BeaconState,
|
||||
headAttesterIndices []uint64,
|
||||
totalBalance uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, helpers.CurrentEpoch(state))
|
||||
didNotAttestIndices := sliceutil.NotUint64(headAttesterIndices, activeValidatorIndices)
|
||||
|
||||
for _, index := range didNotAttestIndices {
|
||||
state.ValidatorBalances[index] -=
|
||||
helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// InactivityExitedPenalties applies additional (2x) penalties
|
||||
// to inactive validators with status EXITED_WITH_PENALTY.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any active_validator index with validator.slashed_epoch <= current_epoch,
|
||||
// loses 2 * inactivity_penalty(state, index, epochs_since_finality) +
|
||||
// base_reward(state, index).
|
||||
func InactivityExitedPenalties(
|
||||
state *pb.BeaconState,
|
||||
totalBalance uint64,
|
||||
epochsSinceFinality uint64) *pb.BeaconState {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
activeValidatorIndices := helpers.ActiveValidatorIndices(state.ValidatorRegistry, currentEpoch)
|
||||
|
||||
for _, index := range activeValidatorIndices {
|
||||
if state.ValidatorRegistry[index].SlashedEpoch <= currentEpoch {
|
||||
state.ValidatorBalances[index] -=
|
||||
2*helpers.InactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality) +
|
||||
helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// InactivityInclusionDistance applies penalties in relation with
|
||||
// inclusion delay to inactive validators.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Any validator index in previous_epoch_attester_indices loses
|
||||
// base_reward(state, index) - base_reward(state, index) *
|
||||
// MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)
|
||||
func InactivityInclusionDistance(
|
||||
state *pb.BeaconState,
|
||||
attesterIndices []uint64,
|
||||
totalBalance uint64,
|
||||
inclusionDistanceByAttester map[uint64]uint64) (*pb.BeaconState, error) {
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
for _, index := range attesterIndices {
|
||||
inclusionDistance, ok := inclusionDistanceByAttester[index]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get inclusion distance for attester: %d", index)
|
||||
}
|
||||
baseReward := helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
state.ValidatorBalances[index] -= baseReward -
|
||||
baseReward*params.BeaconConfig().MinAttestationInclusionDelay/
|
||||
inclusionDistance
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// AttestationInclusion awards the the beacon
|
||||
// proposers who included previous epoch attestations.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// For each index in previous_epoch_attester_indices,
|
||||
// we determine the proposer proposer_index =
|
||||
// get_beacon_proposer_index(state, inclusion_slot(state, index))
|
||||
// and set state.validator_balances[proposer_index] +=
|
||||
// base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
||||
func AttestationInclusion(
|
||||
state *pb.BeaconState,
|
||||
totalBalance uint64,
|
||||
prevEpochAttesterIndices []uint64,
|
||||
inclusionSlotByAttester map[uint64]uint64) (*pb.BeaconState, error) {
|
||||
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
for _, index := range prevEpochAttesterIndices {
|
||||
// Get the attestation's inclusion slot using the attestor's index.
|
||||
slot, ok := inclusionSlotByAttester[index]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get inclusion slot for attester: %d", index)
|
||||
}
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(state, slot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get proposer index: %v", err)
|
||||
}
|
||||
state.ValidatorBalances[proposerIndex] +=
|
||||
helpers.BaseReward(state, proposerIndex, baseRewardQuotient) /
|
||||
params.BeaconConfig().AttestationInclusionRewardQuotient
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// Crosslinks awards or slashs attesters
|
||||
// for attesting shard cross links.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// For slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(current_epoch)),
|
||||
// let crosslink_committees_at_slot = get_crosslink_committees_at_slot(slot).
|
||||
// For every (crosslink_committee, shard) in crosslink_committee_at_slot,
|
||||
// and every index in crosslink_committee:
|
||||
// If index in attesting_validators(crosslink_committee),
|
||||
// state.validator_balances[index] += base_reward(state, index) *
|
||||
// total_attesting_balance(crosslink_committee) //
|
||||
// get_total_balance(state, crosslink_committee)).
|
||||
// If index not in attesting_validators(crosslink_committee),
|
||||
// state.validator_balances[index] -= base_reward(state, index).
|
||||
func Crosslinks(
|
||||
state *pb.BeaconState,
|
||||
thisEpochAttestations []*pb.PendingAttestation,
|
||||
prevEpochAttestations []*pb.PendingAttestation) (*pb.BeaconState, error) {
|
||||
|
||||
prevEpoch := helpers.PrevEpoch(state)
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
startSlot := helpers.StartSlot(prevEpoch)
|
||||
endSlot := helpers.StartSlot(currentEpoch)
|
||||
|
||||
for i := startSlot; i < endSlot; i++ {
|
||||
// RegistryChange is a no-op when requesting slot in current and previous epoch.
|
||||
// Process crosslinks rewards will never request crosslink committees of next epoch.
|
||||
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(state, i, false /* registryChange */)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get shard committees for slot %d: %v",
|
||||
i-params.BeaconConfig().GenesisSlot, err)
|
||||
}
|
||||
for _, crosslinkCommittee := range crosslinkCommittees {
|
||||
shard := crosslinkCommittee.Shard
|
||||
committee := crosslinkCommittee.Committee
|
||||
totalAttestingBalance, err :=
|
||||
epoch.TotalAttestingBalance(state, shard, thisEpochAttestations, prevEpochAttestations)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("could not get attesting balance for shard committee %d: %v", shard, err)
|
||||
}
|
||||
totalBalance := epoch.TotalBalance(state, committee)
|
||||
baseRewardQuotient := helpers.BaseRewardQuotient(totalBalance)
|
||||
|
||||
attestingIndices, err := epoch.AttestingValidators(
|
||||
state,
|
||||
shard,
|
||||
thisEpochAttestations,
|
||||
prevEpochAttestations)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("could not get attesting indices for shard committee %d: %v", shard, err)
|
||||
}
|
||||
for _, index := range committee {
|
||||
baseReward := helpers.BaseReward(state, index, baseRewardQuotient)
|
||||
if sliceutil.IsInUint64(index, attestingIndices) {
|
||||
state.ValidatorBalances[index] +=
|
||||
baseReward * totalAttestingBalance / totalBalance
|
||||
} else {
|
||||
state.ValidatorBalances[index] -= baseReward
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
@@ -1,677 +0,0 @@
|
||||
package balances
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestFFGSrcRewardsPenalties_AccurateBalances(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterSrcRewardPenalties []uint64
|
||||
}{
|
||||
// voted represents the validator indices that voted for FFG source,
|
||||
// balanceAfterSrcRewardPenalties represents their final balances,
|
||||
// validators who voted should get an increase, who didn't should get a decrease.
|
||||
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = ExpectedFFGSource(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterSrcRewardPenalties) {
|
||||
t.Errorf("FFGSrcRewardsPenalties(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterSrcRewardPenalties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFFGTargetRewardsPenalties_AccurateBalances(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterTgtRewardPenalties []uint64
|
||||
}{
|
||||
// voted represents the validator indices that voted for FFG target,
|
||||
// balanceAfterTgtRewardPenalties represents their final balances,
|
||||
// validators who voted should get an increase, who didn't should get a decrease.
|
||||
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = ExpectedFFGTarget(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterTgtRewardPenalties) {
|
||||
t.Errorf("FFGTargetRewardsPenalties(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterTgtRewardPenalties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainHeadRewardsPenalties_AccuratePenalties(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterHeadRewardPenalties []uint64
|
||||
}{
|
||||
// voted represents the validator indices that voted for canonical chain,
|
||||
// balanceAfterHeadRewardPenalties represents their final balances,
|
||||
// validators who voted should get an increase, who didn't should get a decrease.
|
||||
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1}, []uint64{32000286225, 32000286225, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{32000572450, 32000572450, 32000572450, 32000572450}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = ExpectedBeaconChainHead(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(tt.voted))*params.BeaconConfig().MaxDepositAmount,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterHeadRewardPenalties) {
|
||||
t.Errorf("ChainHeadRewardsPenalties(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterHeadRewardPenalties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusionDistRewards_AccurateRewards(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
var participationBitfield []byte
|
||||
// participation byte length = number of validators / target committee size / bits in a byte.
|
||||
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
|
||||
for i := 0; i < byteLength; i++ {
|
||||
participationBitfield = append(participationBitfield, byte(0xff))
|
||||
}
|
||||
|
||||
attestations := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
JustifiedBlockRootHash32: []byte{},
|
||||
Shard: 0,
|
||||
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: params.BeaconConfig().GenesisSlot + 5,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
}{
|
||||
{[]uint64{}},
|
||||
{[]uint64{251, 192}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, len(validators))
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 5,
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
LatestAttestations: attestations,
|
||||
PreviousJustifiedRoot: []byte{},
|
||||
LatestCrosslinks: []*pb.Crosslink{
|
||||
{
|
||||
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:],
|
||||
Epoch: params.BeaconConfig().GenesisEpoch,
|
||||
},
|
||||
},
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Attestations: []*pb.Attestation{
|
||||
{
|
||||
Data: attestations[0].Data,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err := blocks.ProcessBlockAttestations(state, block, false /* verify sig */); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
for _, voted := range tt.voted {
|
||||
inclusionMap[voted] = state.Slot
|
||||
}
|
||||
state, err := InclusionDistance(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
inclusionMap)
|
||||
if err != nil {
|
||||
t.Fatalf("could not execute InclusionDistRewards:%v", err)
|
||||
}
|
||||
|
||||
for _, i := range tt.voted {
|
||||
validatorBalances[i] = 32000055555
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
|
||||
t.Errorf("InclusionDistRewards(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, validatorBalances)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusionDistRewards_OutOfBounds(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
attestation := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
|
||||
AggregationBitfield: []byte{0xff}},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterInclusionRewards []uint64
|
||||
}{
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
LatestAttestations: attestation,
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
_, err := InclusionDistance(state, tt.voted, 0, inclusionMap)
|
||||
if err == nil {
|
||||
t.Fatal("InclusionDistRewards should have failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityFFGSrcPenalty_AccuratePenalties(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterFFGSrcPenalty []uint64
|
||||
epochsSinceFinality uint64
|
||||
}{
|
||||
// The higher the epochs since finality, the more penalties applied.
|
||||
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999422782, 31999422782}, 5},
|
||||
{[]uint64{}, []uint64{31999422782, 31999422782, 31999422782, 31999422782}, 5},
|
||||
{[]uint64{}, []uint64{31999418014, 31999418014, 31999418014, 31999418014}, 10},
|
||||
{[]uint64{}, []uint64{31999408477, 31999408477, 31999408477, 31999408477}, 20},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = InactivityFFGSource(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
tt.epochsSinceFinality)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterFFGSrcPenalty) {
|
||||
t.Errorf("InactivityFFGSrcPenalty(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterFFGSrcPenalty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityFFGTargetPenalty_AccuratePenalties(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterFFGTargetPenalty []uint64
|
||||
epochsSinceFinality uint64
|
||||
}{
|
||||
// The higher the epochs since finality, the more penalties applied.
|
||||
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999422782, 31999422782}, 5},
|
||||
{[]uint64{}, []uint64{31999422782, 31999422782, 31999422782, 31999422782}, 5},
|
||||
{[]uint64{}, []uint64{31999418014, 31999418014, 31999418014, 31999418014}, 10},
|
||||
{[]uint64{}, []uint64{31999408477, 31999408477, 31999408477, 31999408477}, 20},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = InactivityFFGTarget(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
tt.epochsSinceFinality)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterFFGTargetPenalty) {
|
||||
t.Errorf("InactivityFFGTargetPenalty(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterFFGTargetPenalty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityHeadPenalty_AccuratePenalties(t *testing.T) {
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterInactivityHeadPenalty []uint64
|
||||
}{
|
||||
{[]uint64{}, []uint64{31999427550, 31999427550, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1}, []uint64{32000000000, 32000000000, 31999427550, 31999427550}},
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{32000000000, 32000000000, 32000000000, 32000000000}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = InactivityChainHead(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterInactivityHeadPenalty) {
|
||||
t.Errorf("InactivityHeadPenalty(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, tt.balanceAfterInactivityHeadPenalty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityExitedPenality_AccuratePenalties(t *testing.T) {
|
||||
tests := []struct {
|
||||
balanceAfterExitedPenalty []uint64
|
||||
epochsSinceFinality uint64
|
||||
}{
|
||||
{[]uint64{31998273114, 31998273114, 31998273114, 31998273114}, 5},
|
||||
{[]uint64{31998263578, 31998263578, 31998263578, 31998263578}, 10},
|
||||
{[]uint64{31997328976, 31997328976, 31997328976, 31997328976}, 500},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: []*pb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch}},
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
state = InactivityExitedPenalties(
|
||||
state,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
tt.epochsSinceFinality,
|
||||
)
|
||||
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, tt.balanceAfterExitedPenalty) {
|
||||
t.Errorf("InactivityExitedPenalty(epochSinceFinality=%v) = %v, wanted: %v",
|
||||
tt.epochsSinceFinality, state.ValidatorBalances, tt.balanceAfterExitedPenalty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityInclusionPenalty_AccuratePenalties(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
var participationBitfield []byte
|
||||
// participation byte length = number of validators / target committee size / bits in a byte.
|
||||
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
|
||||
for i := 0; i < byteLength; i++ {
|
||||
participationBitfield = append(participationBitfield, byte(0xff))
|
||||
}
|
||||
attestation := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: 5},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
}{
|
||||
{[]uint64{}},
|
||||
{[]uint64{251, 192}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, params.BeaconConfig().SlotsPerEpoch*4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
LatestAttestations: attestation,
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
for _, voted := range tt.voted {
|
||||
inclusionMap[voted] = state.Slot + 1
|
||||
}
|
||||
state, err := InactivityInclusionDistance(
|
||||
state,
|
||||
tt.voted,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
inclusionMap)
|
||||
|
||||
for _, i := range tt.voted {
|
||||
validatorBalances[i] = 32000055555
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not execute InactivityInclusionPenalty:%v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
|
||||
t.Errorf("InactivityInclusionPenalty(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, validatorBalances)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInactivityInclusionPenalty_OutOfBounds(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
attestation := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
|
||||
AggregationBitfield: []byte{0xff}},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterInclusionRewards []uint64
|
||||
}{
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
LatestAttestations: attestation,
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
_, err := InactivityInclusionDistance(state, tt.voted, 0, inclusionMap)
|
||||
if err == nil {
|
||||
t.Fatal("InclusionDistRewards should have failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationInclusionRewards_AccurateRewards(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
var participationBitfield []byte
|
||||
// participation byte length = number of validators / target committee size / bits in a byte.
|
||||
byteLength := int(params.BeaconConfig().DepositsForChainStart / params.BeaconConfig().TargetCommitteeSize / 8)
|
||||
for i := 0; i < byteLength; i++ {
|
||||
participationBitfield = append(participationBitfield, byte(0xff))
|
||||
}
|
||||
atts := []*pb.Attestation{
|
||||
{Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
LatestCrosslink: &pb.Crosslink{},
|
||||
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:]}}}
|
||||
pendingAtts := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: params.BeaconConfig().GenesisSlot},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
}{
|
||||
{[]uint64{}},
|
||||
{[]uint64{251}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 10,
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
LatestAttestations: pendingAtts,
|
||||
LatestCrosslinks: []*pb.Crosslink{{}},
|
||||
}
|
||||
|
||||
_, err := blocks.ProcessBlockAttestations(state, &pb.BeaconBlock{
|
||||
Body: &pb.BeaconBlockBody{
|
||||
Attestations: atts,
|
||||
},
|
||||
}, false /* sig verification */)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
for _, voted := range tt.voted {
|
||||
inclusionMap[voted] = state.Slot
|
||||
}
|
||||
|
||||
state, err = AttestationInclusion(
|
||||
state,
|
||||
uint64(len(validatorBalances))*params.BeaconConfig().MaxDepositAmount,
|
||||
tt.voted,
|
||||
inclusionMap)
|
||||
|
||||
for _, i := range tt.voted {
|
||||
validatorBalances[i] = 32000008680
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not execute InactivityInclusionPenalty:%v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
|
||||
t.Errorf("AttestationInclusionRewards(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, validatorBalances)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationInclusionRewards_NoInclusionSlot(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterAttestationInclusion []uint64
|
||||
}{
|
||||
{[]uint64{0, 1, 2, 3}, []uint64{32000000000, 32000000000, 32000000000, 32000000000}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 128)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
if _, err := AttestationInclusion(state, 0, tt.voted, inclusionMap); err == nil {
|
||||
t.Fatal("AttestationInclusionRewards should have failed with no inclusion slot")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationInclusionRewards_NoProposerIndex(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*2)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
attestation := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
|
||||
AggregationBitfield: []byte{0xff},
|
||||
InclusionSlot: 0},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []uint64
|
||||
balanceAfterAttestationInclusion []uint64
|
||||
}{
|
||||
{[]uint64{0}, []uint64{32000071022, 32000000000, 32000000000, 32000000000}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, 4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: 1000,
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
LatestAttestations: attestation,
|
||||
}
|
||||
inclusionMap := make(map[uint64]uint64)
|
||||
if _, err := AttestationInclusion(state, 0, tt.voted, inclusionMap); err == nil {
|
||||
t.Fatal("AttestationInclusionRewards should have failed with no proposer index")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrosslinksRewardsPenalties_AccurateBalances(t *testing.T) {
|
||||
validators := make([]*pb.Validator, params.BeaconConfig().SlotsPerEpoch*4)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
voted []byte
|
||||
balanceAfterCrosslinkRewards []uint64
|
||||
}{
|
||||
{[]byte{0x0}, []uint64{
|
||||
32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9, 32 * 1e9}},
|
||||
{[]byte{0xF}, []uint64{
|
||||
31585730498, 31585730498, 31585730498, 31585730498,
|
||||
32416931985, 32416931985, 32416931985, 32416931985}},
|
||||
{[]byte{0xFF}, []uint64{
|
||||
32829149760, 32829149760, 32829149760, 32829149760,
|
||||
32829149760, 32829149760, 32829149760, 32829149760}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
validatorBalances := make([]uint64, params.BeaconConfig().SlotsPerEpoch*4)
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
attestation := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Shard: 1, Slot: 0},
|
||||
AggregationBitfield: tt.voted,
|
||||
InclusionSlot: 0},
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
LatestAttestations: attestation,
|
||||
}
|
||||
state, err := Crosslinks(
|
||||
state,
|
||||
attestation,
|
||||
nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not apply Crosslinks rewards: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(state.ValidatorBalances, validatorBalances) {
|
||||
t.Errorf("CrosslinksRewardsPenalties(%v) = %v, wanted: %v",
|
||||
tt.voted, state.ValidatorBalances, validatorBalances)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state/stateutils:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
@@ -17,13 +18,14 @@ go_library(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/forkutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -40,10 +42,10 @@ go_test(
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/core/state/stateutils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/forkutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
@@ -51,8 +53,9 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -6,9 +6,9 @@ package blocks
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
@@ -17,64 +17,49 @@ var clock utils.Clock = &utils.RealClock{}
|
||||
// NewGenesisBlock returns the canonical, genesis block for the beacon chain protocol.
|
||||
func NewGenesisBlock(stateRoot []byte) *pb.BeaconBlock {
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
ParentRootHash32: params.BeaconConfig().ZeroHash[:],
|
||||
StateRootHash32: stateRoot,
|
||||
RandaoReveal: params.BeaconConfig().ZeroHash[:],
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRootHash32: params.BeaconConfig().ZeroHash[:],
|
||||
BlockHash32: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
Slot: 0,
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: stateRoot,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
RandaoReveal: params.BeaconConfig().ZeroHash[:],
|
||||
ProposerSlashings: []*pb.ProposerSlashing{},
|
||||
AttesterSlashings: []*pb.AttesterSlashing{},
|
||||
Attestations: []*pb.Attestation{},
|
||||
Deposits: []*pb.Deposit{},
|
||||
VoluntaryExits: []*pb.VoluntaryExit{},
|
||||
Eth1Data: &pb.Eth1Data{
|
||||
DepositRoot: params.BeaconConfig().ZeroHash[:],
|
||||
BlockHash: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// BlockRoot returns the block root stored in the BeaconState for a given slot.
|
||||
// It returns an error if the requested block root is not within the BeaconState.
|
||||
// Spec pseudocode definition:
|
||||
// def get_block_root(state: BeaconState, slot: int) -> Hash32:
|
||||
// """
|
||||
// returns the block root at a recent ``slot``.
|
||||
// """
|
||||
// assert state.slot <= slot + LATEST_BLOCK_ROOTS_LENGTH
|
||||
// assert slot < state.slot
|
||||
// return state.latest_block_roots[slot % LATEST_BLOCK_ROOTS_LENGTH]
|
||||
func BlockRoot(state *pb.BeaconState, slot uint64) ([]byte, error) {
|
||||
earliestSlot := state.Slot - params.BeaconConfig().LatestBlockRootsLength
|
||||
|
||||
if slot < earliestSlot || slot >= state.Slot {
|
||||
if earliestSlot < params.BeaconConfig().GenesisSlot {
|
||||
earliestSlot = params.BeaconConfig().GenesisSlot
|
||||
}
|
||||
return []byte{}, fmt.Errorf("slot %d is not within expected range of %d to %d",
|
||||
slot-params.BeaconConfig().GenesisSlot,
|
||||
earliestSlot-params.BeaconConfig().GenesisSlot,
|
||||
state.Slot-params.BeaconConfig().GenesisSlot,
|
||||
)
|
||||
// BlockFromHeader manufactures a block from its header. It contains all its fields,
|
||||
// expect for the block body.
|
||||
func BlockFromHeader(header *pb.BeaconBlockHeader) *pb.BeaconBlock {
|
||||
return &pb.BeaconBlock{
|
||||
StateRoot: header.StateRoot,
|
||||
Slot: header.Slot,
|
||||
Signature: header.Signature,
|
||||
ParentRoot: header.ParentRoot,
|
||||
}
|
||||
|
||||
return state.LatestBlockRootHash32S[slot%params.BeaconConfig().LatestBlockRootsLength], nil
|
||||
}
|
||||
|
||||
// ProcessBlockRoots processes the previous block root into the state, by appending it
|
||||
// to the most recent block roots.
|
||||
// Spec:
|
||||
// Let previous_block_root be the tree_hash_root of the previous beacon block processed in the chain.
|
||||
// Set state.latest_block_roots[(state.slot - 1) % LATEST_BLOCK_ROOTS_LENGTH] = previous_block_root.
|
||||
// If state.slot % LATEST_BLOCK_ROOTS_LENGTH == 0 append merkle_root(state.latest_block_roots) to state.batched_block_roots.
|
||||
func ProcessBlockRoots(state *pb.BeaconState, parentRoot [32]byte) *pb.BeaconState {
|
||||
state.LatestBlockRootHash32S[(state.Slot-1)%params.BeaconConfig().LatestBlockRootsLength] = parentRoot[:]
|
||||
if state.Slot%params.BeaconConfig().LatestBlockRootsLength == 0 {
|
||||
merkleRoot := hashutil.MerkleRoot(state.LatestBlockRootHash32S)
|
||||
state.BatchedBlockRootHash32S = append(state.BatchedBlockRootHash32S, merkleRoot)
|
||||
// HeaderFromBlock extracts the block header from a block.
|
||||
func HeaderFromBlock(block *pb.BeaconBlock) (*pb.BeaconBlockHeader, error) {
|
||||
header := &pb.BeaconBlockHeader{
|
||||
Slot: block.Slot,
|
||||
ParentRoot: block.ParentRoot,
|
||||
Signature: block.Signature,
|
||||
StateRoot: block.StateRoot,
|
||||
}
|
||||
return state
|
||||
root, err := ssz.HashTreeRoot(block.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not tree hash block body %v", err)
|
||||
}
|
||||
header.BodyRoot = root[:]
|
||||
return header, nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,12 @@ package blocks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
@@ -16,7 +15,7 @@ func TestGenesisBlock_InitializedCorrectly(t *testing.T) {
|
||||
stateHash := []byte{0}
|
||||
b1 := NewGenesisBlock(stateHash)
|
||||
|
||||
if b1.ParentRootHash32 == nil {
|
||||
if b1.ParentRoot == nil {
|
||||
t.Error("genesis block missing ParentHash field")
|
||||
}
|
||||
|
||||
@@ -24,148 +23,79 @@ func TestGenesisBlock_InitializedCorrectly(t *testing.T) {
|
||||
t.Errorf("genesis block should have 0 attestations")
|
||||
}
|
||||
|
||||
if !bytes.Equal(b1.RandaoReveal, params.BeaconConfig().ZeroHash[:]) {
|
||||
if !bytes.Equal(b1.Body.RandaoReveal, params.BeaconConfig().ZeroHash[:]) {
|
||||
t.Error("genesis block missing RandaoReveal field")
|
||||
}
|
||||
|
||||
if !bytes.Equal(b1.StateRootHash32, stateHash) {
|
||||
if !bytes.Equal(b1.StateRoot, stateHash) {
|
||||
t.Error("genesis block StateRootHash32 isn't initialized correctly")
|
||||
}
|
||||
expectedEth1 := &pb.Eth1Data{
|
||||
DepositRootHash32: params.BeaconConfig().ZeroHash[:],
|
||||
BlockHash32: params.BeaconConfig().ZeroHash[:],
|
||||
DepositRoot: params.BeaconConfig().ZeroHash[:],
|
||||
BlockHash: params.BeaconConfig().ZeroHash[:],
|
||||
}
|
||||
if !proto.Equal(b1.Eth1Data, expectedEth1) {
|
||||
if !proto.Equal(b1.Body.Eth1Data, expectedEth1) {
|
||||
t.Error("genesis block Eth1Data isn't initialized correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockRootAtSlot_AccurateBlockRoot(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("slotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
var blockRoots [][]byte
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().LatestBlockRootsLength; i++ {
|
||||
blockRoots = append(blockRoots, []byte{byte(i)})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
LatestBlockRootHash32S: blockRoots,
|
||||
func TestHeaderFromBlock(t *testing.T) {
|
||||
dummyBody := &pb.BeaconBlockBody{
|
||||
RandaoReveal: []byte("Reveal"),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
slot uint64
|
||||
stateSlot uint64
|
||||
expectedRoot []byte
|
||||
}{
|
||||
{
|
||||
slot: 0,
|
||||
stateSlot: 1,
|
||||
expectedRoot: []byte{0},
|
||||
},
|
||||
{
|
||||
slot: 2,
|
||||
stateSlot: 5,
|
||||
expectedRoot: []byte{2},
|
||||
},
|
||||
{
|
||||
slot: 64,
|
||||
stateSlot: 128,
|
||||
expectedRoot: []byte{64},
|
||||
}, {
|
||||
slot: 2999,
|
||||
stateSlot: 3000,
|
||||
expectedRoot: []byte{183},
|
||||
}, {
|
||||
slot: 2873,
|
||||
stateSlot: 3000,
|
||||
expectedRoot: []byte{57},
|
||||
},
|
||||
dummyBlock := &pb.BeaconBlock{
|
||||
Slot: 10,
|
||||
Signature: []byte{'S'},
|
||||
ParentRoot: []byte("Parent"),
|
||||
StateRoot: []byte("State"),
|
||||
Body: dummyBody,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot + params.BeaconConfig().GenesisSlot
|
||||
wantedSlot := tt.slot + params.BeaconConfig().GenesisSlot
|
||||
result, err := BlockRoot(state, wantedSlot)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get block root at slot %d: %v", wantedSlot, err)
|
||||
}
|
||||
if !bytes.Equal(result, tt.expectedRoot) {
|
||||
t.Errorf(
|
||||
"result block root was an unexpected value. Wanted %d, got %d",
|
||||
tt.expectedRoot,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
header, err := HeaderFromBlock(dummyBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedHeader := &pb.BeaconBlockHeader{
|
||||
Slot: dummyBlock.Slot,
|
||||
Signature: dummyBlock.Signature,
|
||||
ParentRoot: dummyBlock.ParentRoot,
|
||||
StateRoot: dummyBlock.StateRoot,
|
||||
}
|
||||
|
||||
bodyRoot, err := ssz.HashTreeRoot(dummyBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedHeader.BodyRoot = bodyRoot[:]
|
||||
|
||||
if !proto.Equal(expectedHeader, header) {
|
||||
t.Errorf("Expected Header not Equal to Retrieved Header. Expected %v , Got %v",
|
||||
proto.MarshalTextString(expectedHeader), proto.MarshalTextString(header))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockRootAtSlot_OutOfBounds(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("slotsPerEpoch should be 64 for these tests to pass")
|
||||
func TestBlockFromHeader(t *testing.T) {
|
||||
dummyHeader := &pb.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
Signature: []byte{'S'},
|
||||
ParentRoot: []byte("Parent"),
|
||||
StateRoot: []byte("State"),
|
||||
}
|
||||
|
||||
var blockRoots [][]byte
|
||||
block := BlockFromHeader(dummyHeader)
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().LatestBlockRootsLength; i++ {
|
||||
blockRoots = append(blockRoots, []byte{byte(i)})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
LatestBlockRootHash32S: blockRoots,
|
||||
expectedBlock := &pb.BeaconBlock{
|
||||
Slot: dummyHeader.Slot,
|
||||
Signature: dummyHeader.Signature,
|
||||
ParentRoot: dummyHeader.ParentRoot,
|
||||
StateRoot: dummyHeader.StateRoot,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
slot uint64
|
||||
stateSlot uint64
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
slot: params.BeaconConfig().GenesisSlot + 1000,
|
||||
stateSlot: params.BeaconConfig().GenesisSlot + 500,
|
||||
expectedErr: fmt.Sprintf("slot %d is not within expected range of %d to %d",
|
||||
1000,
|
||||
0,
|
||||
500),
|
||||
},
|
||||
{
|
||||
slot: params.BeaconConfig().GenesisSlot + 129,
|
||||
stateSlot: params.BeaconConfig().GenesisSlot + 400,
|
||||
expectedErr: "slot 129 is not within expected range of 272 to 399",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot
|
||||
_, err := BlockRoot(state, tt.slot)
|
||||
if err != nil && err.Error() != tt.expectedErr {
|
||||
t.Errorf("Expected error \"%s\" got \"%v\"", tt.expectedErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlockRoots_AccurateMerkleTree(t *testing.T) {
|
||||
state := &pb.BeaconState{}
|
||||
|
||||
state.LatestBlockRootHash32S = make([][]byte, params.BeaconConfig().LatestBlockRootsLength)
|
||||
state.Slot = params.BeaconConfig().LatestBlockRootsLength + 1
|
||||
|
||||
testRoot := [32]byte{'a'}
|
||||
|
||||
newState := ProcessBlockRoots(state, testRoot)
|
||||
if !bytes.Equal(newState.LatestBlockRootHash32S[0], testRoot[:]) {
|
||||
t.Fatalf("Latest Block root hash not saved."+
|
||||
" Supposed to get %#x , but got %#x", testRoot, newState.LatestBlockRootHash32S[0])
|
||||
}
|
||||
|
||||
newState.Slot = newState.Slot - 1
|
||||
|
||||
newState = ProcessBlockRoots(newState, testRoot)
|
||||
expectedHashes := make([][]byte, params.BeaconConfig().LatestBlockRootsLength)
|
||||
expectedHashes[0] = testRoot[:]
|
||||
expectedHashes[params.BeaconConfig().LatestBlockRootsLength-1] = testRoot[:]
|
||||
|
||||
expectedRoot := hashutil.MerkleRoot(expectedHashes)
|
||||
|
||||
if !bytes.Equal(newState.BatchedBlockRootHash32S[0], expectedRoot[:]) {
|
||||
t.Errorf("saved merkle root is not equal to expected merkle root"+
|
||||
"\n expected %#x but got %#x", expectedRoot, newState.BatchedBlockRootHash32S[0])
|
||||
if !proto.Equal(expectedBlock, block) {
|
||||
t.Errorf("Expected block not equal to retrieved block. Expected %v , Got %v",
|
||||
proto.MarshalTextString(expectedBlock), proto.MarshalTextString(block))
|
||||
}
|
||||
}
|
||||
|
||||
48
beacon-chain/core/blocks/spectest/BUILD.bazel
Normal file
48
beacon-chain/core/blocks/spectest/BUILD.bazel
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"attestation_test.yaml.go",
|
||||
"block_operations.yaml.go",
|
||||
"blocks_mainnet.yaml.go",
|
||||
"blocks_minimal.yaml.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks/spectest",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = ["//proto/beacon/p2p/v1:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"attestation_test.go",
|
||||
"attester_slashing_test.go",
|
||||
"block_header_test.go",
|
||||
"block_processing_test.go",
|
||||
"deposit_test.go",
|
||||
"proposer_slashing_test.go",
|
||||
"transfer_test.go",
|
||||
"voluntary_exit_test.go",
|
||||
],
|
||||
data = glob(["*.yaml"]) + [
|
||||
"@eth2_spec_tests//:test_data",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
shard_count = 4,
|
||||
tags = ["spectest"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/core/state/stateutils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
],
|
||||
)
|
||||
82
beacon-chain/core/blocks/spectest/attestation_test.go
Normal file
82
beacon-chain/core/blocks/spectest/attestation_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runAttestationTest(t *testing.T, filename string) {
|
||||
filepath, err := bazel.Runfile("/eth2_spec_tests/tests/operations/attestation/" + filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file: %v", err)
|
||||
}
|
||||
|
||||
test := &AttestationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
body := &pb.BeaconBlockBody{
|
||||
Attestations: []*pb.Attestation{
|
||||
tt.Attestation,
|
||||
},
|
||||
}
|
||||
|
||||
post, err := blocks.ProcessAttestations(tt.Pre, body, true /*verify sig*/)
|
||||
if !reflect.ValueOf(tt.Post).IsValid() {
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if err == nil {
|
||||
t.Fatal("did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
t.Logf("Expected failure; failure reason = %v", err)
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(post, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(post, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationMinimal(t *testing.T) {
|
||||
runAttestationTest(t, "attestation_minimal.yaml")
|
||||
}
|
||||
|
||||
func TestAttestationMainnet(t *testing.T) {
|
||||
runAttestationTest(t, "attestation_mainnet.yaml")
|
||||
}
|
||||
23
beacon-chain/core/blocks/spectest/attestation_test.yaml.go
Normal file
23
beacon-chain/core/blocks/spectest/attestation_test.yaml.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Code generated by yaml_to_go. DO NOT EDIT.
|
||||
// source: attestation_minimal.yaml
|
||||
|
||||
package spectest
|
||||
|
||||
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
|
||||
type AttestationTest struct {
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ForksTimeline string `json:"forks_timeline"`
|
||||
Forks []string `json:"forks"`
|
||||
Config string `json:"config"`
|
||||
Runner string `json:"runner"`
|
||||
Handler string `json:"handler"`
|
||||
TestCases []struct {
|
||||
Description string `json:"description"`
|
||||
Pre *pb.BeaconState `json:"pre"`
|
||||
Attestation *pb.Attestation `json:"attestation"`
|
||||
Post *pb.BeaconState `json:"post"`
|
||||
BlsSetting uint64 `json:"bls_setting,omitempty"`
|
||||
} `json:"test_cases"`
|
||||
}
|
||||
76
beacon-chain/core/blocks/spectest/attester_slashing_test.go
Normal file
76
beacon-chain/core/blocks/spectest/attester_slashing_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runAttesterSlashingTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
body := &pb.BeaconBlockBody{AttesterSlashings: []*pb.AttesterSlashing{tt.AttesterSlashing}}
|
||||
|
||||
postState, err := blocks.ProcessAttesterSlashings(tt.Pre, body, true)
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(postState, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var attesterSlashingPrefix = "eth2_spec_tests/tests/operations/attester_slashing/"
|
||||
|
||||
func TestAttesterSlashingMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(attesterSlashingPrefix + "attester_slashing_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runAttesterSlashingTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestAttesterSlashingMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(attesterSlashingPrefix + "attester_slashing_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runAttesterSlashingTest(t, filepath)
|
||||
}
|
||||
78
beacon-chain/core/blocks/spectest/block_header_test.go
Normal file
78
beacon-chain/core/blocks/spectest/block_header_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
// Block header test is actually a full block processing test. Not sure why it
|
||||
// was named "block_header". The note in the test format readme says "Note that
|
||||
// block_header is not strictly an operation (and is a full Block), but
|
||||
// processed in the same manner, and hence included here."
|
||||
func runBlockHeaderTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file: %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
post, err := blocks.ProcessBlockHeader(tt.Pre, tt.Block, true)
|
||||
|
||||
if tt.Post == nil {
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if err == nil {
|
||||
t.Fatal("did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(post, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(post, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var blkHeaderPrefix = "eth2_spec_tests/tests/operations/block_header/"
|
||||
|
||||
func TestBlockHeaderMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(blkHeaderPrefix + "block_header_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runBlockHeaderTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestBlockHeaderMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(blkHeaderPrefix + "block_header_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runBlockHeaderTest(t, filepath)
|
||||
}
|
||||
28
beacon-chain/core/blocks/spectest/block_operations.yaml.go
Normal file
28
beacon-chain/core/blocks/spectest/block_operations.yaml.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by yaml_to_go. DO NOT EDIT.
|
||||
// source: voluntary_exit_minimal.yaml
|
||||
|
||||
package spectest
|
||||
|
||||
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
|
||||
type BlockOperationTest struct {
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ForksTimeline string `json:"forks_timeline"`
|
||||
Forks []string `json:"forks"`
|
||||
Config string `json:"config"`
|
||||
Runner string `json:"runner"`
|
||||
Handler string `json:"handler"`
|
||||
TestCases []struct {
|
||||
BlsSetting uint64 `json:"bls_setting,omitempty"`
|
||||
Description string `json:"description"`
|
||||
Pre *pb.BeaconState `json:"pre"`
|
||||
VoluntaryExit *pb.VoluntaryExit `json:"voluntary_exit"`
|
||||
ProposerSlashing *pb.ProposerSlashing `json:"proposer_slashing"`
|
||||
AttesterSlashing *pb.AttesterSlashing `json:"attester_slashing"`
|
||||
Deposit *pb.Deposit `json:"deposit"`
|
||||
Transfer *pb.Transfer `json:"transfer"`
|
||||
Block *pb.BeaconBlock `json:"block"`
|
||||
Post *pb.BeaconState `json:"post"`
|
||||
} `json:"test_cases"`
|
||||
}
|
||||
80
beacon-chain/core/blocks/spectest/block_processing_test.go
Normal file
80
beacon-chain/core/blocks/spectest/block_processing_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func TestBlockProcessingMinimalYaml(t *testing.T) {
|
||||
t.Skip("Test will fail with mainnet protos")
|
||||
|
||||
runBlockProcessingTest(t, "sanity_blocks_minimal.yaml")
|
||||
}
|
||||
|
||||
func TestBlockProcessingMainnetYaml(t *testing.T) {
|
||||
runBlockProcessingTest(t, "sanity_blocks_mainnet.yaml")
|
||||
}
|
||||
|
||||
func runBlockProcessingTest(t *testing.T, filename string) {
|
||||
filepath, err := bazel.Runfile("/eth2_spec_tests/tests/sanity/blocks/" + filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &BlocksMainnet{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatalf("Could not set config: %v", err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
helpers.ClearAllCaches()
|
||||
blocks.ClearEth1DataVoteCache()
|
||||
|
||||
stateConfig := &state.TransitionConfig{
|
||||
VerifySignatures: true,
|
||||
VerifyStateRoot: true,
|
||||
}
|
||||
|
||||
s := tt.Pre
|
||||
for _, b := range tt.Blocks {
|
||||
tt.Pre, err = state.ExecuteStateTransition(ctx, tt.Pre, b, stateConfig)
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Transition did not fail despite being invalid")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Transition failed with block at slot %d: %v", b.Slot, err)
|
||||
}
|
||||
}
|
||||
if tt.Post != nil {
|
||||
if !proto.Equal(s, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(s, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
22
beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go
Normal file
22
beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Code generated by yaml_to_go. DO NOT EDIT.
|
||||
// source: sanity_blocks_mainnet.yaml
|
||||
|
||||
package spectest
|
||||
|
||||
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
|
||||
type BlocksMainnet struct {
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ForksTimeline string `json:"forks_timeline"`
|
||||
Forks []string `json:"forks"`
|
||||
Config string `json:"config"`
|
||||
Runner string `json:"runner"`
|
||||
Handler string `json:"handler"`
|
||||
TestCases []struct {
|
||||
Description string `json:"description"`
|
||||
Pre *pb.BeaconState `json:"pre"`
|
||||
Blocks []*pb.BeaconBlock `json:"blocks"`
|
||||
Post *pb.BeaconState `json:"post"`
|
||||
} `json:"test_cases"`
|
||||
}
|
||||
22
beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go
Normal file
22
beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Code generated by yaml_to_go. DO NOT EDIT.
|
||||
// source: sanity_blocks_minimal.yaml
|
||||
|
||||
package spectest
|
||||
|
||||
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
|
||||
type BlocksMinimal struct {
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ForksTimeline string `json:"forks_timeline"`
|
||||
Forks []string `json:"forks"`
|
||||
Config string `json:"config"`
|
||||
Runner string `json:"runner"`
|
||||
Handler string `json:"handler"`
|
||||
TestCases []struct {
|
||||
Description string `json:"description"`
|
||||
Pre *pb.BeaconState `json:"pre"`
|
||||
Blocks []*pb.BeaconBlock `json:"blocks"`
|
||||
Post *pb.BeaconState `json:"post"`
|
||||
} `json:"test_cases"`
|
||||
}
|
||||
76
beacon-chain/core/blocks/spectest/deposit_test.go
Normal file
76
beacon-chain/core/blocks/spectest/deposit_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
)
|
||||
|
||||
func runDepositTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
helpers.ClearAllCaches()
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
if tt.Description == "invalid_sig_new_deposit" {
|
||||
// TODO(#2857): uncompressed signature format is not supported
|
||||
t.Skip("Uncompressed BLS signature format is not supported")
|
||||
}
|
||||
|
||||
valMap := stateutils.ValidatorIndexMap(tt.Pre)
|
||||
post, err := blocks.ProcessDeposit(tt.Pre, tt.Deposit, valMap, true, true)
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(post, tt.Post) {
|
||||
t.Error("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var depositPrefix = "eth2_spec_tests/tests/operations/deposit/"
|
||||
|
||||
func TestDepositMinimalYaml(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(depositPrefix + "deposit_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runDepositTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestDepositMainnetYaml(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(depositPrefix + "deposit_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runDepositTest(t, filepath)
|
||||
}
|
||||
76
beacon-chain/core/blocks/spectest/proposer_slashing_test.go
Normal file
76
beacon-chain/core/blocks/spectest/proposer_slashing_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runProposerSlashingTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
body := &pb.BeaconBlockBody{ProposerSlashings: []*pb.ProposerSlashing{tt.ProposerSlashing}}
|
||||
|
||||
postState, err := blocks.ProcessProposerSlashings(tt.Pre, body, true)
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(postState, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var proposerSlashingPrefix = "eth2_spec_tests/tests/operations/proposer_slashing/"
|
||||
|
||||
func TestProposerSlashingMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(proposerSlashingPrefix + "proposer_slashing_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runProposerSlashingTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestProposerSlashingMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(proposerSlashingPrefix + "proposer_slashing_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runProposerSlashingTest(t, filepath)
|
||||
}
|
||||
78
beacon-chain/core/blocks/spectest/transfer_test.go
Normal file
78
beacon-chain/core/blocks/spectest/transfer_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runTransferTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
body := &pb.BeaconBlockBody{Transfers: []*pb.Transfer{tt.Transfer}}
|
||||
|
||||
postState, err := blocks.ProcessTransfers(tt.Pre, body, true)
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(postState, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var transferPrefix = "eth2_spec_tests/tests/operations/transfer/"
|
||||
|
||||
func TestTransferMinimal(t *testing.T) {
|
||||
t.Skip("Transfer tests are disabled. See https://github.com/ethereum/eth2.0-specs/pull/1238#issuecomment-507054595")
|
||||
filepath, err := bazel.Runfile(transferPrefix + "transfer_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runTransferTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestTransferMainnet(t *testing.T) {
|
||||
t.Skip("Transfer tests are disabled. See https://github.com/ethereum/eth2.0-specs/pull/1238#issuecomment-507054595")
|
||||
filepath, err := bazel.Runfile(transferPrefix + "transfer_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runTransferTest(t, filepath)
|
||||
}
|
||||
76
beacon-chain/core/blocks/spectest/voluntary_exit_test.go
Normal file
76
beacon-chain/core/blocks/spectest/voluntary_exit_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runVoluntaryExitTest(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
test := &BlockOperationTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(test.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range test.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
body := &pb.BeaconBlockBody{VoluntaryExits: []*pb.VoluntaryExit{tt.VoluntaryExit}}
|
||||
|
||||
postState, err := blocks.ProcessVoluntaryExits(tt.Pre, body, true)
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if tt.Post == nil {
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !proto.Equal(postState, tt.Post) {
|
||||
diff, _ := messagediff.PrettyDiff(postState, tt.Post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var exitPrefix = "eth2_spec_tests/tests/operations/voluntary_exit/"
|
||||
|
||||
func TestVoluntaryExitMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(exitPrefix + "voluntary_exit_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runVoluntaryExitTest(t, filepath)
|
||||
}
|
||||
|
||||
func TestVoluntaryExitMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(exitPrefix + "voluntary_exit_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runVoluntaryExitTest(t, filepath)
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
// Package blocks contains block processing libraries. These libraries
|
||||
// process and verify block specific messages such as PoW receipt root,
|
||||
// RANDAO, validator deposits, exits and slashing proofs.
|
||||
package blocks
|
||||
|
||||
import (
|
||||
@@ -19,12 +16,6 @@ import (
|
||||
var log = logrus.WithField("prefix", "core/blocks")
|
||||
|
||||
// IsValidBlock ensures that the block is compliant with the block processing validity conditions.
|
||||
// Spec:
|
||||
// For a beacon chain block, block, to be processed by a node, the following conditions must be met:
|
||||
// The parent block with root block.parent_root has been processed and accepted.
|
||||
// The node has processed its state up to slot, block.slot - 1.
|
||||
// The Ethereum 1.0 block pointed to by the state.processed_pow_receipt_root has been processed and accepted.
|
||||
// The node's local clock time is greater than or equal to state.genesis_time + block.slot * SECONDS_PER_SLOT.
|
||||
func IsValidBlock(
|
||||
ctx context.Context,
|
||||
state *pb.BeaconState,
|
||||
@@ -35,13 +26,13 @@ func IsValidBlock(
|
||||
|
||||
// Pre-Processing Condition 1:
|
||||
// Check that the parent Block has been processed and saved.
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parentBlock := HasBlock(parentRoot)
|
||||
if !parentBlock {
|
||||
return fmt.Errorf("unprocessed parent block as it is not saved in the db: %#x", parentRoot)
|
||||
}
|
||||
|
||||
h := common.BytesToHash(state.LatestEth1Data.BlockHash32)
|
||||
h := common.BytesToHash(state.Eth1Data.BlockHash)
|
||||
powBlock, err := GetPOWBlock(ctx, h)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve POW chain reference block: %v", err)
|
||||
@@ -51,14 +42,14 @@ func IsValidBlock(
|
||||
// The block pointed to by the state in state.processed_pow_receipt_root has
|
||||
// been processed in the ETH 1.0 chain.
|
||||
if powBlock == nil {
|
||||
return fmt.Errorf("proof-of-Work chain reference in state does not exist: %#x", state.LatestEth1Data.BlockHash32)
|
||||
return fmt.Errorf("proof-of-Work chain reference in state does not exist: %#x", state.Eth1Data.BlockHash)
|
||||
}
|
||||
|
||||
// Pre-Processing Condition 4:
|
||||
// The node's local time is greater than or equal to
|
||||
// state.genesis_time + (block.slot-GENESIS_SLOT)* SECONDS_PER_SLOT.
|
||||
if !IsSlotValid(block.Slot, genesisTime) {
|
||||
return fmt.Errorf("slot of block is too high: %d", block.Slot-params.BeaconConfig().GenesisSlot)
|
||||
return fmt.Errorf("slot of block is too high: %d", block.Slot)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -66,7 +57,7 @@ func IsValidBlock(
|
||||
|
||||
// IsSlotValid compares the slot to the system clock to determine if the block is valid.
|
||||
func IsSlotValid(slot uint64, genesisTime time.Time) bool {
|
||||
secondsPerSlot := time.Duration((slot-params.BeaconConfig().GenesisSlot)*params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
secondsPerSlot := time.Duration((slot)*params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
validTimeThreshold := genesisTime.Add(secondsPerSlot)
|
||||
now := clock.Now()
|
||||
isValid := now.After(validTimeThreshold)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -43,10 +42,10 @@ func TestIsValidBlock_NoParent(t *testing.T) {
|
||||
db := &mockDB{}
|
||||
powClient := &mockPOWClient{}
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
beaconState.Slot = 3
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 4,
|
||||
Slot: 4,
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(0, 0)
|
||||
@@ -67,20 +66,20 @@ func TestIsValidBlock_InvalidSlot(t *testing.T) {
|
||||
db := &mockDB{}
|
||||
powClient := &mockPOWClient{}
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
beaconState.Slot = 3
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 4,
|
||||
Slot: 4,
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(0, 0)
|
||||
|
||||
block.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
block.Slot = 3
|
||||
db.hasBlock = true
|
||||
|
||||
beaconState.LatestEth1Data = &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{2},
|
||||
BlockHash32: []byte{3},
|
||||
beaconState.Eth1Data = &pb.Eth1Data{
|
||||
DepositRoot: []byte{2},
|
||||
BlockHash: []byte{3},
|
||||
}
|
||||
if err := IsValidBlock(ctx, beaconState, block,
|
||||
db.HasBlock, powClient.BlockByHash, genesisTime); err == nil {
|
||||
@@ -96,20 +95,20 @@ func TestIsValidBlock_InvalidPoWReference(t *testing.T) {
|
||||
db := &mockDB{}
|
||||
powClient := &mockPOWClient{}
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
beaconState.Slot = 3
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 4,
|
||||
Slot: 4,
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(0, 0)
|
||||
|
||||
db.hasBlock = true
|
||||
block.Slot = params.BeaconConfig().GenesisSlot + 4
|
||||
block.Slot = 4
|
||||
powClient.blockExists = false
|
||||
beaconState.LatestEth1Data = &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{2},
|
||||
BlockHash32: []byte{3},
|
||||
beaconState.Eth1Data = &pb.Eth1Data{
|
||||
DepositRoot: []byte{2},
|
||||
BlockHash: []byte{3},
|
||||
}
|
||||
|
||||
if err := IsValidBlock(ctx, beaconState, block,
|
||||
@@ -129,15 +128,15 @@ func TestIsValidBlock_InvalidGenesis(t *testing.T) {
|
||||
powClient := &mockPOWClient{}
|
||||
powClient.blockExists = false
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
beaconState.LatestEth1Data = &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{2},
|
||||
BlockHash32: []byte{3},
|
||||
beaconState.Slot = 3
|
||||
beaconState.Eth1Data = &pb.Eth1Data{
|
||||
DepositRoot: []byte{2},
|
||||
BlockHash: []byte{3},
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(0, 0)
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 4,
|
||||
Slot: 4,
|
||||
}
|
||||
|
||||
invalidTime := time.Now().AddDate(1, 2, 3)
|
||||
@@ -159,16 +158,16 @@ func TestIsValidBlock_GoodBlock(t *testing.T) {
|
||||
powClient := &mockPOWClient{}
|
||||
powClient.blockExists = true
|
||||
|
||||
beaconState.Slot = params.BeaconConfig().GenesisSlot + 3
|
||||
beaconState.LatestEth1Data = &pb.Eth1Data{
|
||||
DepositRootHash32: []byte{2},
|
||||
BlockHash32: []byte{3},
|
||||
beaconState.Slot = 3
|
||||
beaconState.Eth1Data = &pb.Eth1Data{
|
||||
DepositRoot: []byte{2},
|
||||
BlockHash: []byte{3},
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(0, 0)
|
||||
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().GenesisSlot + 4,
|
||||
Slot: 4,
|
||||
}
|
||||
|
||||
if err := IsValidBlock(ctx, beaconState, block,
|
||||
|
||||
@@ -2,42 +2,31 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"epoch_operations.go",
|
||||
"epoch_processing.go",
|
||||
],
|
||||
srcs = ["epoch_processing.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"epoch_operations_test.go",
|
||||
"epoch_processing_test.go",
|
||||
],
|
||||
srcs = ["epoch_processing_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
// Package epoch contains epoch processing libraries. These libraries
|
||||
// process new balance for the validators, justify and finalize new
|
||||
// check points, shuffle and reassign validators to different slots and
|
||||
// shards.
|
||||
package epoch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
b "github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
// TotalBalance returns the total balance at stake of the validators
|
||||
// from the shard committee regardless of validators attested or not.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of an array of validators.
|
||||
// """
|
||||
// return sum([get_effective_balance(state, i) for i in validators])
|
||||
func TotalBalance(
|
||||
state *pb.BeaconState,
|
||||
activeValidatorIndices []uint64) uint64 {
|
||||
|
||||
var totalBalance uint64
|
||||
for _, index := range activeValidatorIndices {
|
||||
totalBalance += helpers.EffectiveBalance(state, index)
|
||||
}
|
||||
|
||||
return totalBalance
|
||||
}
|
||||
|
||||
// InclusionSlot returns the slot number of when the validator's
|
||||
// attestation gets included in the beacon chain.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let inclusion_slot(state, index) =
|
||||
// a.slot_included for the attestation a where index is in
|
||||
// get_attestation_participants(state, a.data, a.participation_bitfield)
|
||||
// If multiple attestations are applicable, the attestation with
|
||||
// lowest `slot_included` is considered.
|
||||
func InclusionSlot(state *pb.BeaconState, validatorIndex uint64) (uint64, error) {
|
||||
lowestSlotIncluded := uint64(math.MaxUint64)
|
||||
for _, attestation := range state.LatestAttestations {
|
||||
participatedValidators, err := helpers.AttestationParticipants(state, attestation.Data, attestation.AggregationBitfield)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("could not get attestation participants: %v", err)
|
||||
}
|
||||
for _, index := range participatedValidators {
|
||||
if index == validatorIndex {
|
||||
if attestation.InclusionSlot < lowestSlotIncluded {
|
||||
lowestSlotIncluded = attestation.InclusionSlot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lowestSlotIncluded == math.MaxUint64 {
|
||||
return 0, fmt.Errorf("could not find inclusion slot for validator index %d", validatorIndex)
|
||||
}
|
||||
return lowestSlotIncluded, nil
|
||||
}
|
||||
|
||||
// AttestingValidators returns the validators of the winning root.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let `attesting_validators(crosslink_committee)` be equal to
|
||||
// `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience
|
||||
func AttestingValidators(
|
||||
state *pb.BeaconState,
|
||||
shard uint64,
|
||||
currentEpochAttestations []*pb.PendingAttestation,
|
||||
prevEpochAttestations []*pb.PendingAttestation) ([]uint64, error) {
|
||||
|
||||
root, err := winningRoot(
|
||||
state,
|
||||
shard,
|
||||
currentEpochAttestations,
|
||||
prevEpochAttestations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get winning root: %v", err)
|
||||
}
|
||||
|
||||
indices, err := validators.AttestingValidatorIndices(
|
||||
state,
|
||||
shard,
|
||||
root,
|
||||
currentEpochAttestations,
|
||||
prevEpochAttestations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
|
||||
}
|
||||
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// TotalAttestingBalance returns the total balance at stake of the validators
|
||||
// attested to the winning root.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let total_balance(crosslink_committee) =
|
||||
// sum([get_effective_balance(state, i) for i in crosslink_committee.committee])
|
||||
func TotalAttestingBalance(
|
||||
state *pb.BeaconState,
|
||||
shard uint64,
|
||||
currentEpochAttestations []*pb.PendingAttestation,
|
||||
prevEpochAttestations []*pb.PendingAttestation) (uint64, error) {
|
||||
|
||||
var totalBalance uint64
|
||||
attestedValidatorIndices, err := AttestingValidators(state, shard, currentEpochAttestations, prevEpochAttestations)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("could not get attesting validator indices: %v", err)
|
||||
}
|
||||
|
||||
for _, index := range attestedValidatorIndices {
|
||||
totalBalance += helpers.EffectiveBalance(state, index)
|
||||
}
|
||||
|
||||
return totalBalance, nil
|
||||
}
|
||||
|
||||
// SinceFinality calculates and returns how many epoch has it been since
|
||||
// a finalized slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// epochs_since_finality = next_epoch - state.finalized_epoch
|
||||
func SinceFinality(state *pb.BeaconState) uint64 {
|
||||
return helpers.NextEpoch(state) - state.FinalizedEpoch
|
||||
}
|
||||
|
||||
// winningRoot returns the shard block root with the most combined validator
|
||||
// effective balance. The ties broken by favoring lower shard block root values.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let winning_root(crosslink_committee) be equal to the value of crosslink_data_root
|
||||
// such that get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))
|
||||
// is maximized (ties broken by favoring lexicographically smallest crosslink_data_root).
|
||||
func winningRoot(
|
||||
state *pb.BeaconState,
|
||||
shard uint64,
|
||||
currentEpochAttestations []*pb.PendingAttestation,
|
||||
prevEpochAttestations []*pb.PendingAttestation) ([]byte, error) {
|
||||
|
||||
var winnerBalance uint64
|
||||
var winnerRoot []byte
|
||||
var candidateRoots [][]byte
|
||||
attestations := append(currentEpochAttestations, prevEpochAttestations...)
|
||||
|
||||
for _, attestation := range attestations {
|
||||
if attestation.Data.Shard == shard {
|
||||
candidateRoots = append(candidateRoots, attestation.Data.CrosslinkDataRootHash32)
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidateRoot := range candidateRoots {
|
||||
indices, err := validators.AttestingValidatorIndices(
|
||||
state,
|
||||
shard,
|
||||
candidateRoot,
|
||||
currentEpochAttestations,
|
||||
prevEpochAttestations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
|
||||
}
|
||||
|
||||
var rootBalance uint64
|
||||
for _, index := range indices {
|
||||
rootBalance += helpers.EffectiveBalance(state, index)
|
||||
}
|
||||
|
||||
if rootBalance > winnerBalance ||
|
||||
(rootBalance == winnerBalance && b.LowerThan(candidateRoot, winnerRoot)) {
|
||||
winnerBalance = rootBalance
|
||||
winnerRoot = candidateRoot
|
||||
}
|
||||
}
|
||||
return winnerRoot, nil
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
package epoch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
|
||||
validators := make([]*pb.Validator, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = &pb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
validatorBalances := make([]uint64, len(validators))
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxDepositAmount
|
||||
}
|
||||
return &pb.BeaconState{
|
||||
ValidatorRegistry: validators,
|
||||
ValidatorBalances: validatorBalances,
|
||||
Slot: slot,
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinningRoot_AccurateRoot(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, 100)
|
||||
var participationBitfield []byte
|
||||
participationBitfield = append(participationBitfield, byte(0x80))
|
||||
|
||||
// Generate 10 roots ([]byte{100}...[]byte{110})
|
||||
var attestations []*pb.PendingAttestation
|
||||
for i := 0; i < 10; i++ {
|
||||
attestation := &pb.PendingAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{byte(i + 100)},
|
||||
},
|
||||
AggregationBitfield: participationBitfield,
|
||||
}
|
||||
attestations = append(attestations, attestation)
|
||||
}
|
||||
|
||||
// Since all 10 roots have the balance of 64 ETHs
|
||||
// winningRoot chooses the lowest hash: []byte{100}
|
||||
winnerRoot, err := winningRoot(
|
||||
state,
|
||||
0,
|
||||
attestations,
|
||||
nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not execute winningRoot: %v", err)
|
||||
}
|
||||
if !bytes.Equal(winnerRoot, []byte{100}) {
|
||||
t.Errorf("Incorrect winner root, wanted:[100], got: %v", winnerRoot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinningRoot_EmptyParticipantBitfield(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
|
||||
|
||||
attestations := []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{},
|
||||
},
|
||||
AggregationBitfield: []byte{},
|
||||
},
|
||||
}
|
||||
|
||||
helpers.RestartCommitteeCache()
|
||||
|
||||
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
|
||||
if _, err := winningRoot(state, 0, attestations, nil); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestingValidators_MatchActive(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().SlotsPerEpoch*2)
|
||||
|
||||
var attestations []*pb.PendingAttestation
|
||||
for i := 0; i < 10; i++ {
|
||||
attestation := &pb.PendingAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{byte(i + 100)},
|
||||
},
|
||||
AggregationBitfield: []byte{0xC0},
|
||||
}
|
||||
attestations = append(attestations, attestation)
|
||||
}
|
||||
|
||||
helpers.RestartCommitteeCache()
|
||||
|
||||
attestedValidators, err := AttestingValidators(
|
||||
state,
|
||||
0,
|
||||
attestations,
|
||||
nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not execute AttestingValidators: %v", err)
|
||||
}
|
||||
|
||||
// Verify the winner root is attested by validators based on shuffling.
|
||||
if !reflect.DeepEqual(attestedValidators, []uint64{123, 65}) {
|
||||
t.Errorf("Active validators don't match. Wanted:[123,65], Got: %v", attestedValidators)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestingValidators_EmptyWinningRoot(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
|
||||
|
||||
attestation := &pb.PendingAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{},
|
||||
},
|
||||
AggregationBitfield: []byte{},
|
||||
}
|
||||
|
||||
helpers.RestartCommitteeCache()
|
||||
|
||||
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
|
||||
if _, err := AttestingValidators(state, 0, []*pb.PendingAttestation{attestation}, nil); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalAttestingBalance_CorrectBalance(t *testing.T) {
|
||||
validatorsPerCommittee := uint64(2)
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, 2*params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
// Generate 10 roots ([]byte{100}...[]byte{110})
|
||||
var attestations []*pb.PendingAttestation
|
||||
for i := 0; i < 10; i++ {
|
||||
attestation := &pb.PendingAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{byte(i + 100)},
|
||||
},
|
||||
// All validators attested to the above roots.
|
||||
AggregationBitfield: []byte{0xC0},
|
||||
}
|
||||
attestations = append(attestations, attestation)
|
||||
}
|
||||
|
||||
helpers.RestartCommitteeCache()
|
||||
|
||||
attestedBalance, err := TotalAttestingBalance(
|
||||
state,
|
||||
0,
|
||||
attestations,
|
||||
nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not execute totalAttestingBalance: %v", err)
|
||||
}
|
||||
|
||||
if attestedBalance != params.BeaconConfig().MaxDepositAmount*validatorsPerCommittee {
|
||||
t.Errorf("Incorrect attested balance. Wanted:64*1e9, Got: %d", attestedBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalAttestingBalance_EmptyWinningRoot(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
|
||||
|
||||
attestation := &pb.PendingAttestation{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: params.BeaconConfig().GenesisSlot,
|
||||
CrosslinkDataRootHash32: []byte{},
|
||||
},
|
||||
AggregationBitfield: []byte{},
|
||||
}
|
||||
|
||||
helpers.RestartCommitteeCache()
|
||||
|
||||
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
|
||||
if _, err := TotalAttestingBalance(state, 0, []*pb.PendingAttestation{attestation}, nil); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalBalance_CorrectBalance(t *testing.T) {
|
||||
// Assign validators to different balances.
|
||||
state := &pb.BeaconState{
|
||||
Slot: 5,
|
||||
ValidatorBalances: []uint64{20 * 1e9, 25 * 1e9, 30 * 1e9, 30 * 1e9,
|
||||
32 * 1e9, 34 * 1e9, 50 * 1e9, 50 * 1e9},
|
||||
}
|
||||
|
||||
// 20 + 25 + 30 + 30 + 32 + 32 + 32 + 32 = 233
|
||||
totalBalance := TotalBalance(state, []uint64{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
if totalBalance != 233*1e9 {
|
||||
t.Errorf("Incorrect total balance. Wanted: 233*1e9, got: %d", totalBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusionSlot_GetsCorrectSlot(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
|
||||
var participationBitfield []byte
|
||||
for i := 0; i < 16; i++ {
|
||||
participationBitfield = append(participationBitfield, byte(0xff))
|
||||
}
|
||||
|
||||
state.LatestAttestations = []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: 101},
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: 100},
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: participationBitfield,
|
||||
InclusionSlot: 102},
|
||||
}
|
||||
slot, err := InclusionSlot(state, 251)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not execute InclusionSlot: %v", err)
|
||||
}
|
||||
// validator 45's attestation got included in slot 100.
|
||||
if slot != 100 {
|
||||
t.Errorf("Incorrect slot. Wanted: 100, got: %d", slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusionSlot_InvalidBitfield(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().DepositsForChainStart)
|
||||
|
||||
state.LatestAttestations = []*pb.PendingAttestation{
|
||||
{Data: &pb.AttestationData{Slot: params.BeaconConfig().GenesisSlot},
|
||||
AggregationBitfield: []byte{},
|
||||
InclusionSlot: 100},
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
|
||||
if _, err := InclusionSlot(state, 0); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclusionSlot_SlotNotFound(t *testing.T) {
|
||||
state := buildState(params.BeaconConfig().GenesisSlot, params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
badIndex := uint64(10000)
|
||||
want := fmt.Sprintf("could not find inclusion slot for validator index %d", badIndex)
|
||||
if _, err := InclusionSlot(state, badIndex); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
39
beacon-chain/core/epoch/spectest/BUILD.bazel
Normal file
39
beacon-chain/core/epoch/spectest/BUILD.bazel
Normal file
@@ -0,0 +1,39 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["epoch_processing_test.yaml.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/spectest",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = ["//proto/beacon/p2p/v1:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"crosslink_test.go",
|
||||
"final_updates_test.go",
|
||||
"justification_and_finalization_test.go",
|
||||
"registry_test.go",
|
||||
"slashings_test.go",
|
||||
],
|
||||
data = [
|
||||
"@eth2_spec_tests//:test_data",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = [
|
||||
"spectest",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/params/spectest:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
],
|
||||
)
|
||||
62
beacon-chain/core/epoch/spectest/crosslink_test.go
Normal file
62
beacon-chain/core/epoch/spectest/crosslink_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
)
|
||||
|
||||
func runCrosslinkProcessingTests(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &EpochProcessingTest{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
|
||||
postState, err := epoch.ProcessCrosslinks(tt.Pre)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(postState, tt.Post) {
|
||||
t.Error("Did not get expected state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const crosslinkPrefix = "eth2_spec_tests/tests/epoch_processing/crosslinks/"
|
||||
|
||||
func TestCrosslinksProcessingMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(crosslinkPrefix + "crosslinks_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runCrosslinkProcessingTests(t, filepath)
|
||||
}
|
||||
|
||||
func TestCrosslinksProcessingMainnet(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
filepath, err := bazel.Runfile(crosslinkPrefix + "crosslinks_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runCrosslinkProcessingTests(t, filepath)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Code generated by yaml_to_go. DO NOT EDIT.
|
||||
package spectest
|
||||
|
||||
import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
|
||||
type EpochProcessingTest struct {
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
ForksTimeline string `json:"forks_timeline"`
|
||||
Forks []string `json:"forks"`
|
||||
Config string `json:"config"`
|
||||
Runner string `json:"runner"`
|
||||
Handler string `json:"handler"`
|
||||
TestCases []struct {
|
||||
Description string `json:"description"`
|
||||
Pre *pb.BeaconState `json:"pre"`
|
||||
Post *pb.BeaconState `json:"post"`
|
||||
} `json:"test_cases"`
|
||||
}
|
||||
81
beacon-chain/core/epoch/spectest/final_updates_test.go
Normal file
81
beacon-chain/core/epoch/spectest/final_updates_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func runFinalUpdatesTests(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &EpochProcessingTest{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases[0:1] {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
preState := &pb.BeaconState{}
|
||||
if err := testutil.ConvertToPb(tt.Pre, preState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var postState *pb.BeaconState
|
||||
postState, err = epoch.ProcessFinalUpdates(preState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedPostState := &pb.BeaconState{}
|
||||
if err := testutil.ConvertToPb(tt.Post, expectedPostState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if expectedPostState.CurrentEpochAttestations == nil {
|
||||
expectedPostState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(postState, expectedPostState) {
|
||||
t.Error("Did not get expected state")
|
||||
diff, _ := messagediff.PrettyDiff(expectedPostState, postState)
|
||||
t.Log(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const finalUpdatesPrefix = "eth2_spec_tests/tests/epoch_processing/final_updates/"
|
||||
|
||||
func TestFinalUpdatesMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(finalUpdatesPrefix + "final_updates_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runFinalUpdatesTests(t, filepath)
|
||||
}
|
||||
|
||||
func TestFinalUpdatesMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(finalUpdatesPrefix + "final_updates_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runFinalUpdatesTests(t, filepath)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
// This is a subset of state.ProcessEpoch. The spec test defines input data for
|
||||
// `justification_and_finalization` only.
|
||||
func processJustificationAndFinalizationWrapper(state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
// This process mutates the state, so we'll make a copy in order to print debug before/after.
|
||||
state = proto.Clone(state).(*pb.BeaconState)
|
||||
|
||||
prevEpochAtts, err := epoch.MatchAttestations(state, helpers.PrevEpoch(state))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get target atts prev epoch %d: %v",
|
||||
helpers.PrevEpoch(state), err)
|
||||
}
|
||||
currentEpochAtts, err := epoch.MatchAttestations(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get target atts current epoch %d: %v",
|
||||
helpers.CurrentEpoch(state), err)
|
||||
}
|
||||
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts.Target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get attesting balance prev epoch: %v", err)
|
||||
}
|
||||
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts.Target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get attesting balance current epoch: %v", err)
|
||||
}
|
||||
|
||||
state, err = epoch.ProcessJustificationAndFinalization(state, prevEpochAttestedBalance, currentEpochAttestedBalance)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not process justification: %v", err)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func runJustificationAndFinalizationTests(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &EpochProcessingTest{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
preState := &pb.BeaconState{}
|
||||
if err := testutil.ConvertToPb(tt.Pre, preState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
postState, err := processJustificationAndFinalizationWrapper(preState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedPostState := &pb.BeaconState{}
|
||||
if err := testutil.ConvertToPb(tt.Post, expectedPostState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if postState.JustificationBits[0] != expectedPostState.JustificationBits[0] {
|
||||
t.Errorf("Justification bits mismatch. PreState.JustificationBits=%v. PostState.JustificationBits=%v. Expected=%v", preState.JustificationBits, postState.JustificationBits, expectedPostState.JustificationBits)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(postState, expectedPostState) {
|
||||
diff, _ := messagediff.PrettyDiff(postState, expectedPostState)
|
||||
t.Log(diff)
|
||||
t.Error("Did not get expected state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const justificationAndFinalizationPrefix = "eth2_spec_tests/tests/epoch_processing/justification_and_finalization/"
|
||||
|
||||
func TestJustificationAndFinalizationMinimal(t *testing.T) {
|
||||
// TODO(#2891): Verify with ETH2 spec test.
|
||||
t.Skip("The input data fails preconditions for matching attestations in the state for the current epoch.")
|
||||
filepath, err := bazel.Runfile(justificationAndFinalizationPrefix + "justification_and_finalization_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runJustificationAndFinalizationTests(t, filepath)
|
||||
}
|
||||
|
||||
func TestJustificationAndFinalizationMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(justificationAndFinalizationPrefix + "justification_and_finalization_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runJustificationAndFinalizationTests(t, filepath)
|
||||
}
|
||||
59
beacon-chain/core/epoch/spectest/registry_test.go
Normal file
59
beacon-chain/core/epoch/spectest/registry_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
)
|
||||
|
||||
func runRegisteryProcessingTests(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &EpochProcessingTest{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
postState, err := epoch.ProcessRegistryUpdates(tt.Pre)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(postState, tt.Post) {
|
||||
t.Error("Did not get expected state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const registryUpdatesPrefix = "eth2_spec_tests/tests/epoch_processing/registry_updates/"
|
||||
|
||||
func TestRegistryProcessingMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(registryUpdatesPrefix + "registry_updates_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runRegisteryProcessingTests(t, filepath)
|
||||
}
|
||||
|
||||
func TestRegistryProcessingMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(registryUpdatesPrefix + "registry_updates_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runRegisteryProcessingTests(t, filepath)
|
||||
}
|
||||
61
beacon-chain/core/epoch/spectest/slashings_test.go
Normal file
61
beacon-chain/core/epoch/spectest/slashings_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
)
|
||||
|
||||
func runSlashingsTests(t *testing.T, filename string) {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not load file %v", err)
|
||||
}
|
||||
|
||||
s := &EpochProcessingTest{}
|
||||
if err := yaml.Unmarshal(file, s); err != nil {
|
||||
t.Fatalf("Failed to Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if err := spectest.SetConfig(s.Config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range s.TestCases {
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
postState, err := epoch.ProcessSlashings(tt.Pre)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(postState, tt.Post) {
|
||||
t.Error("Did not get expected state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const slashingsPrefix = "eth2_spec_tests/tests/epoch_processing/slashings/"
|
||||
|
||||
func TestSlashingsMinimal(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(slashingsPrefix + "slashings_minimal.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runSlashingsTests(t, filepath)
|
||||
}
|
||||
|
||||
func TestSlashingsMainnet(t *testing.T) {
|
||||
filepath, err := bazel.Runfile(slashingsPrefix + "slashings_mainnet.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runSlashingsTests(t, filepath)
|
||||
}
|
||||
@@ -3,8 +3,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attestation.go",
|
||||
"block.go",
|
||||
"cache.go",
|
||||
"committee.go",
|
||||
"deposits.go",
|
||||
"eth1data.go",
|
||||
"randao.go",
|
||||
"rewards_penalties.go",
|
||||
"slot_epoch.go",
|
||||
@@ -16,11 +19,11 @@ go_library(
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/utils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bitutil:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
@@ -31,8 +34,10 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"attestation_test.go",
|
||||
"block_test.go",
|
||||
"committee_test.go",
|
||||
"deposits_test.go",
|
||||
"eth1data_test.go",
|
||||
"randao_test.go",
|
||||
"rewards_penalties_test.go",
|
||||
"slot_epoch_test.go",
|
||||
@@ -40,11 +45,13 @@ go_test(
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//beacon-chain/utils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
|
||||
51
beacon-chain/core/helpers/attestation.go
Normal file
51
beacon-chain/core/helpers/attestation.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAttestationDataSlotNilState is returned when a nil state argument
|
||||
// is provided to AttestationDataSlot.
|
||||
ErrAttestationDataSlotNilState = errors.New("nil state provided for AttestationDataSlot")
|
||||
// ErrAttestationDataSlotNilData is returned when a nil attestation data
|
||||
// argument is provided to AttestationDataSlot.
|
||||
ErrAttestationDataSlotNilData = errors.New("nil data provided for AttestationDataSlot")
|
||||
)
|
||||
|
||||
// AttestationDataSlot returns current slot of AttestationData for given state
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
|
||||
// """
|
||||
// Return the slot corresponding to the attestation ``data``.
|
||||
// """
|
||||
// committee_count = get_committee_count(state, data.target.epoch)
|
||||
// offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT
|
||||
// return Slot(compute_start_slot_of_epoch(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
|
||||
func AttestationDataSlot(state *pb.BeaconState, data *pb.AttestationData) (uint64, error) {
|
||||
if state == nil {
|
||||
return 0, ErrAttestationDataSlotNilState
|
||||
}
|
||||
if data == nil {
|
||||
return 0, ErrAttestationDataSlotNilData
|
||||
}
|
||||
|
||||
committeeCount, err := CommitteeCount(state, data.Target.Epoch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
epochStartShardNumber, err := StartShard(state, data.Target.Epoch)
|
||||
if err != nil { // This should never happen if CommitteeCount was successful
|
||||
return 0, fmt.Errorf("could not determine epoch start shard: %v", err)
|
||||
}
|
||||
offset := (data.Crosslink.Shard + params.BeaconConfig().ShardCount -
|
||||
epochStartShardNumber) % params.BeaconConfig().ShardCount
|
||||
|
||||
return StartSlot(data.Target.Epoch) + (offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)), nil
|
||||
}
|
||||
101
beacon-chain/core/helpers/attestation_test.go
Normal file
101
beacon-chain/core/helpers/attestation_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestAttestationDataSlot_OK(t *testing.T) {
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
offset := uint64(0)
|
||||
committeeCount, _ := helpers.CommitteeCount(beaconState, 0)
|
||||
expect := offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, &pb.AttestationData{
|
||||
Target: &pb.Checkpoint{Epoch: 0},
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if attSlot != expect {
|
||||
t.Errorf("Expected %d, received %d", expect, attSlot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithNilState(t *testing.T) {
|
||||
s, err := helpers.AttestationDataSlot(nil /*state*/, &pb.AttestationData{
|
||||
Target: &pb.Checkpoint{Epoch: 0},
|
||||
Crosslink: &pb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
})
|
||||
if err != helpers.ErrAttestationDataSlotNilState {
|
||||
t.Errorf("Expected an error, but received %v", err)
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithNilData(t *testing.T) {
|
||||
s, err := helpers.AttestationDataSlot(&pb.BeaconState{}, nil /*data*/)
|
||||
if err != helpers.ErrAttestationDataSlotNilData {
|
||||
t.Errorf("Expected an error, but received %v", err)
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithErroneousTargetEpoch(t *testing.T) {
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := helpers.AttestationDataSlot(beaconState, &pb.AttestationData{
|
||||
Target: &pb.Checkpoint{Epoch: 1<<63 - 1 /* Far future epoch */},
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected an error, but received nil")
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWhenTargetEpochLessThanCurrentEpoch(t *testing.T) {
|
||||
db := internal.SetupDB(t)
|
||||
defer internal.TeardownDB(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100, false)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, nil); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := helpers.AttestationDataSlot(beaconState, &pb.AttestationData{
|
||||
Target: &pb.Checkpoint{Epoch: 2},
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected an error, but received nil")
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
37
beacon-chain/core/helpers/block.go
Normal file
37
beacon-chain/core/helpers/block.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// BlockRootAtSlot returns the block root stored in the BeaconState for a recent slot.
|
||||
// It returns an error if the requested block root is not within the slot range.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Hash:
|
||||
// """
|
||||
// Return the block root at a recent ``slot``.
|
||||
// """
|
||||
// assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||
// return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||
func BlockRootAtSlot(state *pb.BeaconState, slot uint64) ([]byte, error) {
|
||||
if !(slot < state.Slot && state.Slot <= slot+params.BeaconConfig().HistoricalRootsLimit) {
|
||||
return []byte{}, errors.New("slot out of bounds")
|
||||
}
|
||||
return state.BlockRoots[slot%params.BeaconConfig().HistoricalRootsLimit], nil
|
||||
}
|
||||
|
||||
// BlockRoot returns the block root stored in the BeaconState for epoch start slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_block_root(state: BeaconState, epoch: Epoch) -> Hash:
|
||||
// """
|
||||
// Return the block root at the start of a recent ``epoch``.
|
||||
// """
|
||||
// return get_block_root_at_slot(state, compute_start_slot_of_epoch(epoch))
|
||||
func BlockRoot(state *pb.BeaconState, epoch uint64) ([]byte, error) {
|
||||
return BlockRootAtSlot(state, StartSlot(epoch))
|
||||
}
|
||||
116
beacon-chain/core/helpers/block_test.go
Normal file
116
beacon-chain/core/helpers/block_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestBlockRootAtSlot_CorrectBlockRoot(t *testing.T) {
|
||||
var blockRoots [][]byte
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().HistoricalRootsLimit; i++ {
|
||||
blockRoots = append(blockRoots, []byte{byte(i)})
|
||||
}
|
||||
s := &pb.BeaconState{
|
||||
BlockRoots: blockRoots,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
slot uint64
|
||||
stateSlot uint64
|
||||
expectedRoot []byte
|
||||
}{
|
||||
{
|
||||
slot: 0,
|
||||
stateSlot: 1,
|
||||
expectedRoot: []byte{0},
|
||||
},
|
||||
{
|
||||
slot: 2,
|
||||
stateSlot: 5,
|
||||
expectedRoot: []byte{2},
|
||||
},
|
||||
{
|
||||
slot: 64,
|
||||
stateSlot: 128,
|
||||
expectedRoot: []byte{64},
|
||||
}, {
|
||||
slot: 2999,
|
||||
stateSlot: 3000,
|
||||
expectedRoot: []byte{183},
|
||||
}, {
|
||||
slot: 2873,
|
||||
stateSlot: 3000,
|
||||
expectedRoot: []byte{57},
|
||||
},
|
||||
{
|
||||
slot: 0,
|
||||
stateSlot: params.BeaconConfig().HistoricalRootsLimit,
|
||||
expectedRoot: []byte{0},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s.Slot = tt.stateSlot
|
||||
wantedSlot := tt.slot
|
||||
result, err := BlockRootAtSlot(s, wantedSlot)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get block root at slot %d: %v",
|
||||
wantedSlot, err)
|
||||
}
|
||||
if !bytes.Equal(result, tt.expectedRoot) {
|
||||
t.Errorf(
|
||||
"result block root was an unexpected value, wanted %v, got %v",
|
||||
tt.expectedRoot,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockRootAtSlot_OutOfBounds(t *testing.T) {
|
||||
var blockRoots [][]byte
|
||||
|
||||
for i := uint64(0); i < params.BeaconConfig().HistoricalRootsLimit; i++ {
|
||||
blockRoots = append(blockRoots, []byte{byte(i)})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
BlockRoots: blockRoots,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
slot uint64
|
||||
stateSlot uint64
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
slot: 1000,
|
||||
stateSlot: 500,
|
||||
expectedErr: "slot out of bounds",
|
||||
},
|
||||
{
|
||||
slot: 3000,
|
||||
stateSlot: 3000,
|
||||
expectedErr: "slot out of bounds",
|
||||
},
|
||||
{
|
||||
// Edge case where stateSlot is over slots per historical root and
|
||||
// slot is not within (stateSlot - HistoricalRootsLimit, statSlot]
|
||||
slot: 1,
|
||||
stateSlot: params.BeaconConfig().HistoricalRootsLimit + 2,
|
||||
expectedErr: "slot out of bounds",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot
|
||||
_, err := BlockRootAtSlot(state, tt.slot)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error %s, got nil", tt.expectedErr)
|
||||
}
|
||||
if err != nil && err.Error() != tt.expectedErr {
|
||||
t.Errorf("Expected error \"%s\" got \"%v\"", tt.expectedErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
beacon-chain/core/helpers/cache.go
Normal file
50
beacon-chain/core/helpers/cache.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
)
|
||||
|
||||
// ClearShuffledValidatorCache clears the shuffled indices cache from scratch.
|
||||
func ClearShuffledValidatorCache() {
|
||||
shuffledIndicesCache = cache.NewShuffledIndicesCache()
|
||||
}
|
||||
|
||||
// ClearStartShardCache clears the start shard cache from scratch.
|
||||
func ClearStartShardCache() {
|
||||
startShardCache = cache.NewStartShardCache()
|
||||
}
|
||||
|
||||
// ClearTotalActiveBalanceCache restarts the total active validator balance cache from scratch.
|
||||
func ClearTotalActiveBalanceCache() {
|
||||
totalActiveBalanceCache = cache.NewActiveBalanceCache()
|
||||
}
|
||||
|
||||
// ClearCurrentEpochSeed clears the current epoch seed.
|
||||
func ClearCurrentEpochSeed() {
|
||||
currentEpochSeed = cache.NewSeedCache()
|
||||
}
|
||||
|
||||
// ClearActiveCountCache restarts the active validator count cache from scratch.
|
||||
func ClearActiveCountCache() {
|
||||
activeCountCache = cache.NewActiveCountCache()
|
||||
}
|
||||
|
||||
// ClearActiveIndicesCache restarts the active validator indices cache from scratch.
|
||||
func ClearActiveIndicesCache() {
|
||||
activeIndicesCache = cache.NewActiveIndicesCache()
|
||||
}
|
||||
|
||||
// ActiveIndicesKeys returns the keys of the active indices cache.
|
||||
func ActiveIndicesKeys() []string {
|
||||
return activeIndicesCache.ActiveIndicesKeys()
|
||||
}
|
||||
|
||||
// ClearAllCaches clears all the helpers caches from scratch.
|
||||
func ClearAllCaches() {
|
||||
ClearActiveIndicesCache()
|
||||
ClearActiveCountCache()
|
||||
ClearStartShardCache()
|
||||
ClearShuffledValidatorCache()
|
||||
ClearTotalActiveBalanceCache()
|
||||
ClearCurrentEpochSeed()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user